82353


c++で作成されたdll関数とC#のプログラム間の引数のやり取りについてです。

現在、c++で構築されているソフトウェアのデータ取得ツールをc#を用いて作成しています。  
プログラムの流れはgetTablePtr()でテーブルへのポインタを取得し、getValFromTable()でテーブル内のデータを取得するものです。  
ただし、getValFromTable()は実際はもっと複雑でテーブルのデータが読めれば良いわけではありません。

c++のプログラム、C#のプログラムはそれぞれ下記のように実装しており、getTablePtr()は（正しく使用できているかは別にして）呼び出しができているようで、ptrTに値は入ります。  
ところがgetValFromTable()の呼び出しでツールのウィンドウが固まってしまい、その後エラーなどの音沙汰なくウィンドウが閉じてしまいます。

現時点ではどちらの関数も呼び出し自体はできていて、C#でポインタを扱うという点の理解が浅いがためにちぐはぐな実装を行ってしまっているのではないかと思っています。  
下記のプログラムをベースにMSのドキュメントなども見ながら引数やIntPtrの使い方を何パターンも試してみましたが、全く歯が立ちそうにありません。

C#で下記のプログラムを正しく実装するにはどのようにすればよいのでしょうか？

※関数名がc++側とc#側で異なっていたので修正しました。  
（c#側でrefTable → getValFromTable）  
※構造体名も中途半端に変更前後のものが混在していたので揃えました。  
（STRC → TBL、ptrStrc → ptrTable、ptrS → ptrT）  
せっかく見ていただいているのにサンプルコードががばがばで申し訳ないです。  
回答いただいている点は一つずつ確認していきます。

* * *

**C++プログラム**

[code]

    // データテーブル
    typedef struct{
        unsigned long v[0x10];
    }TBL;
    
    // テーブルのポインタ取得
    extern "C" __declspec(dllexport) TBL* getTablePtr()
    {
        TBL* ptrTable;
        /* 処理 */
        return(ptrTable);
    }
    
    // テーブルからデータ取得
    extern "C" __declspec(dllexport) LONG  getValFromTable(TBL* ptrTable)
    {
        /* 処理 */
        return(val);
    }
    
[/code]

**C#プログラム**

[code]

    class testDLL
    {
        [DllImport("testDLL.dll", EntryPoint = "getTablePtr")]
        public static extern IntPtr getTablePtr();
        [DllImport("testDLL.dll", EntryPoint = "getValFromTable")]
        public static extern long getValFromTable(IntPtr ptrTable);
    }
        
    namespace DLLtest
    {
    
        public partial class MainWindow : Window
        {
            IntPtr ptrT;
    
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Start(object sender, RoutedEventArgs e)
            {
                ptrT = testDLL.getTablePtr();
                ret = testDLL.getValFromTable(ptrT);
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2021-09-08T10:02:33.907
82348
2021-09-09T03:30:54.970
2021-09-09T03:30:54.970
48108
48108
question
c# c++ ポインタ dll
c++で作成されたdll内の関数とC#プログラム間のポインタの取り扱い


質問と無関係でありながら、実は原因かもしれないので指摘します。

C++側

>
[code]

>     extern "C" __declspec(dllexport) LONG  getValFromTable(TBL* ptrStrc)
>  
[/code]

C#側

>
[code]

>     [DllImport("testDLL.dll", EntryPoint = "func2")]
>     public static extern long refTable(IntPtr ptrStrc);
>  
[/code]

となっていますが、C#における`long`は64bitです。対してWindows
APIの`LONG`やC++の`long`は32bitです。これらデータサイズが誤っていると引数や戻り値の受け渡しが正しく動作しません。

  * C++言語 / [Data Type Ranges](https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-160)
  * Windows API / [Windows Data Types](https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types)
  * C#言語 / [整数数値型](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/integral-numeric-types)

を確認してください。


CC BY-SA 4.0
2021-09-08T10:52:41.500
82351
2021-09-08T10:52:41.500
4236
82348
answer


C++(LONG) と C#(int) の型の違いもありますが、おそらくは呼び出し規約の相違ではないでしょうか？  
少なくとも 32bit プロセスでは呼び出し規約を合わせる必要があります。

C++ 側のコンパイラが示されていませんが、Visual C++ では __cdecl がデフォルトになっています。  
対して、.NET側の [DllImport のデフォルトは Winapi = Stdcall
です。](https://docs.microsoft.com/ja-
jp/dotnet/api/system.runtime.interopservices.dllimportattribute.callingconvention?view=net-5.0#--)  
DllImport 属性で CallingConvention = CallingConvention.Cdecl を追加するか、C++ 側で
__stdcall をつけるかが必要だと考えられます。

[code]

    [DllImport("testDLL.dll", EntryPoint = "getValFromTable", CallingConvention = CallingConvention.Cdecl)]
    public static extern int refTable(IntPtr ptrStrc);
    
[/code]


CC BY-SA 4.0
2021-09-08T11:12:03.973
82353
2021-09-08T11:12:03.973
4684
82348
answer