Scenario is like there is GUI and Dll, GUI is in C#(WPF) and it is using c++ Dll and dll needs structure pointer of structure created in GUI for processing commands.
In Dll structure having fixed size array so same created in GUI also.
C++ structure
struct _TempData
{
Int32 iOnFly_StepCalib;
Int32 iOnFly_BiDiCalib;
UNIT Unit;
byte uiPrintDirection;
Int16 siStep_Feed[20];
};
so In GUI(C#) structure is like...
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct _TempData
{
public Int32 iOnFly_StepCalib;
public Int32 iOnFly_BiDiCalib;
public UNIT Unit;
public byte uiPrintDirection;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public Int16[] siStep_Feed;
};
and my c# code for getting this structure pointer is here
[DllImport("Dll12.dll")]
public static extern UIntPtr sendMessageToHSM(Int32 MSG, IntPtr CmdStruct);
static void Main(string[] args)
{
_TempData tempData;
tempData.iOnFly_BiDiCalib = 1;
tempData.iOnFly_StepCalib = 2;
tempData.uiPrintDirection = 1;
tempData.Unit = UNIT.INCH;
unsafe
{
void* tempdata1 = &tempData;
sendMessageToHSM((int)HSM_COMMANDS.HSM_GUI_PC_UPDATE_STEP_CALIB, (IntPtr)tempdata1 );
}
}
This error is showing...
Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type ('_TempData')
on commenting this line in structure
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public Int16[] siStep_Feed;
then code is running fine.
I am new to C#.
Any help will be appreciated.
Thanks.
There are many errors in your code, also unsafe keyword is no use here
I believe what you might be looking for is
// get the size
int size = Marshal.SizeOf(tempData);
// Create some unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(size);
// marshal the structure to pointer
Marshal.StructureToPtr(tempData, ptr, false);
// call the funky api
sendMessageToHSM((int)HSM_COMMANDS.HSM_GUI_PC_UPDATE_STEP_CALIB, ptr);
// get the result if there is any
_TempData tempData2 = (_TempData)Marshal.PtrToStructure(ptr, typeof(_TempData));
// clean up the memory
Marshal.FreeHGlobal(ptr);
Also in C# we don't name types with an underscore
Related
I've been banging my head all day and hope someone can help. I need to marshal a managed data structure to an unmanaged C dll. When I look at all the memory it appears that what I'm doing is working, but the C dll (a black box to me) is returning an error indicating the data is corrupt. Can anyone point out my error?
C declarations
typedef struct _TAG_Data
{
void *data; // Binary data
uint32_t size; // Data size bytes
} Data;
// Parse binary data, extract int value
ParseData(Data ∗ result, uint64_t parameter, int32_t ∗ value)
Managed object:
public class MyData
{
public byte[] data;
public UInt32 size;
}
Packed equivalent for moving to unmanaged memory:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyData_Packed
{
public IntPtr data;
public UInt32 size;
}
At this point I have a managed "MyData" struct called MyResult with valid data that needs to go into the dll. Here's what I'm doing:
[DllImport("Some.dll", EntryPoint = "ParseData", SetLastError = true, CharSet = CharSet.Ansi)]
private static extern Int32 ParseData_Native(IntPtr result, UInt64 parameter, ref Int32 value);
IntPtr MyResultPackedPtr = new IntPtr();
MyData_Packed MyResultPacked = new MyData_Packed();
// Copy "MyResult" into un-managed memory so it can be passed to the C library.
// Allocate un-managed memory for the data buffer
MyResultPacked.data = Marshal.AllocHGlobal((int)MyResult.size);
// Copy data from managed "MyResult.data" into the unmanaged "MyResultPacked.data"
Marshal.Copy(MyResult.data, 0, MyResultPacked.data, (int)MyResult.size);
MyResultPacked.size = MyResult.size;
// Allocate unmanaged memory for the structure itself
MyResultPackedPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyData_Packed)));
// Copy the packed struct into the unmanaged memory and get our pointer
Marshal.StructureToPtr(MyResultPacked, MyResultPackedPtr, false);
// Pass our pointer to the unmanaged struct, which points to the unmanaged data buffer, to the C dll
Int32 tmp = 0;
ErrorCode = ParseData_Native(MyResultPackedPtr, parameter, ref tmp);
When I look at the unmanaged memory pointed to by MyResultPacked.data, the data is correct so the copy was good. And when I look at MyResultPackedPtr in memory, the first 8 bytes are the address of the same unmanaged memory pointed to by MyResultPacked.data (64-bit machine), and the next 4 bytes are the proper size of the data. So it appears that MyResultPackedPtr points to a valid copy of MyResultPacked. But the return value from ParseData() indicates my data must be corrupt, so I must be doing something wrong.
To take it a step further, I wrote the same code 100% in C and it works. And the data in the binary buffer in C matched the data in the binary buffer in C#, going by the memory watch feature in Visual Studio, so it appears my data handling is correct. Which makes me think something is wrong with the way I'm passing MyResultPackedPtr to the dll. Unfortunately I don't have the source for the dll and cannot step into it. Can anyone offer a suggestion on what to try next?
I don't see why you need any of this custom marshalling code in the first place. You should be able to pass the struct with the byte[] array directly, and the marshaller will sort out the copying.
You also need to set the calling convention correctly.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyData_Packed
{
public byte[] data;
public UInt32 size;
}
[DllImport("Some.dll", EntryPoint = "ParseData", SetLastError = true, CallingConvention = CallingConvention.CDecl)]
private static extern int ParseData_Native(ref MyData_Packed result, ulong parameter, out int value);
var MyResultPacked = new MyData_Packed
{
data = MyResult,
size = MyResult.size,
};
ErrorCode = ParseData_Native(ref MyResultPacked, parameter, out var tmp);
I'm trying to access the Nikon image SDK(for those interested see: 1) to implement access to *.nef file in a programm. I'm stuck at a return code from the dll which should be interpreted as "invalid parameter" and I'm running out of ideas.
Yes I know the chance, that somebody is exactly using this dll is sparse, but I'm rather looking for "writing"/"thinking" errors... I'm still learning (so excuse to any wrong used terms, etc...) and also for this reason this is a little "longer" post (some "aloud thinking" on my side ;-) )
1.) the dll has an entry function where you pass a identifier and a struct as parameter. The identifier stands for a specific command (like open,close,etc....). The struct is used for data exchange with the camera.
2.) I do have everything together and working (since, I'm getting a "return code") but I can't figure out the reason for the return code (maybe some datatype is incompatible?)
So first the "C++"-part:
c++ function definition:
extern "C" unsigned long __declspec(dllexport) WINAPI Nkfl_Entry(unsigned long ulCommand, void* pParam );
this is stdcall, so I do need to worry about any further options to dllimport, since usigned long(c++) corresponds to uint(c#) i get two uints one "out" and one "in"...
c++ struct defintion:
typedef struct tagNkflLibraryParam
{
unsigned long ulSize; // Size of structure
unsigned long ulVersion; // Version
unsigned long ulVMMemorySize; // Size of vertual memory
NkflPtr* pNkflPtr; // Pointer of StratoObject
unsigned char VMFileInfo[ MAX_PATH ]; // Swap file info
} NkflLibraryParam, *NkflLibraryPtr;
so I do need to pass 3 times uints, one pointer to an "StratoObject" ((1.) the doc says "typedef void* NkflPtr" so this is "just" a void* pointer 2.) the doc says if this is zero it will be filled up by the sdk) and finally one byte (since unsigned char(c++) corresponds to byte(c#)).
So first question: Is this correct?
Then going to the "coding-part":
c# struct defintion:
namespace NikonStruct
{
[StructLayout(LayoutKind.Sequential)]
public struct NkflLibraryParam
{
public uint ulSize; // size of the NkflLibraryParam structure
public uint ulVersion; // version number of the interface specification
public uint ulVMMMemorySize; // upper limit of the physical memory that can be used
public IntPtr pNkflPtr; // pointer to the StratoManager object
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] VMFileInfo; // swap file information
}
}
now this should correspond to my defintions above...
c# Program class:
class Program
{
public enum eNkflCommand : int
{
kNkfl_Cmd_OpenLibrary = 1,
kNkfl_Cmd_CloseLibrary = 2,
};
[DllImport("NkImgSDK.dll", EntryPoint = "Nkfl_Entry")]
public static extern uint kNkfl_Cmd_OpenLibrary(eNkflCommand ulCommand, ref NikonStruct.NkflLibraryParam data);
[DllImport("NkImgSDK.dll", EntryPoint = "Nkfl_Entry")]
public static extern uint kNkfl_Cmd_CloseLibrary(eNkflCommand ulCommand, IntPtr close);
static void Main(string[] args)
{
try
{
// specify return value of entry function
uint result1, result2;
/// call the kNkfl_Cmd_OpenLibrary Function
// generate data structure, which is used to communicate with kNkfl_Cmd_OpenLibrary function
NikonStruct.NkflLibraryParam _NkflLibraryParam = new NikonStruct.NkflLibraryParam();
// fill the fields of _NkflLibraryParam structure for kNkfl_Cmd_OpenLibrary function
_NkflLibraryParam.ulVersion = 16777216;
_NkflLibraryParam.ulSize = ((uint)Marshal.SizeOf(_NkflLibraryParam)); ;
// call the entry function with parameters for kNkfl_Cmd_OpenLibrary
result1 = kNkfl_Cmd_OpenLibrary(eNkflCommand.kNkfl_Cmd_OpenLibrary, ref _NkflLibraryParam);
Console.WriteLine(result1);
/// call the kNkfl_Cmd_CloseLibrary Function
result2 = kNkfl_Cmd_CloseLibrary(eNkflCommand.kNkfl_Cmd_CloseLibrary, IntPtr.Zero);
Console.WriteLine(result2);
}
catch
{
string errorMsg = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()).Message;
throw new ArgumentException(errorMsg);
}
}
}
So nothing specific here:
eNkflCommand is from the doc
the structure is passed by reference so ref...
the "close" function expects "null pointer" (according to doc)
ulVersion is 0x01000000 (according to doc)
all other struct values are not set (and are zero by default if I understood the clr doc correctly)
Compiles and runs as already mentioned but result1 returns wrong "status-code" which translates to "invalid param" as already mentioned.
Any help appreciated....
FOUND IT:
never trust a documentation of a software developer: there was actually a missing parameter (not declared in the documentation BUT in an additional header definition file which was in another sub-directory of the sdk-package...)
so actually the struct defintion in the c# should be:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct struktur
{
public uint ulSize; // size of the NkflLibraryParam structure
public uint ulVersion; // version number of the interface specification
public uint ulVMMMemorySize; // upper limit of the physical memory that can be used
public IntPtr pNkflPtr; // pointer to the StratoManager object
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] VMFileInfo; // swap file information
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] DefProfPath; // <- this one is not included in the doc of NIKON (I still don't now what this should hold but it works if it's empty...)
}
Thanks to jszigeti and DavidHeffernan for trying...
I'm trying to use C# to get data from a file using a function in an unmanaged 3rd party dll. The function takes a pointer to a struct as an input, and returns a status flag for the success of the operation (not used in the code below). The 3rd party vendor supplies the following code in C for how to call the dll function:
DllCaller.h
#pragma pack(1)
struct Event
{
int event_type;
double time_stamp;
char event_text[200];
};
typedef enum Status (*_GetEventList)(struct Event* event_list);
_GetEventList GetEventList;
DllCaller.c
int event_list_cnt;
struct Event* event_list;
hInstLibrary = LoadLibrary(lib_name);
GetEventList = (_DWGetEventList)GetProcAddress(hInstLibrary, "GetEventList);
printf("\nEVENTS:\n");
event_list_cnt = 2;
event_list = malloc(sizeof(struct Event) * event_list_cnt);
GetEventList(event_list);
for(i = 0; i < event_list_cnt; i++)
{
printf("EVENT: type = %i, text = %s, position = %fsec \n",
event_list[i].event_type, event_list[i].event_text,
event_list[i].time_stamp);
}
free(event_list);
FreeLibrary(hInstLibrary)
The output from running this on an example file is:
EVENTS:
EVENT: type = 1, text = storing started, position = 0.000000sec
EVENT: type = 2, text = storing stopped, position = 110,825682sec
In other words each field in the event_list struct is an array of length two.
In the above code I have simplified the vendor's example code and omitted some things that I deem irrelevant to the present issue.
This I how I have tried to implement the same functionality in C#:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct Event
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4)]
public int[] event_type;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.R8)]
public double[] time_stamp;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 200)]
public char[] event_text;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Status GetEventList(IntPtr ptrToEventList);
public Event GetEventList()
{
// this.pDll is a pointer to the dll library.
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(this.pDll, "GetEventList");
GetEventList getEventList = (GetEventList)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEventList));
int eventListCount;
this.GetEventListCount(out eventListCount, out errorMessage);
int mem = Marshal.SizeOf(typeof(Event));
// The multiplication by two is because I already know that
// the struct should be populated by two events.
IntPtr structPtr = Marshal.AllocCoTaskMem(2 * mem);
Event eventList;
try
{
getEventList(structPtr);
eventList = (Event)Marshal.PtrToStructure(structPtr , typeof(Event));
}
finally
{
Marshal.FreeHGlobal(structPtr);
}
return eventList;
}
If I run this on the same example file as the C code above the event_type and time_stamp vectors in eventList will have length 1, and the event_text will have length 200. The values in these fields will contain correct information for the first event - the one of event_type 1 - as printed out by the C code above. What should be populated in the eventList is of course vectors of length 2 containing the two events, but I have been unsuccessfull in modifying the code to do this. The above code is the only one I have managed to write that populates anything at all in the struct. I have tried to play around with specifying the event_text field as a string instead of a char, but that only results in AccessViolationException errors, probably because I have not implemented it correctly.
Can anyone help me fix the above code so that the eventList is populated correctly?
Thanks!
/Elfendahl
EDIT:
Updated C# code with Event structure corrected and attempt to allocate Event[] in C# before passing it to the unmanaged dll:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct Event
{
[MarshalAs(UnmanagedType.I4)]
public int event_type;
[MarshalAs(UnmanagedType.R8)]
public double time_stamp;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 200)]
public char[] event_text;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Status GetEventList(ref Event[] eventList);
public Event[] GetEventList()
{
// this.pDll is a pointer to the dll library.
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(this.pDll, "GetEventList");
GetEventList getEventList = (GetEventList)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEventList));
Event[] eventList = new Event[2];
getEventList(ref eventList);
return eventList;
}
I don't know if the above code looks as poorly formatted on your screen as it does on mine (indents and blank new lines are omitted) but I have been unable to get to look any better.
This is the code that finally did the trick:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct Event
{
[MarshalAs(UnmanagedType.I4)]
public int event_type;
[MarshalAs(UnmanagedType.R8)]
public double time_stamp;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
public string event_text;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Status GetEventList([out]Event[] eventList);
public Event[] GetEventList()
{
// this.pDll is a pointer to the dll library.
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(this.pDll, "GetEventList");
GetEventList getEventList = (GetEventList)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEventList));
Event[] eventList = new Event[2];
getEventList(eventList);
return eventList;
}
A big thank you to leppie and Panos Rontogiannis who helped me figure it out!
My C declarations are as follows:
int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data);
typedef struct {
byte Rel;
__int64 Time;
char Validated;
unsigned char Data[1];
} DATASTRUCT;
My C# declarations are as follows:
[DllImport("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public sbyte Rel;
public long Time;
public byte Validated;
public double Data;
}
I then call the managed function as follows:
string dataToShow = "description";
long Time;
uint maxData; // How many structs will be returned, i.e., how much data is available?
uint myHandle = 1;
DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // Doesn't it matter what I specify as the array size?
myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation);
Upon execution the above function will return successfully with only one structure even though there are 3 to return. Why is this so?
Additional information; I have tried passing the pointer to a pointer of an array of structs the following ways:
ref DATASTRUCT[] data; // It works, but it only returns one struct
[Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage
As I understand it I might need to do some manual marshalling using IntPtr, I do not know how to implement this however, so any advice would be appreciated.
Okay, it seems as though your native library does the allocation, so really all you need to do is provide a pointer through which you can access the allocated data.
Change your API definition to (note, I changed the maxData param to uint, long is 64 bits in .NET and 32 bits in native.
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData);
Off the top of my head I can't quite remember if you need the out keyword for the final parameter, but I think so.
Then, call myData:
uint nAllocs = 0, time = 0;
IntPtr pAllocs = IntPtr.Zero;
myData(1, "description", out time, out nAllocs, out pAllocs);
Now, pAllocs should point to unmanaged memory, to marshal these into managed memory isn't too difficult:
[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public byte Rel;
public long Time;
public byte Validated;
public IntPtr Data; //pointer to unmanaged string.
}
int szStruct = Marshal.SizeOf(typeof(DATASTRUCT));
DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs];
for(uint i = 0; i < nallocs; i++)
localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT));
And now you should have an array of local structs.
A point to note
You may need to set your project to compile as x86, to standardize the size of an IntPtr to 4 bytes (DWORD) instead of AnyCPU's default 8.
A pointer to a pointer could be represented in your dllimport declaration as ref IntPtr data, so your declaration would become:
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data);
(As an aside, I think a long in C is just equivalent to an int in C#. Long in C# is an Int64, which would be a long long in C)
Marshalling your DATASTRUCT[] to an IntPtr can be done using the GCHandle class
DATASTRUCT [] dataInformation = new DATASTRUCT[3];
GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned);
IntPtr ptr = gch.AddrOfPinnedObject();
myData(myHandle, dataToShow, out Time, out maxData, ref ptr);
//It's absolutely essential you do this next bit so the object can be garbage collected again,
//but it should only be done once the unmanaged code is definitely done with the reference.
gch.Free();
Using the Marshal class and the StructureToPtr or Copy methods of it would also be an option but for the purposes of proving the concept at least the GCHandle should do the trick, it's just not ideal for scenarios where the unmanaged code does long running operations because you've pinned this object in place and the GC can't move it until you free it.
I'm using C# with P/Invoke to access to a DLL method. The definition of the method is the following:
[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);
Original structs:
typedef struct user_list {
unsigned short NumUsers;
USER_LIST_ITEM List[VARLEN];
} USER_LIST
typedef struct user_list_item {
char name[260];
unsigned char address[256];
} USER_LIST_ITEM
And the struct layout I've done is the following:
[StructLayout(LayoutKind.Sequential)]
public class USER_LIST
{
public uint NumUsers;
[MarshalAs(UnmanagedType.ByValArray)]
public USER_LIST_ITEM [] List;
}
[StructLayout(LayoutKind.Sequential)]
public class USER_LIST_ITEM
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string address;
};
But I get an error when I try to unmarshall it:
USER_LIST userList = new USER_LIST();
// Prepare pointer
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
Marshal.StructureToPtr(userList, uList, false);
result = GetUsers(out uList);
Marshal.PtrToStructure(uList, userList); <--
The runtime has encountered a fatal error. The address of the error was at 0x79f82af6, on thread 0x464. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
I get the NumUsers property right, but it seems the error occurs when unmarshalling the array. Any thoughts?
If you specify an array in a structure used as an out parameter, you need to tell the marshaler what length is the array going to be. With your code, the marshaler is probably allocating a zero-length array or just using null, which produces the crash. Unfortunately there seems to be no way to specify a variable-length out array as a member of a structure, because MarshalAs.SizeParamIndex only works for methods. You might get away with specifying a large, constant-size array using MarshalAs.SizeConst, but generally you'd have to parse the (presumably callee-allocated) return buffer like this:
var count = Marshal.ReadInt32 (uList) ;
var users = new List<USER_LIST_ITEM> () ;
var ptr = (long)uList + 4 ;
for (int i = 0 ; i < count ; ++i)
{
users.Add (Marshal.PtrToStructure (typeof (USER_LIST_ITEM),
new IntPtr (ptr))) ;
ptr += Marshal.SizeOf (typeof (USER_LIST_ITEM)) ;
}
You'll have to pay extra attention to alignment&padding and 32/64 bit issues.
That is because List has not been allocated yet.
You will need initialize all the fields.
Another problem I see is with the following:
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
...
result = GetUsers(out uList);
Are you sure that out should not be ref? Else there is no point (not sure if ref is correct either).
Update: Looking at your code again, you should be doing this (and avoid that memory leak poking your eye).
IntPtr uList;
var result = GetUsers(out uList);
var userlist = (USER_LIST) Marshal.PtrToStructure(ulist, typeof(USER_LIST));
Marshal.FreeHGlobal(ulist); // pray here or shoot the author of the C function
Update again:
Your p/invoke signature is likely wrong or you are interpreting it wrong.
I can guess it probably something like:
int GetUsers(USER_LIST* ulist);
And that what you have is not the same thing.
If this is case, the solution is easy.
Change USER_LIST to a class (but keep sequential layout) and use
// pinvoke sig
int GetUsers(USER_LIST ulist);
var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ulist);
-- or --
Call it by ref.
// pinvoke sig
int GetUsers(ref USER_LIST ulist);
var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ref ulist);
This way, you dont have to mess with manual marshalling, and I cant see anymore potential for memory leaks.
Final update:
Given the signature you posted, it looks like GetUsers returns a pointer to a list of USER_LIST with the return value being the count. Nice memory leak there.
Anyways, I would probably experiment with an unsafe approach here, and just walk thru the result , and make sure everything gets freed. (I still think you should shoot the author).
I think your original code isn't probably so wrong.
You've probably just used the wrong overload of Marshal.PtrToStructure.
Have you tried this?
[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);
[DllImport("userManager.dll")]
static extern void UMFree(IntPtr userList);
static void Main()
{
IntPtr userList; // no need to allocate memory in managed code;
GetUsers(out userList); // memory is allocated by native function
USER_LIST u = (USER_LIST)Marshal.PtrToStructure(userList, typeof(USER_LIST));
UMFree(userList);
}
Using unsafe code:
public unsafe struct USER_LIST
{
public uint numUsers;
public USER_LIST_ITEM* list;
}
public unsafe struct USER_LIST_ITEM
{
public fixed byte name[260];
public fixed byte address[256];
}
class Program
{
[DllImport("userManager.dll")]
static unsafe extern int GetUsers(USER_LIST** userList);
[DllImport("userManager.dll")]
static unsafe extern int UMFree(USER_LIST* userList);
private static unsafe void Main()
{
USER_LIST* list;
GetUsers(&list);
UMFree(list);
}
}