9

C#から使用したいCのライブラリがあります。

私がインターネットから収集したものから、1つのアイデアはそれをC ++ dllでラップし、それをDllImportすることです。

問題は、呼び出したい関数にかなり厄介なパラメーターセットがあることです。これには、関数(.NET関数になります)への参照と、いくつかの配列(書き込み、読み取り)が含まれます。

int lmdif(int (*fcn)(int, int, double*, double*, int*), 
          int m, int n, double* x, double ftol, double xtol, double gtol, 
          int maxfev, double epsfcn, double factor)

このようなインターフェースを考えると、私が注目すべき厄介なものは何ですか?(そして解決策もお願いします)

どうして...

-C#で書き直しますか?私は始めましたが、それはすでにFORTRANから機械翻訳されており、理解できないものをコーディングするのはあまり好きではありません。

-既存の.NETライブラリを使用しますか?今試していますが、結果はまったく同じではありません

-C ++で再コンパイルしますか?考えているのですが、大変な苦痛のようです

4

6 に答える 6

4

記録のために、私はこれを機能させるためのほとんどの方法を取得し、適切な C ファイルを C++ プロジェクトにプルすることになりました (コードの互換性の問題と戦っていたため、必要でさえありませんでした)。

この質問に直面している人々に役立つかもしれない途中で私が拾ったいくつかのヒントを次に示します。

配列のマーシャリング

double*C では、double へのポインター ( ) と double の配列( )の間に違いはありませんdouble*。相互運用するときは、あいまいさを解消できる必要があります。の配列を渡す必要がdoubleあったため、署名は次のようになります。

[DllImport(@"PathToDll")]
public static extern Foo(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)[In] double[] x, 
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)[Out] double[] y, 
    int x_size, 
    int y_size)

C では、配列の長さに関する追加情報が必要であり、別のパラメーターとして渡す必要があります。マーシャリングは、このサイズがどこにあるかを知る必要もあるため、SizeParamIndexパラメーターのリスト内のサイズ パラメーターのゼロから始まるインデックスを示す を指定します。

また、配列を渡す方向も指定します。この例xでは、 に渡さ Foo、「送り返されます」y

呼び出し規約

これが意味することの詳細を実際に理解する必要はありません (つまり、私は理解していません)。異なる呼び出し規則が存在し、それらが両側で一致する必要があることを知っておく必要があります。C# のデフォルトはStdCall、C のデフォルトは ですCdecl。これは、使用している側のデフォルトと異なる場合にのみ、呼び出し規約を明示的に指定する必要があることを意味します。

これは、コールバックの場合は特に厄介です。Cfromにコールバックを渡す場合C#、そのコールバックを で呼び出すことを意図していますがStdCall、渡すときは を使用していますCdecl。これにより、次の署名が生成されます (コンテキストについては、この質問を参照してください)。

//=======C-code======
//type signature of callback function
typedef int (__stdcall *FuncCallBack)(int, int);

void SetWrappedCallback(FuncCallBack); //here default = __cdecl

//======C# code======
public delegate int FuncCallBack(int a, int b);   // here default = StdCall 

[DllImport(@"PathToDll", CallingConvention = CallingConvention.Cdecl)]
private static extern void SetWrappedCallback(FuncCallBack func);

コールバックのパッケージ化

明らかですが、すぐにはわかりませんでした:

int MyFunc(int a, int b)
{
   return a * b;
}
//...

FuncCallBack ptr = new FuncCallBack(MyFunc);
SetWrappedCallback(ptr);

.def ファイル

C++ プロジェクトから公開する (DllImport編集する) 関数はすべて、ModDef.defファイル内で把握する必要があります。その内容は次のようになります。

LIBRARY MyLibName
EXPORTS
    SetWrappedCallback  @1

外部「C」

C++ から C 関数を使用する場合は、それらを として宣言する必要がありますextern "C"。C 関数のヘッダー ファイルをインクルードする場合は、次のようにします。

extern "C" {
  #include "C_declarations.h"
}

プリコンパイル済みヘッダー

コンパイル エラーを回避するために私がしなければならなかったもう 1 つのことは、「C」ファイルごとにプリコンパイル済みヘッダーを使用しないようRight-click -> Properties -> C/C++ -> Precompiled Headersに設定することでした。Precompiled header

于 2012-06-05T11:32:26.790 に答える
3

