 |
|
| Computers Forum Index » Computer - Databases - Paradox » Open webpage using POST via Navigate2 needs SAFEARRAY... |
|
Page 1 of 1 |
|
| Author |
Message |
| Jim Moseley... |
Posted: Tue Apr 21, 2009 10:51 pm |
|
|
|
Guest
|
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 |
|
|
| Back to top |
|
|
|
| Liz McGuire... |
Posted: Wed Apr 22, 2009 9:21 pm |
|
|
|
Guest
|
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
"Jim Moseley" <jmose at (no spam) mapson.triptracker.com> wrote:
Quote:
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;
|
|
|
| Back to top |
|
|
|
| Jim Moseley... |
Posted: Wed Apr 22, 2009 9:54 pm |
|
|
|
Guest
|
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.
---------------------- |
|
|
| Back to top |
|
|
|
| Jim Giner... |
Posted: Wed Apr 22, 2009 10:03 pm |
|
|
|
Guest
|
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.
"Liz McGuire" <liz at (no spam) paradoxcommunity.com> wrote in message
news:49ef5233$1 at (no spam) pnews.thedbcommunity.com...
Quote:
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
"Jim Moseley" <jmose at (no spam) mapson.triptracker.com> wrote:
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;
|
|
|
| Back to top |
|
|
|
| Jim Moseley... |
Posted: Wed Apr 22, 2009 10:10 pm |
|
|
|
Guest
|
Jim,
Quote: 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 |
|
|
| Back to top |
|
|
|
| Robert Wiltshire... |
Posted: Thu Apr 23, 2009 10:16 am |
|
|
|
Guest
|
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 |
|
|
| Back to top |
|
|
|
| Jim Moseley... |
Posted: Thu Apr 23, 2009 4:31 pm |
|
|
|
Guest
|
Robert,
Quote: 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 |
|
|
| Back to top |
|
|
|
|
|
All times are GMT
The time now is Mon Dec 07, 2009 5:26 am
|
|