Discussion:
Open webpage using POST via Navigate2 needs SAFEARRAY
(too old to reply)
Jim Moseley
2009-04-21 18:51:41 UTC
Permalink
Here's another stumper. I'm trying to use Tony's MSBrowser form, to display
one of my web pages (for Google Maps) within Pdox. I can get it to open
the page with a GET, but I want to instead use POST to set the desired address,
etc.

The Navigate2 docs say that to issue a POST, postData must be a SAFEARRAY,
and the headerStr must be what I've set. Otherwise, it will issue a GET.

How does one go about constructing a SAFEARRAY in Opal?

BTW, here's my code. No error is shown. It just shows the page without
setting 'address1=jax', and WireShark shows a GET being submitted.

;--------------------
ok = ui.attach(f.myBrowser)
if ok then
ok = oa.attach(ui)
endif
if ok then
url = "http://myurl.com/maps/showMap.html"
flags1 = blank()
targetFrameName = blank()
postData = "address1=jax"
maxI = postData.size()
headerStr = "Content-Type: application/x-www-form-urlencoded" + chr(13)
+ chr(10)
try
oa.Navigate2(url, flags1, targetFrameName, postData, headerStr)
onFail
errorShow()
return
endtry
endif
return
;------------------

TIA,
Jim Moseley
Jim Moseley
2009-04-22 14:45:55 UTC
Permalink
BTW, here's more info on a SAFEARRAY. Now if I only knew how to use this
in OPal.

Thanks, Jim Moseley

-----------------
from http://www.roblocher.com/whitepapers/oletypes.aspx :

So, by now you probably are wondering what a safearray really is. Here is
some code from oaidl.h:

typedef struct tagSAFEARRAYBOUND
{
ULONG cElements;
LONG lLbound;
} SAFEARRAYBOUND;

typedef struct tagSAFEARRAY
{
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[ 1 ];
} SAFEARRAY;



