Second value not getting passed to C++ function during pinvoke - c#

I have a c++ function which I am calling from c# using pinInvoke. Following is my cpp method-
int test(DWORD verb,DWORD verb2 )
{
return verb2 *100;
}
My function is exposed as -
extern "C" {
__declspec(dllexport) int test(DWORD verb, DWORD verb2);
}
Following is my c# code where I am calling the above method:
public class API
{
[DllImport("mydll.dll", EntryPoint = "test", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern uint test(
[MarshalAs(UnmanagedType.U8)]ulong verb,
[MarshalAs(UnmanagedType.U8)]ulong verb2);
static void Main(string[] args)
{
uint x = DPAPI.test(26,10);
Console.Write("result is-"+x);
}
}
Here the second value is getting passed as 0,so wrong result is coming. Am I doing something wrong while passing the value?
What I have tried:
I am relatively new to Pinvoke. So I tried debugging to see whether the value is not getting passed to c++ code or whether the c++ code is not returning proper values.I found that the value getting passed itself was wrong.

DWORD is a 32 but unsigned integer. You are mapping that to a 64 bit type. That is the problem. Your p/invoke should be:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int test(uint verb, uint verb2);
Note that I removed the needless EntryPoint and the erroneous SetLastError from the DllImport attribute.
I also wonder why you have selected DWORD. Unless you are passing those values onto Win32 functions it would likely make more sense to use intrinsic types. Why not use int or unsigned int in your C++ code?

Related

AccessViolationException when accessing function with char * argument in c++ dll with C#

Problem Trying to use a function in a c++ dll with the following prototype...
int connectDfuBootloader(char * usbIndex)
...from C#, using any of these P/Invoke signatures, causes an AccessViolationException:
IntPtr...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int connectDfuBootloader(IntPtr usbIndex);
public static int connectDfuBootloader(string usbIndex)
{
IntPtr value = Marshal.StringToHGlobalAuto(usbIndex);
return connectDfuBootloader(value);
}
String...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader(string usbIndex);
StringBuilder...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader(StringBuilder usbIndex);
Variations on the theme of MarshalAs, tried LPStr, LPTStr etc...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader([MarshalAs(UnmanagedType.LPStr)]string usbIndex);
Variations on the theme of specifying the character set, ANSI, Unicode, etc
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl, CharSet =CharSet.Ansi)]
public static extern int connectDfuBootloader(string usbIndex);
Can anyone offer any suggestions?
Other things I've tried
Various StackOverflow posts, such as
AccessViolationException when accessing unmanaged C++ DLL with C#
Also, I tried making my own small C++ dll for test purposes, which contained a function with the same signature as the problem one above:
int functionAlpha(char * a)
{
if (a[0] == 'a')
return 10;
else
return 20;
}
This function I was able to access from C# without issue, using any of the methods above, e.g.
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int functionAlpha(string a);
Which makes me think there is something special about the 'int connectDfuBootloader(char * usbIndex)' function. But they have the same signature, why would one work and the other not? I do not have the source code for the dll, so can't look in there to see if there are any other differences.
Update - Tried C++/CLI wrapper as suggested by #Taekahn below, same thing!
So, I have this in a header in the C++/CLI wrapper project:
#include "include/CubeProgrammer_API.h"
using namespace System;
namespace CLI
{
public ref class CubeWrapper
{
public:
int wrappedConnectDfuBootloader(String^ a);
};
}
Then in a cpp file in the wrapper project, I have:
namespace CLI
{
static char* string_to_ncchar_array(String^ string)
{
char* str = (char*)(Marshal::StringToHGlobalAnsi(string)).ToPointer();
return str;
}
int CubeWrapper::wrappedConnectDfuBootloader(String^ a)
{
a = "USB0";
// Calls the function from the linked c++ dll I'm trying to wrap
return connectDfuBootloader(string_to_ncchar_array(a));
}
}
And it gives the same result, System.AccessViolationException! What could be going on here?
Also tried using the dll from c++
Tried the same sequence of operations with the dll from a c++ application, and it worked fine.
Turns out that dxiv was on the right track; looked through some of the c++ example code supplied with the API and saw that several callbacks had to be setup first. Once I sorted those, problem fixed.

Calling C++ dll Method from C#

