![]() | |
![]() |
| | Thread Tools | Display Modes |
#1
| |||
| |||
|
#2
| |||
| |||
|
#3
| |||
| |||
|
|
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; |
#4
| |||
| |||
|
#5
| |||
| |||
|
|
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....29&Itemi d=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) mapson (DOT) 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; |
#6
| |||
| |||
|
|
Plus - isn't there a "paradox-web" newsgroup that might have more suggestions? |
#7
| |||
| |||
|
#8
| |||
| |||
|
|
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/For...-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(SafeA rray))); 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. ---------------------- |
#9
| |||
| |||
|
|
What is navigate2 ? |
![]() |
| Thread Tools | |
| Display Modes | |
| |