SAFEARRAYs
If you understand bstrs and variants, then you shouldn't have much trouble
with safearrays. SAFEARRAY was created to suit the needs of Visual Basic
and other weakly-typed languages for a type-safe array of one or more dimensions
of arbitrary bounds. Note that it is not legal to pass a safearray by itself
via an IDispatch interface; for automation purposes, a safearray is only
legal if it is wrapped by a variant. (The ATL wizard won't let you do it.)
If you are wrapping a safearray with a variant, then the member vt of the
variant should be VT_ARRAY bitwise-or'ed ("|") with (something), where (something)
corresponds to the type of the elements of the array. A safearray is responsible
for its contents; properly deallocating a safearray will result in its contents
being safely deallocated. Since a safearray should always be wrapped by
a variant, and a variant is responsible for its contents as long as the variant
doesn't wrap a pointer, then properly deallocating the variant holding the
safearray will properly take care of the variant, the safearray, and the
contents of the safearray.

A safearray can only hold one type at a time, as you might guess; however,
a safearray can hold variants, so that rule really isn't much of a restriction.
In fact, if you wanted to, you could have a multi-dimensional safearray
holding all different kinds of variants, some of which could themselves be
safearrays, which in turn could hold other things... but I recommend not
making things that complicated if it can be avoided.


The SAFEARRAYBOUND structure is simple: it describes how many elements there
are, and what the lower bound is, for a dimension. (The first element of
a VB array doesn't have to be number zero.) As for the SAFEARRAY structure
itself, cDims has the number of dimensions, and rgsabound is an array of
SAFEARRAYBOUND, where there is one element per dimension in the safearray
(contrary to the declaration above). pvData of course points to the actual
data. cLocks holds a lock count, and cbElements holds the size of an element.
fFeatures somehow tells how the data is being stored, and therefore how
it can be freed. See the help topic "SAFEARRAY Data Type" to see more about
the fFeatures member.

There is a surprising number of API functions to deal with safearrays; see
the help topic "Array Manipulation API Functions". You should not attempt
to manipulate a safearray or access an element manually; you should instead
either use the API functions, or much better yet use the helper class CComSafeArray.
(There doesn't seem to be a _safearray_t class.)

CComSafeArray is a template class; you provide the type of an element as
one of the template parameters. (The example code in the MSDN documentation
for CComSafeArray is lousy, if you ask me. If you can't figure it out, check
out the ATLSafeArray sample.) CComSafeArray provides Attach() and Detach()
methods to take over an existing safearray or abandon the wrapped safearray,
respectively, as you would expect. Perhaps the best feature is the overridden
operator[] method, which allows you to access an array element safely and
easily.
-------------------
Liz McGuire
2009-04-22 17:21:55 UTC
Permalink
Jim,

I expect you're going to have to use structures.

Check out this article(note that there's a link at the bottom to part two
of the article):

http://www.thedbcommunity.com/index.php?option=com_content&task=view&id=29&Itemid=44

I'm assuming SAFEARRAYBOUND is a constant which corresponds to an integer
- you'll have to find out what integer values are valid there.

You'll use a longInt for the LONG and perhaps for the ULONG too (not sure
what that is unless it's an unsigned int). USHORT might be a longInt or
smallInt in Paradox (it may not matter as long as the value is valid). No
idea what PVOID might be...

NOTE: I've never done anything like what you're trying, so these are just
suggestions based on those typedef statements below.

FWIW,

Liz
Post by Jim Moseley
BTW, here's more info on a SAFEARRAY. Now if I only knew how to use this
in OPal.
typedef struct tagSAFEARRAYBOUND
{
ULONG cElements;
LONG lLbound;
} SAFEARRAYBOUND;
typedef struct tagSAFEARRAY
{
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[ 1 ];
} SAFEARRAY;
Jim Moseley
2009-04-22 17:54:56 UTC
Permalink
Liz,

I think it is quickly getting out of my/our league. The SAFEARRAY isn't
native to .NET C#, and this is what someone had to do to get it to work in
C# (sorry for the long post, but thought someone would find it useful).


Seems like a lot of effort, so I'll start another post on other options.

Thanks,
Jim Moseley

------------------
from http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/

Last post by JLetz:


I can post excerpts from the code that I used but it is pretty specific to
my application. I am trying to write a Visual Studio 2005 C# program that
interfaces with a Visual Studio 6.0 C library (.dll). In particular, I want
to call a function that has the following signature:



short WINAPI DoInsert(
HANDLEDATA *HandleRec,
SAFEARRAY **psa,
char *err_msg,
unsigned short *relrecnum,
long custom_options)



This is the only use I have for a SAFEARRAY type. The SAFEARRY is a single
dimension array of structures. The lower bound of the single dimension is
always 0. The upper bound of the single dimension varies for each call.
Each element of the array represents a variable length record. However,
the record is represented by a fixed length structure that contains a pointer
to a variable length string and some other fixed length members.

I am using Platform Invoke to call the library function. Following is the
definition of the SAFEARRAY header structure and the declaration of the function
I want to call:



[StructLayout(LayoutKind.Sequential)]
struct SafeArray
{
public ushort dimensions; // Count of dimensions in the SAFEARRAY
public ushort features; // Flags to describe SAFEARRAY usage
public uint elementSize; // Size of an array element
public uint locks; // Number of times locked without unlocking
public IntPtr dataPtr; // Pointer to the array data
public uint elementCount; // Element count for first (only) dimension
public int lowerBound; // Lower bound for first (only) dimension
}


[DllImport("WPApply.dll". EntryPoint="DoOnsert")]
static extern short DoInsert(
ref HandleData databaseHandles,
ref IntPtr safeArrayPtr,
StringBuilder message;
ref short relativeRecordNumber,
int customOptionBits)



At the time of the call to DoInsert, the COM Task memory contains the following
memory allocations:
1. A block containing the SAFEARRAY header, which is described by the
SafeArray structure.
2. A block containing an array of fixed length structures, which represent
the array elements.
3. A separate block of memory for each variable length string that is
pointed to from one of the fixed length structures.

Since the first block never changes in size, I allocate once at the beginning
of the program and it remains allocated until the end of the program. It
is allocated by a statement similar to the following:



IntPtr safeArrayPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SafeArray)));



