Marshalling a complex structure from C# to C - c#

I'm reading all about structure marshalling between C and C# in 64bit environment without success.
What I need to do is to pass the structure
typedef struct DescStructTa
{
char* pszNa;
unsigned long ulTId;
char* pszT;
unsigned short usRId;
unsigned long ulOff;
unsigned long ulSi;
char szAcc[2];
unsigned char bySSize;
} DescStruct;
from a C# application to a C DLL not made by us calling the method
MT_DLL_DECL long GetAll(UINTPTR ulHandler, DescStruct **ppList, unsigned long *pulNumS);
After long search I wrote the following code:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct PlcSymbolDescStruct
{
[MarshalAs(UnmanagedType.LPStr)]
string pszNa;
UInt32 ulTId;
[MarshalAs(UnmanagedType.LPStr)]
string pszT;
[MarshalAs(UnmanagedType.U2)]
ushort usRId;
UInt32 ulOff;
UInt32 ulSi;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
string szAcc;
[MarshalAs(UnmanagedType.U1)]
byte bySSize;
}
[DllImport("DataSource.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.U8)]public static extern long GetAll(ulong ulHandler, out DescStruct [] ppList,
out uint pulNumS);
Unfortunately this is still giving me a heap corruption.
If I try to pack less than 8 it gives (as expected) an access violation, but I expected this to work and I'm not able to figure out what could be wrong.
Any suggestion?
Thanks in advance!

I’m not sure you need Pack = 8. The default packing appears to be more or less compatible between C# and C++.
The szAcc field probably uses Unicode. To switch, specify CharSet=CharSet.Ansi in your [StructLayout]
Finally, if your C API has that double pointer to the array of them, you have to allocate that memory in a special way, either CoTaskMemAlloc or LocalAlloc, forgot which one.
It’s easier, also more efficient, to implement 2 functions instead. One to obtain the required size of the array. Another one to write them into caller-allocated buffer, a single pointer in C++ as opposed to double pointer, and UnmanagedType.LPArray in C#.
Update
allocation should not be necessary since the documentation states that the call returns a pointer to it's internal data structure
The runtime doesn’t know what was written in that documentation, attempts to free anyway, and crashes. You can use out IntPtr in C# API, and marshal manually.
If you have modern .NET, use unsafe, cast the IntPtr to PlcSymbolDescStruct* raw pointer, and construct ReadOnlySpan<PlcSymbolDescStruct> from that raw pointer.
if you’re stuck with old desktop .NET, call Marshal.PtrToStructure<PlcSymbolDescStruct> in a loop, incrementing the pointer by Marshal.SizeOf<PlcSymbolDescStruct>() bytes between loop iterations. No need to use unsafe code in this case.

Related

P/Invoke passing struct longer than native code expects

If I have a native code that expects a structure with two fields:
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern int my_method(ref MyStruct myStruct);
// this is what native code expects
public struct MyStruct {
IntPtr First;
IntPtr Second;
}
but instead I pass another struct to it, will it work or not - by design or by accident?
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern int my_method(ref MyLongerStruct myLongerStruct);
// this is what I want to pass to it
public struct MyLongerStruct {
IntPtr First;
IntPtr Second;
object ObjectPointer;
}
Will the object reference added to the end of the struct at C# side somehow affect P/Invoke call?
I shouldn't work. And even more, you need to add and properly set StructLayoutAttribute to the structure, as it explained here
I think, the result should be like this:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
IntPtr First;
IntPtr Second;
}
If the total difference in structure is fields added to the end, and you use StructLayout to prevent the compiler from optimizing the memory layout of your struct (as Alex Butenko suggests), then it's unlikely that there will be any negative side effects apart from a slight speed hit.
When you pass a managed struct to an external function via P/Invoke (using the DllImport attribute) there is a marshaling phase that converts your struct to a compatible format for the target. For ref and out parameters the temporary is converted back when the invoked function returns, copying the values back to your struct instance. All of this is abstracted away, although exactly how the marshaling is performed for each member can be tweaked with the right attributes.
This is how the .NET framework handles strings in P/Invoke. Since it can't just send a string instance pointer to an API function that is expecting a char * (the two are nothing alike) there has to be some translation.
The fun part is that the marshaling code doesn't know anything about what the target is expecting other than what you tell it at the C# end, so if you are sending an extended version of the structure it will do the whole thing. At the other end the native code will get a pointer to a memory block containing the information it's expecting, and it won't have any way to tell that there is more after the end of the structure.
Apart from that, no problem... as long as you're passing by reference and not by value. Passing structs by value is something that should raise big red stop signs all over your brain. Don't do it, it's evil.

typedef void* alternative in c#

