1

私はC#でC ++ライブラリを使用するのは初めてで、C++プログラミング全般も初めてです。DLLの名前が「TestManaged.dll」であるため、「マネージ」コードであると思われるC++コードから構築されたDLLがあります。dll / C ++コードが管理されている/管理されていないかどうかは100%わかりません。

このDLLのクラスとメソッドをコードで使用したいと思いC# windows forms applicationます。このDLLには複数のクラスがあります。のこれらのクラスとそれらのクラス内のメソッドをチェックしたときObject Browser、それらすべてにPublic識別子があります。

これまで、このDLLをC#アプリケーションコードの参照に追加しました。私の質問で話すクラスは3つあります:Product、、。このDLLのさまざまなクラスのオブジェクトを次のように作成できます。ReqStatusProductData

Product testCall = new ProductClass();

このDLLで呼び出される別のクラスがありProductData、次のようにこのクラスのC++コードを取得できます。この場合、ProductDataはC#ではクラスとして表示されObject Browserますが、実際にはC++コードでは構造体です。これが私の質問に答えるために重要かどうかはわかりません(最後に)。

ProductData以下は、 struct-- ProductData.hfileを定義するC++コードです。

#ifdef WIN32_MANAGED 
public ref  struct ProductData
#else
struct ProductData
#endif
{
    UINT32 ProductId;           //!< Product ID    
    UINT32 PRoductRev;         //!< Build Revision
};

ReqStatus以下は、 enum-- ReqStatus.hfileを定義するC++コードです。識別子を指定せずに、C#コードで同じ列挙型を作成しました。

enum ReqStatus
{
    SUCCESS,            //!< Method was successful

    //Connection errors
    NOT_CONNECTED,      //!< Connection not open 
    CONN_TIMEOUT,       //!< Connection timed out commuincating with device
};

今、私が呼び出したい2つのメソッドがあり、両方に問題があります。

メソッド1:は、型のオブジェクトをパラメーターとして受け取り、C++の列挙型であるを返すクラスgetProductData内のメソッドです。したがって、以下はメソッドの宣言です(に見られるように):ProductProductDataReqStatusgerProductDataObject Browser

public ReqStatus getProductData(ProductData data)

同じメソッドのC++の削除は次のとおりです:(実際のメソッドは長すぎるため、宣言を与えるだけです):このメソッドはProdcut.cppファイル内にあります

ReqStatus Product::getProductData(ProductData PLATFORM_PTR data)

PLATFORM_PTRは、以下のように定義されています。Platform.h

#ifdef WIN32_MANAGED
#define PLATFORM_PTR ^
#else
#define PLATFORM_PTR *
#endif

メソッド2:は、文字配列(これはわかりません)とタイプのオブジェクトをパラメーターとして受け取り、C++の列挙型であるを返すクラスgetConnected内のメソッドです。したがって、以下はメソッドの宣言です(に見られるように):ProductProductDataReqStatusgetConnectedObject Browser

public ReqStatus getConnected(sbyte* someChar, ProductData data)

同じメソッドのC++の削除は次のとおりです:(実際のメソッドは長すぎるため、宣言を与えるだけです):このメソッドはProdcut.cppファイル内にあります

ReqStatus Product::getConnected(const char *someChar, ProductData PLATFORM_PTR data)

C ++コードは、次のようにメソッドを呼び出します。

private : Product^  _testProduct;
testProduct = gcnew Product();
ProductData ^ data = gcnew ProductData();

int portNum = Decimal::ToInt16(7); 
char portName[32];
_snprintf(&portName[0], sizeof(portName),"COM%d", portNum);
ReqStatus status = _testProduct->getConnected(&portName[0], data); //Calling getConnected

getProductDataメソッド内にメソッドへの内部呼び出しがありgetConnectedます。

ReqStatus status = getProductData(data); //data is the same which was passed to the getConnected method

私のC#コードは次のとおりで、両方のメソッド呼び出しでエラーが発生しました。以下のコードスニペットの同じ行にエラーを配置しました。どちらの方法も独立しています。それは、C++コードのメソッドgetProductDataから呼び出されるということだけです。getConnected両方を個別に呼び出すことができるかどうかを確認したかった。

ProductData pData = new ProductData(); // OK
Product _testProduct = new Product();  // OK

