とりあえず、今日から夏休みです。
取得するのに、相当紆余曲折がありましたが…
(というか、話せば話すほど社長は僕は水と油だということを感じる。色々良くないことも多くなってきたし、試用期間までが潮時、って言うか、それすらも持たないだろうな…)
…てな話はともかく、今回は番外編というか、本当に備忘録のような内容です。
ネイティブC++の関数を呼び出すとき、ちゃんとC#側で対応する引数の型を指定しないといけないけど、主な型の対応はこんな感じ。(とりあえず、関係しそうなものだけ)
C++ | C# |
WORD | short |
DWORD | uint |
LPSTR | string |
LPWSTR | |
LPCSTR | |
LPCWSTR | |
void* | IntPtr |
HANDLE | |
LPARAM | |
WPARAM | |
HRESULT | int |
MMRESULT |
ネイティブC++関数呼び出し時の引数指定
MSDNなどに記載してあるネイティブC++関数呼び出し時の汎用的なルール。
※あくまで「汎用的」であって、これに当てはまらないケースも当然ある。
C++での引数指定 | C#での引数指定 |
純粋な「型名」のみ(例:DWORD、WORD) | 対応する型の「値渡し」(ref、outキーワードなし) |
接頭語「LP」+「型名」(例:LPWORD、LPDWORD) | 対応する型の「参照渡し」(ref、outキーワードあり) |
ポインタ渡し(例:LPWORD*、LPDWORD*) | IntPtr型渡し |
配列のポインタ
関数に構造体配列のポインタを渡す場合、例えばC++だと
STRUCTURE[] structure = new STRUCTURE[2]; func(&structure);
みたいな書き方でOKだが、C#で同じことをやろうとして、例えば
STRUCTURE[] structure = new STRUCTURE[2]; int str_size = Marshal.SizeOf(structure[0]) + Marshal.SizeOf(structure[1]); IntPtr ptr_structure = Marshal.AllocCoTaskMem(str_size); Marshal.StructureToPtr(structure, ptr_structure, false); func(ptr_structure);
とやっても、コンパイルエラーになってしまう。
で、これをうまくやる方法を調べたのだが、結局は原始的な方法かも知れないが、1要素目のptr_structureの値と各要素のサイズを計算して
STRUCTURE[] structure = new STRUCTURE[2]; int str_size = Marshal.SizeOf(structure[0]) + Marshal.SizeOf(structure[1]); IntPtr ptr_structure = Marshal.AllocCoTaskMem(str_size); Marshal.StructureToPtr(structure[0], ptr_structure, false); Marshal.StructureToPtr(structure[1], (IntPtr)((int)ptr_structure + Marshal.SizeOf(structure[0])), false);
みたいにするしか無いみたい。(もちろん、要素数が多いならループなどを使用するべき。)
構造体の「union」をC#で実装する方法
例えば、DirectSoundなどで使用する、WAVEFORMATEXTENSIBLE構造体は、
typedef struct { WAVEFORMATEX Format; union { WORD wValidBitsPerSample; WORD wSamplesPerBlock; WORD wReserved; } Samples; DWORD dwChannelMask; GUID SubFormat; } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
となっている。
この構造体の「union」に該当するキーワードはC#にはないが、「StructLayout」と「FieldOffset」の指定をうまく使うことで、構造体のunionと同じことを行うことが出来る。
// FieldOffsetを自分で指定するので、StructLayoutはLayoutKind.Explicitにしないといけない。 [StructLayout(LayoutKind.Explicit)] private struct WAVEFORMAT_EXTENSIBLE { // wValidBitsPerSample,wSamplesPerBlock,wReservedのFieldOffsetを同じにして「union」と同じことを実現している。 [FieldOffset(0)] public WAVEFORMATEX Format; [FieldOffset(20)] public ushort wValidBitsPerSample; [FieldOffset(20)] public ushort wSamplesPerBlock; [FieldOffset(20)] public ushort wReserved; [FieldOffset(22)] public uint dwChannelMask; [FieldOffset(26)] public Guid SubFormat; }
…すいません。ちょっと手抜き感が否めないですが、今回はこれでご勘弁を。