既定のパッキングは、次の MIDL コマンド ライン スイッチ リファレンスによると、8 バイト境界に沿っています。
/Zp スイッチ @ MSDN (MIDL 言語リファレンス)
pack の値が変更された場合、コードの他の部分が最初に壊れる可能性が高くなります。これは、IDL ファイルが通常事前にコンパイルされているためです。 C スコープ#pragma pack
をいじって、デフォルト状態を復元するのを忘れる可能性は非常にまれです)。
設定を変更する正当な理由がある場合は、pragma pack
ステートメントを使用してパッキングを明示的に設定できます。
pragma 属性 @ MSDN (MIDL 言語リファレンス)
デフォルトのパッキングに干渉するような設定を変更した当事者がいないことは、非常に幸運です。うまくいかないことはありますか?はい、誰かがわざわざデフォルトを変更した場合。
IDL ファイルを使用する場合、詳細は通常、typelib (.tlb) にコンパイルされます。同じ typelib を使用する場合、サーバーとクライアントの両方でプラットフォームが同じであると想定されます。/Zp
これは、特定の値が特定の非 x86 または 16 ビット ターゲットに対して失敗するため、スイッチの脚注で提案されています。32bit <-> 64bit 変換のケースもあり、期待が崩れる可能性があります。残念ながら、さらに多くのケースがあるかどうかはわかりませんが、デフォルトは最小限の手間で機能します.
C# と VB には、.tlb 内の情報を処理する固有の動作がありません。代わりに、通常、tlbimp のようなツールを使用して、COM 定義を .NET から使用できる定義に変換します。C#/VB.NET と COM のクライアントおよびサーバーの間ですべての期待が成功するかどうかを確認することはできません。ただし、その設定でコンパイルされた IDL から作成された .tlb を参照する場合、8 以外の特定のプラグマ設定を使用しても機能することを確認できます。デフォルトのプラグマ パックを使用することはお勧めしませんが、実際の例を参照として使用する場合は、次の手順を実行してください。C++ ATL プロジェクトと C# プロジェクトを作成して確認しました。
C++ 側の命令は次のとおりです。
- Visual Studio 2010 の既定の設定でSampleATLProjectという ATL プロジェクトを作成しました。フィールドは変更しませんでした。これにより、dll プロジェクトが作成されます。
- プロジェクトをコンパイルして、適切な C 側インターフェイス ファイル (SampleATLProject_i.c および SampleATLProject_i.h) が作成されていることを確認します。
- プロジェクトに呼び出される ATL Simple Object を追加しました
SomeFoo
。繰り返しますが、デフォルトは変更されていません。CSomeFoo
これにより、プロジェクトに追加されるクラスが作成されます。
- SampleATLProject をコンパイルします。
- SampleATLProject.idl ファイルを右クリックし、MIDL 設定で、Struct Member Alignment を 4 バイト (/Zp4) に設定しました。
- SampleATLProject をコンパイルします。
- IDL を変更して、'BarStruct' という構造体定義を追加しました。これには、MIDL uuid 属性を持つ C スタイルの構造体定義と、構造体定義を参照するライブラリ セクションのエントリを追加する必要がありました。以下のスニペットを参照してください。
- SampleATLProject をコンパイルします。
- クラス ビューから を右クリックして、theBarという [in] パラメータとして を受け取る
ISomeFoo
というメソッドを追加しました。FooIt
struct BarStruct
- SampleATLProject をコンパイルします。
- SomeFoo.cpp に、構造体のサイズを出力し、詳細を含むメッセージ ボックスを表示するコードを追加しました。
これが ATL プロジェクトの IDL です。
import "oaidl.idl";
import "ocidl.idl";
[uuid(D2240D8B-EB97-4ACD-AC96-21F2EAFFE100)]
struct BarStruct
{
byte a;
int b;
byte c;
byte d;
};
[
object,
uuid(E6C3E82D-4376-41CD-A0DF-CB9371C0C467),
dual,
nonextensible,
pointer_default(unique)
]
interface ISomeFoo : IDispatch{
[id(1)] HRESULT FooIt([in] struct BarStruct theBar);
};
[
uuid(F15B6312-7C46-4DDC-8D04-9DEA358BD94B),
version(1.0),
]
library SampleATLProjectLib
{
struct BarStruct;
importlib("stdole2.tlb");
[
uuid(930BC9D6-28DF-4851-9703-AFCD1F23CCEF)
]
coclass SomeFoo
{
[default] interface ISomeFoo;
};
};
クラス内のCSomeFoo
の実装は次のとおりですFooIt()
。
STDMETHODIMP CSomeFoo::FooIt(struct BarStruct theBar)
{
WCHAR buf[1024];
swprintf(buf, L"Size: %d, Values: %d %d %d %d", sizeof(struct BarStruct),
theBar.a, theBar.b, theBar.c, theBar.d);
::MessageBoxW(0, buf, L"FooIt", MB_OK);
return S_OK;
}
次に、C# 側で:
SampleATLProject のデバッグまたは目的の出力ディレクトリに移動し、C++ プロジェクト出力の一部として生成された .tlb ファイルで tlbimp.exe を実行します。以下は私のために働いた:
tlbimp SampleATLProject.tlb /out:Foo.dll /名前空間:SampleATL.FooStuff
次に、C# コンソール アプリケーションを作成し、Foo.dll への参照をプロジェクトに追加しました。
- References フォルダーで、[Properties for] に移動し、[ Embed Interop Types ]を false に設定して
Foo
オフにします。
SampleATL.FooStuff
tlbimp に与えられた名前空間を参照するために using ステートメントを追加し、[STAThread]
属性を追加しMain()
(COM アパートメント モデルはインプロセス消費のために一致する必要があります)、COM コンポーネントを呼び出すコードをいくつか追加しました。
Tlbimp.exe (タイプ ライブラリ インポーター) @ MSDN
そのコンソール アプリのソース コードを次に示します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SampleATL.FooStuff;
namespace SampleATLProjectConsumer
{
class Program
{
[STAThread]
static void Main(string[] args)
{
BarStruct s;
s.a = 1;
s.b = 127;
s.c = 255;
s.d = 128;
ISomeFoo handler = new SomeFooClass();
handler.FooIt(s);
}
}
}
最後に、それが実行され、次の文字列が表示されたモーダル ポップアップが表示されます。
Size: 12, Values: 1 127 255 128
プラグマ パックを確実に変更できるようにするため (4/8 バイト パッキングが使用される最も一般的なアラインメントであるため)、次の手順に従って 1 に変更しました。
- C++ プロジェクトに戻り、SampleATLProject.idl のプロパティに移動して、構造体メンバーの配置を 1 (/Zp1) に変更しました。
- SampleATLProject を再コンパイルする
- 更新された .tlb ファイルを使用して tlbimp を再度実行します。
- への .NET ファイル参照に警告アイコンが表示されます
Foo
が、参照をクリックすると消える場合があります。そうでない場合は、C# コンソール プロジェクトへの参照を削除して再度追加し、新しい更新されたバージョンが使用されていることを確認してください。
ここから実行したところ、次の出力が得られました。
Size: 12, Values: 1 1551957760 129 3
それは変だ。ただし、SampleATLProject_i.h の C レベル プラグマを強制的に編集すると、正しい出力が得られます。
#pragma pack(push, 1)
/* [uuid] */ struct DECLSPEC_UUID("D2240D8B-EB97-4ACD-AC96-21F2EAFFE100") BarStruct
{
byte a;
int b;
byte c;
byte d;
} ;
#pragma pack(pop)
SampleATLProject はここで再コンパイルされ、.tlb または .NET プロジェクトに変更はなく、次の結果が得られます。
Size: 7, Values: 1 127 255 128
に関してIDispatch
は、クライアントが遅延バインドされているかどうかによって異なります。レイト バインド クライアントは、型情報側を解析し、IDispatch
重要な型の適切な定義を識別する必要があります。とのドキュメントは、フィールドが必要な情報を提供することを考えると、それが可能であることITypeInfo
をTYPEATTR
示唆しています。cbAlignment
問題が発生した場合やバージョン間でパックの期待値を変更する必要がある場合にデバッグするのが面倒になるため、ほとんどの場合、デフォルトを変更したり変更したりすることは決してないと思います。また、構造体は通常、 を使用できる多くのスクリプト クライアントではサポートされていませんIDispatch
。多くの場合、IDLoleautomation
キーワードによって管理される型のみがサポートされると予想できます。
IDispatch インターフェイス @ MSDN
IDispatch::GetTypeInfo @ MSDN
ITypeInfo インターフェイス @ MSDN
TYPEATTR 構造体 @ MSDN
oleautomation キーワード @ MSDN