ReqStatus status1 = _testProduct.getConnected("COM5", pData ); //Error 1: The best overloaded method getConnected has some invalid arguments
ReqStatus status2 = (ReqStatus)_testProduct.getProductData(pData ); // Error 2: Method is inaccessible due to its protection level

エラー1については、StackOverflowや他のフォーラムのさまざまな記事の解決策を試しましたが、解決できませんでした。参考までに、「SomePortCOM」を次のように変更してみましたが、うまくいきません。

更新このコードは現在正常に機能しており、エラー1(無効な引数)は表示されません。これで、エラー2(保護レベルのエラー)を取り除くだけで済みます。何か提案をお願いします。ありがとうございました。

String str = "COM5";
byte[] bytes = Encoding.ASCII.GetBytes(str);
unsafe
    {
        fixed (byte* p = bytes)
        {
                sbyte* sp = (sbyte*)p;
                //SP is now what you want
                   ReqStatus status1 = _testProduct.getConnected(sp, pData );
        }
    }

Error2の場合、非常に多くのブログを検索し、考えられる解決策の1つがDLLImportの使用である可能性があることを発見しました。それも試してみましたが、次の問題があります。

DLLImportのC#宣言:

[DllImport("TestManaged.dll",EntryPoint="getConnected")]
        public static extern ReqStatus getConnected(String SerialPort, ref ProductData pData);

この関数をC#コードから次のように呼び出しています。

ProductData pData = new ProductData();
String str = "COM7";
ReqStatus status1 = getConnected(str, ref pData);

ただし、Entry point not foundエラーが発生します。このDLLによってエクスポートされた関数のリストを取得するために、dumpbin関数を実行しようとしました。しかし、機能は見当たりません。むしろ、以下のようにランダムな出力です。

Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Rumit\TestManaged.dll

File Type: DLL

  Summary

        2000 .data
       22000 .rdata
        1000 .reloc
        1000 .rsrc
       13000 .text

更新:また、DependencyWalkerを介してこのDLLにメソッドが表示されません。これで、C++のソースコードを入手できました。しかし、私はC++コーディングにかなり慣れていません。C ++コードに変更が必要な場合は、指示を教えてください。

よろしく、ルミット

4

3 に答える 3

1
 enum ReqStatus

それがあなたの最大の問題です。これはネイティブの列挙型を宣言します。マネージド コードでは使用できず、それを使用するコードにアクセスできなくなります。次のように、 enum クラスキーワードを使用してマネージド バージョンを宣言する必要があります。

public enum class ReqStatus {
    // etc...
}
于 2013-03-19T23:04:00.553 に答える
0

コードの周りの安全でないブロックはそれを作り、あなたのアセンブリがセキュリティプロトコルについて検証されないようにするので、それに注意してください。C#からC ++(ネイティブかどうか)からメソッドを呼び出したとき、それらを呼び出すためにPInvoke(Platform Invoke)を使用する必要がありました。保護レベルについては、すべてがC ++で公開されているとおっしゃっていましたが、C ++を初めて使用する場合は、構文をすばやく間違えた可能性があります。C#では、すべてのメソッドの前にストレージ指定子(public、protectedなど)を付ける必要がありますが、C ++では、ストレージ指定子の後にコロンを配置すると、そのストレージと次に宣言されるストレージの間のすべてがそのストレージになりますタイプ。多分これはあなたの問題ですか?

于 2013-03-19T19:49:42.253 に答える
0

問題を指摘してくれてありがとう、ハンス。それだけで、列挙型の定義を「public」にしました。しかし、あなたが間違って「クラス」を入れたのか、それとも列挙型として取らず、列挙型を使用したすべての場所でオブジェクトを要求していたため、非常に多くのエラーが発生していたため、意図的なものだったのかはわかりません. ここで何か誤解した場合はお知らせください。

それで、列挙型を公開するだけで機能しました。ただし、C# から C++ 関数に正しい値を渡す方法をまだ見つけることができません。(元の投稿のエラー 1)。

getConnectedC++ コードをデバッグすると、メソッドの最初のパラメーターに "0x0034E808 "COM5" 値 (メモリの場所と値だと思いますか?) が渡されます。エラー 1 の元の投稿)、それは "0x0277aab8" を渡します (これもメモリ アドレスのようです)、それに接続できませんでした (シリアル ポートのタイムアウト エラーを取得しています)。

よろしく、ルミット

于 2013-03-19T23:58:27.197 に答える