I have an dll written in C++, and I want to call it from C#. The function outputs outputChar and deadChar, the deadChar variable is also read by the C++ function.
I tried to call function from C# in different ways, but all time I got AccessViolationException: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
C++ dll:
extern "C" _declspec (dllexport) int convertVirtualKeyToWChar(int virtualKey, PWCHAR outputChar, PWCHAR deadChar);
C# code 1:
[DllImport("keylib.dll")]
static extern int convertVirtualKeyToWChar(int virtualKey,
StringBuilder output,
StringBuilder deadchar);
C# code 2:
static extern int convertVirtualKeyToWChar(int virtualKey,
out char output,
ref char deadchar);
Note: The two PWCHAR arguments to your function convertVirtualKeyToWChar are ambiguous. They could be pointers to a single WCHAR or pointers to aWCHAR string. Given the name of the function and arguments, this answer assumes they are pointers to a single WCHAR.
You want to use the following:
[DllImport("keylib.dll", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.Cdecl)]
static extern int convertVirtualKeyToWChar( int virtualKey,
out char output,
ref char deadchar );
Your crash is caused by two reasons: DllImport defaults to the ANSI character set and StdCall calling convention. Your C++ DLL does not specify any calling convention, so it will default to CDecl.
See DllImportAttribute.CallingConvention and DllImportAttribute.CharSet
This is a stab in the dark, so caveat emptor...
static extern int convertVirtualKeyToWChar(int virtualKey,
char[] output,
char[] deadchar);
Pass a single-element array to each char[] parameter.
Try this (bearing in mind that if an exception is thrown between Alloc and Free you will leak memory, so build some error handling in):
static void Main(string[] args)
{
IntPtr pout = Marshal.AllocHGlobal(2);
IntPtr pdead = Marshal.AllocHGlobal(2);
int ret = convertVirtualKeyToWChar(1, pout, pdead);
char output = (char)Marshal.ReadInt16(pout);
char dead = (char)Marshal.ReadInt16(pdead);
Marshal.FreeHGlobal(pout);
Marshal.FreeHGlobal(pdead);
}
static extern int convertVirtualKeyToWChar(int virtualKey,
IntPtr output,
IntPtr deadchar);
Related
I have following method in my C++ dll and I am trying to call it in my C# application by means p/invoke.
void Graphics::Create(char* Project, char* Connection, int Count, char *_Source[], char *_Destination[], char *_Search[], char *_Replace[], int _Block[])
Signature I use in C# is:
[DllImport("Wincc.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static public extern void Create(IntPtr Project, IntPtr Connection, int Count, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] _Source, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] _Destination, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] _Search, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] _Replace, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4)] int[] _Block);
I get unknown method in C#. Seems my signature is wrong. However I cannot figure it out what is wrong.
You can not call a C++ function as C++ does not have C linkage. To achieve this add extern "C" before your function.
extern "C" {
int foo();
}
But it wont work on C++ methods. There is an uglier solution though. But I dont recommend it.
Best approach is to write down a wrapper. Wrap your tasks (only what you need) in C++ only using function. In the function you can call those methods. And then compile it with C linkage. Then you'll be able to call it from C#.
Check your DLL with dependency walker, see if you have proper export in your DLL. Try to define your export in YOURDLLNAME.DEF file. Like this:
YOURLLNAME.DEF
EXPORTS
Create
I think I have basically understood how to write c# delegates for callbacks, but this one is confusing me.
The c++ definition is as follows:
typedef int (__stdcall* Callback)(
long lCode,
long lParamSize,
void* pParam
);
and my c# approach would be:
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);
Although this seems to be incorrect, because I get a PInvokeStackInbalance error, which means my definition of the delegate is wrong.
The rest of the parameters of the function are strings or ints, which means they cannot cause the error, and if I just pass a IntPtr.Zero instead of the delegate (which would mean I'm pointing to a non-existent callback function) I get an AccessViolation error, which makes sense aswell.
What am I doing wrong?
EDIT:
The full c++ function is:
int
__stdcall
_Initialize (
const char* FileName,
Callback cbFunction,
int Code,
const char* Name,
unsigned int Option,
unsigned int Option2
);
My c# version is:
[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int _Initialize (string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2);
The function is (for testing) just called inside of the main routine of a console application:
static void Main(string[] args)
{
CallbackDelegate del = new CallbackDelegate(onCallback);
Console.Write(_Initialize("SomeFile.dat", del, 1000, "", 0, 4));
Console.Read();
}
where onCallback is this:
static int onCallback(int lCode, int lParamSize, IntPtr pParam)
{
return 0;
}
I get the PInvokeStackInbalance error on the line where I call _Initialize, if I pass a IntPtr.Zero instead of the delegate, and change the definition of the function to IntPtr instead of CallbackDelegate then I get a AccessViolationException.
I added your code to my current project where I'm doing a lot of C#/C++ interop in VS2012. And while I hate to use the "it worked on my machine", it worked fine for me. The code as I ran it is listed out below just to illustrate that I didn't make and fundamental changes.
My suggestion is that you build a new native dll with a stub function for _Initialize such as below and see if it works when you can control both sides of the interface. If that works but the real dll doesn't, it comes down to either compiler settings on the native side or their is a bug on the native side and it is stomping on the stack.
extern "C" {
typedef int (__stdcall* Callback)(long lCode,long lParamSize,void* pParam );
TRADITIONALDLL_API int __stdcall _Initialize (const char* FileName,Callback cbFunction, int Code,
const char* Name,unsigned int Option,unsigned int Option2)
{
cbFunction(0, 0, nullptr);
return 0;
}
}
On the C# side, I added the declarations to my interface class:
public delegate int CallbackDelegate(int lCode, int lParamSize, IntPtr pParam);
[DllImport("XXX.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int _Initialize(string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2);
And then in my main:
private int onCallback(int lCode, int lParamSize, IntPtr pParam)
{
return 0;
}
XXXInterface.CallbackDelegate del = new XXXInterface.CallbackDelegate(onCallback);
Console.Write(XXXInterface._Initialize("SomeFile.dat", del, 1000, "", 0, 4));
A .NET long is 64bits. A C++ long may be just 32bits. Check with the C++ compiler which compiled that definition as to what size it's longs are.
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);
If .NET assumes cdecl instead of stdcall, your stack will most assuredly be hosed.
I'm having trouble interoperating with a DLL written in C. I'm not sure what type of param to put in place of void*
This is how the API of given DLL looks like:
POSNET_API POSNET_STATUS __stdcall POS_SetDeviceParam ( POSNET_HANDLE hDevice,
unsigned long paramCode,
void * paramValue
)
this is how I was trying to import it in C#:
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(IntPtr hDevice, int POSNET_DEV_PARAM_IP, *type* paramValue);
in place of type I was putting:
[MarshalAs(UnmanagedType.LPStr)] string and other L*Str
[MarshalAs(UnmanagedType.LPArray)] char[] and other type of arrays including of type byte
IntPtr which where AllocHGlobal, GCHandle.Alloc allocated before
even preceeding the method with unsafe and type = void*, combining above alloc aso.
raw type: string, char[]...
I ran out of possibilities by myself.
paramValue should have a value of an IP in following format: "192.168.1.1" - this is how it looks like in a C demo:
string ip="10.33.44.6";
POS_SetDeviceParam(hDevice,POSNET_DEV_PARAM_IP,(void*)ip.c_str());
In C# code, the hDevice is not being initialized - POS_SetDeviceParam should initialize it with additional params.
Any suggestions are very welcome!
You will need to use this P/Invoke signature:
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(
IntPtr hDevice,
int paramCode,
IntPtr paramValue
);
But you'll have to do some work to that string in order to pass it through the IntPtr paramValue argument.
Perhaps you can try using Marshal.StringToHGlobalAnsi() as that will give you an IntPtr you can use. If using this method though be sure to free the memory once you've finished with it.
Thank you guys for all the suggestions, the code below solved my problem!
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(IntPtr hDevice, UInt32 POSNET_DEV_PARAM_IP, IntPtr paramValue);
void Test() {
POS_SetDeviceParam(new IntPtr(), 0x00020005, Marshal.StringToHGlobalAnsi("192.168.1.1"));
}
I have a problem, I tried to look at almost all the poster solutions, unsuccessful to find a suitable one.
The question is easy, Want to have a return string from unmanaged C code in my managed C#.
The c function is:
extern "C" __declspec(dllexport) int process_batch (char *&result);
and in C# I imported the DLL:
[DllImport("mydll.dll")]
public static extern IntPtr process_batch(StringBuilder result);
I run, but the return value in my StringBuilder is a 7-8 character string of non-sense! (I think the memory address)
I tried adding ref before the StringBuilder, this time I get the correct return value in StringBuilder but I get AccessViolationException :
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
So I need your help to fix this.
Just one more thing, I use malloc in c for allocating the memory to the char* variable.
Thanks,
If you really want to do that, pass the parameter as a ref IntPtr, then call Marshal.PtrToStringAnsi, or similar.
[DllImport("mydll.dll")]
public static extern IntPtr process_batch(ref IntPtr result);
Note that, since you're allocating the string with malloc in C, the .NET program will have to keep track of the returned pointer so that it can pass it back to you to be deallocated. Otherwise you'll have a memory leak.
If you were to pass a ref StringBuilder, you wouldn't be able to deallocate the memory that was allocated by the C program.
Also, as somebody commented in another post, you need to set the calling convention:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
I've been using the following code successfully:
[DllImport("user32.dll", EntryPoint = "GetClassName", ExactSpelling = false,
CharSet = CharSet.Auto, SetLastError = true)]
private static extern int _GetClassName(IntPtr hwnd, StringBuilder lpClassName,
int nMaxCount);
public static string GetClassName(IntPtr hWnd)
{
StringBuilder title = new StringBuilder(MAXTITLE);
int titleLength = _GetClassName(hWnd, title, title.Capacity + 1);
title.Length = titleLength;
return title.ToString();
}
I'd advise for a more specific declaration of the imported method via the DllImportAttribute. Try the CharSet = CharSet.Auto bit for instance
I know that this is not exactly related to your original problem as this makes use of the Windows API, but maybe it is of help nonetheless.
I am new in microsoft world.
I have lot of problem trying to pass a simple string from c# to dll/c++
I have read a lot of post and documentation about but the problem is the same.
C++ code
extern "C" __declspec(dllexport) int Init( long l , char* url );
C# code
[DllImport("MCRenderer.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern int Init(long a, StringBuilder url);
Init(hndl.ToInt64(), str );
what haeppen is that long value is passed correctly while string parameter is
0x00000000 <Bad Ptr>
can you help me ... Iam really confused
thanks!!
AG
You should pass a string, url should be of type string and not StringBuilder.
It's because you're marshalling incorrectly: long and int in C++ are both (usually) int in C#.
From MSDN, you need to:
The only caveat is that the StringBuilder must be allocated enough space for the return value, or the text will overflow, causing an exception to be thrown by P/Invoke
Also from Marshaling between Managed and Unmanaged Code
Don't pass StringBuilder by reference (using out or ref). Otherwise, the CLR will expect the signature of this argument to be wchar_t ** instead of wchar_t *, and it won't be able to pin StringBuilder's internal buffer. Performance will be significantly degraded.
Use StringBuilder when the unmanaged code is using Unicode. Otherwise, the CLR will have to make a copy of the string and convert it between Unicode and ANSI, thus degrading performance. Usually you should marshal StringBuilder as LPARRAY of Unicode characters or as LPWSTR.
Always specify the capacity of StringBuilder in advance and make sure the capacity is big enough to hold the buffer. The best practice on the unmanaged code side is to accept the size of the string buffer as an argument to avoid buffer overruns. In COM, you can also use size_is in IDL to specify the size.
So in your example, you need to specify the native size as the parameter of the StringBuilder like this StringBuilder str = new StringBuilder(SIZE_OF_NATIVE_STRING);
Try using LPCSTR in the dll function parameter
extern "C" __declspec(dllexport) int Init( long l , **LPCTSTR** url );
Here is a good example that I found on how to do this.
C++:
extern "C" __declspec(dllexport) void doSomething(LPCTSTR asString)
{
std::cout << "here" << (wchar_t)asString << endl;
system ("pause");
}
C#:
class Program
{
static void Main(string[] args)
{
String myString = "String in C#";
doSomething(myString);
}
private const String path = #"C:\testdll2.dll"; //Make sure that the DLL is in this location
[DllImport(path)]
private static extern void doSomething(string asString);
}