I am using DLL runtime which is made with C language into C#.
I came across below statement.
typedef void *JCCP_PROPERTY_HANDLE;
In function it is being used as:
JCCP_RESULT __JCCP_FUNCTION__ jccpGetProperty(
JCCP_HANDLE hjccp,
const char *name,
JCCP_PROPERTY_HANDLE *phproperty);
Now I want to call jccpGetProperty() method in my C# code.
Can anybody tell how can I pass third parameter(JCCP_PROPERTY_HANDLE *phproperty) to function from C#.
I tried with below code but not working.
Extern Method:
[DllImport(DLL_NAME, EntryPoint = "_jccpGetProperty", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jccpGetProperty(IntPtr hjccp, string name, ref IntPtr JCCP_PROPERTY_HANDLE);
Usage
IntPtr handle = IntPtr.Zero;
string tag = "server.version";
var result = jccpGetProperty(hjccp, tag, ref handle);
Can anybody help me in this?
IntPtr is the correct type mapping for void*. The native type void* is generally used for an opaque pointer, and that is mapped to IntPtr in C#.
Those parts of the p/invoke declaration that we can verify are correct. The unverifible parts are:
The calling convention. You believe that it is cdecl, but we can't check.
The return type. You believe it to be pointer sized. Again we cannot check. My guess is that a 32 bit integer, int or uint is more likely. That would make a difference in a 64 bit process.
The values passed to the function. It's perfectly possible that the function is declared correctly, but you are passing invalid values.
Because you only showed partial code and details, it's hard to say much more. You will have to verify all the parts of the program that we cannot.
I suggest that you start with working C or C++ code and translate that, looking for the first point of deviation in behaviour between that code and your C# translation.

Are P/Invoke [In, Out] attributes optional for marshaling arrays?

Assume that there is a native function with a pure-C interface like the following one, exported from a native DLL:
// NativeDll.cpp
extern "C" void __stdcall FillArray(
int fillValue,
int count,
int* data)
{
// Assume parameters are OK...
// Fill the array
for (int i = 0; i < count; i++)
{
data[i] = fillValue;
}
}
The following P/Invoke works fine (tested with VS2010 SP1):
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
int fillValue,
int count,
[In, Out] int[] data
);
as well as this P/Invoke, same as above, but without the [In, Out] attributes:
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
int fillValue,
int count,
int[] data
);
So, are those [In, Out] attributes optional for marshaling arrays?
What is their purpose, if any?
Is it OK to omit them in our P/Invoke declarations?
No, they are not exactly optional. It just happens to work by accident. It is however a very common accident. It works because the array doesn't actually gets marshaled. The pinvoke marshaller sees that the C# array is already compatible with the native array so skips the step to create a copy of it. It merely pins the array and passes the pointer to the native code.
This is of course very efficient and you will inevitably get the results back because the native code is directly writing the array elements. So neither the [In] nor the [Out] attributes matter.
It gets much murkier if the array element type isn't that simple. It isn't that easy to identify an element type that's a struct or class type that isn't blittable or whose layout doesn't match after marshaling so the pinvoke marshaller has to make a copy of the array. Especially the layout incompatibility can be very hard to identify because the managed layout is undiscoverable. And can change depending on the jitter that is used. It may work in x86 but not x64 for example, pretty nasty when AnyCPU is selected. Getting it to copy the modified copy back to the C# array does require [Out].
Not sure what to advice, other than that nobody ever got fired for being explicit in their declarations. Perhaps you should always be explicit when the array element type isn't simple so you'll never have an accident.

Marshalling dynamic structures in 32 and 64 bit runtimes

