2011-06-27 18 views
0

COMインターフェイスのC#実装で構造体のパッキングサイズを指定することはできますか?COMインターフェイスのC#実装で構造体パッキングを指定する

(構造体が管理側にに定義されていますが、私の質問は、それが管理されていない側に定義されていたときについてですとは、管理側でを実装したときに、私はそれを行う方法を知っている。)

私はstruct型を定義するCOM型ライブラリと、それらの構造体の配列を返すインターフェイスメソッドがあります。私はC#サーバーとアンマネージC++サーバーを持っています。このサーバーは両方ともこのインターフェイスを実装しており、それを消費するC++クライアントです。 C++サーバーとC++クライアントは、構造体を32ビットのビルドでは4バイトに、64ビットのビルドでは8バイトにパックします。

しかし、プラットフォーム(x86、x64、AnyCPU)に関係なく、C#サーバーは常に4バイトにパックされます。これは正常ですか?オーバーライドできますか?

typedef [v1_enum] enum { blah... } HandlerPriority; 
struct HandlerInfo { BSTR MessageName; HandlerPriority Priority; } 

のVisual Studio C++とMIDLコンパイラはの/ Zp8デフォルトのパッキングを使用します。

構造体は次のようになります。 32ビットのビルドでは、構造体の両方のメンバーが4バイト幅であるため、パディングされません。 64ビットのビルドでは、文字列ポインタは8バイトで、列挙型は4なので、列挙型は埋め込まれます。当然ながら、これはC#クライアントが未実装のデータを送信するときに問題を引き起こします。

パディングを削除するために/ Zp4を指定することで問題を解決できますが、すべて問題なく動作しているようです。しかし、それが最善の解決策であるかどうかは疑問です。

パフォーマンス上の理由から、デフォルトのパッキングは/ Zp8と考えています。私が理解しているように、x64ではデフォルトでハードウェアがトラップし、整列例外を処理するので、少なくともクラッシュすることはありません。この特定の状況では、インタフェース機能はシステムの起動時にのみ呼び出されるため、パフォーマンス上の不利益は気にしません。私が気にしていたとしても、それをCOM/.NET interopのコストとして受け入れているかもしれません。しかし、私はそれが間違っているので少し不安です(C++の背景から来ています)。

一方、管理側のパッキングを変更することはできません。それと。

誰かから助言をいただけますか?

+0

周りにたくさんの掘り下げが終わった後... .NET DLLは実際にはどんなサイズにパックされているようです。構造体定義を含むCOMタイプライブラリをラップするtlbimp生成RCWからパッキングサイズを取得します。 tlbimpを使って、もし私が/ platform:x64と言うなら、RCWは ".pack 8"と言っていますが、私が/ platform:agnosticと言うと ".pack 4"と言います。私はしたい/プラットフォーム:無関係な、私はちょうど両方のプラットフォームで8バイトにパックしたい。解決方法は、RCWを逆コンパイルし、生成されたソースコード内の.packディレクティブを変更し、再コンパイルすることです。 –

答えて

1

生成するRCWにtlbimp.exeで指定します。コマンドラインで/ platform:x64を渡すと、RCWには ".pack 8"と表示されますが、/ platform:x86または/ platform:agnosticと言うと ".pack 4"と表示されます。

私は32ビットと64ビットの両方のプラットフォームで同じRCWを使用できるように、プラットフォームに依存しません。通常、私はAnyCPUがそれよりも面倒ですが、このプロジェクトはSDKであり、私がそれを避けることができれば、私はそのトピックに関する私の見解をユーザーに課したくありません。

x64の4バイトパッキングが高価になる可能性があるので、8バイトパッキングも必要です。 http://msdn.microsoft.com/en-us/library/aa290049%28v=vs.71%29.aspxを参照してください。

私が解決した解決策は、RCWを逆コンパイルし、生成されたソースコード内の.packディレクティブを変更し、再コンパイルすることです。

0

ここではパック属性が使用できますか?ここに私自身のコードからの例です:

[StructLayout(LayoutKind.Sequential, Pack=1)] 
public struct TOKEN_PRIVILEGES 
{ 
    public int privilegeCount; 
    public LUID_AND_ATTRIBUTES privileges; 
} 

サービスを設定するとき、私はこれを使用していると私は、Win32に、この構造体をマーシャリングする際に正確にprivilegeCountフィールドの後に権限のフィールドを揃えるパック= 1属性を使用します。

+0

構造体は.NETではなくCOMで定義されているため、提案するとおりに行うことはできません。しかし、tlbimpで生成されたRCWの逆コンパイル/変更/再コンパイルの私のソリューションは、同じことを実現します。私はポストビルドスクリプトでそれを行うので便利です。ありがとう、すべて同じ。 –

+0

