access violation exception while calling the c++ code from c# - c#

I'm trying to use phash library using this wrapper https://github.com/ludoviclefevre/phash-integration-in-csharp
c++ header:
int ph_dct_imagehash(const char* file,ulong64 &hash)
c# dll import:
DllImport(#"pHash.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ph_dct_imagehash(string file, ref ulong hash);
test code:
ulong hash = 0;
foreach (var file in files)
{
ph_dct_imagehash(file, ref hash);
dictionary.Add(file, hash);
}
It works perfectly for few picture, but when ther's about 200-300 pictures i got Accesviolation exception
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
My first lead is garbage collector but i'm confused.. should i use intpr instead of string and hash parameters? I tried changing ref parameter to out but it doesn't matter..

Related

Passing a return char* from C++ to C# with DllImport

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#.

DLLimport using and Error

I have a c++ native dll file and I should call some functions from Dll by c#
the c++ function that I should call is
extern NCSError NCS_CALL NCSOpenFileViewA(const char *szUrlPath, NCSFileView **ppNCSFileView,NCSReadStatus (*pRefreshCallback)(NCSFileView *pNCSFileView));
NCSFileView object is
typedef struct NCSFileViewStruct NCSFileView;
and working example c++ code is
NCS::CApplication App
NCSCompressClient *pClient;
char *szInputFilename = "C:\\testdata\\RGB_8bit.ecw";
NCSFileView *pNCSFileView;
NCSFileInfo *pNCSFileInfo;
NCSFileMetaData *pNCSFileMeta;
NCSError eError;
NCSInit();
eError = NCSOpenFileViewA(szInputFilename, &pNCSFileView, NULL);
if (eError != NCS_SUCCESS) {
ReportError("Could not open view for file:%s, Error = %s",
szInputFilename, NCSGetErrorText(eError));
exit(1);
}`
I made a c# code like this
[DllImport("C:\\NCSEcw.dll",CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr NCSOpenFileViewA ([MarshalAs(UnmanagedType.LPStr)] string path,
IntPtr a,
IntPtr c);
but i get en error like this
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
so, can you help me to write wrapper?

pinvoke: How to free a malloc'd string?

In a C dll, I have a function like this:
char* GetSomeText(char* szInputText)
{
char* ptrReturnValue = (char*) malloc(strlen(szInputText) * 1000); // Actually done after parsemarkup with the proper length
init_parser(); // Allocates an internal processing buffer for ParseMarkup result, which I need to copy
sprintf(ptrReturnValue, "%s", ParseMarkup(szInputText) );
terminate_parser(); // Frees the internal processing buffer
return ptrReturnValue;
}
I would like to call it from C# using P/invoke.
[DllImport("MyDll.dll")]
private static extern string GetSomeText(string strInput);
How do I properly release the allocated memory?
I am writing cross-platform code targeting both Windows and Linux.
Edit:
Like this
[DllImport("MyDll.dll")]
private static extern System.IntPtr GetSomeText(string strInput);
[DllImport("MyDll.dll")]
private static extern void FreePointer(System.IntPtr ptrInput);
IntPtr ptr = GetSomeText("SomeText");
string result = Marshal.PtrToStringAuto(ptr);
FreePointer(ptr);
You should marshal returned strings as IntPtr otherwise the CLR may free the memory using the wrong allocator, potentially causing heap corruption and all sorts of problems.
See this almost (but not quite) duplicate question PInvoke for C function that returns char *.
Ideally your C dll should also expose a FreeText function for you to use when you wish to free the string. This ensures that the string is deallocated in the correct way (even if the C dll changes).
Add another function ReturnSomeText that calls free or whatever is needed to release the memory again.
If you return to .net memory allocated with your native malloc, then you also have to export the deallocator. I don't regard that to be a desirable action and instead prefer to export the text as a BSTR. This can be freed by the C# runtime because it knows that the BSTR was allocated by the COM allocator. The C# coding becomes a lot simpler.
The only wrinkle is that a BSTR uses Unicode characters and your C++ code uses ANSI. I would work around that like so:
C++
#include <comutil.h>
BSTR ANSItoBSTR(const char* input)
{
BSTR result = NULL;
int lenA = lstrlenA(input);
int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
if (lenW > 0)
{
result = ::SysAllocStringLen(0, lenW);
::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
}
return result;
}
BSTR GetSomeText(char* szInputText)
{
return ANSItoBSTR(szInputText);
}
C#
[DllImport("MyDll.dll", CallingConvention=CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText(string strInput);

P/Invoke code works on WinXP, exception on Win2k8

I'm attempting to access a function in a DLL in C# and C++.
C++ is working fine, as is C# on WinXP. However I'm getting the following error when attempting to access the function on a Win2k8 system:
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory
is corrupt.
at Router.GetAddress()
The declaration in C# is:
[DllImport("Constants.dll")]
static extern String GetAddress();
Usage in C# (at the moment) is just outputting it:
Console.WriteLine(GetAddress());
And the contents of the DLL's function are just:
const static WCHAR* szAddress= L"net.tcp://localhost:4502/TestAddress";
extern "C" __declspec(dllexport) const WCHAR* GetAddress()
{
return szAddress;
}
I really didn't think there was anything controversial here. The only thing I can think of is the const return from GetAddress, but I'm not sure how to apply the corresponding keyword to C# as I'm not as familiar with that language yet.
Any suggestions would be greatly appreciated.
I ended up fixing this problem using the details in http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/4e387bb3-6b99-4b9d-91bb-9ec00c47e3a4.
I changed the declaration to:
[DllImport("Constants.dll", CharSet = CharSet.Unicode)]
static extern int GetAddress(StringBuilder strAddress);
The usage therefore became:
StringBuilder sb = new StringBuilder(1000000); // Arbitrary length for the time being
GetAddress(sb);
Console.WriteLine(sb.ToString());
And the DLL was changed to:
const static WCHAR* szAddress = L"net.tcp://localhost:4502/TestAddress";
extern "C" __declspec(dllexport) int GetAddress(WCHAR* strAddress)
{
wcscpy(strAddress, szAddress);
return 0;
}

Passing a string variable as a ref between a c# dll and c++ dll

I have a c# dll and a c++ dll . I need to pass a string variable as reference from c# to c++ . My c++ dll will fill the variable with data and I will be using it in C# how can I do this. I tried using ref. But my c# dll throwed exception . "Attempted to read or write protected memory. ... This is often an indication that other memory is corrupt". Any idea on how this can be done
As a general rule you use StringBuilder for reference or return values and string for strings you don't want/need to change in the DLL.
StringBuilder corresponds to LPTSTR and string corresponds to LPCTSTR
C# function import:
[DllImport("MyDll.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static void GetMyInfo(StringBuilder myName, out int myAge);
C++ code:
__declspec(dllexport) void GetMyInfo(LPTSTR myName, int *age)
{
*age = 29;
_tcscpy(name, _T("Brian"));
}
C# code to call the function:
StringBuilder name = new StringBuilder(512);
int age;
GetMyInfo(name, out age);
Pass a fixed size StringBuilder from C# to C++.

Categories

Resources