デフォルトの梱包は、ここではMIDLコマンドラインスイッチのリファレンスによると、8バイト境界に沿っている:あなたのコードの
/Zp switch @ MSDN (MIDL Language Reference)
その他のパーツパック場合は最初のブレークする可能性が高くなります値は変更されます。IDLファイルは通常事前にコンパイルされているため、誰かが故意にMIDLに与えられたコマンドラインスイッチを変更することはまれです(ただし、Cスコープである#pragma pack
を忘れて、デフォルトの状態に戻す)。
設定を変更する必要がある場合は、pragma pack
ステートメントで明示的にパッキングを設定できます。
pragma Attribute @ MSDN (MIDL Language Reference)
それは何の当事者がデフォルトのパッキングを妨害する任意の設定を変更していないことはかなり幸運です。それは間違っていますか?はい、誰かがデフォルトを変更するために道を離れる場合。
IDLファイルを使用する場合、詳細は通常typelib(.tlb)にコンパイルされ、同じタイプライブラリを使用している場合はプラットフォームがサーバとクライアントの両方で同じとみなされます。これは/Zp
スイッチの脚注で示唆されています。特定の値が特定の非x86または16ビットターゲットに対して失敗するためです。また、32ビットの< - > 64ビットの変換ケースがあり、期待しないことがあります。残念ながら、そこにもっと多くのケースがあるかどうかは分かりませんが、デフォルトはちょっとした騒ぎで動作します。
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)。
SomeFoo
というATLシンプルオブジェクトをプロジェクトに追加しました。ここでも、デフォルトは変更されていません。これにより、プロジェクトに追加されるCSomeFoo
というクラスが作成されます。
- SampleATLProjectをコンパイルします。
- SampleATLProject.idlファイルを右クリックし、MIDL設定の下で、構造体メンバーのアライメントを4バイト(/ Zp4)に設定します。
- SampleATLProjectをコンパイルします。
- IDLを変更して、 'BarStruct'という構造体定義を追加しました。これには、MIDL uuid属性を持つCスタイルの構造体定義と、構造体定義を参照するライブラリセクションのエントリが追加されました。以下のスニペットを参照してください。
- SampleATLProjectをコンパイルします。クラスビューから
は、私が
ISomeFoo
上で右クリックし、
theBarと呼ばれる[中]パラメータとして
struct BarStruct
を取る
FooIt
というメソッドを追加しました。
- SampleATLProjectをコンパイルします。
- SomeFoo.cppでは、構造体のサイズを出力し、詳細を含むメッセージボックスをスローするコードをいくつか追加しました。
ここは私のATLプロジェクトのIDLです。 CSomeFoo
クラスインサイド
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;
};
};
、ここ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 /namespace:SampleATL.FooStuff
次に、私はC#コンソールアプリケーションを作成し、そしてへFoo.dllへの参照を追加しましたプロジェクト。
- Referencesフォルダで、
Foo
のプロパティに移動し、埋め込みInteropタイプをfalseに設定してオフにします。
SampleATL.FooStuff
をtlbimpに指定してusingステートメントを追加し、[STAThread]
属性をMain()
に追加しました(COMアパートメントモデルはインプロセス消費のために一致する必要があります)。COMコンポーネントを呼び出すコードが追加されました。ここTlbimp.exe (Type Library Importer) @ 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);
}
}
}
最後に、それが実行され、私は次の文字列を持つモーダルポップアップが表示され得る:4/8バイトパッキングがそうであるように(
Size: 12, Values: 1 127 255 128
は、プラグマパックの変更を行うことができることを確認します使用される最も一般的なアライメント)、私はこれを1に変更するには、次の手順に従っ:
- 私はC++プロジェクトに戻り、SampleATLProject.idlのプロパティに行き、1(/ Zp1の)に構造体メンバのアライメントを変更しました。
- サンプルを再コンパイルするサンプルプロジェクト
- 更新された.tlbファイルでtlbimpを再度実行します。
- .NETファイルリファレンスの警告アイコンが
Foo
に表示されますが、参照をクリックすると警告アイコンが表示されなくなることがあります。そうでない場合は、C#コンソールプロジェクトへの参照を削除して再追加して、新しい更新版を使用していることを確認することができます。
私はここからそれを実行し、この出力ました:
Size: 12, Values: 1 1551957760 129 3
奇妙です。しかし、SampleATLProject_i.hのCレベルのプラグマを強制的に編集すると、正しい出力が得られます。 SampleATLProjectが、ここでは.TLBまたは.NETプロジェクトへの変更を再コンパイルされていない、と私たちは、次の取得
#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)
:
Size: 7, Values: 1 127 255 128
IDispatch
に関しては、それはあなたのクライアントは遅延バインディングであるかどうかに依存します。遅延バインドされたクライアントは、タイプ情報側をIDispatch
と解析し、重要でないタイプの適切な定義を識別する必要があります。 ITypeInfo
とTYPEATTR
のドキュメントは、cbAlignment
フィールドが必要な情報を提供することを前提として可能であることを示しています。私は、ほとんどがデフォルトに反して行ったり、デフォルトに反することがないと思っています。これは、状況が間違っていたり、バージョン間でパックの期待値が変更されなければデバッグするのが面倒です。また、構造体は通常、IDispatch
を消費する多くのスクリプトクライアントによってサポートされていません。 IDL oleautomation
キーワードで管理されているタイプのみがサポートされることがよくあります。
IDispatch interface @ MSDN
IDispatch::GetTypeInfo @ MSDN
ITypeInfo interface @ MSDN
TYPEATTR structure @ MSDN
oleautomation keyword @ MSDN