I am trying to call method available in C++ dll
HRESULT WINAPI TestMethod(
_Out_ BOOL *isSuccess,
_In_opt_ DWORD UsernmaeLength,
_Out_opt_ LPWSTR userName );
Wrapper method Which I have written in C# looks like this
[DllImport("Test.dll", CharSet = CharSet.Unicode, SetLastError = true ,CallingConvention = CallingConvention.StdCall)]
public static extern int TestMethod (
IntPtr isSuccess,
[In, Optional] int UsernmaeLength,
out string userName
);
I am calling this method in program
Wrapper. TestMethod (isSuccess, 200, out userName);
I am getting System.AccessViolationException
tried changing the C# wrapper method with
[DllImport("Test.dll", CharSet = CharSet.Unicode, SetLastError = true ,CallingConvention = CallingConvention.StdCall)]
public static extern int TestMethod (
bool isSuccess,
[In, Optional] int UsernmaeLength,
out string userName
);
//Caller
bool isSuccess = false;
Wrapper. TestMethod (isSuccess, 200, out userName);
Could you please help me to understand what I am doing wrong here?
_In_opt_ DWORD UsernmaeLength
The SAL annotation is not very useful. What it probably is trying to tell you is that you can pass NULL for the string buffer argument. In which case what you pass for the buffer length doesn't matter. It is not actually [Optional], you'd consider simply passing 0 if you really don't want a string back.
The 3rd argument cannot be String or out since that is an immutable type and the function wants to write into the buffer you pass. It must be StringBuilder. The 2nd argument must be its Capacity. Be sure to make the StringBuilder big enough to fit a user name. If it is not then it isn't very obvious what will happen, hopefully the function then just returns an error code instead of silently truncating string. Test that.
The 1st argument is bool passed by reference, [Out] out bool. Not very likely that it SetLastError, that is only done by winapi functions. It already returns an error code embedded in the HResult. A value less than 0 is an error. Stdcall is the default. Summarizing:
[DllImport("Test.dll", CharSet = CharSet.Unicode)]
public static extern int TestMethod (
[Out] out bool isSuccess,
int userNameLength,
StringBuilder userName
);
Called as:
bool success;
var name = new StringBuilder(666);
int hr = TestMethod(out success, name.Capacity, name);
if (hr < 0) Marshal.ThrowExceptionForHR(hr);
If you still have trouble then you need the help of the author of this code if you cannot debug it yourself. Have a small repro available so he can easily repro the issue.

Calling non-managed DLL

