Why am I getting System.AccessViolationException during c#/c++ dll interop? [duplicate] - c#

This question already has answers here:
Why are Cdecl calls often mismatched in the "standard" P/Invoke Convention?
(2 answers)
Closed 4 years ago.
I am new to C# and .NET. I want to use my existing C++ dlls within .NET, so I was experimenting with interop. I don't know why this exception is raised. I know what that exception means, I read the MSDN, but not sure why that is raised.
My code is so.
The dll code is a simple function that takes a char*.
extern "C" __declspec(dllexport)void test(char *text)
{
// these raise System.AccessViolationException
// char c = text[0];
// int n = strlen(text);
// char *copy = strdup(text);
}
The C# code is a simple class calling this function
namespace Interop_test
{
class Program
{
static void Main(string[] args)
{
char[] text = new char[10];
for (int i = 0; i < 10; i++)
text[i] = (char)('A' + i);
test(text);
}
[DllImport("interop_test_dll.dll")]
private static extern void test(char[] text);
}
}
The said exception is raised if the commented out code in the dll function is removed. Why?
How can (or must) I pass an array of data from C# to C++?
Where can I find more information about dealing with memory management during C++/C# interop?

Use a string and try this:
private static extern void test(ref string text);
If you want to change your string in your test function then use ref. Otherwise you can leave it.

Where do you put a zero at the end of the string? I think no where. I don't know if that is a problem but you can try increasing the size of of text to 11 and then putting a zero in text[10].

This should do the trick. CharSet.Ansi and using an actual string. Those are the keys here.
namespace Interop_test
{
class Program
{
static void Main(string[] args)
{
char[] text = new char[10];
for (int i = 0; i < 10; i++)
text[i] = (char)('A' + i);
test(new string(text));
}
[DllImport("interop_test_dll.dll", CharSet = CharSet.Ansi)]
private static extern void test(
[MarshalAs(UnmanagedType.LPStr, CharSet = CharSet.Ansi)] string text);
}
}

These articles won't answer your specific question but they may come in useful to you as you carry on with this.
Calling Win32 dlls in .NET with P/Invoke
Default Marshalling for value types
Also, in your dllimport statements in additional to indicating the marshalling type, you can add information to indicate if they should be marshalled as input or output or both.
[DllImport("interop_test_dll.dll")]
private static extern void test([MarshalAs(UnmanagedType.LPStr), In, Out]string text);

Related

How to read dll from specific path in C#

