2017-04-17 7 views
-1

を整列化時に、それががmscorlib.dllでアクセス違反の例外が発生し起動++私は、サードパーティのコードから構造体次ています変換のstd ::ベクトルその後、P/Cで

typedef struct NodeInfoTag 
{ 
    long lResult; 
    int bComplete; 
    char *pszNodeAddr; 
    char *pszParentAddr; 
    RTS_WCHAR *pwszNodeName; 
    RTS_WCHAR *pwszDeviceName; 
    RTS_WCHAR *pwszVendorName; 
    unsigned long ulTargetType; 
    unsigned long ulTargetId; 
    unsigned long ulTargetVersion; 
    unsigned short wMaxChannels; 
}NodeInfotyp; 

と定義

# ifndef RTS_WCHAR_DEFINED 
#  define RTS_WCHAR_DEFINED 
     typedef wchar_t RTS_WCHAR; /* wide character value */ 
# endif 

(だから、基本的にはwchar_tです)

その後、私はCPLCHandlerCallbackクラスを拡張CScanNetworkCallbackと呼ばれる自分のクラスを、持っている、A:RTS_WCHARへ

.hファイル:

class CScanNetworkCallback : public CPLCHandlerCallback 
{ 
    public: 
     bool bScanComplete; 
     NodeInfotyp* pNodeInfo; 
     NodeInfotyp* pNodeInfoList; 
     std::vector<NodeInfotyp> vList; 
     CScanNetworkCallback(); 
     virtual ~CScanNetworkCallback(void); 

     virtual long Notify(CPLCHandler *pPlcHandler, CallbackAddInfoTag CallbackAdditionalInfo); 
}; 

実装がでスロー私自身のものの一部で、独自のガイドラインに従います、したがって、基本的

CScanNetworkCallback::CScanNetworkCallback(void) : CPLCHandlerCallback() 
{ 
    bScanComplete = false; 
} 

CScanNetworkCallback::~CScanNetworkCallback() 
{ 
    delete pNodeInfo; 
    delete pNodeInfoList; 
} 

long CScanNetworkCallback::Notify(CPLCHandler *pPlcHandler, CallbackAddInfoTag CallbackAdditionalInfo) 
{ 
    if (pPlcHandler != NULL) 
    { 
     if (CallbackAdditionalInfo.ulType == PLCH_SCAN_NETWORK_CALLBACK) 
     { 
      pNodeInfo = CallbackAdditionalInfo.AddInf.pNodeInfo; 
      if (pNodeInfo->lResult == RESULT_OK) 
      { 
       vList.push_back(*pNodeInfo); 
       bScanComplete = false; 
      } 
      else 
      { 
       pNodeInfoList = &vList[0]; //New pointer points to the vector elements, which will be used as an array later on 

       // I have also tried copying it, to the same result: 
       //std::copy(vList.begin(), vList.end(), pNodeInfoList); 

       bScanComplete = true; 
      } 
     } 
    } 
    return RESULT_OK; 
} 

Notifyを同じベンダーからのクラスメソッドは、ノードの情報をpNodeInfoに割り当てる(ノードが何であるか無視してください、それはATMではありません)、ネットワーク内に「ノード」が見つかるたびに呼び出されます。スキャンプロセス中にネットワーク内のすべてのノードに呼び出され、この情報をC++に送信する必要があるため、std::vectorを使用して、後で使用するすべてのコールバック情報を格納する以外の方法はありません。コンパイル時にいくつのノードがあるか分からない。 else部分は、すべてのノードが見つかった後に呼び出されます。

PROASADLL __declspec(dllexport) void scanNetwork(){ 
    pScanHandler->ScanNetwork(NULL, &scanNetworkCallback); 
} 

オブジェクトscanNetworkCallback静的である:C#コードのうち、意味を理解するためには、私は、p /呼び出されますが、いくつか他のC++メソッドの実装を記述する必要があります。 pScanHandlerはサードパーティのベンダの別のクラスへのポインタであり、そのメソッドのScanNetworkは別のスレッドで実行されます。

:内部(と私は唯一の原因このAPIのガイドラインに、私はそのソースコードを持っていないことを知っている)、それは Notifyノードがネットワークで発見されたときに方法、またはその効果

そして最後に何かを呼び出します

PROASADLL __declspec(dllexport) NodeInfotyp* getScanResult(int* piSize) { 
    *piSize = scanNetworkCallback.vList.size(); 
    return scanNetworkCallback.pNodeInfoList; 
} 

これは、すべてのノードの情報を指すポインタと、その量をoutパラメータとして返します。今度は、C#のコードを見てみましょう:

