代码之家  ›  专栏  ›  技术社区  ›  OwenP

你能把多字节的ansi转换成varargs吗?我做错什么了?

  •  0
  • OwenP  · 技术社区  · 16 年前

    **主要更新** 我犯了一个小错误,但我仍然对到底发生了什么感到好奇。

    我调用的函数实际上是“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()并传递该指针。这对英语和日语都有效。我对不理解为什么这是解决方案感到不舒服,但它起作用了。

    1 回复  |  直到 16 年前
        1
  •  2
  •   Michael Burr    16 年前

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

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