I need to import SglW32.dll to my solution.
But I get:
AccessViolation exeption : Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.
I could not use just DllImport. In that case dll is not found.
This is whole example.
using System;
using System.Runtime.InteropServices;
namespace TestDllimport
{
class Program
{
static void Main(string[] args)
{
var a = new MyClass();
var result = a.getValue();
}
}
class FunctionLoader
{
[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string path);
[DllImport("Kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
public static Delegate LoadFunction<T>(string dllPath, string functionName)
{
var hModule = LoadLibrary(dllPath);
var functionAddress = GetProcAddress(hModule, functionName);
return Marshal.GetDelegateForFunctionPointer(functionAddress, typeof(T));
}
}
public class MyClass
{
//Define your path to dll.
//Get dll from: http://www.sg-lock.com/download/sglw32_v2_28.zip
private const string DLL_Path = #"C:\Users\admin123\Desktop\MyDlls\SglW32.dll";
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ulong SglAuthentA(IntPtr AuthentCode);
static MyClass()
{
sglAuthentA = (SglAuthentA)FunctionLoader.LoadFunction<SglAuthentA>(DLL_Path, "SglAuthentA");
}
static private SglAuthentA sglAuthentA;
unsafe public ulong getValue()
{
IntPtr d = new IntPtr(5);
var a1 = sglAuthentA(d); // Exception IS HERE !!!!!
return a1;
}
}
}
I am using load function to get dll from any path. After that I crate delegate from required function. In my case function is SglAuthentA.
This solution in working with one other dll, but not for SglW32.dll.
Product: http://www.sg-lock.com/us/
Required dll : http://www.sg-lock.com/download/sglw32_v2_28.zip
Manual: http://www.sg-lock.com/download/SG-Lock_Manual_Eng.pdf
Source 1: https://stackoverflow.com/a/8836228/2451446
EDIT: Solution thanks to Hans Passant answer and ja72 comment
See How to import dll
using System.Runtime.InteropServices;
namespace TestDllimport
{
class Program
{
static void Main(string[] args)
{
var testA = DllImportClass.SglAuthentA(new uint[] { 5, 6, 7 }, new uint[] { 5, 6, 7 }, new uint[] { 5, 6, 7 });
var testB = DllImportClass.SglAuthentB(new uint[] { 5, 6, 7 });
}
}
static class DllImportClass
{
[DllImport("SglW32.dll")]
public static extern uint SglAuthentA(uint[] AuthentCode0, uint[] AuthentCode1, uint[] AuthentCode2);
[DllImport("SglW32.dll")]
public static extern uint SglAuthentB(uint[] AuthentCode);
}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ulong SglAuthentA(IntPtr AuthentCode);
The delegate declaration is not correct and does not match the api function signature. An ULONG in C is an uint in C#. An ULONG* in C is ambiguous, could be a ref uint or it could be a uint[]. Since you are supposed to pass a 48 byte authentication code, you know it is an array. Fix:
private delegate uint SglAuthentA(uint[] authentCode);
Be sure to pass the proper authentication code. It is not 5, the array must have 12 elements. If you don't have one then call the manufacturer to acquire one.
private const string DLL_Path = #"C:\Users\admin123\Desktop\MyDlls\SglW32.dll";
Do beware that this is not a workaround for not being able to use [DllImport]. Hardcoding the path is a problem, the file is not going to be present in that directory on the user's machine. The DLL itself does not have any dependencies that prevents it from loading, the only plausible reason for having trouble is you just forgetting to copy the DLL into the proper place. There is only one such place, the same directory as your EXE.
Fix this the right way, use Project > Add Existing Item > select the DLL. Select the added file in the Solution Explorer window. In the Properties window, change the Copy to Output Directory setting to "Copy if newer". Rebuild your project and note that you'll now get the DLL in your project's bin\Debug directory. Now [DllImport] will work.
A caution about the manual, it lists code samples in Visual Basic. Which is in general what you'd normally use as a guide on learning how to use the api. The code is however not VB.NET code, it is VB6 code. Where ever you see Long in the sample code, you should use uint or int instead.
Very sloppy, it casts a big question mark on the quality of the product. Something else they don't seem to address at all is how to get your own code secure. Very important when you use a dongle. Beware it is very trivial for anybody to reverse-engineer your authentication code. And worse, to decompile your program and remove the authentication check. You need to use an obfuscator.

Calling C++ code from C# error using references in c++

In my C++ dll named "scandll.dll" I have the following function
extern "C" __declspec(dllexport) void scanfile(char * returnstring)
{
strcpy(returnstring, "return string");
}
in my C# code I'm doing this
[DllImport("scandll.dll", CharSet = CharSet.Ansi, SetLastError = true )]
public static extern int scanfile(ref IntPtr strReturn);
and this is the method that I'm using to get the value from the dll
public void Scan()
{
string filename = "";
IntPtr ptr = new IntPtr();
scanfile(ref ptr);//Here i get the error
filename = Marshal.PtrToStringAnsi(ptr);
}
I get an exception thrown as "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I'm following this link
Calling C++ code from C# error using references in c++ ref in c#
Any help would be appreciated.
Thanks
Your C++ code is wrong - all it's doing is changing the value of the pointer which has been passed to it to point to a constant string. The caller knows nothing about that change.
It's the same as if you'd done this:
void MyCfunction(int number)
{
number = 3;
}
and then hoped that a caller of that function would somehow see the number '3' anywhere.
You could do something like this in C++:
void MyCFunction(char* pReturnString)
{
strcpy(pReturnString, "Hello, world");
}
but you also need to make sure on the C# side that you had provided a buffer with pReturnString pointing to it. One way to do this by using a StringBuilder, and then having your C# declaration of the C function take a parameter like this:
[MarshalAs(UnmanagedType.LPStr)] StringBuilder returnString
You need to reserve space in the StringBuilder before calling into the C++ function.
In real life, C/C++ functions like this pretty much always take an additional length parameter, which allows the caller to tell the callee the maximum length of string it's allowed to copy into the buffer. There is no consistency of convention about whether the length includes the terminating \0 or not, so people are often careful about running right up to the end of the buffer.
memcpy ( destination, * source, size_t num );
Try this to assign the value to returnstring = "rturn name";

Passing String from Native C++ DLL to C# App

I have written a DLL in C++. One of the functions writes to a character array.
C++ Function
EXPORT int xmain(int argc, char argv[], char argv2[])
{
char pTypeName[4096];
...
//Other pTypeName ends up populated with "Portable Network Graphics"
//This code verifies that pTypeName is populated with what I think it is:
char szBuff[64];
sprintf(szBuff, pTypeName, 0);
MessageBoxA(NULL, szBuff, szBuff, MB_OK);
//The caption and title are "Portable Network Graphics"
...
//Here, I attempt to copy the value in pTypeName to parameter 3.
sprintf(argv2, szBuff, 0);
return ret;
}
C# Import
//I believe I have to use CharSet.Ansi because by the C++ code uses char[],
[DllImport("FirstDll.dll", CharSet=CharSet.Ansi)]
public static extern int xmain(int argc, string argv, ref string zzz);
C# Function
private void button2_Click(object sender, EventArgs e)
{
string zzz = "";
int xxx = xmain(2, #"C:\hhh.bmp", ref zzz);
MessageBox.Show(zzz);
//The message box displays
//MessageBox.Show displays "IstuÈst¼ÓstÄstlÄstwÄstiÑstõÖstwÍst\
// aÖst[ÖstÃÏst¯ÄstÐstòÄstŽÐstÅstpÅstOleMainThreadWndClass"
}
I have attempted to pass a parameter from C# by reference and have the C++ DLL populate the parameter. Even though I have verified that the value is correct in the DLL, gibberish gets passed to the C# application.
What can I do to write the correct string value to the C# string?
Use a StringBuilder to pass a character array that native code can fill in (see Fixed-Length String Buffers).
Declare the function:
[DllImport("FirstDll.dll", CharSet=CharSet.Ansi)]
public static extern int xmain(int argc, string argv, StringBuilder argv2);
Use it:
// allocate a StringBuilder with enough space; if it is too small,
// the native code will corrupt memory
StringBuilder sb = new StringBuilder(4096);
xmain(2, #"C:\hhh.bmp", sb);
string argv2 = sb.ToString();
Give some other information to the DLLImport call. Look at the following example of my own:
[DllImport("tcpipNexIbnk.dll", EntryPoint = "SendData", CallingConvention = CallingConvention.Cdecl)]
public static extern int Send([MarshalAs(UnmanagedType.LPWStr)]string message);
Notice two things, the CallingConvention parameter:
CallingConvention = CallingConvention.Cdecl)
Use that as it is.
And then just behind the c# string type, you can play with the different Unmanaged types using the MarshalAS instruction, that will cast your C# string parameter to the native string type you have in your c++ program:
public static extern int Send([MarshalAs(UnmanagedType.LPWStr)]string message);
Hope it helps.

C# P/Invoke: Varargs delegate callback

I was just trying to do some managed/unmanaged interop. To get extended error information I decided to register a log callback offered by the dll:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt);
This definition works, but i get strings like "Format %s probed with size=%d and score=%d".
I tryed to add the __arglist keyword, but it is not allowed for delegates.
Well, it is not so dramatic for me, but I am just curious wether one could get the varargs parameters in C#.
I know that I could use c++ for interop.
So: Is there a way to do this purely in C#, with reasonable efford?
EDIT: For those who still did not get it: I am NOT IMPORTING a varargs function BUT EXPORTING it as a callback, which is then called my native code. I can specify only one at a time -> only one overload possible and __arglist does NOT work.
No there is no possible way to do it. The reason it is impossible is because of the way variable argument lists work in C.
In C variable arguments are just pushed as extra parameters on to the stack (the unmanaged stack in our case). C does not anywhere record the number of parameters on the stack, the called function takes its last formal parameter (the last argument before the varargs) gets its location and starts popping arguments off the stack.
The way that it knows how many variables to pop off the stack is completely convention based - some other parameter indicates how many variable arguments are sitting on the stack. For printf it does that by parsing the format string and popping off the stack every time it sees a format code. It seems like your callback is similar.
For the CLR to handle that, it would have to be able to know the correct convention to determine how many arguments it needed to pickup. You can't write your own handler, because it would require access to the unmanaged stack which you don't have access to. So there is no way you can do this from C#.
For more information on this you need to read up on C calling conventions.
Here is the way to deal with it. It may or may not be applicable to your case, depending on whether your callback arguments are meant to be used with printf family of functions.
First, import vsprintf and _vscprintf from msvcrt:
[DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int vsprintf(
StringBuilder buffer,
string format,
IntPtr args);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int _vscprintf(
string format,
IntPtr ptr);
Next, declare your delegate with IntPtr args pointer:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(
void* arg1,
int level,
[In][MarshalAs(UnmanagedType.LPStr)] string fmt,
IntPtr args);
Now when your delegate is invoked via native code, simply use vsprintf to format the message correctly:
private void LogCallback(void* data, int level, string fmt, IntPtr args)
{
var sb = new StringBuilder(_vscprintf(fmt, args) + 1);
vsprintf(sb, fmt, args);
//here formattedMessage has the value your are looking for
var formattedMessage = sb.ToString();
...
}
Actually it is possible in CIL:
.class public auto ansi sealed MSIL.TestDelegate
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
}
.method public hidebysig newslot virtual
instance vararg void Invoke() runtime managed
{
}
}
I disagree with #shf301, It's possible.
You can use __arglist in case of PInvoke, like this:
[DllImport("msvcrt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "printf")]
public static extern int PrintFormat([MarshalAs(UnmanagedType.LPStr)] string format, __arglist);
Calling: PrintFormat("Hello %d", __arglist(2019));
In the case of delegates and callbacks:
Define the following struct:
public unsafe struct VariableArgumentBuffer
{
public const int BufferLength = 64; // you can increase it if needed
public fixed byte Buffer[BufferLength];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static VariableArgumentBuffer Create(params object[] args)
{
VariableArgumentBuffer buffer = new VariableArgumentBuffer();
Write(ref buffer, args);
return buffer;
}
public static void Write(ref VariableArgumentBuffer buffer, params object[] args)
{
if (args == null)
return;
fixed (byte* ptr = buffer.Buffer)
{
int offset = 0;
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
if (offset + Marshal.SizeOf(arg) > BufferLength)
throw new ArgumentOutOfRangeException();
switch (arg)
{
case byte value:
*(ptr + offset++) = value;
break;
case short value:
*(short*)(ptr + offset) = value;
offset += sizeof(short);
break;
case int value:
*(int*)(ptr + offset) = value;
offset += sizeof(int);
break;
case long value:
*(long*)(ptr + offset) = value;
offset += sizeof(long);
break;
case IntPtr value:
*(IntPtr*)(ptr + offset) = value;
offset += IntPtr.Size;
break;
default: // TODO: Add more types
throw new NotImplementedException();
}
}
}
}
}
Define your delegate
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int PrintFormatDelegate([MarshalAs(UnmanagedType.LPStr)] string format, VariableArgumentBuffer arglist);
For calling
callback("Hello %d %s", VariableArgumentBuffer.Create(2019, Marshal.StringToHGlobalAnsi("Merry christmas")));
For implementing
public static int MyPrintFormat(string format, VariableArgumentBuffer arglist)
{
var stream = new UnmanagedMemoryStream(arglist.Buffer, VariableArgumentBuffer.BufferLength);
var binary = new BinaryReader(stream);
....
}
You have to parse format to know what is pushed into the stack, and then read arguments using binary. For example, if you know an int32 is pushed, you can read it using binary.ReadInt32(). If you don't understand this part, please tell me in comments so I can provide you more info.
The following article covers a slightly different scenario and may be helpful:
How to P/Invoke VarArgs (variable arguments) in C#
You'd need support from the P/invoke marshaller for this to be possible. The marshaller does not provide such support. Thus it cannot be done.

Interop sending string from C# to C++ [duplicate]

This question already has answers here:
Closed 13 years ago.
Duplicate of 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(string str);
public void func2(string str)
{
func1(str);
}
The C++ side:
void func1(wchar_t *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send. Help?
I already asked it here before, but I didn't get an answer that worked.
Thanks.
You need
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22", CharSet = CharSet.Unicode)]
private static extern void func1 ([MarshalAs (UnmanagedType.LPWSTR)] string str) ;
in this case (wchar_t*). And pay attention to the calling convention, as #danbystrom suggests.
have you read this ?
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h(VS.71).aspx
Try to put
MarshalAs(UnmanagedType.BStr)
for string type that you are passing to method.
You should declare your C++ func like this:
extern "C" void __stdcall func1(wchar_t *path)
If that doesn't help, try passing a StringBuilder instead of a string.
(Disclaimer: I've never actually passed Unicode code strings, so if neither of the above suggestions work, then just a a test, you could try with "Ansi" instead just to see what happens.)

Categories