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);
Related
I am working in C# and I need to call a function in a C++ dll library. This function returns a struct but I can´t get anything.
This is the function I need to call and the struct that returns in C++ library:
ATHENA_API _DEVICE_INFO* __stdcall GetDeviceInfoKeepConnection(_DEVICE_INFO* pDeviceInfo);
typedef struct TD_DEVICE_INFO{
TCHAR chDeviceName[256];
int nCommPort;
int nECGPos;
int nNumberOfChannel;
int nESUType;
int nTymestampType;
int nDeviceHandle;
TCHAR chDeviceID[260];
}_DEVICE_INFO;
This is my C# code trying to call the function:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
static extern _DEVICE_INFO GetDeviceInfoKeepConnection(_DEVICE_INFO deviceInfo);
[StructLayout(LayoutKind.Sequential)]
struct _DEVICE_INFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string chDeviceName;
public int nCommPort;
public int nECGPos;
public int nNumberOfChannel;
public int nESUType;
public int nTymestampType;
public int nDeviceHandle;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string chDeviceID;
}
void Test_Click()
{
_DEVICE_INFO d = new _DEVICE_INFO();
_DEVICE_INFO deviceInfo = GetDeviceInfoKeepConnection(d);
}
The only I can get is an empty _DEVICE_INFO object. I think my problem is that I am not defining correctly the DEVICE INFO struct.
I have never worked with dll´s to this level. Can you help me?
Thanks in advance.
Thanks to all!! The problem has solved with this:
Parameter pass by reference and struct charset Unicode.
It seems that the function GetDeviceInfoKeepConnection returns a pointer to _DEVICE_INFO struct. So, in your C# code, you need to change the definition of the function to:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
static extern IntPtr GetDeviceInfoKeepConnection(IntPtr deviceInfo);
And then you can access the struct data like this:
void Test_Click()
{
_DEVICE_INFO d = new _DEVICE_INFO();
IntPtr pDeviceInfo = Marshal.AllocHGlobal(Marshal.SizeOf(d));
Marshal.StructureToPtr(d, pDeviceInfo, false);
IntPtr deviceInfo = GetDeviceInfoKeepConnection(pDeviceInfo);
_DEVICE_INFO result = (_DEVICE_INFO)Marshal.PtrToStructure(deviceInfo, typeof(_DEVICE_INFO));
Marshal.FreeHGlobal(pDeviceInfo);
}
Note that, to ensure that the memory is cleaned up after use, you should use the Marshal.FreeHGlobal method to free the memory that was allocated by Marshal.AllocHGlobal.
let say we have following code in a dll written in C-lang, where i try to map some functions defined in the dll as functionpointers, map to their actual functions, i follow following link to get till here
https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays
Dlltest.h
typedef struct VersionInfo
{
UINT uMajor;
UINT uMinor;
UINT uMRevision;
} STRUCT_VERSION_INFO;
typedef struct DllTestFPStruct
{
int(*Close) (void);
int(*Init) (STRUCT_VERSION_INFO *sVersInfo);
int(*RegisterClient) (int id);
} STRUCT_DLL_TEST_FP_STRUCT;
typedef struct DllTestMgr
{
STRUCT_DLL_TEST_FP_STRUCT *psItf;
int iDummy;
} STRUCT_DLL_TEST_MGR;
extern "C"
{ __declspec(dllexport) void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP); }
Dlltest.c
static int Close(void);
static int Init(STRUCT_VERSION_INFO *sVersInfo);
static int RegisterClient(int id);
STRUCT_DLL_TEST_FP_STRUCT sFP =
{
&Close,
&Init,
&RegisterClient,
};
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{ psFP->psItf = &sFP; }
static int Close(void)
{ printf("Closed called.\n"); }
static int Init(STRUCT_VERSION_INFO *sVersInfo)
{ printf("Init called.\n");}
static int RegisterClient(STRUCT_VERSION_INFO *sVersInfo)
{ printf("RegisterClient called.\n");}
Now i want to write a c# application which uses this DLL, specially it should make use of the "GetDllTestFP"-Function which maps the functionpointers to their actuall function. right now my C#-Application is as follow:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_CLOSE(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_INIT(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_RC(ref VersionInfo sVersInfo);
[StructLayout(LayoutKind.Sequential)]
public struct DllTestFPStruct
{
public FP_DLL_TEST_CLOSE Close;
public FP_DLL_TEST_INIT Init;
public FP_DLL_TEST_RC RegisterClient;
}
[StructLayout(LayoutKind.Sequential)]
public struct DllTestMgr
{
public IntPtr psItf;
public int iDummy;
}
[DllImport("DllTestC.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void GetDllTestFP(ref DllTestMgr ps);
static void Main(string[] args)
{
VersionInfo vInfo = new VersionInfo();
DllTestFPStruct dllFPs = new DllTestFPStruct();
DllTestMgr dllTest = new DllTestMgr();
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(dllFPs));
Marshal.StructureToPtr(dllFPs, buffer, false);
dllTest.psItf = buffer;
GetDllTestFP(ref dllTest); // Funtionpointers are not mapped, still null
dllFPs.Close(ref vInfo);
}
The problem is, that the functions do not get mapped to their actuall functions in the dll.
Any idea how can i achieve my goal?
Thanks
The C# code is:
DllTestMgr dllTest = new DllTestMgr();
GetDllTestFP(ref dllTest);
DllTestFPStruct dllFPs = (DllTestFPStruct)Marshal.PtrToStructure(dllTest.psItf, typeof(DllTestFPStruct));
VersionInfo vInfo = new VersionInfo();
dllFPs.Close(ref vInfo);
You don't need to allocate dllTest.psItf becase in GetDllTestFP you do:
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{
psFP->psItf = &sFP;
}
So you copy the address of sFP.
Note that in general this is a bad idea, because you are giving to the "client" direct access to your data (the sFP struct). The alternative is that the client passes the memory (as you wrote before) and then you do:
(*psFP->psItf) = sFP;
(but then remember to free the allocated memory!)
Third alternative, the C-side allocates a block of memory through a shared allocator (one that can be used by C#, so no malloc/new here) and then the C# has to deallocate it.
wrong solution is
STRUCT_DLL_TEST_FP_STRUCT sFP2 = sFP;
psFP->psItf = &sFP2;
The lifetime of sFP2 ends when the method returns. psFP->psItf is now pointing to a piece of stack that doesn't exist anymore. don't do it!
Ah... as written by #Hans Passant, depending on who allocates the memory, the GetDllTestFP can be ref or out. If the memory is allocated by C# then it must be ref, if it isn't allocated (as is now) or is allocated by C++, then out is ok and you'll save on the marshaling in one direction.
I have a C structure used for callback that I need to marshall to C# .NET:
struct CTMDeviceInfo {
enum CTMDeviceType eDeviceType;
char * szDeviceModel;
char * szDeviceSubModel;
int32_t * piDeviceID;
};
This is my C# version:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceModel;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceSubModel;
public IntPtr deviceId;
};
Which is used inside another structure:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceError
{
public CTMDeviceInfo deviceInfo;
[MarshalAs(UnmanagedType.I4)]
public Int32 resultCode;
[MarshalAs(UnmanagedType.I4)]
public Int32 extendedResultCode;
public IntPtr denomination;
public IntPtr changeDue;
};
My problem is that the "IntPtr deviceId" does not consistently return the correct value every time a callback was made.
I was expecting an integer value of 5, 15 or 16 but it keeps returning random values like 106, 865412, 652272, etc.
I don't know what I did wrong. What I did though is to prevent the callback in my managed code to be garbage collected using GCHandle.
Here is the sequence on how I did it:
From my unmanaged code I have this CDECL callback method:
void ctm_add_device_error_event_handler(CTMDeviceErrorCallback);
typedef void (CTMDeviceErrorCallback) (struct CTMEventInfo, struct CTMDeviceError );
This is my managed code:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnDeviceErrorCallBack(CTMEventInfo evtInfo, CTMDeviceError deviceError);
[DllImport("libctmclient-0.dll", EntryPoint = "ctm_add_device_error_event_handler", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddDeviceErrorEventHandler([MarshalAs(UnmanagedType.FunctionPtr)] OnDeviceErrorCallBack deviceErrorCallBack);
OnDeviceErrorCallBack deviceErrorCallback;
GCHandle deviceErrorCallbackGCHandle;
deviceErrorCallback = new OnDeviceErrorCallBack(OnDeviceError);
deviceErrorCallbackGCHandle = GCHandle.Alloc(deviceErrorCallback);
AddDeviceErrorEventHandler(deviceErrorCallback);
And this is where the callback is handled:
public void OnDeviceError(CTMEventInfo evtInfo, CTMDeviceError deviceError)
{
int nDeviceId = Marshal.ReadInt32(deviceError.deviceInfo.deviceId);
}
I tried to use unsafe to use pointers directly but the issue is still the same.
public unsafe int *deviceId; //instead of IntPtr
int nDeviceId = 0;
unsafe
{
nDeviceId = *(deviceError.deviceInfo.deviceId);
}
I'm sure that my unmanaged code returned the correct value because I have logs but when it reached in my managed code, somehow another value was returned.
It's like it is reading on a different reference or something.
Hope somewhat could help me because I am stuck for a while now.
Thanks!
Use following c# structure. You can get the two string from the pointer later in the code. :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
public IntPtr deviceModel;
public IntPtr deviceSubModel;
public IntPtr deviceId;
};
I am using API written in C++ in my code (writting in C#).
API requires a parameter as Pointer to Structure.
The Structure consists of "Int"s and Char Arrays:
for example
unsafe public struct ToBePassed
{
Int32 Num1;
Int32 Num2;
Char[] Data; // or fixed Char Data[255];
}
I can not directly pass the structure pointer to API because in that case, I am getting error as "Pointers cannot reference Marshaled structures". Code get compiled successfully but this Error comes when I execute (Debug) the code.
Now I have two options:
1st:- Passing Structure by Ref: I want to ask does an API requiring A Structure Pointer can receive the address when I pass the structure by ref. Note that API will return Data in "Char[] Data".
2nd:- Using Marshal.StructureToPtr: This will convert Structure Pointer to IntPtr. Again the Doubt is same, Will that API receive it correctly?
Thanks for Your Time!
Regards,
Swanand
If it only requires pointer, you can allocate some unmanaged memory, marshal the structure to the memory, and pass that pointer to your function. Then afterwards you could marshal back to the structure (if you wish) and free the memory. Before you marshal anything, you need to properly define the structure. Something like this:
[StructLayout(
LayoutKind.Sequential, //must specify a layout
CharSet = CharSet.Ansi)] //if you intend to use char
public struct ToBePassed
{
public Int32 Num1;
public Int32 Num2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
public Char[] Data; //specify the size using MarshalAs
}
[DllImport("...")]
public static extern void APICall(IntPtr argPtr);
public static void CallFunction(ToBePassed managedObj)
{
IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(managedObj));
Marshal.StructureToPtr(managedObj, unmanagedAddr, true);
APICall(unmanagedAddr);
Marshal.PtrToStructure(unmanagedAddr, managedObj);
Marshal.FreeHGlobal(unmanagedAddr);
unmanagedAddr = IntPtr.Zero;
}
[edit]
To simulate variable length arrays, allocate unmanaged memory within the structure and initialize as usual.
[StructLayout(LayoutKind.Sequential)]
public struct SomeStruct
{
public Int32 X;
public Int32 Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct VLA
{
public Int32 intArrayLength;
public Int32 SomeStructArrayLength;
public IntPtr intArray;
public IntPtr SomeStructArray;
}
public static VLA CreateVLA(int[] intArray, SomeStruct[] SomeStructArray)
{
var vla = new VLA()
{
intArrayLength = intArray.Length,
SomeStructArrayLength = SomeStructArray.Length,
intArray = Marshal.AllocHGlobal(intArray.Length * Marshal.SizeOf(typeof(int))),
SomeStructArray = Marshal.AllocHGlobal(SomeStructArray.Length * Marshal.SizeOf(typeof(SomeStruct))),
};
Marshal.Copy(intArray, 0, vla.intArray, intArray.Length);
//there's no overload to copy arbitrary arrays, do it manually
for (int i = 0; i < SomeStructArray.Length; i++)
{
Marshal.StructureToPtr(
SomeStructArray[i],
vla.SomeStructArray + i * Marshal.SizeOf(typeof(SomeStruct)),
true);
}
return vla;
}
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.