Access Violation Exception accessing C DLL function by the second access - c#

I have the following code:
Generic.cs
class Generic
{
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern String Consulta(int NumeroSecao);
}
And
main.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
int random = rnd.Next(9999);
Stopwatch sw = new Stopwatch();
sw.Start();
String teste = Generic.Consult(random);
sw.Stop();
TextoLog = "Tempo de Execução:"+sw.Elapsed.Milliseconds+"ms | ConsultarSAT";
tempoEntreComandos();
}
The code of DLL is:
Generic.c
__declspec(dllexport) char* __stdcall Consult(int numeroSessao)
{
memset(pcReturn,0,sizeof(char) * BUFFER_SIZE);
HANDLE fileHandle;
Communicate(&fileHandle, pcReturn,10);
return pcReturn;
}
The problem occurs when I call function Consult the second time, the message received is an Access Violation Exception. What is happening here?

The solution is the type for the return in the declaration of function, instead of use String is necessary use of IntPtr.

Native strings have to be marshaled to be used in c#. Your native code is not actually returning a string object, but a char pointer. You have to tell the marshaler what the return type is, as so:
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern String Consulta(int NumeroSecao);
Using LPStr for ASCII strings and LPWStr for unicode.
Actually it might be a better idea to marshal the string manually as the marshaler deallocates the native string right after conversion. The declaration would be then
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr Consulta(int NumeroSecao);
And you manually marshal the IntPtr to string like so
String str = Marshal.PtrToStringAnsi(strptr); // for ansi
String str = Marshal.PtrToStringUni(strptr); // for unicode

Related

Unknown error when using .NET 4.0 with dll

c++ code is
MSIPC_SDK LONG __stdcall Ms_IpcClient_CaptureImage(LONG nUserId, char *sFilePath,
int nPathLen, const char *sDiskPath = NULL);//sDiskPath example: "C: \\".
Affect: Take a snapshoot
Parameters remark:
LONG nUserId: Ms_Ipc_Login()//Return value after login successfully
char *sFilePath: //destination for saving the recording files
int nPathLen: //the length of the path
const char *sDiskPath = NULL: //which disk to be saved
my c# code is:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
[MarshalAs(UnmanagedType.LPStr)]
string sFilePath,
int nPathLen,
[MarshalAs(UnmanagedType.LPStr)]
string sDiskPath
);
and using is method:
var ret = Ms_IpcClient_CaptureImage(loginID, "C:\\a.bmp", 10000, "C:\\");
It is working in .Net Framework 2 but does not work in .Net Framework 4.
How can I fix it in .Net Framework 4?
sFilePath is used to pass a string from callee to caller. That's why the type is char* rather than const char*, and that's why there is a parameter for buffer length. That means you need to use StringBuilder rather than string. The p/invoke should be:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
StringBuilder sFilePath,
int nPathLen,
string sDiskPath
);
And the call should be:
var filePath = new StringBuilder(260);
var ret = Ms_IpcClient_CaptureImage(loginID, filePath, filePath.Capacity, "C:\\");
Your code has always been wrong and you've just been getting away with it up until now. The fact that you passed a made up buffer length value of 10000 should have set the alarm bells ringing!

Passing strings from C# to C++ DLL and back -- minimal example

