**主要更新**
我犯了一个小错误,但我仍然对到底发生了什么感到好奇。
我调用的函数实际上是“foov”,一个具有以下签名的函数:
foo(const char *, const char *, EnumType, va_list)
这将清除我获得的AccessViolationExceptions,但无法解释为什么除了必须转换为多字节ANSI字符的字符串之外,params参数对其他所有.NET类型都有效。我要让dll的开发人员公开实际使用的版本…在参数列表中,如果va_list是参数,是否有任何关于pinvoking的提示?
**旧帖**
这与
recent question I asked
.
我必须使用pinvoke调用具有以下签名的c库函数:
foo(const char *, const char *, EnumType, ...)
该函数以不同结构类型的方式配置资源;在这种情况下,我感兴趣的是配置一些期望varargs是单个ansi多字节字符串的内容。我用这个pinvoke签名调用函数:
[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3);
这个
params string[]
对我来说似乎很奇怪,但是以前调用此函数的签名将其用于其他类型,如
bool
所以我遵循了这个模式。
我用一个更友好的.net方法来包装它:
public void Foo(string s1, string s2, string s3)
{
int error = Dll.Foo(s1, s2, EnumType.Foo, s3);
// handle errors
}
最近,我将签名更改为在dllimport属性中包含“bestFitMapping=false,throwonUnmappableChar=true”,以符合fxcop的建议。这是一条红鲱鱼,我稍后会描述。
我所期望的改变是对Unicode的有限支持。例如,在具有英文代码页的计算机上,如果传递包含日文字符的字符串,则会引发异常。在日语机器上,我希望能够将日语字符串传递给函数。英语测试按预期工作,但日语测试抛出一个system.runtime.interopservices.comexception,其hresult为0x8007007a。即使没有bestfitmapping和throwonUnmappableChar设置,也会发生这种情况。
我环顾四周,看到一些网站建议您只需指定普通参数就可以pinvoke varargs,所以我尝试了这个签名:
[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, string s3);
当我在英语或日语机器上使用它时,这会引发AccessViolationException异常。
我不能使用utf-8或其他unicode编码,因为这个c库只需要并处理多字节的ansi。我发现为这个函数指定charset.unicode是可行的,但我担心这只是巧合,不是我应该依赖的东西。我曾考虑过使用系统代码页将字符串转换为字节[],但无法确定如何指定要将字节数组传递给varargs参数。
发生什么事?在英语机器上,英文字符运行良好,日文字符按预期抛出一个议论文异常。在日文机器上,英文字符运行良好,日文字符抛出了comexception。我的pinvoke签名有问题吗?我试过用
MarshalAs
属性来指定lparray的类型和lpstr的子类型,但以相同的方式失败。unmanagedtype.lpstr表示它是单字节ANSI字符串;是否有方法指定多字节ANSI字符串?
**更新**
这里有一个附加的东西考虑到当前的评论。
如果我指定了cdecl的调用约定,并采用以下常规参数:
[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3)
当我使用这个规范时,我会得到一个accessviolationexception。所以我试了一下:
[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.CDecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3)
这对英语有效,并且在我使用日语字符时抛出了comexception。
我发现唯一能持续有效的是:
[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int Foo(string s1, string s2, EnumType e1, params IntPtr[] s3)
为了使其工作,我使用marshal.stringToHglobalansi()并传递该指针。这对英语和日语都有效。我对不理解为什么这是解决方案感到不舒服,但它起作用了。