问题
I have the following C-code signature in a dll:
extern __declspec(dllexport) unsigned char *
funct_name (int *w, int *h, char **enc, int len, unsigned char *text, int *lp, int *mp, int *ep)
The C function can modify w, h, enc, lp, mp, and ep (though the latter three can be null and it won't do anything.
I'm using the following in C#
[DllImport("iec16022ecc200.dll", EntryPoint = "iec16022ecc200", ExactSpelling = false, CharSet = CharSet.Ansi, SetLastError = true, CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr CallEncode(
[In,Out,MarshalAs(UnmanagedType.LPArray)] Int32[] width,
[In,Out,MarshalAs(UnmanagedType.LPArray)] Int32[] height,
[In,Out,MarshalAs(UnmanagedType.LPStr)] ref StringBuilder encoding,
int barcodeLen,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder barcode,
IntPtr lenp,
IntPtr maxp,
IntPtr eccp
);
public void Foo (string textToEncode,out int width, out int height) {
StringBuilder text = new StringBuilder(textToEncode);
StringBuilder encoding = new StringBuilder(new string('a', text.Length));
Int32[] w = new Int32[1];
Int32[] h = new Int32[1];
string encodedStr = Marshal.PtrToStringAnsi(CallEncode(w, h, ref encoding, text.Length, text, (IntPtr)0, (IntPtr)0, (IntPtr)0));
width = w[0];
height = h[0];
}
I am getting a SystemAccessViolation and I'm not entirely sure when my problem is.
回答1:
Don't pass a StringBuilder reference to a unmanaged method that takes a char*. StringBuilder content is unicode (wchar).
Instead, replace the StringBuilder parameter with and IntPtr parameter and allocate a appropriate size buffer using Marshal.AllocHGlobal.
Also I don't think passing a StringBuilder to unmanaged code using "ref" is supported by the .Net marshaller.
回答2:
You mentioned that the 'char **enc' parameter may be modified by the callee, this could be causing the 'SystemAccessViolation' error. A StringBuilder can be dereferenced and modified by the callee, provided it does not exceed the StringBuilders capacity.
回答3:
StringBuilder should not be passed by reference using out or ref.
//...
[In,Out,MarshalAs(UnmanagedType.LPStr)] ref StringBuilder encoding,
//...
Remove the ref
keyword from the encoding
parameter that is of type StringBuilder
. That is what is probably causing your SystemAccessViolation
.
回答4:
I was trying to pass a string from C++ to C# in Unity. And all I got was gibberish. Finally after I saw your comment @popcatalin I discovered the solution using AllocHGlobal from C# to allocate memory for the string.
This is my C++ function:
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API TestString(char* str, int len)
{
strcpy_s(str, len, "C++ String!!! UniversalCpp");
return;
}
This is at the top of the C# script in Unity:
#if WRCCPP
[DllImport("UniversalWRCCpp", CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport("UniversalCpp", CallingConvention=CallingConvention.Cdecl)]
#endif
private static extern void TestString(IntPtr str, int len);
private IntPtr myPtr = IntPtr.Zero; // Pointer to allocated memory
And this is my C# script in Unity calling that function:
int sizeInBytes = 100;
myPtr = Marshal.AllocHGlobal(new IntPtr(sizeInBytes));
TestString(myPtr, sizeInBytes);
Debug.Log("ANSI " + Marshal.PtrToStringAnsi(myPtr)); // This prints the correct format
来源:https://stackoverflow.com/questions/803488/marshalling-c-dll-code-into-c-sharp