I'm calling SetupDiGetDeviceInterfaceDetail() here and the SP_DEVICE_INTERFACE_DETAIL_DATA structure is not marshaling correctly. The structures definition can be found here. I've tried using the definition for this structure from PInvoke.net, here, but to no avail.
So far, when the call to the function succeeds (i.e. the marshaler doesn't throw an error), the return value is 1784 (INVALID_USER_BUFFER). The kicker is, when this code executes from a 32-bit process on my box, all of this works just fine. When it's run in a 64-bit process, I have this problem.
My current SetupDiGetInterfaceDetailData() signature looks like this:
[DllImport(#"c:\Windows\System32\SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
IntPtr requiredSize,
IntPtr deviceInfoData);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public UInt32 cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string DevicePath;
}
Currently, I am allocating memory with Marshal.AllocHGlobal() and writing/reading data from that buffer using the Marshal.* family of functions.
For reference, this is what I'm doing:
public string GetPathToDevice(SafeHandleZeroOrMinusOneIsInvalid hDevList,
SP_DEVICE_INTERFACE_DATA devIntfData)
{
uint sizeNeeded = 0;
// get's the size needed
SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
ref devIntfData,
IntPtr.Zero,
0,
ref sizeNeeded,
IntPtr.Zero);
IntPtr pBuffer = Marshal.AllocHGlobal((int)(sizeNeeded + 4)); // +4 for cbSize
SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
ref devIntfData,
pBuffer,
sizeNeeded,
IntPtr.Zero,
IntPtr.Zero);
// copy bytes from unmanaged space in pBuffer to a manged byte array
// free unmanaged memory
return theStringParsedFromByteArray;
}
As I mentioned, I've tried defining a structure as outlined by PInvoke.net for SP_DEVICE_INTERFACE_DETAIL_DATA (see above link) and made a new PInvoke method signature to handle that. When running from the 64-bit system, I get the same issue, i.e. the function returns 1784. The reason seems to be that references in C#, when running in a 64-bit runtime, are 8-byte aligned (found that in another StackOverflow article). I've tried various layouts to that structure trying to force the layout (using explicit and field offset) to a 4-byte aligned struct, but that didn't work for me either. I had compile time problems.
I have tried using various decorations to the PInvoke method sigature parameters. Such as, MarshalAs(UnmanagedType.LPStruct) to which I'm continually pairing improperly. I'm now to the point that I need help with this.
What I really don't understand is why it's happening at all. Even if it does work on my box when running in a 32-bit runtime, wouldn't a 64-bit runtime simply connect me to the correct 64-bit versions of the Setup API? What's the problem?
Thanks for any help,
Andy
Problem is solved
The good thing is, it's solved now, the irritating thing is I don't like fixing things within an hour or two of posting here. So, the problem was indeed that it was a 64 bit issue. The error code from Marshal.GetLastWin32Error() was telling me the problem. The cbSize value was incorrect. I changed it to 8 and everything works now.
Please, someone, explain to me why the size is now 8 on 64 bits? The structure is now above (a commenter asked me to include it). The structure consists of two members, a single DWORD and a TCHAR[ANYSIZE_ARRAY]. ANYSIZE_ARRAY evaluates to 1, TCHAR is always a WCHAR if Unicode and a char otherwise. A DWORD is always a 32-bit quantity (4 bytes) and a single TCHAR for Unicode is 2 bytes. So, 4 + 2 = 6. Why is it 8? Is this because of byte alignment for that structure in 64-bits? I'd really like to understand this.
At any rate, setting the cbSize member to 8 for 64 bit, and 6 for 32 bit, works and I'm able to use the structure defined above instead of the raw memory allocating/deallocating and marshaling.
I also fell over this problem, because i copied the code from this answer: https://stackoverflow.com/a/2937588/1070906
which has an error in the struct definition.
Maybe you did the same mistake.
Looking at the definition of SP_DEVINFO_DATA at http://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx it's last parameter is a pointer, not an uint as in the other post.
Since i changed the struct definition to:
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
/// <summary>Size of the structure, in bytes.</summary>
public uint cbSize;
/// <summary>GUID of the device interface class.</summary>
public Guid ClassGuid;
/// <summary>Handle to this device instance.</summary>
public uint DevInst;
/// <summary>Reserved; do not use.</summary>
public IntPtr Reserved;
}
it works!
Marshall.SizeOf(new SP_DEVINFO_DATA ()) returns 32 now instead of 28 and my serial ports are shown.

C# dll import function correctly

I am trying to import a function from a c dll into C#. The c function looks like this
unsigned short write_buffer( unsigned short device_number, unsigned short word_count, unsigned long buffer_link, unsigned short* buffer)
my attempt at a C# import looks like this
[DllImport("sslib32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern ushort write_buffer(ushort deviceNumber, ushort wordCount, UInt32 bufferLink, IntPtr buffer)
In C# i have a Dictionary of messages that i would like to pass to this function. The Dictionary looks like this:
Dictionary<string, List<ushort>> msgs
I am a bit confused how to make a make a proper call to pass msgs as the buffer. deviceNumber is 2, wordCount is 32, and buffLink is 0. So i know the call should look something like this
write_buffer(2,32,0, msgs[key]);
Obviously i am getting an invalid argument for the IntPtr. What is the proper way to make this call?
It is quite unclear what buffer should contain and in which direction its data flows. Your dictionary suggests it should be an array. Just declare it that way:
private static extern ushort write_buffer(.., ushort[] buffer);
And use msgs[key].ToArray() in the call.
Using constants in the write_buffer() call does not make that a likely scenario though, there ought to be msgs[key].Count in there somewhere.
You can generate P/Invoke signatures using the P/Invoke Interop Assistant tool that is referenced here.
In the January 2008 issue of the MSDN
Magazine, Yi Zhang and Xiaoying Guo
have published a CLR Inside Out column
on marshaling between managed and
unmanaged code. In that column, they
introduce the P/Invoke Interop
Assistant, an automatic GUI and
command-line utility for converting
between managed and unmanaged
signatures (in both directions). This
conversion, of course, is not limited
just to Windows signatures; give the
tool a snippet of your own C header
files and it will dutifully convert
them to pretty-printed C#
[DllImport]'s.
If you don't mind marking the code unsafe, you can simply do:
fixed(ushort* pData = msgs[key])
{
write_buffer(2,32,0, pData);
}
And declare your DllImport to take ushort* as the last argument.
The alternative is to use Marshal.StructureToPtr to get the array marshalled into fixed memory. This requires you to allocate memory first using AllocHGlobal, and freeing using FreeHGlobal.

Categories

Resources