I have this function in c++
extern "C" __declspec(dllexport) void SendPacketToServer(BYTE *packet, int Length)
{
_SendToServer(packet, Length);
}
How can I use it in c#?
I tried this so far:
[DllImport("DAFramework 1.0.dll", SetLastError = true)]
internal static extern void SendPacketToServer(IntPtr packet, int length);
unsafe
{
fixed (byte* pByte = new byte[] { 0x13, 0x00 })
{
IntPtr data = new IntPtr((void*)pByte);
SendPacketToServer(data, 2);
}
}
Am I doing something incorrect? If so, How can I make it work? I'm getting the error: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
can I do it a simpler way?
You must set appropriate calling convention:
[DllImport("DAFramework 1.0.dll", SetLastError = true, CallingConvention=CallingConvention.Cdecl)]
because your native function declares it (extern "C" __declspec(dllexport)).
I think its a similar problem, in my case was due to char* parameter. I think you have to allocate previously the parameter.
So your only solution is to pass the string parameters as IntPtr.
Allocate the memory with Marshal.StringToHGlobalAnsi
Attempted to read or write protected memory with dllimport in c#
Related
I am a newbie on both C# WPF and C++.
Recently, I got an external .dll, which returns a char*, and I want to receive the return value in C# by using DllImport. Then, using str.Split(';') to separate the characters. To the purpose, I created a button to show the first character in the string I split on a label when I click the button.
Therefore, I use an IntPtr to receive a char*, which is from C++ .dll, and call Marshal.PtrToStringAnsi() to turn it into a string. However, when I execute the code, it sometimes works but sometimes crushes. Then error code always shows
Unhandled exception at 0x00007FFC06839269 (ntdll.dll) in UITest.exe: 0xC0000374: heap corruption (parameters: 0x00007FFC068A27F0).
I thought my code is reasonable and I couldn't find root causes. Can anyone help me? Thanks!
The below shows the content in .dll and also the code I used in C#.
C++ code in Dlltest.h:
#define DLL_EXPORT extern "C" __declspec(dllexport)
char* getRbtData = nullptr;
DLL_EXPORT char* func_getRbtData();
C++ code in Dlltest.cpp:
char* func_getRbtData()
{
getRbtData = new char(128);
memset(getRbtData, 0, strlen(getRbtData));
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
return getRbtData;
};
C# code in UITest.xaml.cs:
[DllImport("DllTest.dll",EntryPoint = "func_getRbtData", CharSet = CharSet.Ansi)]
public static extern IntPtr func_getRbtData();
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
There are several problems with your code.
On the C++ side, your DLL function is implemented all wrong:
getRbtData = new char(128);
You are allocating a single char whose value is 128, not an array of 128 chars. You need to use new char[128] instead for that.
memset(getRbtData, 0, strlen(getRbtData));
getRbtData is not a pointer to a null-terminated string, so strlen(getRbtData) is undefined behavior. It reads into surrounding memory while calculating the length until it finds a random 0x00 byte in memory.
And then the subsequent memset() into getRbtData will overwrite that surrounding memory. If it doesn't just crash outright.
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
Prior to C++11, this assignment is OK but discouraged. In C++11 and later, this assignment is actually illegal and won't compile.
String literals are read-only data, so you need to use const char instead of char in your pointer type. You should do that even in older compilers.
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
strlen(_getRbtData) is OK since _getRbtData is a pointer to a null-terminated string.
However, since getRbtData is not allocated with enough memory to receive all of the copied chars, memcpy() into getRbtData is also undefined behavior and will trash memory, if not crash outright.
return getRbtData;
This is OK to pass the pointer to C#.
However, since the memory is being allocated with new (better, new[]), it needs to be freed with delete (delete[]), which you are not doing. So you are leaking the memory.
Marshal.PtrToStringAnsi() on the C# side will not (and cannot) free new'ed memory for you. So your C# code will need to pass the pointer back to the DLL so it can delete the memory properly.
Otherwise, you will need to allocate the memory using the Win32 API LocalAlloc() or CoTaskMemAlloc() function so that the Marshal class can be used on the C# side to free the memory directly without passing it back to the DLL at all.
On the C# side, you are using the wrong calling convention on your DllImport statement. The default is StdCall for compatibility with most Win32 API functions. But your DLL function is not specifying any calling convention at all. Most C/C++ compilers will default to __cdecl unless configured differently.
With that said, try this instead:
Dlltest.h
#define DLL_EXPORT extern "C" __declspec(dllexport)
DLL_EXPORT char* func_getRbtData();
DLL_EXPORT void func_freeRbtData(char*);
Dlltest.cpp
char* func_getRbtData()
{
const char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
int len = strlen(_getRbtData);
char *getRbtData = new char[len+1];
// alternatively:
/*
char *getRbtData = (char*) LocalAlloc(LMEM_FIXED, len+1);
if (!getRbtData) return NULL;
*/
memcpy(getRbtData, _getRbtData, len+1);
return getRbtData;
}
void func_freeRbtData(char *p)
{
delete[] p;
// alternatively:
// LocalFree((HLOCAL)p);
}
UITest.xaml.cs
[DllImport("DllTest.dll", EntryPoint = "func_getRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr func_getRbtData();
[DllImport("DllTest.dll", EntryPoint = "func_freeRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern void func_freeRbtData(IntPtr p);
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
func_freeRbtData(intptr);
// alternatively:
// Marshal.FreeHGlobal(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
new char(128) returns a pointer to one character, with initial value 128.
I could tell you how to allocate 128 characters, but the chief problem with that is that you can't clean it up so that's not a useful answer. Check the existing questions about returning a string to C#.
How transferring an image to buffer in c++(dll) and then read/write in buffer in C# and return to c++(dll) in real time? The process I'm looking is in the following :
1- First, I read an image form hard drive;
Mat inputImage = read(“/,…./Test.jpg”);
2- Put in the buffer:
imencode(".jpg", inputImage, inputBuff, paramBuffer);
3- send form c++ to c#
??? (I don’t know).
4- read in c# from buffer
??? (I don’t know).
5- write the changes that happens through c++ and c# in buffer
??? (I don’t know).
I'm using Opencv c++.
I really thank you.
You can use System.Runtime.InteropServices in C# to call functions in external libraries, for example
c++ code
extern "c"
{
__declspec(dllexport) void __cdecl Read(unsigned char*& buffer)
{
//allocate and write to buffer
}
__declspec(dllexport) void __cdecl Write(unsigned char* buffer)
{
//do something with the buffer
}
}
and compile it into a dll
C# code
using System.Runtime.InteropServices;
class Main
{
[DllImport(DLL_FILE, CallingConvention = CallingConvention.Cdecl)]
private static extern void Read(out IntPtr buffer);
[DllImport(DLL_FILE, CallingConvention = CallingConvention.Cdecl)]
private static extern void Write(byte[] buffer);
public static void ReadFromExtern()
{
IntPtr bufferPtr = IntPtr.Zero;
Read(bufferPtr);
int length = LENGTH;
byte[] buffer = new byte[length];
Marshal.Copy(bufferPtr, buffer, 0, length);
//do something with buffer
}
public static void WriteToExtern(byte[] buffer)
{
Write(buffer);
//or do something else
}
}
Note:
If you used dynamically allocated memory in the c++ code, remember to also write a wrapper function to free it, otherwise causing memory leak.
__declspec(dllexport) is Windows specific to export functions to dll(s).
extern "C" is to avoid name mangling by c++ compilers, can be omitted if using c compilers
For string transferring, use char* in c++ and System.Text.StringBuilder in c#
I'm trying to pass raw texture data from Texture2D (byte array) to unmanaged C++ code. In C# code array length is about 1,5kk, however in C++ 'sizeof' always returns 8.
C# declaration of native method :
[DllImport("LibName", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ProcessData(byte[] data);
C++:
extern "C" {
__declspec(dllexport) void ProcessData(uint8_t *data) {
//sizeof(data) is always 8
}
}
What am I doing wrong? Is there is a way to pass array without additional memory allocation in C++ code?
Few things you need to do with your current code:
1.You have to send the size of the array to the C++ plugin in another parameter as UnholySheep mentioned.
2.You also have to pin the array before sending it to the C++ side. This is done with the fixed keyword or GCHandle.Alloc function.
3.If the C++ function has a void return type, your C# function should also have the void return type.
Below is a corrected version of your code. No additional memory allocation is performed.
C#:
[DllImport("LibName", CallingConvention = CallingConvention.Cdecl)]
static extern void ProcessData(IntPtr data, int size);
public unsafe void ProcessData(byte[] data, int size)
{
//Pin Memory
fixed (byte* p = data)
{
ProcessData((IntPtr)p, size);
}
}
C++:
extern "C"
{
__declspec(dllexport) void ProcessData(unsigned char* data, int size)
{
//sizeof(data) is always 8
}
}
I am trying to pass a byte array to a c++ dll:
c++:
extern "C" __declspec(dllexport) char* myfunction(byte bytes[])
{
char *byteschar = (char*)bytes;
//do somethings with it
return byteschar;
}
c#:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string myfunction(byte[] bytes);
but I get a System.AccessViolationException when I call myfunction.
When I run the executable without the debugger it seems to be working fine
If you want a buffer be allocated in C# and filled in C++, the approach is a little bit different.
You should allocate a kind of "unmanaged" buffer, pass to the DLL and then convert the result and free the buffer. It's exactly the same way in C, but calling from a managed environment.
Your C++ code should be something like:
extern "C" __declspec(dllexport) void myfunction(char* buffer, int length)
{
//Fill buffer with something observing the maximum length of the buffer.
}
The signature of your DLL in C# should be:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
public static extern string myfunction(IntPtr buffer, Int32 length);
To call it from C#, you should do:
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(100);
// Your Unmanaged Call
myfunction(unmanagedBbuffer, 100);
string yourString = Marshal.PtrToStringUni(unmanagedBuffer);
Marshal.FreeHGlobal(unmanagedBuffer);
Don't forget to call FreeHGlobal if you don't want a memory leak in your app. It's interesting to protect this in "try/finally" clauses.
Other observation is the encoding of the string. Uni, means Unicode. If you use another string representation, check for an equivalent PtrToStringXXX function.
It suppose to be:
extern "C" __declspec(dllexport) char* myfunction(unsigned char * bytes)
{
//do somethings with it
return bytes;
}
I am trying to use a C++ dll to edit my StringBuilder object in C#. My C++ code looks like this:
extern "C" __declspec(dllexport) void __stdcall PrintHead(char* myString)
{
myString = "testIsOkey";
}
and my C# code is:
[DllImport("StringEdit.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern void PrintHead([MarshalAs(UnmanagedType.LPStr)] StringBuilder stringBuilder);
private void button1_Click(object sender, EventArgs e)
{
StringBuilder stringBuilder = new StringBuilder("123456");
PrintHead(stringBuilder);
}
After PrintHead is called, i am expecting to see that the stringBuilder object's value is changed from "123456" to "testIsOkey" , but it does not change. I can't figure out where do i make a mistake.
Thanks for your help.
void __stdcall PrintHead(char* myString) {
myString = "testIsOkey";
}
That's not correct C++ code. It merely changes the pointer that was passed to the function. This has no side effects whatsoever. Fix:
void __stdcall PrintHead(char* myString) {
strcpy(myString, "testIsOkey");
}
But never write interop code like this, the C++ function can easily destroy the garbage collected heap this way. Which is exactly what happens in your code, the StringBuilder's Capacity isn't enough. You should add an extra argument that provides the size of the passed buffer. Fix:
void __stdcall PrintHead(char* myString, size_t bufferSize) {
strcpy_s(myString, bufferSize, "testIsOkey");
}
Pass the string builder's Capacity for that extra argument, like this:
var buffer = new StringBuilder(666);
PrintHead(buffer, buffer.Capacity);
var result = buffer.ToString();
Doesn't it have to be marked ref or with some other attribute, so that .NET knows that the marshalling should occur both ways?