Passing `null` reference for a `ref struct` parameter in interop method - c#

I am using C# to call a DLL function.
[DllImport("MyDLL.dll", SetLastError = true)]
public static extern uint GetValue(
pHandle handle,
ref somestruct a,
ref somestruct b);
How can I pass a null reference for argument 3?
When I try, I am getting a compile-time error:
Cannot convert from <null> to ref somestruct.
I also tried IntPtr.Zero.

You have two options:
Make somestruct a class, and change the function signature to:
[DllImport("MyDLL.dll", SetLastError = true)]
public static extern uint GetValue(
pHandle handle, somestruct a, somestruct b);
Usually this must not change anything else, except that you can pass a null as the value of a and b.
Add another overload for the function, like this:
[DllImport("MyDLL.dll", SetLastError = true)]
public static extern uint GetValue(
pHandle handle, IntPtr a, IntPtr b);
Now you can call the function with IntPtr.Zero, in addition to a ref to an object of type somestruct:
GetValue(myHandle, ref myStruct1, ref myStruct2);
GetValue(myHandle, IntPtr.Zero, IntPtr.Zero);

Since .NET 5.0 there is System.Runtime.CompilerServices.Unsafe.NullRef<T>()
GetValue(myHandle, ref myStruct1, ref myStruct2);
GetValue(myHandle, ref Unsafe.NullRef<somestruct>(), ref Unsafe.NullRef<somestruct>());

This answer suggests to make SomeStruct a class. I would like to show an implementation of that idea which appears to work nicely… even when you cannot change the definition of SomeStruct (such as when it is a predefined type like System.Guid; see also this answer).
Define a generic wrapper class:
[StructLayout(LayoutKind.Explicit)]
public sealed class SomeStructRef
{
[FieldOffset(0)]
private SomeStruct value;
public static implicit operator SomeStructRef(SomeStruct value)
{
return new SomeStructRef { value = value };
}
}
The basic idea here is identical to boxing.
Change your interop method definition to the following:
[DllImport("MyDLL.dll", SetLastError = true)]
public static extern uint GetValue(
pHandle handle,
ref SomeStruct a,
[MarshalAs(UnmanagedType.LPStruct)] SomeStructRef b);
The third parameter b will then be "nullable". Since SomeStructRef is a reference type, you can pass a null reference. You can also pass a SomeStruct value because an implicit conversion operator from SomeStruct to SomeStructRef exists. And (at least in theory), due to the [StructLayout]/[FieldOffset] marshalling instructions, any instance of SomeStructRef should get marshalled just like an actual instance of SomeStruct.
I'd be happy if someone who is an interop expert could validate the soundness of this techinque.

Another obvious solution is to resort to unsafe code and change the interop method declaration to this:
[DllImport("MyDLL.dll", SetLastError = true)]
unsafe public static extern uint GetValue(
pHandle handle,
ref somestruct a,
somestruct* b);
Notice that the method is now marked unsafe, and that the parameter has changed from ref somestruct to somestruct*.
This has the following implications:
The method can only be called from inside an unsafe context. For example:
somestruct s;
unsafe { GetValue(…, …, &s); } // pass a struct `s`
unsafe { GetValue(…, …, null); } // pass null reference
In order for the above to work, unsafe code must be allowed for the project (either in the project settings, or via the /unsafe command-line compiler switch).
Using unsafe leads to unverifiable IL code. IIRC, this means that loading this assembly will require full trust (which can be problematic in some situations).

Related

Passing different type of objects to unmanaged function

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);

C# How to define new pointer struct?

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);

passing structure from c# to C dll

