我有一个由两个项目组成的解决方案:一个C#控制台应用程序和一个C库。 C库有一个返回HRESULT的函数。我需要以某种方式更改此函数,以使其返回一个字符串到我的C#代码。这应该是它的样子:
C#:
[DllImport("MyLib.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern long MyFunction(bunch of params, [MarshalAs(UnmanagedType.BStr)] out string text);
C:
extern "C" HRESULT __declspec(dllexport) MyFunction(bunch of params, BSTR* text)
{
PWSTR finalResult;
//lots of code
(*text) = SysAllocString(finalResult);
//cleanup code
}
我可以改变这两个项目。但是,没有办法知道字符串有多大。因此,我尝试在C lib中分配字符串,但这会导致访问冲突异常和各种问题。解决这个问题的最佳方法是什么?
答案 0 :(得分:0)
您是否尝试过string str = Marshal.PtrToStringAuto((IntPtr)MyFunction(...))
?
答案 1 :(得分:0)
首先,你不能在C中分配字符串,因为string - 是对类的引用,它曾经由GC管理。 即使你编组它并在托管内存中创建副本,你也需要在之后释放非托管内存,否则会导致内存泄漏。
或者,您可以在C中创建第二个例程,它将为您提供字符串长度。然后你可以使用例如char数组。在c#中分配数组并赋予其设置结果的功能。
public static extern long MyFunctionCalcLength(bunch of params, [OUT]int textLength);
public static extern long MyFunction(bunch of params, [OUT]Char[] text);
答案 2 :(得分:0)
类似的东西:
public static extern long MyFunction(bunch of params, StringBuilder text);
只要C字符串类型可以作为某种字符串指针类型进行编组,这应该可行。您可能需要使用[MarshalAs(UnmanagedType.LPWStr)]
,具体取决于您的实施。 [out]
只应在非托管代码分配内存空间的情况下使用。
无论如何,您永远不应该从您计划返回托管空间的非托管库中分配内存;如你所见,这是一个很大的禁忌。
请注意,如果使用StringBuilder
,则必须在构造函数中预先分配一定量的最大空间。
答案 3 :(得分:0)
您需要手动封送BSTR数据。尝试以下内容:
[DllImport("MyLib.dll", SetLastError = true]
public static extern long MyFunction(bunch of params, out IntPtr text);
//Create location for BSTR to be placed (BSTR is a pointer not a buffer).
IntPtr pBSTR;
//Call function and check for error.
if(MyFunction(bunch of params, out pBSTR) != S_OK)
{
//Handle error.
}
//Retrieve BSTR data.
string data = Marshal.PtrToStringBSTR(pBSTR);
//Free the memory allocated in the unmanaged function.
Marshal.FreeBSTR(pBSTR);
答案 4 :(得分:0)
哇!差不多3年了,这个问题还没有得到正确答案!
从非托管中传输字符串的正确方法应该是,恕我直言,将StringBuilder
类与表示“缓冲区”大小的附加参数结合起来。
这样的事情:
// C#
[DllImport("MyLib.dll",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.Cdecl)]
public static extern bool MyFunction(
bunch of params,
StringBuilder buffer,
[MarshalAs(UnmanagedType.U4)] int bufferSize
);
// C:
extern "C" __declspec(dllexport) BOOL MyFunction(bunch of params, LPTSTR* text, unsigned int textSize)
{
//lots of code
if ( textSize < requiredSize) {
SetLastError(ERROR_INSUFFICIENT_BUFFER)
return FALSE;
}
return TRUE;
}
并以这种方式使用它:
StringBuilder sb = new StringBuilder(128);
while (!NativeMethods.MyFunction(bunch of params, sb, sb.Capacity))
{
if (Marshal.GetLastWin32Error() != 0x7A) {
// throw
}
// Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER
sb.Capacity *= 2;
}