私は混乱しています(しかし、それは全体の山を取るわけではありません)。上記の例では、COMで定義されている構造体は.NETではありません(構造化された構造体を使用するには構造化されたコードを使用しなければいけません)。管理側の梱包... 'を参照してください。http://support.microsoft.com/kb/922785たぶん私はあなたが達成しようとしているものとの十字架上の目的ですが、これは前にやったことでした – noonand

+0

あなたはP/Invokeを使用しています。これはあなたに低レベルの詳細を扱う必要があり、安全性。 .NETの観点からは、TOKEN_PRIVILEGES構造体はWin32 API TOKEN_PRIVILEGESとは何ら関係がありません。それらは同じバイナリレイアウトを持つことはありますが、同じタイプではありません。私はCOM相互運用機能を使用していますが、最終的に同じマーシャリングの問題に対処する必要がありますが、.NETタイプライブラリインポータ(tlbimp.exe)はそのすべての機能を果たします。まあ、すべての汚い仕事、私の質問のポイントだった。 –

1

同様の問題がありました.C#からC++にCOM経由で渡されたときに、32ビットCOMで処理されたが、64ビットで破損した構造もありました。

私はしばらく時間をかけて調査しましたが、このスレッドも役に立ちました。

TlbImp.exeはすべての構造体を4バイト整列でパックしようとしますが、特定の条件を満たす場合にのみ構造体を8バイト整列してパックするように見えます。これは、32ビットモードと64ビットモードの両方で実行されます。

しかし、それを決定するにはいくつかの奇妙なルールがあります。私が見つけたのは、構造体が4バイトの整数、列挙型、およびsafearrayしか持たない場合、それは4バイトの配列としてパックされています。そうでなければ、8バイトの配列としてパックされます。 x86の場合、構造体は64ビットの変数が存在する場合には8バイトの配列にのみパックされ、それ以外の場合は4バイトの配列としてパックされます。たぶん、別の亜種があるかもしれませんが、これは私たちの製品で見つかったものだけです。一方、デフォルトのパッキング(8バイト)で、各タイプを最小8バイトまたはメンバのサイズに揃えてパックします。

したがって、構造に8バイトのサイズのメンバーがない場合、4バイトと8バイトのアラインメントに違いはないため、32ビットモードの場合は問題ありません。しかし、存在する場合、TlbImp.exeは構造体を8バイトに整列させてパックするので、C++にもマッチします。

しかし、64ビットモードでは、構造体が8バイトのメンバを持つが、4バイトの整列としてパックされている場合があります。私たちの場合、それはSAFEARRAYを持っているときです。構造体にSAFEARRAYと32ビット(またはそれ以下)の整数だけがある場合は、x64プラットフォーム用に4バイト整列された状態でパックされます。しかし、SAFEARRAYはC++側の64ビット・ポインタなので、SAFEARRAYが8バイトのマージンになる前に領域をパディングします。私はそれがバグか機能かどうかはわかりません。

この問題を解決するために、このような4バイト整列構造の前に64ビットでC++プラグマパック命令を挿入しました。このような構造は、ildasm.exeを使用してアセンブリを逆コンパイルし、 ".pack 4"(ヒント用のCiaranに感謝)を検索することで特定しました。

IDLでこのような構造は次のようになります。

また
// some of our structures must be 4 bytes aligned because TlbImp packs them this way 
    cpp_quote("#pragma pack(push, 4)") 

    typedef [uuid("3F253C09-D7F5-3BE-9698-00CB49A7005C"), 
     helpstring("SOME structure"), 
     version(5.1)] struct SOMEINFO 
    { 
     long ItemsActive; 
     SAFEARRAY(BSTR) ItemIDs; // without pack 4 it would be 8 bytes aligned on x64 
     SAFEARRAY(long) ItemRuns; 
     SAFEARRAY(SOMEFunctions) ItemFuncList; 
     SAFEARRAY(VARIANT) ItemFactors; 
     long MIndex; 
     SAFEARRAY(VARIANT) DeltaItems; 
    } SOMEINFO; 

    cpp_quote("#pragma pack(pop)") // matches pack(push, 4) placed before the structure 

、私たちは.pack 8にそれを強制するために、この構造体に、64ビットのダミー変数、またはBSTRを追加することができますが、我々は」didnのインタフェースを変更したくありません。

したがって、私たちは基本的にTlbImpにその奇妙なロジックを従います。しかし、コードにはこのような構造がほんのわずかしかないので、私たちのためにはうまくいきます。この方法では、32ビットと64ビットの両方のCOMがすべての構造体を正しく渡します。

+0

こんにちはウラジミール。私は、自動的に生成されたinteropアセンブリの不具合に対応するために、製品の内部を変更しないことを非常に熱心でした。実際には、必要に応じて手動でinteropアセンブリを生成することを希望したこともあります。あなたは私のように頑固ではないように見えます! –

関連する問題