none
C++で作ったdllをC#で使う RRS feed

  • 質問

  • dll側(straylight.dll):
    #pragma once
    #   define API   __declspec(dllexport)
    #else
    #   define API   __declspec(dllimport)
    #endif
    #ifdef __cplusplus
     extern "C"{
    #endif
    API const char* str();
    #ifdef __cplusplus
     }
    #endif

    API const char* str()
    {
     const char* a="000"; 

     return a;
    }

    C#側:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    using System.Runtime.InteropServices;


    namespace test
    {

        public partial class MainForm : Form
        {
            [DllImport("straylight.dll", EntryPoint = "str", CallingConvention = CallingConvention.Cdecl)]
            public static extern string str();

            public MainForm()
            {
                InitializeComponent();

                string a;
                a = str();   //----①ブレークポイント付き

                if (a == null)  //---②ブレークポイント付き
                {
                    MessageBox.Show("1");
                }
                else
                {
                    MessageBox.Show("0");
                }
            } 
           
        }
    }

    C#側に付いているブレークポイントは、①番を進んでから、②番に行けないでデバック終了しました。

    出力元のメッセージは、「マネージ (v4.0.30319)' はコード -2147483645 (0x80000003) で終了しました。」

    どの原因でしょうか?教えてください。

    2012年11月29日 5:10

回答

  • // 返値にchar*って気持ち悪い……。

    返値としてStringで受け取った場合に発生するメモリ解放操作の影響かな?

    返値をStringではなくIntPtrにして、Marshal.PtrToStringAnsiで文字列を取得するようにするとどうでしょうか?

    あと、この例ではリテラルだから大丈夫ですが、実際には動的なローカル変数をreturnしてたりしていませんか?

    • 回答の候補に設定 佐伯玲 2012年11月30日 6:11
    • 回答としてマーク nob9292 2012年12月4日 1:15
    2012年11月29日 6:08
  • 補足です。

    相互運用マーシャラーによるメモリ管理に次のように説明があります。

    ランタイムは常に CoTaskMemFree メソッドを使用してメモリを解放します。操作の対象であるメモリが CoTaskMemAlloc メソッドで割り当てられていない場合は、IntPtr を使用し、適切なメソッドを使って手動でメモリを解放する必要があります。同様に、メモリを解放するべきではない場合に、自動的なメモリの解放を回避することもできます。たとえば、Kernel32.dll から、カーネル メモリへのポインターを返す GetCommandLine 関数を使用するような場合です。手動によるメモリの解放の詳細については、「Buffers のサンプル」を参照してください。

    Buffersのサンプルには

    ネイティブ GetCommandLine 関数は、オペレーティング システムが割り当てて所有する、バッファーへのポインターを返します。 戻り値の型として文字列をマーシャリングする場合、相互運用マーシャラーは、関数から返された元の LPTSTR 型が指すメモリを解放する必要があると見なします。 このメモリがマーシャラーによって自動的にクリアされるのを回避するために、マネージ GetCommandLine プロトタイプは文字列の代わりに IntPtr 型を返します。 PtrToStringAuto メソッドはアンマネージ LPSTR 型をマネージ文字列オブジェクトにコピーし、必要に応じて文字形式を拡張します。

    と書かれています。

    • 回答の候補に設定 佐伯玲 2012年11月30日 6:11
    • 回答としてマーク nob9292 2012年12月4日 1:19
    2012年11月29日 6:48

すべての返信

  • // 返値にchar*って気持ち悪い……。

    返値としてStringで受け取った場合に発生するメモリ解放操作の影響かな?

    返値をStringではなくIntPtrにして、Marshal.PtrToStringAnsiで文字列を取得するようにするとどうでしょうか?

    あと、この例ではリテラルだから大丈夫ですが、実際には動的なローカル変数をreturnしてたりしていませんか?

    • 回答の候補に設定 佐伯玲 2012年11月30日 6:11
    • 回答としてマーク nob9292 2012年12月4日 1:15
    2012年11月29日 6:08
  • 補足です。

    相互運用マーシャラーによるメモリ管理に次のように説明があります。

    ランタイムは常に CoTaskMemFree メソッドを使用してメモリを解放します。操作の対象であるメモリが CoTaskMemAlloc メソッドで割り当てられていない場合は、IntPtr を使用し、適切なメソッドを使って手動でメモリを解放する必要があります。同様に、メモリを解放するべきではない場合に、自動的なメモリの解放を回避することもできます。たとえば、Kernel32.dll から、カーネル メモリへのポインターを返す GetCommandLine 関数を使用するような場合です。手動によるメモリの解放の詳細については、「Buffers のサンプル」を参照してください。

    Buffersのサンプルには

    ネイティブ GetCommandLine 関数は、オペレーティング システムが割り当てて所有する、バッファーへのポインターを返します。 戻り値の型として文字列をマーシャリングする場合、相互運用マーシャラーは、関数から返された元の LPTSTR 型が指すメモリを解放する必要があると見なします。 このメモリがマーシャラーによって自動的にクリアされるのを回避するために、マネージ GetCommandLine プロトタイプは文字列の代わりに IntPtr 型を返します。 PtrToStringAuto メソッドはアンマネージ LPSTR 型をマネージ文字列オブジェクトにコピーし、必要に応じて文字形式を拡張します。

    と書かれています。

    • 回答の候補に設定 佐伯玲 2012年11月30日 6:11
    • 回答としてマーク nob9292 2012年12月4日 1:19
    2012年11月29日 6:48
  • ご返事ありがとうございます。戻り値はintを直し、解決しました。

    ありがとうございました。またよろしくお願いします。

    2012年12月4日 1:18