public static List<NodeInfoTag> AsaScanNetworkAsync() 
    { 
     Console.WriteLine("SCANNING NETWORK"); 
     scanNetwork(); // C++ Method 

     while (!isScanComplete()) // Holds the C# thread until the scan is complete 
      Thread.Sleep(50); 

     int size = 0; 
     IntPtr pointer = getScanResult(out size); // works fine, I get some IntPtr and the correct size 
     List<NodeInfoTag> list = Marshaller.MarshalPointerToList<NodeInfoTag>(pointer, size); // PROBLEM!!! 

     // Continue doing stuff 
    } 

は、これはC++ NodeInfotyp構造体に一致するように、クラスNodeInfoTagです:

[StructLayout(LayoutKind.Sequential)] 
public class NodeInfoTag 
{ 
    public int Result; 
    public int Complete; 
    [MarshalAs(UnmanagedType.LPStr)] //char* 
    public string NodeAddress; 
    [MarshalAs(UnmanagedType.LPStr)] //char* 
    public string ParentAddress; 
    [MarshalAs(UnmanagedType.LPWStr)] //wchar_t 
    public string VendorName; 
    public uint TargetType; 
    public uint TargetId; 
    public uint TargetVersion; 
    public short MaxChannels; 
} 

そして、私は私のメモリアクセス違反を取得する場所です。

internal class Marshaller 
{ 
    public static List<T> MarshalPointerToList<T>(IntPtr pointer, int size) 
    { 
     if (size == 0) 
      return null; 

     List<T> list = new List<T>(); 

     var symbolSize = Marshal.SizeOf(typeof(T)); 
     for (int i = 0; i < size; i++) 
     { 
      var current = (T)Marshal.PtrToStructure(pointer, typeof(T)); 
      list.Add(current); 
      pointer = new IntPtr(pointer.ToInt32() + symbolSize); 
     } 
     return list; 
    } 
} 

特に、マーシャリングを行うときにエラーが発生するのは、var current = (T)Marshal.PtrToStructure(pointer, typeof(T));という行です。このC#コードはうまく動作するために使用されていましたが、C++の部分はひどく畳み込まれていてエラーが発生しやすいため、よりシンプルにすることにしましたが、なぜこの例外が発生するのかわかりませんC++ではすべてのC++リソースが利用可能であることを確認しています。なぜなら、テスト目的ではC++で何も削除しないため、スタティックメモリに割り当てられたクラス内のグローバルスコープの変数のみを使用しているからです。それで、私は何を逃したのですか?

編集:私はpNodeInfoList = &vList[0];を削除しないと、次のようにgetScanResultを書き直し:

static NodeInfotyp pNodeInfoList; 

//(...) 

PROASADLL __declspec(dllexport) NodeInfotyp* getScanResult(int* piSize) { 
    *piSize = scanNetworkCallback.vList.size(); 
    std::move(scanNetworkCallback.vList.begin(), 
scanNetworkCallback.vList.end(), &pNodeInfoList); 
    return &pNodeInfoList; 
} 

ませサイコロを。関係する変数にnewまたはmallocを使用せず、pNodeInfoList(配列)をクラスメンバーからグローバル変数に変更しました。また、私が言われたように、私はmoveを使用しています、所有権の問題を解決するために使用することができます。その他のヒント?

+0

"その他のヒント?"すでにC++コードを書いているので、P/Invokeの代わりにC++/CLIを使用してみてはいかがですか? –

+0

それは長い時間がかかります... – makoshichi

+0

"長い時間を取る"?あなたが書くことを意味しますか?実行時間ですか? –

答えて

1

オーナーシップは未知のC++タイプシステムの一部ではないため、所有していないポインタを削除したり、所有権を放棄せずに移動したりするときにエラーは発生しません。

しかし、意味的には、特定の値やポインタ、データブロックは、特定の型や値によって所有されています。

この場合、ベクトルはそのメモリブロックを所有しています。それを求めたり、所有権を放棄したりする方法はありません。

を呼び出す.data() onkyはポインタを提供しますが、それはではありません。はそのポインタの意味所有権を与えます。

メンバ変数に戻り値.data()を格納します。後でそのメンバ変数にdeleteを呼び出します。これは、メンバー変数がそのデータを所有することになっていることを私に示します。つまり、ベクトルとポインタの両方がポイントされたデータを所有していると思うので、ダブル・デリートすると、コンパイラがプログラムをクラッシュさせます。

あなたが作業しているすべてのメモリブロックの期限と所有権を考慮して、コードを書き直す必要があります。 1つのアプローチは、新しい、mallocを呼び出したり、直接削除したり解放したりすることなく、vectorやunique ptrなどのメモリ管理型を常に使用することです。その所有権セマンティクスはその型から明らかではないので、生ポインタを永続化しないでください。

+0

私の投稿 – makoshichi

+1

@ S.Oの編集をご覧ください。あなたの編集内容が私のコメントとどのように関係しているか分かりません。新しいコードは、初期化されていないランダムなメモリをコピーしているようです。 – Yakk

関連する問題