C++;dll和C#调用 我有一个C++函数,调用COM接口的函数 签字: BOOL func(LPWSTR strIn, __out LPWSTR strOut) { //initcom //do something // release pointers }
在C#中:C++;dll和C#调用 我有一个C++函数,调用COM接口的函数 签字: BOOL func(LPWSTR strIn, __out LPWSTR strOut) { //initcom //do something // release pointers },c#,c++,pinvoke,C#,C++,Pinvoke,在C#中: [DllImport(“funcdll.dll”)] 静态外部布尔函数(String strIn,ref String strOut); //使用它 对于(int i=0;i<p>),很难在没有看到C++方法的细节的情况下进行说明……但是,我从来没有过多的运气,使用p/INKEKE使用字符串< /代码>。 尝试使用IntPtr而不是String,对传出字符串使用Marshal.PtrToStringUni,并使用Marshal.StringToHGlobalUni将托管字符串封送到非
[DllImport(“funcdll.dll”)]
静态外部布尔函数(String strIn,ref String strOut);
//使用它
对于(int i=0;i<p>),很难在没有看到C++方法的细节的情况下进行说明……但是,我从来没有过多的运气,使用p/INKEKE使用<代码>字符串< /代码>。
尝试使用IntPtr
而不是String
,对传出字符串使用Marshal.PtrToStringUni
,并使用Marshal.StringToHGlobalUni
将托管字符串封送到非托管区域,在函数调用后,确保使用Marshal.FreeHGlobal
释放非托管字符串
也从C99土地上来,我的BOOLS不与.NET BOOL一起工作…我不知道为什么。我必须使用字节和Chc==1。不知道你是否会用C++来运行这个……但是,如果你想让我的建议在一个地方开始,在我的经验中,这个地方似乎是最不可破解的,那么,YAG: < /P>
[DllImport("funcdll.dll")]
static extern byte func(IntPtr strIn, out IntPtr strOut);
// use it
string myString = "Testing";
IntPtr stringOut;
IntPtr stringIn = Marshal.StringToHGlobalUni(myString);
if(func(stringIn, out stringOut) == 1)
{
//do something with strout
string stringOutValue = Marshal.PtrToStringUni(stringOut);
// Depending on how you dealt with stringOut in your
// unmanaged code, you may have to: Marshal.FreeCoTaskMem(stringOut);
// or Marshal.FreeHGlobal(stringOut) if you're returning
// an owning reference to a copied string.
}
Marshal.FreeHGlobal(stringIn);
我相信你有一个调用约定不匹配。C++中的默认调用约定是CDECL,而.NET是STDCULL。
[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool func(String strIn, ref String strOut);
另外,您可能必须明确地告诉marshallar,您希望使用[Marshallas(UnmanagedType.LPWSTR)]属性将字符串封送为LPWSTR
[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool func([MarshalAs(UnmanagedType.LPWStr)]String strIn
,[MarshalAs(UnmanagedType.LPWStr)]ref String strOut);
看看我可以看出你有三个问题
调用约定不匹配。您的C++代码是“代码> CDECL < /COD>”,而C代码是“代码> STDCALL< /COD> < < /LI>”。
C++代码使用宽字符串,但C代码则封ANSI字符串。
<> LI>第二个参数不匹配。C++代码假定C++代码返回一个C指针的新指针,C代码随后与COM分配器进行分配。C++代码不执行此操作。
现在,更详细地处理这些问题
呼叫约定
<>这是很容易解决的。简单的将C++代码改为<代码> STDCALL> /CODE >,或者C代码到 CCDL> <代码>。但是不要同时做这两个。我将更改C代码:
Unicode/ANSI字符串
我假定你想使用Unicode字符串,因为你已经在C++代码中明确地选择了它们。但是P/Uncess默认是编组ANSI字符串的。你可以在 dLimPult
中再次改变这一点:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
<强>将字符串从C++返回到C<<强>
您当前的C++函数声明为:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
\uuu out
装饰器没有实际效果,除了记录您想要修改strOut
指向的缓冲区并将这些修改返回给调用方之外
您的C#声明是:
static extern bool func(String strIn, ref String strOut);
现在,ref String strOut
根本不匹配。在C++中,ref
String参数与此匹配:
BOOL func(LPWSTR strIn, LPWSTR *strOut)
换句话说,C#code希望您返回一个新指针。事实上,它将通过调用CoTaskMemFree
,继续释放您在strOut
中返回的缓冲区。我相信这不是您想要的
原始的C++代码只能通过修改传递给它的缓冲区来将字符串返回到C代码中。
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
...
wcscpy(strOut, L"the returned string");
...
}
如果这是您想要的,那么您应该在StringBuilder
对象中的C#中分配足够的缓冲区
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();
如果你不能在C代码中决定你需要多大的缓冲区,那么你最好用一个<代码> BSTR < /C>来整理Strut。请参阅详细信息。C++中的
< P>确保你有类似的东西(至少对于第二个参数) 在C#中写下如下内容(对于第二个参数):很抱歉,我没有一个大的答案,但只是从我的经验中记住一些东西。您是否尝试使用StringBuilder,例如将c#导入函数签名更改为
[System.Runtime.InteropServices.DllImport("funcdll.dll")]
static extern bool func(String strIn, System.Text.StringBuilder strOut);
您是否尝试过添加一些调试输出或日志记录,并看到函数中的哪个部分崩溃了?在DllImport属性中使用字符集比在单个Marshallas中使用字符集要简单得多。我不认为两者相等,将字符集设置为Unicode只会告诉它字符串是Unicode,Bstr和LPWStr都是Unicode,但您仍然不知道是哪一个显然,使用MalSalas来显式设置它是更好的,两个选项是等价的。当你使用字符集时默认为LPTSTR。更重要的是,REF LPWSTR实际上是BSTR,因为这是封送者将要处理的返回内存。这与代码中的C++声明不匹配。harSet默认为任何值。在没有任何显式文档的情况下,我不信任隐式。至于第二部分,是的,这是一个问题,当我第一次阅读这个问题时,我没有看到这一点。但是从什么时候开始LPWSTR(可能以null结尾,可能以长度为前缀)和BStr(总是以null结尾,以长度为前缀)平等?这在很大程度上是一个糟糕的建议。在这里使用IntPtr只会在C#端产生不必要的复杂性。这种复杂性很容易避免。Hrm…它对我来说始终如一。来自未管理的土地,很容易理解正在发生的事情,而不是其他一些“神奇”的可能性。我想称之为“糟糕的建议”可能有点太远了。当然它是有效的,但是它不必要的复杂。在没有IntPtr的情况下封送字符串是非常容易的。你把它称为魔法的事实仅仅表明你还没有掌握这样做。这是你的问题。如果它有效,那么它肯定不是“坏建议”。“次优”可能…但远不是“坏建议”.你为什么这么暴躁?不,我会说这是个错误的建议。要正确编写代码,你真的需要尝试/最终,但你错过了。你对第二个参数的处理非常不完整。根本不需要如此复杂。而且你所说的
bool
是错误的。C#bool
的默认编组是bool代码>。这里没有C99bool
。我不是脾气暴躁。我只是相信给别人最好的建议很重要
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();
extern "C" BOOL __stdcall func( BSTR * pBstr )
{
*pBstr = SysAllocString( L"Foobar" );
return 0;
}
static extern bool func( [MarshalAs(UnmanagedType.BStr)] ref String strOut);
[System.Runtime.InteropServices.DllImport("funcdll.dll")]
static extern bool func(String strIn, System.Text.StringBuilder strOut);