これは、私が通常C#からCDLLを操作する方法です。

  public static unsafe class WrapCDll {
    private const string DllName = "c_functions.dll";

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern void do_stuff_in_c(byte* arg);
  }

C ++でラップする必要はなく、必要な呼び出し規約が適用されます。

于 2012-06-01T15:59:34.077 に答える
3

唯一の「厄介な」パラメーターは関数ポインターです。しかし幸いなことに、.NET はデリゲートを介してそれらを適切に処理します。

唯一の問題は、呼び出し規約にあります。C# では、1 つの型 (iirc stdcall) のみを出力しますが、C コードではcdecl. 後者の問題は、IL レベルで (または を使用してReflection.Emit) 処理できます。

これを行うコードを次に示しますReflection.Emit(これは、デリゲートのメソッドに配置する必要がある疑似属性を理解するのに役立ちますInvoke)。

于 2012-06-01T09:07:54.397 に答える
3

Marshal.GetFunctionPointerForDelegateの Microsoft ドキュメントはこちら:

"アンマネージ コードから呼び出し可能な関数ポインターにデリゲートを変換します。"

于 2012-06-01T09:30:10.800 に答える
1

StringBuilder を介して C 関数に大量の数値データを送信する方法を次に示します。区切り文字を適切な位置に設定しながら、数値を StringBuilder にダンプするだけです。

class Program
    {  
        [DllImport("namEm.DLL", CallingConvention = CallingConvention.Cdecl, EntryPoint = "nameEm", 
            CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
        public static extern int nameEm([MarshalAs(UnmanagedType.LPStr)] StringBuilder str);
        static void Main(string[] args)
        {
            int m = 3;
            StringBuilder str = new StringBuilder();
            str.Append(String.Format("{0};", m));
            str.Append(String.Format("{0} {1:E4};", 5, 76.334E-3 ));
            str.Append(String.Format("{0} {1} {2} {3};", 65,45,23,12));
            m = nameEm(str);
        }
    }

C 側では、StringBuilder を char* として取得します。

extern "C"
{
    __declspec(dllexport) int __cdecl nameEm(char* names)
    {
        int n1, n2, n3[4];
        char *token,
             *next_token2 = NULL,
             *next_token = NULL;
        float f;

        sscanf_s(strtok_s(names, ";", &next_token), "%d", &n2);
        sscanf_s(strtok_s(NULL, ";", &next_token), "%d %f", &n1, &f);
        // Observe the use of two different strtok-delimiters.
        // the ';' to pick the sequence of 4 integers,
        // and the space to split that same sequence into 4 integers.
        token = strtok_s(strtok_s(NULL, ";", &next_token)," ",&next_token2);
        for (int i=0; i < 4; i++)
        {
             sscanf_s(token,"%d", &n3[i]);
             token = strtok_s(NULL, " ",&next_token2);
        }    
        return 0;
    }
}
于 2016-07-12T12:57:07.123 に答える
1

何年も前に InteropSignatureToolkit をもたらしMSDN の記事がありました。この小さなツールは、C インターフェースをマーシャリングするのに依然として役立ちます。インターフェイス コードをコピーして「SigImp Translation Sniplet」に貼り付け、結果を確認します。

結果は次のとおりですが、デリゲートがどのように使用されているか、または機能しているかどうかはわかりません。したがって、うまくいく場合は、コメントを追加してください。

/// Return Type: int
///param0: int
///param1: int
///param2: double*
///param3: double*
///param4: int*
public delegate int Anonymous_83fd32fd_91ee_45ce_b7e9_b7d886d2eb38(int param0, int param1, ref double param2, ref double param3, ref int param4);

public partial class NativeMethods {

    /// Return Type: int
    ///fcn: Anonymous_83fd32fd_91ee_45ce_b7e9_b7d886d2eb38
    ///m: int
    ///n: int
    ///x: double*
    ///ftol: double
    ///xtol: double
    ///gtol: double
    ///maxfev: int
    ///epsfcn: double
    ///factor: double
    [System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="lmdif", CallingConvention=System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern  int lmdif(Anonymous_83fd32fd_91ee_45ce_b7e9_b7d886d2eb38 fcn, int m, int n, ref double x, double ftol, double xtol, double gtol, int maxfev, double epsfcn, double factor) ;

}
于 2012-06-01T17:59:22.553 に答える