Calling C++ dll Method from C# - 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.

Related

Second value not getting passed to C++ function during pinvoke

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?

Trouble calling SystemParametersInfo

Recently I've been trying to call the SystemParametersInfo method from managed code, without any success.
The problem is that, after calling the method, the method returns false (indicating failure), however GetLastError (retrieved by Marshal.GetLastWin32Error()) is 0.
I tried to invoke the method from C++ as a test (with the exact same parameters), and it works completely fine from there.
The P/Invoke declaration of the method is this:
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, ref STICKYKEYS pvParam, SPIF fWinIni);
internal struct STICKYKEYS
{
public int cbSize;
public int dwFlags;
}
And the invocation is as follows:
NativeMethods.STICKYKEYS stickyKeys = default(NativeMethods.STICKYKEYS);
bool result = NativeMethods.SystemParametersInfo(NativeMethods.SPI.SPI_GETSTICKYKEYS, StickyKeysSize, ref stickyKeys, 0);
int error = Marshal.GetLastWin32Error();
The SPI.SPI_GETSTICKYKEYS is0x003A (as seen on MSDN).
Here the result is false, and the error returned is 0.
Also this is complied as a 64-bit executable if that matters.
I'm completely at my wit's end, do you have any idea what I might be doing wrong?
As GSerg pointed it out to me, my issue was that I need to pass in the size of the struct both directly as a parameter, and as the cbSize member of the struct that I passed in by reference.
The correct code is:
int stickyKeysSize = Marshal.SizeOf(typeof (NativeMethods.STICKYKEYS));
NativeMethods.STICKYKEYS stickyKeys = new NativeMethods.STICKYKEYS {cbSize = stickyKeysSize, dwFlags = 0};
bool result = NativeMethods.SystemParametersInfo(NativeMethods.SPI.SPI_GETSTICKYKEYS, stickyKeysSize, ref stickyKeys, 0);
if (!result) throw new System.ComponentModel.Win32Exception();
return stickyKeys;

A call to PInvoke function has unbalanced the stack