The size of the second block is dependent on the number of elements in the
array. This block is originally allocated based on the number of elements
in the array for the first call to DoInsert. The block remains allocated
after the call to DoInsert. For subsequent calls, if an array with a greater
number of elements is required, the block is deallocated and then reallocated
to the newer, larger size; otherwise, the block is just reused. This logic
is contained in a separate function as follows:



public void AllocateRecordBuffer(
int recordCount)
{
// Check if required number of records exceeds current capacity of data
buffer
if (recordCount > recordBufferCount)
{
// Current record data buffer capacity exceeded -- free existing
data buffer
Marshal.FreeCoTaskMem(recordBufferPtr);



// Allocate new record data buffer with new capacity
int recordSize =

WpInfoxData.FIXED_LENGTH_DATA_LENGTH * Marshal.SizeOf(typeof(byte))
+

Marshal.SizeOf(typeof(IntPtr));
recordBufferPtr = Marshal.AllocCoTaskMem(recordCount * recordSize);



// Indicate current maximum number of records that can fit in data
buffer
recordBufferCount = recordCount;
}
}



In this function, recordCount indicated the required capacity of this block
measured in records. The recordBufferCount value indicates the specified
current capacity of the memeory block also specified in records. This value
is initialized to 0 before the first call to this function. The class that
holds the record data is called WpInfoxData and the member called FIXED_LENGTH_DATA_LENGTH
is the length of the fixed data not including the pointer to th variable
length.

Now it is time to call DoInsert. The array records is an array of WpInfoxData
elements. The array is passed as a SAFEARRAY to DoInsert as follows:



// Ensure record buffer is large enough
AllocateRecordBuffer(records.Count);



// Build SAFEARRAY header for array of records
SafeArray safeArray;
safeArray.dimensions = 1;
safeArray.features = 0;
safeArray.elementSize =

(uint)((WpInfoxData.FIXED_LENGTH_DATA_LENGTH * Marshal.SizeOf(typeof(byte)))
+

Marshal.SizeOf(typeof(IntPtr)));
safeArray.locks = 0;
safeArray.dataPtr = recordBufferPtr;
safeArray.elementCount = (uint)records.Count;
safeArray.lowerBound = 0;



// Build SAFEARRAY array data
int offset = 0;
for (int i = 0; i < records.Count; i += 1)
{
// Add record to array buffer
offset = records.AddToRecordBuffer(recordBufferPtr, offset);
}



// Copy SAFEARRAY header for use by DoInsert
Marshal.StructureToPtr(safeArray, safeArrayPtr, false);



The AddToRecordBuffer method is a very repetive function that packs a large
number of data elements into the record buffer. The following provides a
sample of how a few such elements are packed into the record buffer.