I am trying to learn enough C# so that I can pass a strcture by reference to a C DLL; but it never gets to the "cFunction". As you can see in the cFunction, I am explicitly setting the streamSubset value to 44; but back in the c# portion it does not return "44".
Here is the C code:
typedef struct s_mPlot
{
double price[100];
int streamSubset;
} mPlot;
extern "C" __declspec( dllexport )
void cFunction(mPlot *Mplot){
Mplot->streamSubset = 44;}
// and here is the c# code
using System;
using Vibe.Function;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class MPLOT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double [] price;
public int streamSubset;
}
namespace Vibe.Indicator{
public class myIdx : IndicatorObject {
[DllImport("C:\\Users\\joe\\mcDll.dll", CharSet = CharSet.Auto)]
public static extern void cFunction(
[In, MarshalAs(UnmanagedType.LPStruct)] MPLOT mPlot );
public myIdx(object _ctx):base(_ctx){}
private IPlotObject plot1;
protected override void Create()
{
MPLOT mPlot = new MPLOT();
mPlot.streamSubset = 2;
cFunction(mPlot);
if (mPlot.streamSubset == 44)
go();
}
}
}
I can see the following:
You almost certainly need to specify the cdecl calling convention in your DllImport attribute. Add CallingConvention=CallingConvention.Cdecl.
I believe that UnmanagedType.LPStruct adds an extra level of indirection. But you are passing a C# class which is a reference type. That means you are passing a pointer to a pointer. That's one level of indirection too many. First of all remove [In, MarshalAs(UnmanagedType.LPStruct)] altogether. Then your code should work. If you switched to a struct rather than a class for MPLOT then you'd need to pass by ref to get the indirection.
I think I would have the code like this:
[StructLayout(LayoutKind.Sequential)]
public struct MPLOT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double [] price;
public int streamSubset;
}
[DllImport("dllname.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void cFunction(
ref MPLOT mPlot
);
Try specifying the calling convention explicitly:
[DllImport("C:\\Users\\joe\\mcDll.dll", CallingConvention=CallingConvention.Cdecl CharSet = CharSet.Auto)]
VC exports with calling convention cdecl by default, but DllImport uses stdcall by default. So you have to specify at least one of them explicitly, or better, both.
Replace [In, MarshalAs(UnmanagedType.LPStruct)] with ref.

Strange consequences of "ref" keyword in C# to COM interop

Consider an excerpt from code that can be found here:
namespace WinSearchFile
{
public class Parser
{
[DllImport("query.dll", CharSet = CharSet.Unicode)]
private extern static int LoadIFilter (string pwcsPath, ref IUnknown pUnkOuter, ref IFilter ppIUnk);
[ComImport, Guid("00000000-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IUnknown
{
[PreserveSig]
IntPtr QueryInterface( ref Guid riid, out IntPtr pVoid );
[PreserveSig]
IntPtr AddRef();
[PreserveSig]
IntPtr Release();
}
private static IFilter loadIFilter(string filename)
{
IUnknown iunk = null;
IFilter filter = null;
// Try to load the corresponding IFilter
int resultLoad = LoadIFilter( filename, ref iunk, ref filter );
if (resultLoad != (int)IFilterReturnCodes.S_OK)
{
return null;
}
return filter;
}
}
Parser::loadIFilter() in that code basically calls LoadIFilter() function. The latter looks up the registry, finds which class id corresponds to the specified file extension, instantiates a corresponding COM class (calls CoCreateInstance()) and calls IPersistFile::Load() from it.
Now the problem is that the signature for LoadIFilter() is the following:
HRESULT __stdcall LoadIFilter( PCWSTR pwcsPath, __in IUnknown *pUnkOuter, __out void **ppIUnk );
so the second parameter is IUnknown* of the aggregating object. If the COM class for the extension of interest doesn't support aggregation and the IUnknown* passed is not null CoCreateInstance() returns CLASS_E_NOAGGREGATION and so does LoadIFilter().
If I remove the ref keyword from the pUnkOuter parameter in the declaration and at the site of LoadIFilter() call the function is called with null IUnknown*. If I retain the ref keyword the function is called with non-null IUnknown* and returns CLASS_E_NOAGGREGATION for classes that don't support aggregation.
My question is - why is non-null IUnknown* passed when the keyword is retained? IUnknown iunk local variable is initialized to null so where does non-null IUnknown* come from in the invoked unmanaged code?
When you use the ref, you're not actually sending in the null, you're sending in a reference to where that null is stored, but when you're sending in without the ref you're sending in the actual value, which is null.
So:
With ref it's a reference to a null pointer.
Without it it's just a null pointer.
Edit: If I understand your question correctly, which I'm not 100% certain off...
Using ref is simply wrong, your IUnknown is already passed as a pointer since it is an interface. Passing ref would be equivalent to IUnknown**
The non-null bit comes from inside the method - it is instantiating an object onto the reference you've provided. Using the ref keyword on a reference type will pass the callers reference to the object, not creating another reference to the object (which is what happens when you pass by reference normally). Try this:
static void Main()
{
object foo = null;
SetMyObject(ref foo);
bool test = foo == null;
}
public static void SetMyObject(ref object foo)
{
foo = new object();
}
The variable test will be false.

Parameter in C#

When I want get total value of memory in C# I found a kernel32 function in MSDN to invoke data from system. MSDN declare function this way:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
but this don't work correctly. I change "ref" to "[In, Out]" then it work correctly.
How can tell me what is [In, Out] parameters in C#?
In: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.inattribute.aspx
Out: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.outattribute.aspx
Short: They control the way data is marshalled. In this case, where you specify both of them, it means that data is marshalled to both sides (caller and callee).
The out and the ref parameters are used to return values in the same variables, ref is enough if you don't know you will use it in or out.
Out if you just want to use the variable to receive data from the function, In if you just want to send data to the function.
ref if you want to send and receive data from a function, if you put nothing so it will be In by default
Note: ref and out parameters are very useful when your method needs to return more than one values.
The following definition works (define the MEMORYSTATUSEX as a class):
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GlobalMemoryStatusEx(MEMORYSTATUSEX lpBuffer);
[StructLayout(LayoutKind.Sequential)]
public sealed class MEMORYSTATUSEX {
public uint dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
Usage
var status = new MEMORYSTATUSEX();
GlobalMemoryStatusEx(status);
If you look at the function definition on MSDN it will tell you whether the parameters are In/Out:
BOOL WINAPI GlobalMemoryStatusEx(
__inout LPMEMORYSTATUSEX lpBuffer
);
In general if it says out, you should use a ref parameter, it makes is easier on any future developers trying to figure out how the code is working. When looking at the function call, you know the developer meant for the argument to be affected.

Categories

Resources