4

次のシグネチャを持つアンマネージC++関数があります。

int function(char* param, int ret)

私はそれをC#から呼び出そうとしています:

unsafe delegate int MyFunc(char* param, int ret);

..。

int Module = LoadLibrary("fullpathToUnamanagedDll");
IntPtr pProc = GetProcAddress(Module, "functionName");
MyFunc func = (MyFunc)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pProc, typeof(MyFunc));

unsafe
{
    char* param = null;
    int ret = 0;
    int result = func(param, ret);
}

古いC++プロジェクトの仕様からわかる限り、paramの場合はnull、 retの場合は0の両方が関数への有効な入力です。電話をかけようとするとうまくいくようですが、終了すると次のエラーが発生します。

PInvokeStackImbalanceが検出されました

PInvoke関数'...::Invoke'の呼び出しでスタックのバランスが崩れました。これは、マネージドPInvokeシグニチャがアンマネージドターゲットシグニチャと一致しないことが原因である可能性があります。PInvokeシグニチャの呼び出し規約とパラメータがターゲットのアンマネージドシグニチャと一致することを確認してください。

私は考えられることはほとんど何でも試しましたが(安全ではないことが最後の手段でした)、不均衡なスタックを取得せずに関数を実行する方法を見つけることができません。他に試すことができるものはありますか?

4

3 に答える 3

8

この質問はもう1年前のものですが、型を動的に作成するよりも簡単な方法はUnmanagedFunctionPointer、次のように、デリゲートの属性を使用して呼び出し規約を宣言することです。

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
unsafe delegate int MyFunc(char* param, int ret);

MSDNから:

アンマネージコードとの間でアンマネージ関数ポインターとして渡されるデリゲート署名のマーシャリング動作を制御します。

于 2011-09-15T20:07:30.810 に答える
2

IIRC、呼び出し規約でデリゲート署名を装飾する必要があります。残念ながら、これはILを介して、またはReflection.Emitを使用してスタブを生成することによってのみ実行できます。

あなたはこれを試すことができます:

protected static Type MakeDelegateType(Type returntype, List<Type> paramtypes)
{
  ModuleBuilder dynamicMod = ... ; // supply this

  TypeBuilder tb = dynamicMod.DefineType("delegate-maker" + Guid.NewGuid(), 
      TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate));

  tb.DefineConstructor(MethodAttributes.RTSpecialName | 
       MethodAttributes.SpecialName | MethodAttributes.Public |
       MethodAttributes.HideBySig, CallingConventions.Standard,
       new Type[] { typeof(object), typeof(IntPtr) }). 
       SetImplementationFlags(MethodImplAttributes.Runtime);

  var inv = tb.DefineMethod("Invoke", MethodAttributes.Public | 
       MethodAttributes.Virtual | MethodAttributes.NewSlot | 
       MethodAttributes.HideBySig, 
       CallingConventions.Standard ,returntype,null, 
       new Type[] 
       { 
          // this is the important bit
          typeof(System.Runtime.CompilerServices.CallConvCdecl)
       }, 
       paramtypes.ToArray(), null, null);

  inv.SetImplementationFlags(MethodImplAttributes.Runtime);

  var t = tb.CreateType();
  return t;
}
于 2010-07-22T08:27:26.407 に答える
-1

注意すべき点が2つあります。C#ビットとDLLの間の呼び出し規約と、このインターフェイス全体でchar*データがどのようにマーシャリングされるかです。これらのいずれかを間違えると、スタック破損の苦情が発生します。インターフェイスを定義する際に、データブロックのサイズを固定されたものに制限できると、はるかに簡単になります。文字列の最大長を設定します。

これが静的バージョンです。DLL名は固定されており、文字列はbyte []として処理され、サイズは2Kバイトに制限されています。これから、動的バージョンを把握できます。

    private const string MYDLL = @"my.dll";

    [DllImport(MYDLL, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
    public static extern int DataBlockDownload([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C);

    // NOTE: The data block byte array is fixed at 2Kbyte long!!
public delegate int DDataBlockCallback([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C);

上記のように、charタイプを使用したい場合は、使用している文字セットを指定することもできます。

char *データがパラメーターとしてC++コードに入力されている場合、またはC ++コードがデータをマネージドワールドに戻している場合は、char*データで何をしているのかはわかりません。char *型と安全でない修飾子を回避する方法として、C#キーワードrefとoutを読んでください。

それから少しグーグルすることで、あなたはそれを理解することができるはずです。

于 2010-07-22T09:18:34.773 に答える