**主要更新** 我犯了一个小错误,但我仍然对正在发生的事情感到好奇。

我调用的函数实际上是“fooV”,具有此签名的函数:

foo(const char *, const char *, EnumType, va_list)

这清除了我得到的AccessViolationExceptions,但没有解释为什么params参数适用于所有其他.NET类型,除了必须转换为多字节ANSI字符的字符串。我将让DLL的开发人员在参数列表中公开实际使用的版本...如果va_list是参数,PInvoking的任何提示都会被提供?

**老帖**

这与最近的问题有关,但有所不同我问了

我必须使用PInvoke来调用具有以下签名的C库函数:

foo(const char *, const char *, EnumType, ...)

该函数以不同于StructType的方式配置资源;我感兴趣的情况是配置一些要求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 ,所以我遵循了模式。 / p>

我用友好的.NET方法包装它:

public void Foo(string s1, string s2, string s3)
{
    int error = Dll.Foo(s1, s2, EnumType.Foo, s3);
    // handle errors
}

最近我更改了签名以包含“BestFitMapping = false,ThrowOnUnmappableChar = true”在DLLImport属性中符合FxCop的建议。这是一个红鲱鱼,我将在后面描述。

我对进行此更改的期望是对Unicode的有限支持。例如,在具有英语代码页的计算机上,如果传递包含日语字符的字符串,则会引发异常。在日本机器上,我希望能够将日语字符串传递给该函数。英语测试按预期工作,但日语测试抛出一个带有HRESULT 0x8007007A的System.Runtime.InteropServices.COMException。即使没有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是有效的,但我担心这只是巧合而不是我应该依赖的东西。我曾考虑使用系统代码页将字符串转换为byte [],但无法弄清楚如何指定我想将字节数组传递给varargs参数。

发生了什么事?在英语机器上,英文字符工作正常,日语字符按预期抛出ArgumentException。在日语机器上,英文字符工作正常,日文字符抛出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.Stri

有帮助吗?

解决方案

您可能还需要指定 CallingConvention = CallingConvention.Cdecl ,因为varargs函数将使用 _cdecl 调用约定(默认为Winapi,而默认为StdCall)

你可能还需要别的东西,但我很确定你至少需要它。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top