

ご回答いただけると幸いです。

C++で文字列(英数字混在)をcharでreturnする関数を書き、  
C#からその関数を利用し、文字列を受け取ろうと考えています。  
調べてみたところ、C#から文字列を渡すような手順はあっても、  
C++の方から戻す方法が見つからず、困り果てております。  
どなたか、ご存知の方がいらっしゃれば、お教えいただけないでしょうか。


CC BY-SA 3.0
2016-11-16T02:17:53.530
30434
2017-01-05T13:15:39.583
19566
question
c# c++
C#でC++のDLLから文字列を受け取る


C# で /unsafe コンパイルして byte* (ポインタ） を使うか  
/unsafe を使わずに 領域を Pinned して そのアドレスを渡すかですね。

<https://stackoverflow.com/questions/1579446/how-can-i-pass-a-pointer-to-an-
integer-in-c-sharp>

の回答を ちょっと改良して引用すると

【unsafe使うパターン】

[code]

    unsafe static void TestMethod()
    {
        fixed (byte* buffer = new byte[4096])
        {
            int maxSize = buffer.Length;
            GetBuffer(buffer, ref maxSize);
        }
    }
    
[/code]

【unsafe使わないパターン】

[code]

    byte[] buf = new byte[kBufSize];
    GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); 
    // 領域を確保して Pinned する事でアドレスが固定される。
    IntPtr p = handle.AddrOfPinnedObject();
    int size = buf.Length;
    int ret = GetBuffer(p, ref size); // DLLImport された C++ の関数
    handle.Free();
    
[/code]

受け取った byte 配列は その文字列のエンコードに合わせて 文字に変換すればいいです。


CC BY-SA 3.0
2016-11-16T02:34:33.207
30435
2016-11-16T02:34:33.207
2017-05-23T12:38:55.250
-1
18851
30434
answer


C/C++ で「関数が文字列を返す」というアクションは２つのパターンがあって  
１．呼ばれる関数が `malloc()` 等で動的にメモリを取り、そこに文字列を格納する (`strdup`)  
２．呼ぶ側の関数が固定長のバッファを取り、呼ばれる関数がそこに文字列を格納する (`snprintf`)  
今回の事案はどっちでしょう。

後者であれば `StringBuilder` が使えます。これなら unsafe 不要で、エンコーディング変換も自動。

C++ 側

[code]

    extern "C" __declspec(dllexport) bool __stdcall GetText(char* buf, size_t bufsize) {
        if (bufsize<4) return false
        buf[0]='a';
        buf[1]='\x82'; // 82a0 = CP932 の 'あ'
        buf[2]='\xa0';
        buf[3]='\0';
        return true;
    }
    
[/code]

C# 側

[code]

    [DllImport("example.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    static extern Boolean GetText(StringBuilder s, Int32 bufsize);
    
    // CharSet.Ansi は OS の標準現地固有エンコーディングを UTF-16 に自動変換する指定
    // 日本語 OS の場合 CP932 → UTF-16
    System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
    GetText(sb, sb.Capacity);
    Console.WriteLine(sb.ToString()); // aあ
    
[/code]

前者の場合 `malloc()` 系の結果をいつどうやって `free()` するかによって話が違い  
C/C++ 側の仕様というか実装というかを見ないと答えが出せません。


CC BY-SA 3.0
2016-11-16T02:58:51.753
30438
2016-11-17T01:42:43.407
2016-11-17T01:42:43.407
8589
8589
30434
answer


::CoTaskMemAllocで確保したメモリを使用すると.Net側でのGC対象とすることができるので  
C++からC#への文字列の受け渡しは以下のリンク先のような実装にするとC#側の実装が楽になります。  
<https://limbioliong.wordpress.com/2011/06/16/returning-strings-from-a-c-api/>

C/C++

[code]

    extern "C" __declspec(dllexport) char*  __stdcall StringReturnAPI01()
    {
        char szSampleString[] = "Hello World";
        ULONG ulSize = strlen(szSampleString) + sizeof(char);
        char* pszReturn = NULL;
    
        pszReturn = (char*)::CoTaskMemAlloc(ulSize);
        // Copy the contents of szSampleString
        // to the memory pointed to by pszReturn.
        strcpy(pszReturn, szSampleString);
        // Return pszReturn.
        return pszReturn;
    }
    
[/code]

C#

[code]

    [DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.LPStr)]
    public static extern string StringReturnAPI01();
    
    static void CallUsingStringAsReturnValue()
    {
      string strReturn01 = StringReturnAPI01();
      Console.WriteLine("Returned string : " + strReturn01);
    }
    
[/code]


CC BY-SA 3.0
2017-01-05T13:00:43.853
31640
2017-01-05T13:15:39.583
2017-01-05T13:15:39.583
20114
20114
30434
answer