4

C++ COM ヘッダーと IDL ファイルに次の宣言があります。

//Header file:
#define MAX_LENGTH      320
typedef BYTE            PRE_KEY [MAX_LENGTH];

//IDL file:
#define MAX_COUNT       10
HRESULT Save([in] DWORD dwCommand, [in]float fdata[MAX_COUNT], [out] PRE_KEY* phKey);

これは C# クライアント コードです。

//After C# interop compilation, the method's signature in C# becomes:
Save(uint dwCommand, float[] fdata, out byte[] phKey);

//The code to call the C++ COM server:    
uint dwCommand = 2;
float[] fdata = new float[dwCommand];

fdata[0] = 1; 
fdata[1] = 2;

byte[] phKey = new byte[320];

save(dwCommand, fdata, out phKey);

呼び出しが C# に戻る前に、コードは ntdll.dll でクラッシュしますが、C++ サーバーは既に処理を終了しており、スタックにはありません。

誰でもこの問題を解決する方法を理解できますか? また、相互運用コンパイルを使用して idl ファイルをコンパイルして C# 署名を生成しているため、C++ IDL ファイルで何かを実行して C# 署名を手動で変更することはできません。

これについて面白いのは、C++ から C# にまったく同じ phKey を返す別の同様の呼び出しがあり、完全に機能することです。唯一の違いは、呼び出し phKey が構造内にあり、構造全体が '[out]' パラメータであることです。これが構造内で返されるが、直接パラメータとして返されない理由が本当にわかりません。

4

1 に答える 1

1

IDL 宣言の [out] 属性は、深刻な相互運用の問題です。これは、COM サーバーが配列を割り当て、呼び出し元がそれを解放する必要があることを意味します。サーバーとクライアントが同じヒープを使用するという保証はまったくありません。C ランタイム アロケーターを malloc() 関数または new[] 演算子と共に使用すると、常に失敗します。CRT は、まったく同じバージョンの CRT を共有しない限り、呼び出し元がアクセスできない独自のプライベート ヒープを使用します。これが一般的に当てはまる可能性は非常に低く、CLR を介して相互運用する場合はゼロです。

これが爆撃の理由です.CLRは、配列をマネージド配列にコピーした後、配列を解放する必要があることを認識しています. COM 相互運用割り当て用に予約されているヒープを使用して、CoTaskMemFree() を使用します。CoTaskMemAlloc() を使用して配列を割り当てていないことは確かです。

この問題の一般的な解決策は、呼び出し元が配列を提供し、呼び出し先がそれを埋めることです。これには、パラメーターで [in, out] が必要です。そして、渡された配列のサイズを示す追加のパラメーター [sizeis] は、マーシャラーにそのことを伝えます。非常に効率的で、割り当ては必要ありません。オートメーション SAFEARRAY 型を使用すると、追加の引数を指定する必要がなくなり、CLR はその型を認識します。

于 2012-09-08T14:08:03.560 に答える