I am going mad trying to call a DLL function for days from a C# application.
Here is the definition of the DLL call:
phStatus_t phbalReg_Rd70xUsbWin_Init
( phbalReg_Rd70xUsbWin_DataParams_t * pDataParams,
uint16_t wSizeOfDataParams )
Here is the definition of phbalReg_Rd70xUsbWin_DataParams_t:
And here is my C# code for calling the DLL:
public static data_params parameters;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct data_params
{
internal ushort wId; //Layer ID for this BAL component, NEVER MODIFY!
internal byte ucTxSeq; //Sequence counter for packets.
[MarshalAs(UnmanagedType.LPStr)]
String pDeviceName;
internal IntPtr pDeviceHandle; //Handle to the USB device.
internal IntPtr pPipeOut; //Handle to Usb Out-pipe.
internal IntPtr pPipeIn; //Handle to Usb In-pipe.
internal ushort wTimeoutWrMs; //TO value for Usb Write pipe transfer.
internal ushort wTimeoutRdMs; //TO value for Usb Read pipe transfer.
}
[DllImport("NxpRdlib.dll", EntryPoint = "phbalReg_Rd70xUsbWin_Init")]
public static extern uint phbalReg_Rd70xUsbWin_Init(ref data_params data_parameters,
public static unsafe uint connectToPegoda()
{
parameters = new data_params();
parameters.wId = 0x05;
parameters.ucTxSeq = 0;
parameters.pDeviceHandle = IntPtr.Zero;
parameters.pPipeOut = IntPtr.Zero;
parameters.pPipeIn = IntPtr.Zero;
parameters.wTimeoutWrMs = 0xFFFF;
parameters.wTimeoutRdMs = 0xFFFF;
return phbalReg_Rd70xUsbWin_Init(ref parameters, (uint)Marshal.SizeOf(parameters));
}
The problem is that I receive a PInvokeStackImbalance exception.
I tried to change type of paramaters with different things and never achieved to get this work. I am sure I am doing something wrong with types, but can't find what. Can someone help me?
The most common explanation is a calling convention mis-match. As written, the unmanaged function declaration uses cdecl. You did not specify a calling convention in your p/invoke and so the default of stdcall is used.
To fix this, specify cdecl in your p/invoke:
[DllImport("NxpRdlib.dll", CallingConvention = CallingConvention.Cdecl)]
You also specified only part of the p/invoke declaration. You missed the second parameter. The full declaration should be:
[DllImport("NxpRdlib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint phbalReg_Rd70xUsbWin_Init(
ref data_params data_parameters,
ushort wSizeOfDataParams
);
The other unknown here is phStatus_t. You've translated that as uint, an unsigned 32 bit integer. We can only take your word that the translation is correct.
Update: From your comment to the question, phStatus_t should be translated as ushort. So, finally, we have:
[DllImport("NxpRdlib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ushort phbalReg_Rd70xUsbWin_Init(
ref data_params data_parameters,
ushort wSizeOfDataParams
);

Return contents of a std::wstring from C++ into C#

I have an unmanaged C++ DLL that I have wrapped with a simple C interface so I can call PInvoke on it from C#. Here is an example method in the C wrapper:
const wchar_t* getMyString()
{
// Assume that someWideString is a std::wstring that will remain
// in memory for the life of the incoming calls.
return someWideString.c_str();
}
Here is my C# DLLImport setup.
[DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl )]
private static extern string GetMyString();
However the string is not correctly marshalled, often screwing up the first character or sometimes way off showing a bunch of chinese characters instead. I have logged output from the implementation on the C side to confirm that the std::wstring is correctly formed.
I have also tried changing the DLLImport to return an IntPtr and convert with a wrapped method using Marshal.PtrToStringUni and it has the same result.
[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern IntPtr GetMyString();
public string GetMyStringMarshal()
{
return Marshal.PtrToStringUni( GetMyString() );
}
Any ideas?
Update with Answer
So as mentioned below, this is not really an issue with my bindings but the lifetime of my wchar_t*. My written assumption was wrong, someWideString was in fact being copied during my calls to the rest of the application. Therefore it existed only on the stack and was being let go before my C# code could finish marshalling it.
The correct solution is to either pass a pointer in to my method as described by shf301, or make sure my wchar_t* reference does not get moved / reallocated / destroyed before my C# interface has time to copy it.
Returning the std::wstring down to my C layer as a "const &std::wstring" means my call to c_str() will return a reference that won't be immediately dealloc'd outside the scope of my C method.
The calling C# code then needs to use Marshal.PtrToStringUni() to copy data from the reference into a managed string.
You are going to have to rewrite your getMyString function for the reasons mentioned in Hans Passant's answer.
You need to have the C# code pass a buffer in to your C++ code. That way the your code (ok, the CLR Marshaller) controls the lifetime of the buffer and you don't get into any undefined behavior.
Below is an implementation:
C++
void getMyString(wchar_t *str, int len)
{
wcscpy_s(str, len, someWideString.c_str());
}
C#
[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode )]
private static extern void GetMyString(StringBuffer str, int len);
public string GetMyStringMarshal()
{
StringBuffer buffer = new StringBuffer(255);
GetMyString(buffer, buffer.Capacity);
return buffer.ToString();
}
You need to specify MarshalAs attribute for the return value:
[DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.LPWStr)]
private static extern string GetMyString();
Make sure the function is indeed cdecl and that the wstring object is not destroyed when the function returns.

How to import void * C API into C#?

Given this C API declaration how would it be imported to C#?
int _stdcall z4ctyget(CITY_REC *, void *);
I've been able to get this far:
[DllImport(#"zip4_w32.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "z4ctygetSTD",
ExactSpelling = false)]
private extern static int z4ctygetSTD(ref CITY_REC args, void * ptr);
Naturally in C# the "void *" doesn't compile.
Some Googling indicates that it should be translated as "object." Which seems like it should work. But others indicate that "Void * is called a function pointer in C/C++ terms which in C# terms is a delegate". That doesn't make a whole lot of sense here as what would it delegate to? Some similar calls for other APIs found through Googling use other functions in the respective API. But in this API no other call would make sense.
The documentation for the call shows an example:
z4ctyget(&city, “00000”);
Which seems to show that even a static value could be passed.
It will compile with object in place of the void *. I don't know whether this is right and I haven't had an opportunity to test it (licensing issue).
For the void* parameter you can just use an IntPtr
[DllImport(#"zip4_w32.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "z4ctygetSTD",
ExactSpelling = false)]
private extern static int z4ctygetSTD(ref CITY_REC args, IntPtr ptr);
You can also use void* if you mark your class as unsafe.
It really depends on what the API is looking for in that parameter.
You can add IntPtr or Object* to get past compiler, but you will still need to pass it the correct data when you call it.
As far as I can tell the C declaration of z4ctyget is:
int z4ctyget(CITY_REC *cityrec, char *zipcode);
The second parameter is a 5 character ANSI string representing the zip code at which you want to start your search or "00000" to start at the beginning of the file. So your declaration should be:
[DllImport(#"zip4_w32.dll", CharSet = CharSet.Ansi)]
private extern static int z4ctygetSTD(ref CITY_REC args, string zipcode);

Categories