C++ int & long marshalling in C# - c#

I'm currently porting a proprietary dll (an API) to C#, and I have some problems when marshalling some of the used data types.
For example the API header file defines the following types:
typedef unsigned int tINT; /* natural unsigned */
typedef signed int tINTs; /* natural signed */
typedef unsigned int tLONG; /* 32 bit unsigned */
typedef unsigned long tPTR; /* 32/64-bit pointer */
Then I for example have the following function definition:
tINTs SomeApiFunction ( const tPTR hdl, tINT param );
I'm usually marshalling the int directly with C#'s int (Int32), or the unsigned version. The API's tLONG and tINT are as such the same (at least for the API). But I'm not sure with the tPTR. I have now made it a uint in C#, because it is a 32bit int, but I'm not sure how the long would behave on a 64bit machine.
What do I have to do to make sure that the API will work on both 32bit and 64bit machines correctly?
This is what I would have done now:
[DllImport( "api.dll" )]
public static extern int SomeApiFunction ( uint hdl, uint param );
Sadly the API is not really well documented, as such I am not sure what the intention was with all those (overlapping) typedefs. Apart from the header comments I have included above there is no information about the actual types.

Try to Marshal it as an IntPtr. IntPtr is 4 bytes on a 32bit system and 8 bytes on a 64bit system in .Net.

It is an implementation detail of the C/C++ compiler. You should go by the comment and declare it IntPtr. If the code is compiled by the MSVC compiler then it isn't going to work in 64-bit mode, it uses 32-bits for a long. Not enough to store a pointer.

Here is good list of type correspondence.
For pointer types is usually IntPtr in C#.

If I remember correctly int uint and other primitive types are the right type for the underlying windows API.
For pointers however, its best to use IntPtr on C# side and void* on C/C++ side.

Related

How do I handle a complex struct return type from a C DLL file within C#?

