I am working with SQL VDI and attempting to pass a structure from C# to C++ via a COM interface. The structure is defined in the C++ header as:
#pragma pack(8)
struct VDConfig
{
unsigned long deviceCount;
unsigned long features;
unsigned long prefixZoneSize;
unsigned long alignment;
unsigned long softFileMarkBlockSize;
unsigned long EOMWarningSize;
unsigned long serverTimeOut;
unsigned long blockSize;
unsigned long maxIODepth;
unsigned long maxTransferSize;
unsigned long bufferAreaSize;
} ;
To emulate this, I have defined the structure in C# as:
[StructLayout(LayoutKind.Explicit)]
public struct VDConfig
{
[FieldOffset(0)]
public uint deviceCount;
[FieldOffset(4)]
public uint features;
[FieldOffset(8)]
public uint prefixZoneSize;
[FieldOffset(12)]
public uint alignment;
[FieldOffset(16)]
public uint softFileMarkBlockSize;
[FieldOffset(20)]
public uint EOMWarningSize;
[FieldOffset(24)]
public uint serverTimeout;
[FieldOffset(28)]
public uint blockSize;
[FieldOffset(32)]
public uint maxIODepth;
[FieldOffset(36)]
public uint maxTransferSize;
[FieldOffset(40)]
public uint bufferAreaSize;
}
I have also tried to define the structure as LayoutKind.Sequential and tried it with Pack=8. However I define the structure, when I attempt to pass it to the function, it fails and I receive the error "Alignment must be 2**n and <= system allocation granularity." I've tried defining the function that accepts the structure as:
int CreateEx([MarshalAs(UnmanagedType.LPWStr)]string instanceName,
[MarshalAs(UnmanagedType.LPWStr)]string name,
IntPtr config);
and
int CreateEx([MarshalAs(UnmanagedType.LPWStr)]string instanceName,
[MarshalAs(UnmanagedType.LPWStr)]string name,
ref VDConfig config);
I get the same result with either definition. Can anyone tell me what I'm doing wrong here?
Edit:
In looking a little closer, I'm also getting the error "Device count must be in [1..64]." I'm setting the device count to 1 and in concert with the error above, it almost looks like the function isn't getting my structure at all. Don't know if this helps or not, but maybe it'll spark something for someone.
Per request, here are the interface structures. In C++:
MIDL_INTERFACE("d0e6eb07-7a62-11d2-8573-00c04fc21759")
IClientVirtualDeviceSet2 : public IClientVirtualDeviceSet
{
public:
virtual HRESULT STDMETHODCALLTYPE CreateEx(
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg) = 0;
virtual HRESULT STDMETHODCALLTYPE OpenInSecondaryEx(
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName) = 0;
};
And my C# version:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("d0e6eb07-7a62-11d2-8573-00c04fc21759")]
public interface IClientVirtualDeviceSet2
{
void CreateEx([In, MarshalAs(UnmanagedType.LPWStr)]string instanceName,
[In, MarshalAs(UnmanagedType.LPWStr)]string name,
[In]ref VDConfig config);
void OpenInSecondaryEx([MarshalAs(UnmanagedType.LPWStr)]string instanceName,
[MarshalAs(UnmanagedType.LPWStr)]string lpSetName);
}
For anyone else who comes across this, this is the answer:
As you can see in vdi.h, IClientVirtualDeviceSet2 "inherits" from IClientVirtualDeviceSet. As far as COM is concerned, there is no such thing as interface inheritance.
Therefore, when calling CreateEx on IClientVirtualDeviceSet2, you're actually calling Create on IClientVirtualDeviceSet (because Create is the first method in the vtable of that combined IClientVirtualDeviceSet + IClientVirtualDeviceSet2). That's why you end up getting invalid parameters.
The fix for this is to create a single interface (IClientVirtualDeviceSet2) with all the methods, IClientVirtualDeviceSet first, then the two IClientVirtualDeviceSet2 methods (obviously in order). This ensures when CreateEx() is called, it uses the correct DispId.
I'm sure you could probably use inheritance and set the DispIdAttribute accordingly:
https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dispidattribute(v=vs.110).aspx
but there is probably little point.
Related
I'm trying to build a struct for LDAP in C#, but I if I try to convert the InPtr to the struct I defined it throws the following exception:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_sslinitW",
SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr ldap_sslinit(string hostName, uint portNumber, int secure);
//https://learn.microsoft.com/en-us/windows/win32/api/winldap/ns-winldap-ldap
[StructLayout(LayoutKind.Sequential)]
public struct LDAP
{
[StructLayout(LayoutKind.Sequential)]
struct ld_sb
{
System.UIntPtr sb_sd;
byte Reserved1;
System.UIntPtr sb_naddr;
byte Reserved2;
}
string ld_host;
UInt32 ld_version;
byte ld_lberoptions;
UInt32 ld_deref;
UInt32 ld_timelimit;
UInt32 ld_sizelimit;
UInt32 ld_errno;
string ld_matched;
string ld_error;
ulong ld_msgid;
string Reserved3;
UInt32 ld_cldaptries;
UInt32 ld_cldaptimeout;
UInt32 ld_refhoplimit;
UInt32 ld_options;
}
private const uint LDAP_SSL_PORT = 636;
static void Main(string[] args)
{
IntPtr ld = ldap_sslinit("test", LDAP_SSL_PORT, 1);
var ldap = Marshal.PtrToStructure(ld, typeof(LDAP));
}
Before this I tried to declare the ldapsslinit method with private static extern LDAP ldap_sslinit(string hostName, uint portNumber, int secure); However, it returns the following error:
'Method's type signature is not PInvoke compatible.'
I think the problem is caused by the LDAP struct I defined, but I don't known which type from unmanged to managed was wrong.
unmanaged
managed
UINT_PTR
UIntPtr
UCHAR*
byte[]
ULONG_PTR
UIntPtr
PCHAR
string
ULONG
UInt32
UCHAR
byte
Did I use the wrong mapping in this table?
What specifically are you trying to accomplish. There are managed types in C# for connecting to and using LDAP that don't require native implementations like you are trying to use.
If for some reason you need the native implementation (Note: since you are stuck on step one of a complex handshake I would recommend using a managed implemtation), Instead of the c++ definition of the nested ldap_sslinit struct declare it outside of LDAP
struct ld_sb
{
System.UIntPtr sb_sd;
byte Reserved1;
System.UIntPtr sb_naddr;
byte Reserved2;
}
public struct LDAP
{
ld_sb ld_sb;
string ld_host;
//...
}```
First: I'm sorry if the title is wrong. I'm not sure how to name my problem.
In my C API I have a function:
MYAPI_STATUS SetParam(void *hInst, unsigned long param, void *value);
This function accepts different types of pointers depending on param type. Like this:
SetParam(hInst, 1, (void*)"somevalue");
int x = 55;
SetParam(hInst, 2, &x);
I'm just writing a wrapper/binding in C# and I have a problem.
[DllImport("myapi", CallingConvention = CallingConvention.Cdecl]
public static extern uint SetParam(IntPtr hInst, uint paramCode, IntPtr paramValue);
What's the best way to replicate behaviour from C? So the function would look like:
public static uint SetParam(IntPtr hInst, uint paramCode, ref object paramValue);
or possibly:
public static uint SetParam(IntPtr hInst, uint paramCode, object paramValue);
I solved it by marshalling manually first checking type of object if the object is string then I use Marshal.StringToHGlobalAnsi if it's something else then I marshall differently based on what I need.
If someone has any better solution feel free to write :)
The * sign in C programming means give parameter by reference, so this code is not match:
public static uint SetParam(IntPtr hInst, uint paramCode, object paramValue);
Because it gives parameter by value.
This code is very similar to what you want:
public static uint SetParam(IntPtr hInst, uint paramCode, ref object paramValue);
But there is a bit difference. When you using ref before a parameter you have to initialize it before sending to the method, but by using out you don't have this limitation for passing it. So I think the best possible match will be this code:
public static uint SetParam(IntPtr hInst, uint paramCode, out object paramValue);
This structure is define at http://msdn.microsoft.com/en-us/library/windows/hardware/ff541621%28v=vs.85%29.aspx
typedef struct _FILTER_MESSAGE_HEADER {
ULONG ReplyLength;
ULONGLONG MessageId;
} FILTER_MESSAGE_HEADER, *PFILTER_MESSAGE_HEADER;
I defined it in C# code as below:
[StructLayout(LayoutKind.Sequential)]
public struct FILTER_MESSAGE_HEADER {
public uint replyLength;
public ulong messageId;
};
I only define FILTER_MESSAGE_HEADER in C# code, PFILTER_MESSAGE_HEADER isn't.
How should I do to define PFILTER_MESSAGE_HEADER??
P/S: I want to define PFILTER_MESSAGE_HEADER to use this struct in a function.
You don't have to (can't) define PFILTER_MESSAGE_HEADER. Just specify it as either out or ref as appropriate.
[DllImport("foo")]
void SomeMethod(ref FILTER_MESSAGE_HEADER lpMessageBuffer);
If you are specifically interested in FilterGetMessage, I'm not sure what if any dll it is exported from, but one possible signature would be as below:
[DllImport(fltmgr, CharSet=CharSet.Unicode, ExactSpelling=true, PreserveSig=false)]
void FilterGetMessage(
CommunicationPortSafeHandle hPort,
ref FILTER_MESSAGE_HEADER lpMessageBuffer,
uint dwMessageBufferSize,
IntPtr lpOverlapped);
I used PreserveSig to automatically translate the HRESULT to an exception in the event of failure, the CharSet specification is defensive and results in the need for ExactSpelling. CommunicationPortSafeHandle would be a class which inherits from SafeHandleMinusOneIsInvalid based off of the documentation on FilterConnectCommunicationPort.
You would use this signature as:
FILTER_MESSAGE_HEADER header;
FilterGetMessage(hFilter, ref header,
Marshal.SizeOf(typeof(FILTER_MESSAGE_HEADER)), IntPtr.Zero);
According to my understanding, MarshalAsAttribute(UnmanagedType.SysUInt) is supposed to marshal a platform-specific unsigned integer type (either 32 or 64 bytes) into the managed type (ulong).
/// Return Type: size_t->unsigned int
///bgr: uint8_t*
///width: int
///height: int
///stride: int
///output: uint8_t**
[DllImportAttribute("libwebp.dll", EntryPoint = "WebPEncodeLosslessBGR")]
[return: MarshalAsAttribute(UnmanagedType.SysUInt)]
public static extern ulong WebPEncodeLosslessBGR([InAttribute()] IntPtr bgr, int width, int height, int stride, ref IntPtr output);
But it doesn't work - I get this error:
Cannot marshal 'return value': Invalid managed/unmanaged type combination (Int64/UInt64 must be paired with I8 or U8).
I know I can switch the return type to IntPtr, but that's very un-intuitive on the people using my API.
Why is SysUInt not working?
You could keep the PInvoke method private with UIntPtr, and implement another method with your preferred signature, that call the PInvoke mapping everything correctly, and this one would be public:
/// Return Type: size_t->unsigned int
///bgr: uint8_t*
///width: int
///height: int
///stride: int
///output: uint8_t**
public static ulong WebPEncodeLosslessBGR([InAttribute()] IntPtr bgr, int width, int height, int stride, ref IntPtr output)
{
return (ulong)_WebPEncodeLosslessBGR(bgr, width, height, stride, ref output);
}
[DllImportAttribute("libwebp.dll", EntryPoint = "WebPEncodeLosslessBGR")]
[return: MarshalAsAttribute(UnmanagedType.SysUInt)]
private static extern UIntPtr _WebPEncodeLosslessBGR([InAttribute()] IntPtr bgr, int width, int height, int stride, ref IntPtr output);
When frameworks gets to difficult to deal with... just don't use them. Marshaling is a pain, I tend to just use the things that I already know... everything else, I just go around.
EDIT
It is not working because the marshaler is not smart enough to see that every SysUInt type fits in a ulong type. It is checking the return, the same as arguments.
It is true that you cannot use ulong and SysUInt for an argument, but you could for the return... it is just not smart to see the difference. =\
What alternatives are there?
UIntPtr seems to be the best choice... but there are other choices: implement a custom marshaler, using interface ICustomMarshaler... and use UnmanagedType.CustomMarshaler:
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CustomMarshalerType))]
ICustomMarshaler implementation
With this implementation of ICustomMarshaler you may be abled to do what you want. I did not test it, since I don't have an unmanaged library to do the test, but it is straight forward, and very simple... so I think it will work as it is, without any changes. If it don't, just leave a comment, and I'll revise it.
public class CustomMarshalerType : ICustomMarshaler
{
public object MarshalNativeToManaged(IntPtr pNativeData)
{
return (ulong)Marshal.ReadIntPtr(pNativeData).ToInt64();
}
public IntPtr MarshalManagedToNative(object ManagedObj)
{
throw new InvalidOperationException();
}
public void CleanUpNativeData(IntPtr pNativeData)
{
}
public void CleanUpManagedData(object ManagedObj)
{
}
public int GetNativeDataSize()
{
return IntPtr.Size;
}
}
I have a dll which accepts a struct that contains a pointer to a function to do a callback.
How can I get an IntPtr to a function of my application to build the struct?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class OPERATION {
public uint OperationID;
public IntPtr Context;
public IntPtr Callback; -> How to pass this?
}
Here is the delegate accepting the OPERATION struct
public delegate void MY_CALLBACK([In] OPERATION operation, [In] uint msgId, [In] IntPtr msgDataPtr);
use Marshal.GetFunctionPointerForDelegate
Maybe the Marshal.GetFunctionPointerForDelegate method may help you.