public int AddToRecordBuffer(
IntPtr bufferPtr,
int offset)
{
// Check if a variable length data buffer already exists
if (varDataPtr != IntPtr.Zero)
{
// Variable length data buffer already exists -- free the buffer
Marshal.FreeCoTaskMem(varDataPtr);
}



// Allocate variable length data buffer and copy variable length byte
array into it
varDataPtr = Marshal.AllocCoTaskMem(bytes.Length * Marshal.SizeOf(typeof(byte)));
Marshal.Copy(bytes, 0, varDataPtr, bytes.Length);



// Store record number in data buffer and update offset
Marshal.WriteInt32(bufferPtr, offset, recordNumber);
offset += Marshal.SizeOf(typeof(int));



// Store relative record number in data buffer and update offset
Marshal.WriteInt16(bufferPtr, offset, relativeRecordNumber);
offset += Marshal.SizeOf(typeof(short));



// Store transaction type in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, transactionType);
offset += Marshal.SizeOf(typeof(byte));



// Store state array in data buffer and update offset
for (int i = 0; i < state.Length; i += 1)
{
// Store byte of state array in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, state);
offset += Marshal.SizeOf(typeof(byte));
}



// Store filler byte in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, 0);
offset += Marshal.SizeOf(typeof(byte));



// Store flags in data buffer and update offset
uint temp = flags;
for (int i = 0; i < sizeof(uint); i += 1)
{
// Store byte of flags in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, (byte)(temp & 0xff));
offset += Marshal.SizeOf(typeof(byte));



// Shift next byte into place
temp >>= 8;
}



// Store variable length data length in data buffer and update offset
Marshal.WriteInt32(bufferPtr, offset, bytes.Length);
offset += Marshal.SizeOf(typeof(int));



// Store variable length data buffer pointer in data buffer and update
offset
Marshal.WriteIntPtr(bufferPtr, offset, varDataPtr);
offset += Marshal.SizeOf(typeof(IntPtr));



// Return updated offset
return offset;
}



The call to DoInsert is made as follows:



short status = DoInsert(ref lsdbHandles2, ref safeArrayPtr, sbMessage,

ref relativeRecordNumber, customOptionBits);



Following the call, the memory for the SAFEARRAY header and the array buffer
remains but the memory for each variable length data buffer is freed as follows:



// Free memory allocated for each record
foreach (WpInfoxData record in records)
{
// Free memory allocated for variable length data for record
record.FreeVarDataMemory();
}



The FreeVarDataMemory function is as follows:



public void FreeVarDataMemory()
{
// Check if variable length data buffer is allocated
if (varDataPtr != IntPtr.Zero)
{
// Variable length data buffer exists -- free the buffer
Marshal.FreeCoTaskMem(varDataPtr);
varDataPtr = IntPtr.Zero;
}
}



I realize this is sketchy and fairly explicit to my application, but I hope
it helps.
----------------------
Leslie Milburn
2009-04-23 10:15:55 UTC
Permalink
Hi Jim

Basically the SAFEARRAY is a convoluted method of data passing for
multi-dimensional data - I loathe this generic approach Microsoft has taken
over the past few years as reliability takes a back seat to "too clever by
half" programming.

Liz could be correct in that structures are all you need, note that PVOID
(or LPVOID) is a pointer to "something" and so you could simply store the
address of another structure which contains the column data itself or the
maximum storage space for retrieved data - this depends entirely upon the
function you are calling. Having said that, I suspect that it would be
easier to dynamically allocate the memory on a column (aka element) by
column basis. This approach is harder to manage and it is easier to have
memory leaks if you are not careful, but this is the sort of method used in
C all of the time but C# and to some extent C++ hide this away, think NEW
operator in C++ and you sort of get the idea.

Here is an example of data passing between Visual BASIC and a C DLL
http://support.microsoft.com/kb/207931 which also contains a definition of
SAFEARRAY using the modern DWORD defines rather than the original C
definitions you previously found. As mentioned above, I suspect Microsoft
may be clarifiying this because the header file for the 64bit Platform SDK
version might be different to the 32 Bit version (ie you might have to have
one version for each platform).

