0

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

(構造体が管理側で定義されている場合の方法は知っていますが、私の質問は、構造体が非管理側で定義され、管理側で実装されている場合についてです。)

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

ただし、C#サーバーは、プラットフォーム(x86、x64、AnyCPU)に関係なく、常に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相互運用のコストとしてそれを受け入れるかもしれません。しかし、私はそれが間違っていると感じるので少し不安です(C ++のバックグラウンドから来ていると思います)。

逆に、管理側のパッキングを変更することが単純に不可能な場合は、私はそれを使用します。

誰かアドバイスをいただけますか?

4

3 に答える 3

3

パッキングサイズは、生成されるRCWのtlbimp.exeによって指定されます。コマンドラインで/platform:x64を渡すと、RCWは「.pack8」と表示されますが、/ platform:x86または/ platform:agnosticと言うと、「。pack4」と表示されます。

32ビットプラットフォームと64ビットプラットフォームの両方で同じRCWを使用できるように、/ platform:agnosticが必要です。通常、AnyCPUはその価値よりも厄介であることがわかりますが、このプロジェクトはSDKであり、回避できるのであれば、そのトピックに関する私の見解をユーザーに押し付けたくありません。

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

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

于 2012-03-19T02:15:20.860 に答える
2

Pack属性はここで機能しますか?これが私自身のコードからの例です:

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

これはサービスの構成時に使用し、Pack = 1属性を使用して、この構造体をWin32にマーシャリングするときにprivilegeCountフィールドの直後に特権フィールドを配置します。

于 2012-03-13T17:39:15.257 に答える
2

同様の問題が発生しました。一部の構造体は、COMを介してC#からC ++に渡されると、32ビットCOMで機能しましたが、64ビットでは破損していました。

調査に時間を費やしましたが、このスレッドも役に立ちました。

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

しかし、それを決定するためのいくつかの奇妙なルールがあります。私が見つけたのは、x64の場合、構造体に4バイトの整数、enum、safearrayしかない場合は、4バイトの整列としてパックされ、そうでない場合は8バイトのalingとしてパックされます。x86の場合、構造体は、64ビット変数が存在する場合にのみ8バイトに整列されてパックされます。それ以外の場合は、4バイトに整列されてパックされます。他のバリエーションもあるかもしれませんが、これは私たちの製品で見つけたものだけです。

一方、C ++は、デフォルトのパッキング(8バイト)を使用して、各タイプを最小8バイトまたはメンバーのサイズで整列してパックします。

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

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

この問題を修正するために、64ビットでこのような4バイトに整列された構造の前に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

または、この構造に64ビットのダミー変数またはBSTRを追加して、強制的に.pack 8に入れることもできますが、インターフェイスを変更したくありませんでした。

したがって、基本的にはTlbImpをその奇妙なロジックに従っています。しかし、私たちのコードにはそのような構造がいくつかあるので、それは私たちにとっては問題ありません。このようにして、32ビットと64ビットの両方のCOMがすべての構造を正しく渡します。

于 2013-03-28T23:47:07.160 に答える