14

質問のタイトルは基本的に私が聞きたいものです:

[MarshalAs(UnmanagedType.LPStr)]-これはどのようにしてutf-8文字列をchar*に変換しますか?

c#とc ++ dllの間で通信しようとするときは、上記の行を使用します。より具体的には、

somefunction(char *string) [c++ dll]

somefunction([MarshalAs(UnmanagedType.LPStr) string text) [c#]

utf-8テキスト(scintilla.Text)をc#を介してc ++ dllに送信すると、VS10デバッガーに次のように表示されます。

  1. c#文字列は正常に変換されましたchar*

  2. 結果char*は、ウォッチウィンドウの対応するutf-8文字(韓国語のビットを含む)を適切に反映します。

これがスクリーンショットです(詳細を含む):

ss

ご覧のとおりinitialScriptText[0]、シングルbyte(char):'B'を返し、の内容が char* initialScriptTextVSウォッチウィンドウに正しく表示されます(韓国語を含む)。

ポインタcharを見ると、英語は1バイトbyteあたりchar1バイトとして保存されているようですが、韓国語は1バイトあたり2バイトとして保存されているようcharです。(スクリーンショットの韓国語は3文字なので、6バイトで保存されます)

これは、各「文字」が同じサイズのコンテナに保存されていないことを示しているようですが、言語によって異なります。(タイプに関するヒントの可能性はありますか?)

純粋なC++で同じ結果を達成しようとしています。つまり、utf-8ファイルを読み込んで、結果を。として保存しますchar*

char*utf-8ファイルを読み取ってC++で変換しようとした例を次に示します。

ここに画像の説明を入力してください

観察:

  1. wchar_t*からに変換するときの視覚の損失char*
  2. wchar_t*結果として、s8は文字列を正しく表示するので、utf-8ファイルの内容をに正常に変換したことがわかります。char*
  3. 'result'はファイルから直接取得したバイトを保持しますが、c#で取得した結果とは異なる結果を取得しているため(同じファイルを使用しました)、c#マーシャルが出力したと結論付けました。テキストをさらにに変更するためのその他の手順によるファイルの内容char*

(スクリーンショットは、wcstombsの使用における私のひどい失敗も示しています)

注:(http://utfcpp.sourceforge.net/)のutf8ヘッダーを使用しています

私のコード/観察の間違いについて私を訂正してください。

私はc#マーシャルを通過した結果を模倣できるようにしたいと思っています。これらすべてを実行した後、私は完全に立ち往生していることに気付きました。何か案は?

4

4 に答える 4

16

[MarshalAs(UnmanagedType.LPStr)]-これはutf-8文字列をchar *にどのように変換しますか?

そうではありません。マネージコードには「utf-8文字列」のようなものはありません。文字列は常にutf-16でエンコードされます。LPStrとの間のマーシャリングは、デフォルトのシステムコードページを使用して行われます。これにより、コードページ949を使用しない限り、デバッガーに韓国語のグリフが表示されることはかなり注目に値します。

utf-8との相互運用が難しい要件である場合は、pinvoke宣言でbyte[]を使用する必要があります。そして、System.Text.Encoding.UTF8を使用して自分自身を前後に変換します。GetString()メソッドを使用してbyte []を文字列に変換し、GetBytes()メソッドを使用して文字列をbyte[]に変換します。ネイティブコードでwchar_t[]を使用して、可能であればこれをすべて回避してください。

于 2012-11-08T13:45:47.213 に答える
13

他の答えは正しいですが、.NET4.7では大きな進展がありました。これで、UTF-8が必要とすることを正確に実行するオプションがありますUnmanagedType.LPUTF8Str。私はそれを試しました、そしてそれはスイスのクロノメーターのように機能し、それがどのように聞こえるかを正確に実行します。

実際、私MarshalAs(UnmanagedType.LPUTF8Str)はあるパラメーターとMarshalAs(UnmanagedType.LPStr)別のパラメーターでさえ使用しました。また動作します。これが私のメソッドです(文字列パラメーターを受け取り、パラメーターを介して文字列を返します):

[DllImport("mylib.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern void ProcessContent([MarshalAs(UnmanagedType.LPUTF8Str)]string content, [MarshalAs(UnmanagedType.LPUTF8Str), Out]StringBuilder outputBuffer,[MarshalAs(UnmanagedType.LPStr)]string settings);

ありがとう、マイクロソフト!もう一つの迷惑はなくなりました。

于 2018-03-21T03:34:23.373 に答える
3

UTF-8をマーシャリングする必要がある場合stringは、手動でマーシャリングしてください。

IntPtr文字列の代わりに関数を定義します。

somefunction(IntPtr text)

次に、テキストをゼロで終了するバイトのUTF8配列に変換し、次のように書き込みますIntPtr

byte[] retArray = Encoding.UTF8.GetBytes(text);
byte[] retArrayZ = new byte[retArray.Length + 1];
Array.Copy(retArray, retArrayZ, retArray.Length);
IntPtr retPtr = AllocHGlobal(retArrayZ.Length);
Marshal.Copy(retArrayZ, 0, retPtr, retArrayZ.Length);
somefunction(retPtr);      
于 2014-11-29T04:10:27.923 に答える
3

4.7より前の.NETFrameworkを使用している場合は、ICustomMarshalerを使用できます。


        class UTF8StringCodec : ICustomMarshaler
        {
            public static ICustomMarshaler GetInstance(string cookie) => new UTF8StringCodec();

            public void CleanUpManagedData(object ManagedObj)
            {
                // nop
            }

            public void CleanUpNativeData(IntPtr pNativeData)
            {
                Marshal.FreeCoTaskMem(pNativeData);
            }

            public int GetNativeDataSize()
            {
                throw new NotImplementedException();
            }

            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                var text = $"{ManagedObj}";
                var bytes = Encoding.UTF8.GetBytes(text);
                var ptr = Marshal.AllocCoTaskMem(bytes.Length + 1);
                Marshal.Copy(bytes, 0, ptr, bytes.Length);
                Marshal.WriteByte(ptr, bytes.Length, 0);
                return ptr;
            }

            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                if (pNativeData == IntPtr.Zero)
                {
                    return null;
                }

                var bytes = new MemoryStream();
                var ofs = 0;
                while (true)
                {
                    var byt = Marshal.ReadByte(pNativeData, ofs);
                    if (byt == 0)
                    {
                        break;
                    }
                    bytes.WriteByte(byt);
                    ofs++;
                }

                return Encoding.UTF8.GetString(bytes.ToArray());
            }
        }

P / Invoke宣言:

        [DllImport("native.dll", CallingConvention = CallingConvention.Cdecl)]
        private extern static int NativeFunc(
            [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8StringCodec))] string path
        );

コールバック内での使用法:

        [StructLayout(LayoutKind.Sequential)]
        struct Options
        {
            [MarshalAs(UnmanagedType.FunctionPtr)]
            public CallbackFunc callback;
        }

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int CallbackFunc(
            [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8StringCodec))] string path
        );
于 2020-11-18T15:45:24.740 に答える