

題名の通りです。  
過去に作ったdllを再利用して、ダブルポインタ（実体はchar型配列）の変数を含んだ構造体を引数にして呼び出しが行いたいのですが、以下の例外が発生してしまいうまくいきません。

[例外]  
System.TypeInitializationException: ''PayGwApp.ComTask' のタイプ初期化子が例外をスローしました。  
AccessViolationException:
保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。

構造体は以下の様なデータです。

[code]

        unsafe struct cmdq {
            public ulong Size;
            public char** ppData;
        }
    
[/code]

ppDataの箇所に配列データ設定してc++側の関数を呼び出したいのですが、うまくいきません。

実装は以下の様にしております。

[code]

        // 呼び出したい関数
        [DllImport("mycmdq.dll")]
        private extern static long cmdq_exec(ushort i_id, IntPtr CmdQ);
    
        private unsafe void InitData()
        {
            cmdq t_cmdQ = new cmdq();
            t_cmdQ.Size = 200;
    
            try
            {
                char[] Data = new char[200];
    
                // テスト用ダミー
                for (int idx = 0; idx < Data.Length; idx++)
                {
                    Data[idx]  = (char)(idx + 1);
                }
    
                fixed (char* pData = &Data[0])
                {
                    t_cmdQ.ppData = &pData;
                }
    
                IntPtr cmdqPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ulong)) + Marshal.SizeOf(typeof(char)) * Data.Length);
                Marshal.StructureToPtr(t_cmdQ, cmdqPtr, false);
    
                ret = cmdq_exec(20, cmdqPtr);
                Console.WriteLine("sndbuf " + ret.ToString());
    
                Marshal.FreeCoTaskMem(cmdqPtr);
            }
            catch (Exception exception)
            {
                Console.WriteLine("");
            }
        }
    
[/code]

色々試してはいるのですが、解消できません。  
詳しい方、助けて頂けませんでしょうか。


CC BY-SA 4.0
2019-10-23T09:16:01.417
59910
2019-10-24T06:27:25.417
22484
question
c# c++ windows dll
c#からダブルポインタの変数を含んだ構造体を引数にしてのdl関数を呼び出したい


挙げられているソースコードには問題点が何箇所もあり指摘することはできますが、求められている正しい処理がわからないため、解決するコードは提示できません。

## 明らかに誤っている

[`fixed`](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-
reference/keywords/fixed-
statement)はステートメント内の期間だけメモリ移動を抑止します。ステートメントを抜けた時点で解除されるためメモリ移動が発生し得ます。ですので`fixed`ステートメント内で得られたポインターを保持し続けるのは誤っています。

[`Marshal.AllocHGlobal`](https://docs.microsoft.com/ja-
jp/dotnet/api/system.runtime.interopservices.marshal.allochglobal?view=netframework-4.8)に対応するのは[`Marshal.FreeHGlobal`](https://docs.microsoft.com/ja-
jp/dotnet/api/system.runtime.interopservices.marshal.freehglobal?view=netframework-4.8)です。逆に[`Marshal.FreeCoTaskMem`](https://docs.microsoft.com/ja-
jp/dotnet/api/system.runtime.interopservices.marshal.freecotaskmem?view=netframework-4.8)に対応するのは[`Marshal.AllocCoTaskMem`](https://docs.microsoft.com/ja-
jp/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem?view=netframework-4.8)です。正しく対応できていません。

## 誤っている可能性が高い

C#の[`ulong`](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-
reference/builtin-types/integral-numeric-
types)は64bit符号なし整数型であり、C++で対応するのは[`uint64_t`](https://ja.cppreference.com/w/cpp/types/integer)です。WindowsのC++における[`unsigned
long`](https://docs.microsoft.com/ja-jp/cpp/cpp/data-type-
ranges?view=vs-2019)は32bitと規定されているためこれを期待しているのであれば正しくありません。  
同様にC#の[`char`](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-
reference/keywords/char)は16bitのUnicode文字を表します。C++で対応するのは[`char16_t`](https://ja.cppreference.com/w/c/string/multibyte/char16_t)です。WindowsのC++における[`char`](https://docs.microsoft.com/ja-
jp/cpp/cpp/data-type-
ranges?view=vs-2019)は8bitのANSI文字を表すためこれを期待しているのであれば正しくありません。


CC BY-SA 4.0
2019-10-23T21:54:05.923
59917
2019-10-23T21:54:05.923
4236
59910
answer


質問の現象にピンポイントで回答を得るためには、`mycmdq.dll`側の`struct
cmdq`定義や`cmdq_exec`エントリ部分のソースがあれば、話は早かったですね。  
**過去に作ったdll** というならば、自分でそのソースも持っているはずですから。

例えば質問内容からダミーのC++dllをVS2019で作ると、動くバージョンではこんなソースになります。呼び出し・復帰・パラメータデータの有効性チェックだけする形です。  
UNICODEモードで、C#,C++ともに32bitでビルドしました。

[code]

    #include "pch.h"
    BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
        return TRUE;
    }
    
    typedef struct cmdq {
        ULONGLONG   Size;  //## ここが ULONG ならば C#側を uint にする
        TCHAR** ppData;
    };
                                          //## 以下の cmdqptr の型がポインタ cmdq* か？
    extern "C" __declspec(dllexport) long cmdq_exec(USHORT i_id, cmdq* cmdqptr)
    {
        TCHAR** ppData = cmdqptr->ppData;
        TCHAR* pData = *ppData;
        for (ULONGLONG i = 0; i < cmdqptr->Size; i++)
        {
            TCHAR c = pData[i];  //## 意味の無い処理だが、Debugモードならチェックになる
        }
        return (long)cmdqptr->Size;
    }
    
[/code]

C++dllの上記部分とか、ビルドのUNICODE/ANSIモードが合っているかチェックしてみてください。こうしたダミーを作って1つ1つ確かめるのも手だと思います。

なお、「System.TypeInitializationException: ''PayGwApp.ComTask'
のタイプ初期化子が例外をスローしました。」という文言からすると、実際には上記はクリアしていて、本当の`cmdq_exec`の処理内で問題が発生している可能性も考えられます。

その場合の調査方法は、 **過去に作ったdll** のプロジェクト一式を C#
ソリューションに組み込んで、両方をデバッグモードで動作させるくらいでしょうか。


CC BY-SA 4.0
2019-10-24T06:05:40.827
59934
2019-10-24T06:27:25.417
2019-10-24T06:27:25.417
26370
26370
59910
answer