我有一个接受BSTR参数的dll函数。在用于其他事情之前,它们被转换为char *。
当从VBA代码调用dll时,这可行。但是,当从C#代码调用它时,只指向第一个字符。
这两个版本都是适用于2007之前版本和2007年版本的Office的Excel addIns,它们可以调用更快的C ++ AddIn。他们直接称呼它,而不是通过Excel。
VBA函数声明:
Private Declare Function Test Lib "ExcelAddIn.xll" (ByVal param As String) As String
C#函数声明:
[DllImport("ExcelAddIn.xll", CharSet=CharSet.Ansi)]
[return:MarshalAs(UnmanagedType.BStr)]
private static extern string Test([MarshalAs(UnmanagedType.BStr)] string param);
调试dll并观察输入的BSTR值时,两者看起来都是正确的;只有C#one才会投出第一个角色。
Charset=CharSet.Unicode
没有任何区别。
C ++代码。
BSTR __stdcall Test(BSTR param)
{
char* Param= "";
if(param!= NULL)
Param= (char*)param;
return OtherClass.DoOtherStuff(Param);
}
答案 0 :(得分:1)
BSTR是Unicode字符串。正如您所看到的,如果您尝试使用LE Unicode字符串作为ANSI,您将只获得第一个字母(如果它是ASCII等)
ATL / MFC的CComBSTR类可能有所帮助:您可以附加您收到的BSTR,我认为它将为您处理单字节转换。 如果你想在内部使用Unicode字符串,那么使用它也可能更安全 - 我不认为BSTR可以保证是零终止的(它们的长度在索引-1处)。 [edit]纠正由dkackman在下面,谢谢。
您最好的解决方案是将OtherClass转换为使用Unicode字符串而不是MBCS,或者如果它仅用于COM上下文,您可以将其转换为使用BSTR甚至。您的计划将更好地支持国际化扩展您的市场等。但这并不总是一个简单的转换。
答案 1 :(得分:1)
原因与您编组数据的方式有关。
VB声明不包含string
参数的注释,因此它将作为普通char*
参数进行编组。 CLR不能对pinvoke进行类型验证,并且基本上将char*
放入需要BSTR
的插槽中。您的代码虽然立即将其转换为char*
,但它修复了编组问题。
在C#示例中,虽然您明确表示将string
编组为BSTR
。 BSTR
实际上是一个在引擎盖下使用wchar
的字符串。通过对char*
执行简单投射,您实际上是将wchar*
投射到char*
,这解释了为什么您只能看到第一个角色。
最简单的解决方案是让Test方法采用char*
参数并删除C#版本上的编组注释。