Can you use multibyte ANSI for vargars? What am I doing wrong?

** MAJOR UPDATE ** I made a small mistake, but I'm still wondering what exactly is going on.

The function I'm calling is actually "fooV", a function with this signature:

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

      

This clears up the AccessViolationExceptions I'm getting, but doesn't explain why params parameters work for any other .NET type, except for strings that need to be converted to ANSI multibyte characters. I'm going to force the DLL developer to expose the version that actually uses ... in the parameter list, any hints for PInvoking if the va_list parameter is a parameter?

** old post **

This is related, but different from the recent question I asked .

I need to use PInvoke to call a C library function that has the following signature:

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

      

The function configures the resource in a way that depends on the StructType; the case I'm interested in is setting up what expects varargs to be a single multibyte ANSI string. I used this PInvoke signature to call the function:

    [DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
    public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3);

      

params string[]

struck me as odd, but the previous signatures that called this function used it for other types like bool

, so I followed the pattern.

I wrapped this up with a friendlier .NET method:

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

      

I recently changed the signature to include "BestFitMapping = false, ThrowOnUnmappableChar = true" in the DLLImport attribute to match FxCop's suggestions. This is a red herring, as I will describe below.

What I expect from this change is limited support for Unicode. For example, on a machine with an English code page, an exception will be thrown if you pass in a string that contains a Japanese character. On a Japanese computer, I expect to be able to pass a Japanese string to a function. The English test worked as expected, but the Japanese test is throwing System.Runtime.InteropServices.COMException with HRESULT 0x8007007A. This happens even without the BestFitMapping and ThrowOnUnmappableChar settings.

I looked around a bit and saw several sites that suggested PInvoke varargs for you just by specifying normal arguments, so I tried this signature:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, string s3);

      

This throws an AccessViolationException when I use it on an English or Japanese computer.

I cannot use UTF-8 or any other Unicode encoding because this C library only expects and handles multibyte ANSI. I found that specifying CharSet.Unicode for this function works, but I only worry about coincidence, not what I should rely on. I was thinking about converting a string to byte [] using the system code page, but can't figure out how to indicate that I would like to pass a byte array to the varargs parameter.

What's happening? On an English machine, English characters work fine and Japanese characters are throwing ArgumentException as expected. On a Japanese computer, English characters work fine and Japanese characters throw a COMException. Is there something wrong with my PInvoke signature? I tried to use an attribute MarshalAs

to indicate the LPArray type and the LPStr subtype, but it doesn't. UnmanagedType.LPStr indicates that this is an ANSI single-byte string; is there a way to specify an ANSI multibyte string?

** UPDATE ** Material is added here, taking into account the current comments.

If I set the calling convention to CDecl and accept normal parameters like this:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

      

When I use this specification, I get an AccessViolationException. So I tried this:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

      

This works in English and throws a COMException when I use Japanese characters.

The only thing I have found that works consistently is:

[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)

      

To make this work, I use Marshal.StringToHGlobalAnsi () and pass this pointer. This works in English and Japanese. I'm not comfortable not understanding why this solution is, but it works.

0


source to share


1 answer


You probably need to specify as well CallingConvention=CallingConvention.Cdecl

, as the varargs function will use the calling convention _cdecl

(Winapi defaults to StdCall by default).



You may need something else, but I'm sure you will need at least this.

+2


source







All Articles