I've been trying to get a C library (DLL) working with some simple test code in C#. So far I've been able to import and use the simple functions just fine. The issue I'm having right now is that I don't know how to receive the complex struct return type from this imported function.
Here are the two function signatures:
C:
#define HID_API_EXPORT __declspec(dllexport)
#define HID_API_CALL
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
C#:
[DllImport("hidapi.dll")]
public static extern hid_device_info hid_enumerate(ushort vendor_id, ushort product_id);
And here are the two structs:
C:
struct hid_device_info {
char *path;
unsigned short vendor_id;
unsigned short product_id;
wchar_t *serial_number;
unsigned short release_number;
wchar_t *manufacturer_string;
wchar_t *product_string;
unsigned short usage_page;
unsigned short usage;
int interface_number;
struct hid_device_info *next;
};
C#:
[StructLayout(LayoutKind.Sequential)]
public struct hid_device_info
{
public IntPtr path;
public ushort vendorid;
public ushort productid;
public IntPtr serialnumber;
public ushort releasenumber;
public IntPtr manufacturer;
public IntPtr product;
public ushort usagepage;
public ushort usage;
public int interfacenumber;
public IntPtr next;
}
I'm currently getting this error when I run the program:
Managed Debugging Assistant 'PInvokeStackImbalance' has detected a
problem in
'C:\Users\tajensen\Documents\hidapiCS\hidapitest\bin\Debug\hidapitest.vshost.exe'.
Additional information: A call to PInvoke function
'hidapiCS!hidapiCS.hidapi::hid_enumerate' has unbalanced the stack.
This is likely because the managed PInvoke signature does not match
the unmanaged target signature. Check that the calling convention and
parameters of the PInvoke signature match the target unmanaged
signature.
I've done a bit of digging and the only things I've been able to find describe how to receive return types of very simple structs (i.e. no pointers, and only basic types like ints and bools). I would really appreciate some additional insight on the issue, as I know where I want to be, but I don't know enough about this kind of code to dig deeper on my own.
Thanks in advance,
Toms
Your structure looks good, baring any command line flags that change the packing of it.
Likely, it's because of this line
#define HID_API_CALL
This means you're using the default calling convention, which is generally __cdecl. So, change the P/Invoke definition to:
[DllImport("hidapi.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern hid_device_info hid_enumerate(ushort vendor_id, ushort product_id);
So the rules for how to manage the stack on the C side are followed properly.
There is a tool named SWIG, a code generation tool, connects programs written in C and C++ with a variety of high-level programming languages like c#. It generates wrapper code for twenty three different target languages:
It's very powerful for these tasks, specially data type/pointer conversion and many others. It saves manual conversion.
Try to use this tool to generate C# code for your case and finally compiled Dll.
Download the binary from: http://prdownloads.sourceforge.net/swig/swigwin-3.0.10.zip.
For tutorial: http://www.swig.org/tutorial.html

P/Invoke Calls Failing

I have the following struct defined in C++:
struct GraphicsAdapterDesc {
// ... Just some constructors / operators / destructor here
DEFINE_DEFAULT_CONSTRUCTOR(GraphicsAdapterDesc);
DEFINE_DEFAULT_DESTRUCTOR(GraphicsAdapterDesc);
ALLOW_COPY_ASSIGN_MOVE(GraphicsAdapterDesc);
std::wstring AdapterName;
int32_t AdapterNum;
std::wstring HardwareHash;
int64_t DedicatedVMEM;
int64_t DedicatedSMEM;
int64_t SharedSMEM;
int32_t NumOutputs;
};
In C#, I have a 'mirror' struct declared thusly:
[StructLayout(LayoutKind.Sequential)]
public struct GraphicsAdapterDesc {
string AdapterName;
int AdapterNum;
string HardwareHash;
long DedicatedVMEM;
long DedicatedSMEM;
long SharedSMEM;
int NumOutputs;
};
I've tried to be really careful about matching up the widths of the variables (although I'm a bit unsure on what to do with the strings exactly).
Anyway, I have the following exported C method:
extern "C" __declspec(dllexport) bool GetGraphicsAdapter(int32_t adapterIndex, GraphicsAdapterDesc& outAdapterDesc) {
outAdapterDesc = RENDER_COMPONENT.GetGraphicsAdapter(adapterIndex);
return true;
}
And, the following extern method in my C# app:
[DllImport(InteropUtils.RUNTIME_DLL, EntryPoint = "GetGraphicsAdapter", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool _GetGraphicsAdapter(int adapterIndex, out GraphicsAdapterDesc adapterDesc);
However, this doesn't work right when I call it. I get a different result depending on whether or not I'm compiling in x64 or x86 mode (both the C++ DLL and the C# app are compiled as x86 or x64):
In x86 mode, the call returns, but the struct has 'nonsense' values in, and the strings are all null,
In x64 mode, the call throws a NullPointerException.
My expectation is that I'm doing something wrong marshalling the strings, and that I need to specify 'wide-mode' for the characters, but I don't know how (or if that's even the right option).
Thank you in advance.
C++ types are not compatible with C# unless they're wrapped in managed C++. and you're using std::wstring which cannot be marshaled into .NET.
To interop successfully you'll either need to use a wchar_t[] or a whar_t* and tell C# now to marshal it.
I don't know what your macros are doing but this will only work if your c++ type is POD. c++11 Has an expanded sense of POD but I don't think you meet the expanded criteria anyway. Otherwise you can't guarantee layout. If you want to export your c++ classes to C# I would suggest you use c++\cli. Also you have wstring defined in your stuct which are definitely not POD. When you use DLLImport think C constructs only or you are going to have a bad time.

Interfacing with C/C++ routine in C# causes rip

I have a vendor's API call, which I want to use in my C# Windows Forms application. The problem is that no matter what P/Invoke I try, I get a crash on the line stating that the call has unbalanced the stack. I tried working with the vendor, but they do not have Visual Studio 2012 nor have any experience with C# or .NET and after several back and forth emails, they basically washed their hands of the problem.
Here is their official C/C++ declaration
const char *<dll name here>(int Param1,
const char *Param2,
const char *Param3,
unsigned long Param4,
unsigned short Param5,
unsigned short Param6,
unsigned short Param7,
unsigned short Param8,
unsigned short Param9);
Here is a recent P/Invoke DLLImport line that I tried.
[System.Runtime.InteropServices.DllImport(
"<dll name here>", EntryPoint = "<AnsiFunctionName>", ExactSpelling = true,
CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern String <AnsiFunctionName>(int Param1,
String Param2,
String Param3,
ulong Param4,
Int16 Param5,
Int16 Param6,
Int16 Param7,
Int16 Param8,
Int16 Param9);
Here is the actual error message:
Subject Line: PInvokeStackImbalance was detected
A call to P/Invoke function '' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the P/Invoke signature match the target unmanaged signature.
I tried various things, even putting a void return type. I tried not using the return type. I tried instead of Strings, StringBuilders and IntPtrs. No matter what I tried, nothing works.
I mentioned to the vendor after reading an article here on Stack Overflow that returning a string is dangerous and should never be done. Their response was:
"The string return problems mentioned on that page don't apply to
our functions. They return a pointer to a statically-allocated string, so you don't need to free it (and must not try to do so). It looks like the "UnmanagedType.LPStr" should work (though the IntPtr thing should work too, with the proper conversions or casting).
Update #1: As mentioned in the comment, I was using uint, not ulong, up until this post. I just thought that I make the comparison look better. Both uint and ulong do not work.
Update #2: Maybe giving the actual function name will help. It is Armadillo CreateCodeShort3A. Going forward, I am using pure .NET, but I do still need to interface to that, just it is not really possible at the moment. More than that, the problem got me curious why I cannot resolve something seemingly so simple as a P/Invoke. After all, how many variations are there? (not a serious question).
Here are the possibilities that I can see:
The return value should not be String. Use IntPtr and convert to a string with Marshal.PtrToStringAnsi.
The C++ type unsigned long should be matched with uint. You used ulong in C# which is a 64-bit type. In C++ on Windows, unsigned long is an unsigned 32-bit type.
The C++ type unsigned short should be matched with ushort.
The calling convention used by the C++ DLL is probably cdecl. Add CallingConvention = CallingConvention.Cdecl to your P/Invoke.
Don't use SetLastError for such a function. That's used with Win32 API functions that return error conditions through GetLastError. This DLL file almost certainly does not.
So the P/Invoke should look something like this:
[DllImport(#"mydll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr FunctionName(
int Param1,
string Param2,
string Param3,
uint Param4,
ushort Param5,
ushort Param6,
ushort Param7,
ushort Param8,
ushort Param9
);

DataType between different process and architectures - Writing a correct DllImport

I have a main application written in C# that runs as a x64 bit application, it communicates through dll import with a standard native unmanaged C/C++ dll of which I have also the header.
I need help for setting out the correct dataTypes.
So i expose one of the methods I have to call and the data types defined in the dll header.
typedef int DLL_IMP_EXP (* INJECTDLL)(HANDLE, DWORD, LPCTSTR, DWORD, HINSTANCE *);
HANDLE is defined as void*
DWORD is defined as unsigned long
LPCTSTR is defined as __nullterminated CONST CHAR*
HINSTANCE gives me this line for definition: DECLARE_HANDLE(HINSTANCE); ?!?
Using Unicode declaration of the function:
LPCWSTR is defined as __nullterminated CONST WCHAR*
Please help me writing the correct declaration of the:
[DllImport ("Inject.dll", VariousParameters)]
public static extern int InjectDll(CorrectDataTypes);
Compiling VariousParameters if needed, and obviously CorrectDataTypes.
And IntPtr is used for a pointer or a handle - it will be 32 bits on a 32 bit system and 64 on a 64 bit system. So if you have anything that is a raw pointer or a handle use an IntPtr and it will work correctly on both systems. However your last parameter is a pointer to a handle - use a ref to handle the pointer. So in this case since it's a pointer to a handle, the parameter will be a ref to an IntPtr.
For standard numeric types, those will map directly to the .NET data types - you can get more details at MSDN.
Null terminated strings are handled correctly, although you'll need to specify whether it uses ANSI or Unicode strings.
Finally P/Invoke assumes a StdCall calling convention (which is what the Windows API uses). If you're not using that, which the function prototype would include STDCALL or __stdcall in it, the standard C calling convention is Cdecl. Although you'll have to find out what DLL_IMP_EXP expands to.
So your P/Invoke declaration should be:
[DllImport ("Inject.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int InjectDll(IntPtr handle, uint dword, string str, uint dword2, ref IntPtr hInstance);

How to map Win32 types to C# types when using P/Invoke?

I am trying to do something like this in C#. I found out how to call Win32 methods from C# using P/Invoke from this link. However I met some difficulties in implementing P/Invoke.
For example, one of the methods that I would like to access is PdhOpenQuery,
signature:
PDH_STATUS PdhOpenQuery(
__in LPCTSTR szDataSource,
__in DWORD_PTR dwUserData,
__out PDH_HQUERY *phQuery
);
I figure the corresponding C# declaration should be something like this
[DllImport("Pdh.dll")]
static extern PDH_STATUS PdhOpenQuery(LPCTSTR szDataSource,
DWORD_PTR dwUserData, out PDH_HQUERY *phQuery);
My questions:
What is LPCTSTR, and to what data type does it map in C#?
How to map a pointer type DWORD_PTR? The pinvoke article says DWORD maps to UInt32, but how about pointers?
I think PDH_STATUS and PDH_HQUERY are specific struct to the library (I'm not sure yet). how do I map these?
What is the correct method declaration, and how do you call it correctly?
What is LPCTSTR, and to what data type
does it map in C#?
LPCTSTR is a typedef for const TCHAR*.
TCHAR is an attempt to abstract away the fact that the Windows API exists in both "ANSI" (char strings in a locale-specific encoding) and "Unicode" (UTF-16) versions. There is no actual PdhOpenQuery function; there is a PdhOpenQueryA function that takes an ANSI string and a PdhOpenQueryW function that takes a UTF-16 string.
C# uses UTF-16 strings, so you'll want to prefer the "W" version of these functions. Use PdhOpenQueryW. Then the first parameter has C++ type const wchar_t*. The C# type is [MarshalAs(UnmanagedType.LPWStr)] string.
How to map a pointer type DWORD_PTR?
The pinvoke article says DWORD maps to
UInt32, but how about pointers?
DWORD_PTR isn't a pointer. It's an unsigned integer big enough to hold a pointer. The equivalent C# type is System.UIntPtr.
I think PDH_STATUS and PDH_HQUERY are
specific struct to the library (I'm
not sure yet). how do I map these?
PDH_STATUS appears to be just an int.
PDH_HQUERY is a pointer to a handle (another pointer), but you can just pretend it's an integer and use IntPtr.
Putting it all together, your declaration should be:
[DllImport("Pdh.dll")]
static extern int PdhOpenQueryW(
[MarshalAs(UnmanagedType.LPWStr)] string szDataSource,
UIntPtr dwUserData,
out IntPtr phQuery);
iirc LPCTSTR breaks down to:
LP == Long Pointer // relic from 16- to 32-bit thunking
C == Constant
TSTR == TStr // TString, kind of a placeholder that would get substituted for different kinds of strings depending on various C headers and #defines
what it means to you: it is a pointer to a string, for C# just use string and you'll be okay.
PDH_STATUS is a pointer to a DH_STATUS struct, so you will need to define a C# structure to match.
Check out P/Invoke.Net for some examples of using p/invoke on standard Windows headers, structures, and functions. The website is kind of klunky, you just have to keep clicking and expanding items in the left column.
P/Invoke isn't well documented, but once you map the structure and the extern function call you should be in business.
[DllImport("Pdh.dll")]
static extern Int32 PdhOpenQuery(string szDataSource,
IntPtr dwUserData, ref IntPtr phQuery);

Categories