3

私は C# は初めてですが、C++ を幅広く使用してきました。C# から呼び出す必要がある C++ 関数があります。SO からのいくつかの回答といくつかのグーグルを読んだ後、関数への純粋な C インターフェイスを作成する必要があると結論付けました。私はこれを行いましたが、C# から呼び出す方法についてはまだ混乱しています。

C++ の関数は次のようになります。

int processImages(
    std::string& inputFilePath,                      // An input file
    const std::vector<std::string>& inputListOfDirs, // Input list of dirs
    std::vector<InternalStruct>& vecInternalStruct,  // Input/Output struct
    std::vector<std::vector< int > >& OutputIntsForEachFile,
    std::vector< std::vector<SmallStruct> >& vecVecSmallStruct, // Output
    int verboseLevel
    );

同じ関数を C に変換すると、次のようになります。

int processImagesC(
    char* p_inputFilePath,               // An input file
    char** p_inputListOfDirs,            // Input list of dirs
    size_t* p_numInputDirs,              // Indicating number of elements
    InternalStruct* p_vecInternalStruct, // Input/Output struct
    size_t* p_numInternalStructs, 
    int** p_OutputIntsForEachFile,       // a 2d array each row ending with -1
    size_t* p_numOutputIntsForEachFile //one number indicating its number of rows
    SmallStruct** p_vecVecSmallStruct,   // Output
    size_t* p_numInVecSmallStruct,
    int verboseLevel
    );

これは、このアドバイスに基づいています。

ここで、これを C# から呼び出す必要がありますが、これが混乱の原因です。私は構造を変換するために最善を尽くしました。

C# コードは次のようになります。

[DllImport(
    @"C:\path\to\cppdll.dll", CallingConvention=CallingConvention.Cdecl, 
    EntryPoint="processImagesC", SetLastError=true)]
[return: MarshalAs(UnmanagedType.I4)]
unsafe public static extern int processImagesC(
    String inputFilePath,
    String[] inputListOfDirs,
    ref uint numInputListOfDirs,

    // Should I use ref InternalStruct * vecInternalStruct?
    ref InternalStruct[] vecInternalStruct, 

    ref uint numInternalStruct,

    // Or ref int[] or ref int[][] or int[][]?
    ref int[][] OutputIntsForEachFile, 

    ref uint numOutputIntsForEachFile,

    // again, ref ..[], [][], or ref [][]?
    ref SmallStruct[][] vecVecSmallStruct, 

    int verboseLevel
);

C/C++ コード内で行われるすべての出力変数 (ポインター) のメモリ割り当てがあります。これはおそらく、コードを安全でないと宣言する必要があることを意味しますね。

メモリの解放をどのように処理しますか? C/C++ によって割り当てられたオブジェクト/配列の割り当てを解除する別の API (関数) を作成する必要がありますか?

C++ コードは標準に準拠し、プラットフォームに依存しない必要があるため、Windows 固有のものを挿入することはできません。

誰かがこれを理解して答えを提供するか、少なくとも私を正しい方向に向けてくれることを願っています.

4

4 に答える 4

4

It Just Works (IJW) を C++/CLI で使用することに関心があるようですので、それについての情報を投稿します。すべてを把握するには、さらに Google 検索と調査を行う必要があります。C++/CLI は、1 つのコンパイラ フラグで有効にできます (/CLI、[プロパティ ページ] -> [全般] -> [共通言語ランタイム サポート] で有効にします)。C++/cli はc ++ ではなく、別のマネージ言語です。C++/CLI クラスは dll にコンパイルして、他の .NET プロジェクト (C#、VB.NET など) から直接呼び出すことができます。ただし、他の .NET 言語とは異なり、C++ コードと直接対話できます。

これは、C++/CLI を学習するための適切なスタートです。学ぶべき大きなことは、クラスがマネージ (.NET クラス) であり、Vanila C++ ではないことを示す装飾です。「ref」キーワードは、定義を .NET 定義として宣言します。

public ref class Foo{ public: void bar();};//Managed class, visible to C#
public ref struct Foo{};//Managed struct, visible to C#

すべての参照クラスは、ポインタや参照ではなくハンドルで参照されます。ハンドルは ^ 演算子で表されます。新しいハンドルを作成するには gcnew を使用し、ハンドルの関数/メンバーにアクセスするには -> 演算子を使用します。

//in main
Foo^ a = gcnew Foo();
a->bar();

共通の構造体を C# からネイティブ型に移動したり、元に戻す必要があることがよくあります。(マネージド Array^ または String^ から void* または std::string への変換など)。このプロセスはマーシャリングと呼ばれます。この便利な表は、それを理解するのに非常に役立ちます。

一般的なタスクは、ネイティブ クラスのラッパーを次のように作成することです。

//Foo.h
#include <string>
namespace nativeFoo
{
    class Foo
    {
     private:
        std::string fooHeader;
     public:
        Foo() {fooHeader = "asdf";}
        std::string Bar(std::string& b) {return fooHeader+b;}
    };
}
//ManagedFoo.h
#include "foo.h"
namespace managedFoo
{
    public ref class Foo
    {
        private:
             nativeFoo::Foo* _ptr;
        public:
             Foo(){_ptr = new nativeFoo::Foo();}
             ~Foo(){!Foo();}
             !Foo(){if (_ptr){delete ptr;ptr = NULL;}}

             String^ bar(String^ b)
             {
                 return marshal_as<String^>(_ptr->bar(marshal_as<std::string>(b)));
             }
    };
}

警告: #include および #using ステートメントの束が完全に欠落しています。これは、これを使用する方法の一般的な要点を示すためのものです。

于 2013-03-28T14:58:16.563 に答える
1

これから始めます:

そして、これでマーシャリングについて何か:

Marshal.Copyも配列の使用のためにオーバーロードすることに注意してください。refマーシャリングを使用すると、必要な場合を除いて取り除くことができます。彼らのやり方で C/C++ を書くだけです。

そして、以下は少し複雑です:

于 2013-03-28T01:54:56.943 に答える
0

これが処理されるのを私がよく見た 2 つの方法は、「FreeResource」スタイルの API を使用するか、関数で出力バッファーのサイズを指定することです。

方法 1

C++

void GetName(char ** _str)
{
    if (!_str)
        return; // error
    *_str = new char[20];
    strcpy(*str, "my name");
}

void FreeString(char * _str)
{
    delete str;
}

クライアント (任意の言語)

char * name;
GetName(&name);
...
FreeString(name);

方法 2

C++

void GetName(char * _str, size_t _len)
{
    if (_len < 20)
        return; // error
    strcpy(str, "my name");
}

クライアント (任意の言語)

char * name = new char[20];
GetName(name, 20);
...
于 2013-03-28T01:11:07.750 に答える