Making a function call to .NET 4 to native code is resulting in the following exception:
A call to PInvoke 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 PInvoke signature match the target unmanaged signature.
Here is my definition in Native Code:
typedef void ( CALLBACK* CB_DOWNLOADING )
(
ULONG, // secs elapsed
LPARAM, // custom callback param
LPBOOL // abort?
);
FETCH_API HttpFetchW
(
LPCWSTR url,
IStream** retval,
LPCWSTR usrname = NULL,
LPCWSTR pwd = NULL,
BOOL unzip = TRUE,
CB_DOWNLOADING cb = NULL,
LPARAM cb_param = 0,
LPWSTR ctype = NULL,
ULONG ctypelen = 0
);
Here is the .NET counterpart:
[DllImport(dllPath, CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern FETCH HttpFetchW
(
[MarshalAs(UnmanagedType.LPWStr)]
string url,
out System.Runtime.InteropServices.ComTypes.IStream retval,
[MarshalAs(UnmanagedType.LPWStr)]
string usrname,
[MarshalAs(UnmanagedType.LPWStr)]
string pwd,
bool unzip,
dlgDownloadingCB cb,
IntPtr cb_param,
[MarshalAs(UnmanagedType.LPWStr)]
string ctype,
ulong ctypelen
);
Where FETCH is an enum.
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
internal delegate void dlgDownloadingCB(ulong elapsedSec, IntPtr lParam, bool abort);
internal static void DownloadingCB(ulong elapsedSec, IntPtr lParam, bool abort)
{
// Console.WriteLine("elapsedSec = " + elapsedSec.ToString());
}
Can anyone suggest an alternative within the .NET 4?
Thank you very much for your help.
The only clear error in HttpFetchW that I can see is that C# ulong is 64 bits wide, by C++ ULONG is 32 bits wide. The C# code for the final parameter should be uint.
Other things to check include the calling convention. Are you sure it is cdecl? Are you sure that SetLastError should be true?
As for the callback, you make the same error with ulong. And the abort parameter is LPBOOL. That is a pointer to BOOL. So you should declare the C# parameter as a ref parameter. And the callback is declared with CALLBACK which is stdcall.
So the delegate should be:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate void dlgDownloadingCB(
uint elapsedSec,
IntPtr lParam,
ref bool abort
);
Or simply delete the UnmanagedFunctionPointer attribute since the default is stdcall.
I think it exceptionally likely that since the callback is stdcall, so will the function. I expect that the FETCH_API macro expands to both the return enum and the calling convention. Since you did not supply those details, I can only guess. You'll need to check.
The other thing that we cannot check is CB_DOWNLOADING.
Anyway, with those assumptions, the function becomes:
[DllImport(dllPath, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern FETCH HttpFetchW(
string url,
out System.Runtime.InteropServices.ComTypes.IStream retval,
string usrname,
string pwd,
bool unzip,
dlgDownloadingCB cb,
IntPtr cb_param,
string ctype,
uint ctypelen
);
Source : MSDN
In the .NET Framework version 3.5, the pInvokeStackImbalance MDA is disabled by default. When you use the .NET Framework version 3.5 with Visual Studio 2005, the pInvokeStackImbalance MDA will appear in the Managed Debugging Assistants list in the Exceptions dialog box (which is displayed when you click Exceptions on the Debug menu). However, selecting or clearing the Thrown check box for pInvokeStackImbalance does not enable or disable the MDA; it only controls whether Visual Studio throws an exception when the MDA is activated.
https://msdn.microsoft.com/en-us/library/0htdy0k3(v=vs.110).aspx

how to call a C function from C# with a WCHAR* out parameter?

I'm having a bit of problem with marshaling and I just can't figure it out by myself. I've searched for this topic, but hadn't have any luck yet, so basically I'm trying to call an unmanaged C function from my managed C# application. The signature of the C function looks like this:
long MyFunction(WCHAR* upn, long upnSize, WCHAR* guid, long* guidSize);
I don't access to the .dll file, I just know that the function is being exposed for usage and I know what the function is supposed to do, but I don't know what happening inside, so the function receives a WCHAR* upn holding a UserPricipalName and a long with the length of the supplied UPN. Also a WCHAR pointer is passed along, where the function writes back a corresponding GUID which is associated with the passed UPN. The guidSize pointer supplies the size of the pointer, if it's too small the written GUID is not fully written. If everything goes fine the function should return 0 (it never happened yet, when called from c#)
Now my efforts to invoke and call this function look like this:
[DllImport(#"MyDll.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern long MyFunction(IntPtr upnPtr, long upnSize, [Out, MarshalAsAttribute(UnmanagedType.LPWStr) ] StringBuilder guidPtr, IntPtr guidSizePtr);
//my attempt to call the Dll's exposed function
string upn = foo#bar.com;
long upnSize = upn.Length;
IntPtr upnPtr = Marshal.StringToHGlobalUni(upn);
IntPtr guidSizePtr = Marshal.AllocHGlobal(sizeof(long));
Marshal.WriteInt32(GuidSizePtr, 128);
var guidSB = new StringBuilder(128);
result = MyFunction(upnPtr, upnSize, guidSB, guidSizePtr);
as a result I receive an AccessViolationException. I've played around with many variations to call the function, but I never managed to receive a 0 as return value and I was never able to read out the GUID as I'm supposed to do.
Any help with this would be appreciated.
Declare the function as:
[DllImport(#"MyDll.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int MyFunction([MarshalAsAttribute(UnmanagedType.LPWStr)] string upnPtr, int upnSize, [MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder guidPtr, ref int guidSizePtr);
Call it as follows:
string upn = "foo#bar.com";
var guidSB = new StringBuilder(128);
int guidSizePtr =guidSB.Capacity;
MyFunction(upn, upn.Length, guidSB, ref guidSizePtr);
Note that long in C++ is 32-bit, so you should define all such instances as int in your C# code.

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.

Categories