![]() | |
![]() |
| | Thread Tools | Search this Thread | Display Modes |
#1
| |||
| |||
|
#2
| |||
| |||
|
|
I have to use PInvoke to call a C library function that has the following signature: foo(const char *, const char *, EnumType, ...) The function configures a resource in a way that varies by StructType; the case I'm interested in configures something that expects the varargs to be a single ANSI multibyte 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 e1, params string[] s3); The params string[] seemed odd to me, but previous signatures that called this function were using it for other types such as bool so I followed the pattern. I wrap this 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 } Recently I changed the signature to include "BestFitMapping = false, ThrowOnUnmappableChar = true" in the DLLImport attribute to comply with FxCop's suggestions. This is a red herring as I will describe later. What I expect out of making 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 a string that contains a Japanese character. On a Japanese machine, I'd expect to be able to pass a Japanese string to the function. The English test worked as expected, but the Japanese test throws a System.Runtime.InteropServices.COMException with HRESULT 0x8007007A. This happens even without the BestFitMapping and ThrowOnUnmappableChar settings. I've done a little looking around and saw some sites that suggested you could PInvoke varargs by just 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 either the English or Japanese machine. After some digging around, I found out that you have to use the CDecl If I specify a calling convention of CDecl and take 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. The only thing I've found that works consistently is this: [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 that pointer. This works for English and Japanese. I'm not comfortable with not understanding why this is the solution, but it works. I cannot use UTF-8 or another Unicode encoding because this C library expects and handles only multibyte ANSI. I have found that specifying CharSet.Unicode for this function works, but I'm worried it's only by coincidence and not something upon which I should rely. What's going on? What is wrong with my PInvoke signatures? Why is it that manually marshaling the string seems to be the only thing that works? |
![]() |
| Thread Tools | Search this Thread |
| Display Modes | |
| |