I am trying to make the absolute simplest minimal example of how to pass strings to and from a C++ DLL in C#.
My C++ looks like this:
using std::string;
extern "C" {
string concat(string a, string b){
return a + b;
}
}
With a header like
using std::string;
extern "C" {
// Returns a + b
__declspec(dllexport) string concat(string a, string b);
}
My C# is
[DllImport("*****.dll", CallingConvention = CallingConvention.Cdecl)]
static extern string concat(string a, string b);
}
And I am calling it with:
Console.WriteLine(concat("a", "b"));
But this gives a System.AccessViolationException. This seems like it out to be the most trivial thing to deal with, but I am completely stuck on it. When I tried to do a similar experiment with a function "Add" that took two doubles and returned a double I had no problems.
You cannot pass a C++ std::string across an interop boundary. You cannot create one of those in your C# code. So your code can never work.
You need to use interop friendly types at the interop boundary. For instance, null-terminated arrays of characters. That works well when you allocate and deallocate the memory in the same module. So, it's simple enough when passing data from C# to C++.
C++
void foo(const char *str)
{
// do something with str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);
....
foo("bar");
In the other direction you would typically expect the caller to allocate the buffer, into which the callee can write:
C++
void foo(char *str, int len)
{
// write no more than len characters into str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);
....
StringBuilder sb = new StringBuilder(10);
foo(sb, sb.Capacity);
This is the simplest way I like - pass a string in, and use a lambda to get the response
C#
public delegate void ResponseDelegate(string s);
[DllImport(#"MyDLL.dll", EntryPoint ="Foo", CallingConvention = CallingConvention.StdCall)]
public static extern void Foo(string str, ResponseDelegate response);
...
Foo("Input", s =>
{
// response is returned in s - do what you want with it
});
C++
typedef void(_stdcall *LPEXTFUNCRESPOND) (LPCSTR s);
extern "C"
{
__declspec(dllexport) void __stdcall Foo(const char *str, LPEXTFUNCRESPOND respond)
{
// Input is in str
// Put your response in respond()
respond("HELLO");
}
}

Attempted to read or write protected memory with dllimport in c#

I have a problem with my project:
In dll c++:
extern "C" __declspec(dllexport) int results(char* imgInput, void* tree)
{
struct kd_node* nodeTree = new(tree)kd_node ; // new kd_tree with data from memory address
...
...
int ret = atoi(retValueStr.c_str());
return ret;
}
extern "C" __declspec(dllexport) void* buildKDTree(char* folder)
{
struct kd_node* kd_root;
....
feature *LFData = listFeat.data();
kd_root = kdtree_build(LFData,listFeat.size());
void* address_kdtree = (void*)&kd_root; // get memory address of kd_tree
return address_kdtree;
}
and I use to dllimport in c#:
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "buildKDTree", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void* buildKDTree(byte[] urlImage);
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "results", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I4)]
public unsafe static extern int results(byte[] imgInput, void* tree);
static unsafe void Main()
{
string urlImg1 = "C:/Users../test img/1202001T1.jpg";
string urlImg = "C:/export_features";
try
{
IntPtr result;
int result1;
result1 = results(convertStringToByte(urlImg1), 5, buildKDTree(convertStringToByte(urlImg))); // this error
Console.WriteLine("results = %d",result1);
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
}
when i run the program, this program show error :
Attempted to read or write protected memory. This is often an indication that other memory is corrupt
what error do you know and how to resolved ?
thank you!
You don't need a convertStringToByte method here. You can tell the runtime to marshal your string as a char *. Also, I would suggest that you make the method return an IntPtr, like this:
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "buildKDTree",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr buildKDTree([MarshalAs(UnmanagedType.LPStr)]string urlImage);
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "results",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I4)]
public static extern int results([MarshalAs(UnmanagedType.LPStr)]string imgInput, IntPtr tree);
You can then call it with:
IntPtr tree = buildKDTree(urlImg);
int result1 = results(urlImg, 50, tree);
Console.WriteLine("results = {0}",result1);
Well, for one thing, the C function is called buildKDTree, but you are importing it in the C# code with entry point "buildKDTreeWithFeatures". Try making these consistent and see if you get better results.
I try to call it :
IntPtr tree = buildKDTree(urlImg);
int result1 = results(urlImg, 50, tree);
Console.WriteLine("results = {0}",result1);
but it is not your fault where you said.
I think the variable intPtr tree in function results([MarshalAs(UnmanagedType.LPStr)]string imgInput, IntPtr tree); that caused the error
I think its similar problem due to char* parameters, in my own problem thanks to this below link question solves the problem.
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#

StringBuilder Marshalling problem in C#

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?

Interop sending string from C# to C++

I want to send a string from C# to a function in a native C++ DLL.
Here is my code:
The C# side:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Unicode)]
private static extern void func1(byte[] path);
public void func2(string path)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] arr = encoding.GetBytes(path);
func1(this.something, arr);
}
The C++ side:
void func1(char *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send.
Help?
Thanks.
It looks like you have 2 issues. The first is your native C++ uses an ANSI string but you are specifying unicode. Secondly, it's easiest to just marshal a string as a string.
Try changing the DllImport to the following
[DllImport(
#"Native3DHandler.dll",
EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(void* something, [In] string path);
Works fine for me with no extra marshalling instructions in VS2008:
C# side:
[DllImport("Test.dll")]
public static extern void getString(StringBuilder theString, int bufferSize);
func()
{
StringBuilder tstStr = new StringBuilder(BufSize);
getString(tstStr, BufSize);
}
C++ side:
extern "C" __declspec(dllexport) void getString(char* str, int bufferSize)
{
strcpy_s(str, bufferSize, "FOOBAR");
}
Your declaration is wrong. The parameter should be of type string, and you should set the character set encoding to Ansi, like so:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(string path);
This assumes that you are not modifying the contents of the path variable in your C++ code. Then, you pass the string parameter directly (no need for the wrapper).
If you just want to send a string, just declare func1's parameter as a string. If you want to receive a string, declare it as a StringBuilder and allocate enough buffer space for what you want to receive.
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

Categories

Resources