An alternative approach could be to write a DLL in plain C or C++ (without
MFC) which does the work and it is this DLL that you call from Paradox which
could have a very simple interface. Also, did I forget to mention ANSI
versus UNICODE, this would need to be considered as well I think.
Leslie.
Post by Jim Moseley
Liz,
I think it is quickly getting out of my/our league. The SAFEARRAY isn't
native to .NET C#, and this is what someone had to do to get it to work in
C# (sorry for the long post, but thought someone would find it useful).
Seems like a lot of effort, so I'll start another post on other options.
Thanks,
Jim Moseley
------------------
from
http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/
I can post excerpts from the code that I used but it is pretty specific to
my application. I am trying to write a Visual Studio 2005 C# program that
interfaces with a Visual Studio 6.0 C library (.dll). In particular, I want
short WINAPI DoInsert(
HANDLEDATA *HandleRec,
SAFEARRAY **psa,
char *err_msg,
unsigned short *relrecnum,
long custom_options)
This is the only use I have for a SAFEARRAY type. The SAFEARRY is a single
dimension array of structures. The lower bound of the single dimension is
always 0. The upper bound of the single dimension varies for each call.
Each element of the array represents a variable length record. However,
the record is represented by a fixed length structure that contains a pointer
to a variable length string and some other fixed length members.
I am using Platform Invoke to call the library function. Following is the
definition of the SAFEARRAY header structure and the declaration of the function
[StructLayout(LayoutKind.Sequential)]
struct SafeArray
{
public ushort dimensions; // Count of dimensions in the SAFEARRAY
public ushort features; // Flags to describe SAFEARRAY usage
public uint elementSize; // Size of an array element
public uint locks; // Number of times locked without unlocking
public IntPtr dataPtr; // Pointer to the array data
public uint elementCount; // Element count for first (only) dimension
public int lowerBound; // Lower bound for first (only) dimension
}
[DllImport("WPApply.dll". EntryPoint="DoOnsert")]
static extern short DoInsert(
ref HandleData databaseHandles,
ref IntPtr safeArrayPtr,
StringBuilder message;
ref short relativeRecordNumber,
int customOptionBits)
At the time of the call to DoInsert, the COM Task memory contains the following
1. A block containing the SAFEARRAY header, which is described by the
SafeArray structure.
2. A block containing an array of fixed length structures, which represent
the array elements.
3. A separate block of memory for each variable length string that is
pointed to from one of the fixed length structures.
Since the first block never changes in size, I allocate once at the beginning
of the program and it remains allocated until the end of the program. It
IntPtr safeArrayPtr =
Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SafeArray)));
The size of the second block is dependent on the number of elements in the
array. This block is originally allocated based on the number of elements
in the array for the first call to DoInsert. The block remains allocated
after the call to DoInsert. For subsequent calls, if an array with a greater
number of elements is required, the block is deallocated and then reallocated
to the newer, larger size; otherwise, the block is just reused. This logic
public void AllocateRecordBuffer(
int recordCount)
{
// Check if required number of records exceeds current capacity of data
buffer
if (recordCount > recordBufferCount)
{
// Current record data buffer capacity exceeded -- free existing
data buffer
Marshal.FreeCoTaskMem(recordBufferPtr);
// Allocate new record data buffer with new capacity
int recordSize =
WpInfoxData.FIXED_LENGTH_DATA_LENGTH *
Marshal.SizeOf(typeof(byte))
+
Marshal.SizeOf(typeof(IntPtr));
recordBufferPtr = Marshal.AllocCoTaskMem(recordCount * recordSize);
// Indicate current maximum number of records that can fit in data
buffer
recordBufferCount = recordCount;
}
}
In this function, recordCount indicated the required capacity of this block
measured in records. The recordBufferCount value indicates the specified
current capacity of the memeory block also specified in records. This value
is initialized to 0 before the first call to this function. The class that
holds the record data is called WpInfoxData and the member called FIXED_LENGTH_DATA_LENGTH
is the length of the fixed data not including the pointer to th variable
length.
Now it is time to call DoInsert. The array records is an array of WpInfoxData
// Ensure record buffer is large enough
AllocateRecordBuffer(records.Count);
// Build SAFEARRAY header for array of records
SafeArray safeArray;
safeArray.dimensions = 1;
safeArray.features = 0;
safeArray.elementSize =
(uint)((WpInfoxData.FIXED_LENGTH_DATA_LENGTH *
Marshal.SizeOf(typeof(byte)))
+
Marshal.SizeOf(typeof(IntPtr)));
safeArray.locks = 0;
safeArray.dataPtr = recordBufferPtr;
safeArray.elementCount = (uint)records.Count;
safeArray.lowerBound = 0;
// Build SAFEARRAY array data
int offset = 0;
for (int i = 0; i < records.Count; i += 1)
{
// Add record to array buffer
offset = records.AddToRecordBuffer(recordBufferPtr, offset);
}
// Copy SAFEARRAY header for use by DoInsert
Marshal.StructureToPtr(safeArray, safeArrayPtr, false);
The AddToRecordBuffer method is a very repetive function that packs a large
number of data elements into the record buffer. The following provides a
sample of how a few such elements are packed into the record buffer.
public int AddToRecordBuffer(
IntPtr bufferPtr,
int offset)
{
// Check if a variable length data buffer already exists
if (varDataPtr != IntPtr.Zero)
{
// Variable length data buffer already exists -- free the buffer
Marshal.FreeCoTaskMem(varDataPtr);
}
// Allocate variable length data buffer and copy variable length byte
array into it
varDataPtr = Marshal.AllocCoTaskMem(bytes.Length *
Marshal.SizeOf(typeof(byte)));
Marshal.Copy(bytes, 0, varDataPtr, bytes.Length);
// Store record number in data buffer and update offset
Marshal.WriteInt32(bufferPtr, offset, recordNumber);
offset += Marshal.SizeOf(typeof(int));
// Store relative record number in data buffer and update offset
Marshal.WriteInt16(bufferPtr, offset, relativeRecordNumber);
offset += Marshal.SizeOf(typeof(short));
// Store transaction type in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, transactionType);
offset += Marshal.SizeOf(typeof(byte));
// Store state array in data buffer and update offset
for (int i = 0; i < state.Length; i += 1)
{
// Store byte of state array in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, state);
offset += Marshal.SizeOf(typeof(byte));
}
// Store filler byte in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, 0);
offset += Marshal.SizeOf(typeof(byte));
// Store flags in data buffer and update offset
uint temp = flags;
for (int i = 0; i < sizeof(uint); i += 1)
{
// Store byte of flags in data buffer and update offset
Marshal.WriteByte(bufferPtr, offset, (byte)(temp & 0xff));
offset += Marshal.SizeOf(typeof(byte));
// Shift next byte into place
temp >>= 8;
}
// Store variable length data length in data buffer and update offset
Marshal.WriteInt32(bufferPtr, offset, bytes.Length);
offset += Marshal.SizeOf(typeof(int));
// Store variable length data buffer pointer in data buffer and update
offset
Marshal.WriteIntPtr(bufferPtr, offset, varDataPtr);
offset += Marshal.SizeOf(typeof(IntPtr));
// Return updated offset
return offset;
}
short status = DoInsert(ref lsdbHandles2, ref safeArrayPtr, sbMessage,
ref relativeRecordNumber, customOptionBits);
Following the call, the memory for the SAFEARRAY header and the array buffer
// Free memory allocated for each record
foreach (WpInfoxData record in records)
{
// Free memory allocated for variable length data for record
record.FreeVarDataMemory();
}
public void FreeVarDataMemory()
{
// Check if variable length data buffer is allocated
if (varDataPtr != IntPtr.Zero)
{
// Variable length data buffer exists -- free the buffer
Marshal.FreeCoTaskMem(varDataPtr);
varDataPtr = IntPtr.Zero;
}
}
I realize this is sketchy and fairly explicit to my application, but I hope
it helps.
----------------------
Jim Giner
2009-04-22 18:03:23 UTC
Permalink
Plus - isn't there a "paradox-web" newsgroup that might have more
suggestions? I don't subscribe to that one cause I don't do any web-based
work, so you might get a different audience for the same reason.
Post by Liz McGuire
Jim,
I expect you're going to have to use structures.
Check out this article(note that there's a link at the bottom to part two
http://www.thedbcommunity.com/index.php?option=com_content&task=view&id=29&Itemid=44
I'm assuming SAFEARRAYBOUND is a constant which corresponds to an integer
- you'll have to find out what integer values are valid there.
You'll use a longInt for the LONG and perhaps for the ULONG too (not sure
what that is unless it's an unsigned int). USHORT might be a longInt or
smallInt in Paradox (it may not matter as long as the value is valid). No
idea what PVOID might be...
NOTE: I've never done anything like what you're trying, so these are just
suggestions based on those typedef statements below.
FWIW,
Liz
Post by Jim Moseley
BTW, here's more info on a SAFEARRAY. Now if I only knew how to use this
in OPal.
typedef struct tagSAFEARRAYBOUND
{
ULONG cElements;
LONG lLbound;
} SAFEARRAYBOUND;
typedef struct tagSAFEARRAY
{
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[ 1 ];
} SAFEARRAY;
Jim Moseley
2009-04-22 18:10:05 UTC
Permalink
Jim,
Post by Jim Giner
Plus - isn't there a "paradox-web" newsgroup that might have more
suggestions?
I thought about putting it there, but I think everyone who looks at it also
checks this one. And this NG is searchable via Google, so it is easier to
find answers.

Thanks,
Jim Moseley
Robert Wiltshire
2009-04-23 06:16:37 UTC
Permalink
What is navigate2 ?

Can you provide a sample call to a method or procedure
where the safearray actually would get used ?

Is the safearray to pass or receive information ?


The community xls library has good examples of structures used with windows
api.

Rick Kelly has also posted some excellent code dealing with memory
manipulation,
if you care to google/search for that.

Good luck
Robert Wiltshire
Jim Moseley
2009-04-23 12:31:02 UTC
Permalink
Robert,
Post by Robert Wiltshire
What is navigate2 ?
It is a method for the InternetExplorer COM object. This is what Tony's
MS Browser form uses to show any webpage on a Pdox form.

From: http://support.microsoft.com/kb/815722

To perform a post, only the URL parameter, the PostData parameter, and the
Headers parameter are relevant.

To call the Navigate2 method and to post form data to an HTTP server, the
URL parameter must specify a valid address, the PostData parameter must contain
a SAFEARRAY of bytes, and the Headers parameter must contain a BSTR string
that contains the following HTTP header:
Content-Type: application/x-www-form-urlencoded


From http://msdn.microsoft.com/en-us/library/aa752094(VS.85).aspx

Navigate2 Method (of InternetExplorer and WebBrowser):
Navigates the browser to a location that might not be expressed as a URL,
such as a pointer to an item identifier list (PIDL) for an entity in the
Microsoft Windows Shell namespace.

Syntax

object.Navigate2( _
URL As Variant, _
[Flags As Variant,] _
[TargetFrameName As Variant,] _
[PostData As Variant,] _
[Headers As Variant])Parameters

URL
Required. A variable or expression that evaluates to the URL of the resource
to display, the full path to the file location, or a PIDL that represents
a folder in the Shell namespace.

Flags
Optional. A constant or value that specifies a combination of the values
defined by the BrowserNavConstants enumeration.

TargetFrameName
Optional. Case-sensitive string expression that evaluates to the name of
the frame in which to display the resource.

PostData
Optional. Data that is sent to the server as part of a HTTP POST transaction.
A POST transaction typically is used to send data collected by an HTML form.
If this parameter does not specify any POST data, this method issues an HTTP
GET transaction. This parameter is ignored if URL is not an HTTP URL.

Headers
Optional. A String that contains additional HTTP headers to send to the server.

Remarks

See Navigate for additional usage notes.

This method extends the Navigate method to allow for Shell integration; however,
this method does not make Navigate obsolete. The original method can still
be used for URL navigations.

Applies To

InternetExplorer, WebBrowser



Thanks,
Jim Moseley

Loading...