In an attempt to learn to use PInvoke in C#, I'm a little unsure how to handle various cases with pointers involving simple value types.
I'm importing the following two functions from an unmanaged DLL:
public int USB4_Initialize(short* device);
public int USB4_GetCount(short device, short encoder, unsigned long* value);
The first function uses the pointer as an input, the second as an output. Their usage is fairly simple in C++:
// Pointer as an input
short device = 0; // Always using device 0.
USB4_Initialize(&device);
// Pointer as an output
unsigned long count;
USB4_GetCount(0,0,&count); // count is output
My first attempt in C# results in the following P/Invokes:
[DllImport("USB4.dll")]
public static extern int USB4_Initialize(IntPtr deviceCount); //short*
[DllImport("USB4.dll")]
public static extern int USB4_GetCount(short deviceNumber, short encoder, IntPtr value); //ulong*
How do I use these functions in C# in the same way as the C++ code above? Is there a better way to declare these types, perhaps using MarshalAs?
If the pointer is to a single primitive type and not an array, use ref / out to describe the parameter
[DllImport("USB4.dll")]
public static extern int USB4_Initialize(ref short deviceCount);
[DllImport("USB4.dll")]
public static extern int USB4_GetCount(short deviceNumber, short encoder, ref uint32 value)
In these examples out is probably more appropriate but either will work.
The .NET runtime can do a lot of that conversion (referred to as "marshaling") for you. While an explicit IntPtr will always do EXACTLY what you tell it to, you can likely substitute the ref keyword for a pointer like that.
[DllImport("USB4.dll")]
public static extern int USB4_Initialize(ref short deviceCount); //short*
[DllImport("USB4.dll")]
public static extern int USB4_GetCount(short deviceNumber, short encoder, ref short value); //ulong*
You can then call them like this:
short count = 0;
USB4_Initialize(ref count);
// use the count variable now.
Related
Good Morning, I am attempting to create a C# dot net core wrapper for the Audio functionality of SDL.
In particular,
I am attempting to create a wrapper for https://wiki.libsdl.org/SDL_LoadWAV
The existing code is:
To represent the non basic types being marshalled:
public delegate void SDL_AudioCallback(IntPtr userdata, byte[] stream,out int len);
[StructLayout(LayoutKind.Sequential)]
public struct SDL_AudioSpec
{
public int freq;
public ushort format;
public byte channels;
public byte silence;
public ushort samples;
public UInt32 size;
public IntPtr callback;
IntPtr userdata;
}
public class SDL_AudioSpecPtr
{
public readonly IntPtr NativePointer;
public SDL_AudioSpecPtr(IntPtr pointer)
{
NativePointer = pointer;
}
public static implicit operator IntPtr(SDL_AudioSpecPtr Sdl2Window) => Sdl2Window.NativePointer;
public static implicit operator SDL_AudioSpecPtr(IntPtr pointer) => new SDL_AudioSpecPtr(pointer);
}
Importing the Method signature:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate SDL_AudioSpec SDL_LoadWAV_t(string file, IntPtr spec, byte[] audio_buf, out UInt32 audio_len);
private static SDL_LoadWAV_t s_sdl_LoadWAV = LoadFunction<SDL_LoadWAV_t>("SDL_LoadWAV");
public static void SDL_LoadWAV(string file, IntPtr spec, byte[] audio_buf, out UInt32 audio_len) => s_sdl_LoadWAV(file,spec,audio_buf,out audio_len);
Attempting to call the function yields null exception:
Sdl2Native.SDL_Init(SDLInitFlags.Audio);
SDL_AudioSpec spec = new SDL_AudioSpec();
byte[] bytes=new byte[20000];
uint len;
SDL_AudioSpec* specptr = &spec;
Sdl2Native.SDL_LoadWAV(#"G:\Assets\sfx_movement_jump2.wav", (IntPtr)specptr, bytes, out len);
You should rely more heavily on the CLR's marshalling rather than doing your own, it's a lot easier to make serious errors.
Furthermore:
Strings need marshalling to ANSI with [MarshalAs(UnmanagedType.LPStr)]
You shouldn't just convert & pointer to * like that, the struct needs marshalling.
A callback given to unmanaged code needs to be held onto, so that the GC doesn't sweep it away.
It's unclear why you have a LoadFunction setup rather than just using standard P/Invoke, but I've run with that.
So the delegate should be
private static SDL_AudioSpec SDL_LoadWAV_t(
[MarshalAs(UnmanagedType.LPStr)] string file,
in SDL_AudioSpec spec,
ref byte[] audio_buf,
out UInt32 audio_len);
and then SDL_AudioSpec struct should declare the callback
public SDL_AudioCallback callback;
The callback is wrong also: len is not a out parameter, it is by-value; and it's unclear if stream should be ref or out or in, I think out.
public delegate void SDL_AudioCallback(IntPtr userdata, out byte[] stream, int len);
Depending on how long the callback is being used for, you need to hold onto it
If it is just used within a single call, and is not held by unmanaged code, then place GC.KeepAlive(callback); after the call
If unmanaged code holds it further then you would need to store it in a field somewhere, where you can guarantee it will not be deleted by GC before the last callback
Your calling code is then:
var callbackDlgt = new SDL_AudioCallback(myCallbackFunction);
SDL_AudioSpec spec = new SDL_AudioSpec{
callback = callbackDlgt,
// set other values here
};
byte[] bytes = new byte[20000];
Sdl2Native.SDL_LoadWAV(#"G:\Assets\sfx_movement_jump2.wav", in specptr, ref bytes, out uint len);
GC.KeepAlive(callbackDlgt);
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);
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;
}
}
How can I call the following method from C#, which is in a C++ dll? How Can I recreate the following structure in C#?
Original
Method:
LONG MyMethod (P_HOLO_INFO pInfo, LPVOID pBuffer, LPUSHORT pTracksWritten);
Structure: This method uses the following structure:
typedef struct _HOLO_INFO
{
LONG lHoloType;
LONG lStatus;
HANDLE lThreadHandle;
LONG lErrorCode;
LONG lDriveIndex;
LONG lHeight;
SHORT iFormat;
INT iStartingTrack;
LONG lWrite;
LONG lSkip;
BOOL bSkipZero;
BOOL bInvert;
LONG lMaxConsecutiveErrors;
LONG lMaxTotalErrors;
LONG lMostConsecutiveErrors;
LONG lTotalErrors;
LPBYTE pBuffer;
LPUSHORT pTracksWritten;
LONG bUpsideDown;
} HOLO_INFO, *P_HOLO_INFO;
I worked in C# like this
Method:
[DllImport("My.dll", EntryPoint = "_MyMethod#12")]
public unsafe static extern long MyMethod(ref HOLO_INFO pInfo, Byte[] pBuffer,ref ushort pTracksWritten);
Structure:
This method uses the following structure:
unsafe public struct HOLO_INFO
{
public long lHoloType;
public long lStatus;
public long lThreadHandle;
public ulong lErrorCode;
public long lDriveIndex;
public long lHeight;
public short iFormat;
public int iStartingTrack;
public long lWrite;
public long lSkip;
public bool bSkipZero;
public bool bInvert;
public long lMaxConsecutiveErrors;
public long lMaxTotalErrors;
public long lMostConsecutiveErrors;
public long lTotalErrors;
public Byte* pBuffer;
public long* pTracksWritten;
public long bUpsideDown;
};
I made a call to the method like this:
do
{
result = MyMethod(ref pInfo,ptrBuf,pTracksWritten);
} while (result ==1 );
Because, it returns 1, if it is Active
0, if it completed successfully
3, if it stopped because of error.
if the method is in running state(Active-1). it modifies pInfo and pTracksWritten to update the status information.
Lots of issues:
LONG should be declared as int in C#
HANDLE is IntPtr.
pTracksWritten is missing. You probably need to make it, and pBuffer, an IntPtr and use Marshal.AllocHGlobal to allocate memory for them, depends.
You need the CallingConvention in the [DllImport] declaration to use Cdecl.
Odds of getting this to work are not great if you can't debug the unmanaged code. One basic sanity test is to make sure that Marshal.SizeOf() returns the same length as sizeof() in the unmanaged code. Next verify that passed arguments look good when debugging the native code. Next triple-check the pointer usage in the native code and verify that they are not getting copied.
See Using a Very C# DLL in C++
You can do a 'simple' trick [this answer](answer Using a Very C# DLL in C++) or you can have a look at fullblown embedding as per my answer
Give this a shot:
[DllImport("My.dll", EntryPoint = "_MyMethod#12")]
int MyMethod (HOLO_INFO pInfo, IntPtr pBuffer, IntPtr pTracksWritten);
public class HOLO_INFO
{
public int lHoloType;
public int lStatus;
public IntPtr lThreadHandle;
public int lErrorCode;
public int lDriveIndex;
public int lHeight;
public short iFormat;
public int iStartingTrack;
public int lWrite;
public int lSkip;
public bool bSkipZero;
public bool bInvert;
public int lMaxConsecutiveErrors;
public int lMaxTotalErrors;
public int lMostConsecutiveErrors;
public int lTotalErrors;
public IntPtr pBuffer;
public IntPtr pTracksWritten;
public int bUpsideDown;
}
Depending on how they're allocated, you may need to use Marshal.Copy to access HOLO_INFO.pBuffer and Marshal.PtrToStructure to access HOLO_INFO.pTracksWritten (or Marshal.Copy if it's an array vs. a pointer to a singular value).
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.