How to call a function in an unmanaged dll from C# Code? - c#

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

Related

C# DllImport - Call a function that returns an struct

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.

using nested structure pointers in C/C++ DLL in C#

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.

Marshalling an integer pointer inside a structure as a callback

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

Issue Marshalling C# Structure to call C .DLL

I'm having difficulty trying to marshal a structure I've defined in a C# program that is required for calling an unmanaged C .DLL file I do not have access to the source code for. A sample unmanaged C program C program can call this .DLL with no issue. The problem structure is fa_keylist below. There are multiple sub structures contained in the structure I am having issues with:
From the C header file:
struct fa_keypart {
short kp_start;
short kp_leng;
long kp_flags;
};
struct fa_keydesc {
long k_flags;
long k_nparts;
struct fa_keypart k_part [FA_NPARTS];
};
struct fa_keylist {
long kl_nkeys;
char kl_reserve[4];
struct fa_keydesc *kl_key [FA_NKEYS];
}
In C#, I have this defined as:
[StructLayout(LayoutKind.Sequential)]
public struct fa_keypart
{
public Int16 kp_start;
public Int16 kp_leng;
public Int32 kp_flags;
}
[StructLayout(LayoutKind.Sequential)]
public struct fa_keydesc
{
public Int32 k_flags;
public Int32 k_nparts;
[MarshalAs(UnmanagedType.ByValArray)]
public fa_keypart[] kparts;
};
[StructLayout(LayoutKind.Sequential)]
public struct fa_keylist
{
public Int32 kl_nkeys;
public UInt32 kl_reserve;
[MarshalAs(UnmanagedType.ByValArray)]
public fa_keydesc[] kl_keys;
}
The DLLIMPORT signature for the actual call is defined as:
[STAThread]
[DllImport("F4AGFCFA.dll", EntryPoint = "cobfa_open", CallingConvention = CallingConvention.StdCall)]
public static extern Int32 cobfa_open(
string fileName,
Int32 openFlags,
ref fa_keylist keyList,
Int32 recordLength);
The call to the function is coded as:
handle = cobfa_open(filename, fileFlags, ref keyList, 80);
I've tried a number of different Marshalling options by the way. The current error I receive is an Access Violation (Attempt to read or write protected memory).
Any suggestions would be greatly appreciated.
You need to specify the size for the arrays. Assuming that FA_NPARTS in C is 128, you could do the following:
[StructLayout(LayoutKind.Sequential)]
public struct fa_keydesc
{
public Int32 k_flags;
public Int32 k_nparts;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public fa_keypart[] kparts;
};
UnmanagedType.ByValArray only works with SizeConst set as well.

Using TaskDialogIndirect in C#

I've been working for a while with the regular Windows Vista/7 TaskDialog for a while, and I wanted to add some additional functionality (like custom buttons and a footer), so I need to use TaskDialogIndirect.
Following the MSDN documentation for TaskDialogIndirect, I got this signature:
[DllImport("comctl32.dll",CharSet = CharSet.Unicode,EntryPoint="TaskDialogIndirect")]
static extern int TaskDialogIndirect (TASKDIALOGCONFIG pTaskConfig, out int pnButton, out int pnRadioButton, out bool pfVerificationFlagChecked);
The TASKDIALOGCONFIG class is shown below:
public class TASKDIALOGCONFIG
{
public UInt16 cbSize;
public IntPtr hwndParent;
public IntPtr hInstance;
public String dwFlags;
public String dwCommonButtons;
public IntPtr hMainIcon;
public String pszMainIcon;
public String pszMainInstruction;
public String pszContent;
public UInt16 cButtons;
public TASKDIALOG_BUTTON pButtons;
public int nDefaultButton;
public UInt16 cRadioButtons;
public TASKDIALOG_BUTTON pRadioButtons;
public int nDefaultRadioButton;
public String pszVerificationText;
public String pszExpandedInformation;
public String pszExpandedControlText;
public String pszCollapsedControlText;
public IntPtr hFooterIcon;
public IntPtr pszFooterText;
public String pszFooter;
// pfCallback;
// lpCallbackData;
public UInt16 cxWidth;
}
The TASKDIALOG_BUTTON implementation:
public class TASKDIALOG_BUTTON
{
public int nButtonID;
public String pszButtonText;
}
I am not entirely sure if I am on the right track here. Did anyone use TaskDialogIndirect from managed code directly through WinAPI (without VistaBridge or Windows API Code Pack)? I am curious about the possible implementations, as well as the callback declarations (I am not entirely sure how to implement TaskDialogCallbackProc).
PS: I am looking for a direct WinAPI implementation, not one through a wrapper.
Look into the VistaBridge library. It will give you a pleasant wrapper around all this stuff, including TaskDialogIndirect..
http://code.msdn.microsoft.com/VistaBridge
It's worth looking through the Windows API code pack source code as it contains a reasonably complete implementation of TaskDialogIndirect using WinAPI - including callbacks - which would be a good starting point for your own implementation.
PInvoke.NET is a great resource for PInvoke techniques. Unfortunately, they only have a TODO stub for TaskDialogIndirect so far.
Look at http://www.codeproject.com/Articles/21276/Vista-TaskDialog-Wrapper-and-Emulator. Specifically the file VistaUnsafeNativeMethods.cs has the relevant DllImport bits, including VistaTaskDialogCallback which I think is the callback you are interested in.

Categories

Resources