I have a structure:
typedef struct _wfs_bcr_caps
{
WORD wClass;
BOOL bCompound;
BOOL bCanFilterSymbologies;
LPUSHORT lpwSymbologies;
DWORD dwGuidLights[32];
LPSTR lpszExtra;
BOOL bPowerSaveControl;
BOOL bAntiFraudModule;
}
I need to make a correct copy of this structure in C#.
But I have a problem with LPUSHORT type. Could some one help me to set up correct marshal attributes for lpwSymbologies property?
LPUSHORT is just long pointer to ushort value. You can marshal it as IntPtr and than read a value using Marshal.ReadInt16 or Marshal.ReadInt32 (since you are using unsigned short). Another option is described in this article, Unmanaged to Managed type translation table, e.g. marshalling LP<struct> into [In] ref <struct>
Related
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.
I am calling a C++ COM component using interop and the marshalling requires one of the parameters to be passed in as ref Byte. The argument is actually a string. How do I convert a string (or a char array) to Byte to pass to this method?
Method IDL
[helpstring("method Read")]
HRESULT _stdcall Read(
[in] unsigned char* path,
[in] unsigned char command,
[in] unsigned char nShortAddr,
[out] short* pnDataSize,
[out] unsigned char** ppbyData,
[out] unsigned long* pnError);
The IL
.method public hidebysig newslot virtual
instance void Read([in] uint8& path,
[in] uint8 command,
[in] uint8 nShortAddr,
[out] int16& pnDataSize,
[out] native int ppbyData,
[out] uint32& pnError) runtime managed internalcall
The method in the wrapper as seen in Visual Studio
public virtual void Read(ref byte path, byte command, byte nShortAddr,
out short pnDataSize, System.IntPtr ppbyData, out uint pnError)
This is just a poorly authored COM interface. It can only work when it is used in-process from C++ code. Which is probably all that the creator had intended. An argument type declaration like unsigned char* is ambiguous, it could mean that a single byte is passed by reference but could also indicate an array of bytes. You can use attributes in IDL to distinguish between the two but they were not used. The type library importer therefore could not assume anything else but that this was a ref byte.
There's only one decent fix if the source IDL can't be updated, you have to edit the interop library. Do so by decompiling the existing one first with ildasm.exe. Then edit the argument type, best done by creating a dummy C# interface with the same declaration as an example. Put humpty-dumpty back together with ilasm.exe
You cannot convert String to Byte
But a conversion of String to Byte[] is possible by using the ASCII encoding
String is .Net is - A string is a sequential collection of Unicode characters that is used to represent text. A String object is a sequential collection of System.Char objects that represent a string.
you can get String as Byte[] using System.Text.Encoding.ASCII.GetBytes(my_string)
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(my_string)
This question already has an answer here:
How to pass a nullable type to a P/invoked function [duplicate]
(1 answer)
Closed 5 years ago.
How can i skip the optional parameter (pioRecvPci) in C#?
I think the main problem is that in C the parameter is a pointer so it is possible to supply NULL while in C# the ref keyword on a struct is used which can't be null by definition.
C Code
typedef struct {
DWORD dwProtocol;
DWORD cbPciLength;
} SCARD_IO_REQUEST;
LONG WINAPI SCardTransmit(
__in SCARDHANDLE hCard,
__in LPCSCARD_IO_REQUEST pioSendPci,
__in LPCBYTE pbSendBuffer,
__in DWORD cbSendLength,
__inout_opt LPSCARD_IO_REQUEST pioRecvPci,
__out LPBYTE pbRecvBuffer,
__inout LPDWORD pcbRecvLength
);
C# Code
[StructLayout(LayoutKind.Sequential)]
public struct SCARD_IO_REQUEST
{
public int dwProtocol;
public int cbPciLength;
}
[DllImport("winscard.dll")]
public static extern int SCardTransmit(
int hCard,
ref SCARD_IO_REQUEST pioSendRequest,
ref byte SendBuff,
int SendBuffLen,
ref SCARD_IO_REQUEST pioRecvRequest,
ref byte RecvBuff,
ref int RecvBuffLen);
You can change the struct to a class and then pass null. Remember that a C# struct is quite different from a C++ struct, and here your really want to use a C# class.
Or if you always want to ignore pioRecvRequest change the signature of SCardTransmit so that pioRecvRequest is of type IntPtr. Then pass IntPtr.Zero for the value.
Actually, the SCARD_IO_REQUEST is just a header and if you want to pass it in the call you will have to manage this structure and the additional buffer space yourself anyway so IntPtr is the right choice. You will then have to use the Marshal functions to allocate and fill the structure before the call and unmarshal the data and free it after the call.
C# supports method overloads. Something you can take advantage of here, redeclare the method but now give it an IntPtr argument type (no ref). And pass IntPtr.Zero.
Second way is to marshal the structure yourself. Declare the argument type IntPtr. And use Marshal.AllocHGlobal() to allocate memory for the structure, Marshal.StructureToPtr() to copy the structure into it. Marshal.FreeHGlobal() after the call. Or pass IntPtr.Zero. Clearly the overload trick is much less painful.
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.
I'M browsing through the whole stackoverflow forum but I'm quite unsure if my marshalling solution fits to my problem.
I got a c++ method returning an array of integer via a parameter. The prototype is the following:
method1(uint aId, uint*& aNewIntArray, uint& aNewIntArrayCount);
I marshal the parameters like:
method1(UInt32 aId, ref UIntPtr aNewIntArray, ref UInt64 aNewIntArrayCount);
I marshal uint*& to ref UIntPtr but I'm not very sure if this is correct and while i not found another one having the same problem i'll ask on myself.
Another idea is: Is it possible to marshal int* and int& parameter the same way using
ref UInt32
? Or did I need to use UIntPtr / IntPtr without a "ref" Keyword?
In this case I would prefer to use ref instead of out to avoid the C++ uses a not initialized int reference.
Your C++ method is allocating an array of integers and returning a pointer to the first element in aNewIntArray. Therefore the match C# definition for that parameter is
ref IntPtr aNewIntArray
You used UIntPtr which is basically equivalent. I think you are under the impression that UIntPtr is what you use if the underlying array is unsigned but that is not the case. This is effectively a void* pointer and you will have to use Marshal.Copy to transfer to a C# array, uint[].
Note that since the aNewIntArray really is an out parameter I think you should declare it as such
You also have declared the final parameter incorrectly. It is a 32 bit integer.
I would therefore declare the C# function like this:
method1(uint aId, out IntPtr aNewIntArray, out uint aNewIntArrayCount);