E-MU 1616M CardBus が刺さってる Panasonic Let's Note CF-S10E がスリープからの復帰に失敗する (その 4)
Unix C がちょっと書けるだけの俺による令和最新式 C++/COM プログラミング入門
なんと CtHelper.exe から呼び出される他のドライバ DLL はすべて Common Object Model (COM) 経由になってるのだ。
こんなん C ABI で DLL 直接リンクした方がオーバーヘッドも無くコードもシンプルになりそうなもんだが、このドライバって謎に 32bit と 64bit が混在してるので COM 経由を強いられるジレンマ。 まだ当時は 64bit cleanness に注意してコード書くなんて一般的じゃなかったからね、人類の大多数は 16bit → 32bit で何も学ばなかったから仕方がないのである。
おそらく 64bit への移植検証が中途半端のままディスコンになったんだろう、ASIOドライバも 64bit バイナリあるのに 32bit の方がインストールされるし。 Emulator X3 64bit を単体で起動すると ASIO 設定が有効にならない原因もこのあたりにあるのかもしれん、まぁ VST では問題ないのでこの問題は後回しでいい。
そもそも COM って何だよ
手塚治虫の出版してた雑誌でもないしトップレベルドメインでも無い COM とは何ぞやと問われてもこいつは
- Distributed COM (DCOM) … 分散コンピューティング の基盤となる Remote Procedure Call (RPC)
- Object Linking Embedding (OLE) … アプリケーションから他アプリケーションファイル形式を埋め込みして相互利用
- ActiveX … Internet Explorer による Web ベースアプリケーション技術
のベースとなる技術で、これらがごっちゃにして語られてしまうことが多いから正しい回答はなかなか出てこなさそうである。
本来の意図としては
- クッソ貧弱な C ABI を使わず
- コンパイラ実装による C++ ABI の非互換性問題を回避し
- 16bit と 32bit (現在では さらに 64bit) の垣根を超えたライブラリの再利用を実現し
- C/C++ のクラスを Visual Basic などの別の言語からも利用できるようにする相互運用性
が目的だったはずである、いや俺 Windows Internals とか Inside OLE とか Windows プログラマの聖書を一切読んでねえから開発者は違う事言ってるかもしんねえ。
なんせ Unix の聖書クラスの名著すら読んでねえからな、俺が頼りにするのはいつだってソースコードとマニュアルなのである。
しかしこれってやってることはコードインジェクションなわけで、攻撃に利用し放題の当たり前田のクラッカーへのボーナスタイムが生まれたのがこの30年なのである。 そんなこんなでプラットフォーム非依存な通信技術として XML-RPC や SOAP が生まれ XML とかいうウンコへの憎悪が極まった今では JSON-RPC を使えという事になる。
ただもはや Windows の基盤技術なのでそう易々と捨てられるわけでもなく今でも PowerShell でも New-Object -ComObject を使ってコード書く羽目になるのである、地獄である。
ちなみに COM にはオープンソースの実装もあってそれが Mozilla プロジェクトによる XPCOM である、こっちも捨てられたけど。
C++ から COM を使うのは非常にめんどくさい
本末転倒であるのだがマジでめんどくさい、一例を上げると Windows Scripting Host (WSH) では WScript クラスの CreateObject メソッド や ActiveXObject 関数を使えば文字列からオブジェクトのインスタンスを生成できる。
var wsh = WScript.CreateObject("WScript.Shell");
var fso = WScript.CreateObject("Scripting.FileSystemObject");
fso.DeleteFolder(wsh.ExpandEnvironmentStrings("%TEMP%\\foo"), true);
上記の JScript コードでは環境変数 TEMP で指定されたフォルダ以下の foo フォルダを削除する処理の為に
- WScript.Shell
- Scripting.FileSystemObject
という 2 つのオブジェクトのインスタンスを生成している。
これも実のところ本末転倒で JScript は ECMAScript 3 (ES3) との互換性を保つため、頻繁に使うシェル機能やファイルシステム操作であっても ES3 に無い機能は COM 経由でやる必要があるという地獄変なのだ。 書き手にこんな苦行を強いる割に文法レベルでの非互換性(まるで Visual Basic のような引数付きプロパティ)をブッコんでしまったので Node.js あたりにすんなり移行できないのが最高のお笑いである。
まぁでもそこらの事務屋さんが「できる! MS Office」読んで書いた VBA マクロだって COM のお陰で成り立ってるのだ、でも書き手はその存在を意識することすらないわけで、COM は C/C++ 以外の他言語から見たら神といえよう。
しかし C/C++ の場合 COM は言語組込みではないので、いろいろおまじない(禁句)が必要で非常にめんどくさいのである。
まず初期化処理に CoInitialize あるいは CoInitializeEx そして終了時に CoUninitialize を呼ぶ必要がある。
int
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
CoInitialize(NULL);
...
CoUninitialize();
}
そしてオブジェクトのインスタンス生成には CoCreateInstance 関数を使うのだが、文字列による指定でなく
- CLSID(Class ID)
- REFIID(Reference Interface ID)
と 2 つの GUID を指定する必要がある、めんどくせえ。
static CLSID clsid = { 0x285B5C41, 0xCDBF, 0x11D3, { 0x8D, 0xD4, 0x00, 0xA0, 0xC9, 0x8E, 0x9F, 0xB1 } };
static IID resiid = { 0xC3ED4631, 0x2768, 0x43E0, { 0xBE, 0xBC, 0x1C, 0x39, 0x99, 0x89, 0xE6, 0x66 } };
int
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
LPVOID instance;
CoInitialize(NULL);
if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, resiid, &instance) == S_OK) {
...
}
CoUninitialize();
}
CtHelper.exe が呼び出してる COM オブジェクトを追跡する
Ghidra はポンコツなのでこの CoCreateInstance の引数となる構造体型 CLSID/IID (どちらも GUID のエイリアス) は既知であるにも関わらず .data セクションにグローバル変数であることを示すラベルを貼るだけで、未知のデータとしか解釈してくれないのだ。
DAT_01001510 COM XREF[2]: FUN_01002532:0100257e(*),
FUN_010034ee:01003533(*)
01001510 41 ?? 41h A
01001511 5c ?? 5Ch \
01001512 5b ?? 5Bh [
01001513 28 ?? 28h (
01001514 bf ?? BFh
01001515 cd ?? CDh
01001516 d3 ?? D3h
01001517 11 ?? 11h
01001518 8d ?? 8Dh
01001519 d4 ?? D4h
0100151a 00 ?? 00h
0100151b a0 ?? A0h
0100151c c9 ?? C9h
0100151d 8e ?? 8Eh
0100151e 9f ?? 9Fh
0100151f b1 ?? B1h
このままだと非常に可読性が悪いので明示的に型を指定する。
- Listing (疑似アセンブラ) 画面 … ラベルを右クリック → Data → Choose Data Type
- Decompile (疑似 C/C++) 画面 … 変数を右クリック → Retype Global
を選択すると Data Type Chooser Dialog が開くのでそこで CLSID/IID を指定してやればよい。
CLSID_01001510 XREF[2]: FUN_01002532:0100257e(*),
FUN_010034ee:01003533(*)
01001510 41 5c 5b CLSID
28 bf cd
d3 11 8d
01001510 41 5c 5b 28 ulong 285B5C41h Data1 XREF[2]: FUN_01002532:0100257e(*),
FUN_010034ee:01003533(*)
01001514 bf cd ushort CDBFh Data2
01001516 d3 11 ushort 11D3h Data3
01001518 8d d4 00 a0 c9 uchar[8] 8Dh,D4h Data4
8e 9f b1
01001518 8d uchar 8Dh [0]
01001519 d4 uchar D4h [1]
0100151a 00 uchar 00h [2]
0100151b a0 uchar A0h [3]
0100151c c9 uchar C9h [4]
0100151d 8e uchar 8Eh [5]
0100151e 9f uchar 9Fh [6]
0100151f b1 uchar B1h [7]
このように構造体のメンバ毎に値を表示してくれるようになる。
なお困ったことにデータにもかかわらず関数と誤判定されるケースが発生する。
LAB_01001460 XREF[2]: FUN_01002532:01002576(*),
FUN_010034ee:0100352b(*)
01001460 31 46 ed XOR dword ptr [ESI + -0x13],EAX
01001463 c3 RET
01001464 68 ?? 68h h
01001465 27 ?? 27h '
01001466 e0 ?? E0h
01001467 43 ?? 43h C
01001468 be ?? BEh
01001469 bc ?? BCh
0100146a 1c ?? 1Ch
0100146b 39 ?? 39h 9
0100146c 99 ?? 99h
0100146d 89 ?? 89h
0100146e e6 ?? E6h
0100146f 66 ?? 66h f
このままだとラベル上で右クリックしても Data がメニューに出てこないのだけど
- 8086 命令と誤判定されてる部分のニーモニックの上で右クリック → Clear → Clear Code Byte
としてやれば未知のデータに戻せる。
DAT_01001460 XREF[2]: FUN_01002532:01002576(*),
FUN_010034ee:0100352b(*)
01001460 31 ?? 31h 1
01001461 46 ?? 46h F
01001462 ed ?? EDh
01001463 c3 ?? C3h
01001464 68 ?? 68h h
01001465 27 ?? 27h '
01001466 e0 ?? E0h
01001467 43 ?? 43h C
01001468 be ?? BEh
01001469 bc ?? BCh
0100146a 1c ?? 1Ch
0100146b 39 ?? 39h 9
0100146c 99 ?? 99h
0100146d 89 ?? 89h
0100146e e6 ?? E6h
0100146f 66 ?? 66h f
そしたらさっきと同じ手順で型を再指定すればいい。
IID_01001460 XREF[2]: FUN_01002532:01002576(*),
FUN_010034ee:0100352b(*)
01001460 31 46 ed IID
c3 68 27
e0 43 be
01001460 31 46 ed c3 ulong C3ED4631h Data1 XREF[2]: FUN_01002532:01002576(*),
FUN_010034ee:0100352b(*)
01001464 68 27 ushort 2768h Data2
01001466 e0 43 ushort 43E0h Data3
01001468 be bc 1c 39 99 uchar[8] BEh,BCh,1Ch,"9",99h,89 Data4
89 e6 66
01001468 be uchar BEh [0]
01001469 bc uchar BCh [1]
0100146a 1c uchar 1Ch [2]
0100146b 39 uchar '9' [3]
0100146c 99 uchar 99h [4]
0100146d 89 uchar 89h [5]
0100146e e6 uchar E6h [6]
0100146f 66 uchar 'f' [7]
この作業の結果、おぼろげながら見えてきたんです CLSID/REFIID が。
- CLSID …
{285B5C41-CDBF-11D3-8DD4-00A0C98E9FB1} - REFIID …
{C3ED4631-2768-43E0-BEBC-1C399989E666}
CLSID の値でレジストリ検索すると以下のエントリが見つかる。
[HKEY_CLASSES_ROOT\CTDCIFCE.DevConDevice]
@="DevConDevice Class"
[HKEY_CLASSES_ROOT\CTDCIFCE.DevConDevice\CLSID]
@="{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}"
[HKEY_CLASSES_ROOT\CTDCIFCE.DevConDevice\CurVer]
@="CTDCIFCE.DevConDevice.1"
[HKEY_CLASSES_ROOT\CTDCIFCE.DevConDevice.1]
@="DevConDevice Class"
[HKEY_CLASSES_ROOT\CTDCIFCE.DevConDevice.1\CLSID]
@="{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}"
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}]
@="DevConDevice Class"
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}\InprocServer32]
@="C:\\Windows\\SysWow64\\CTDCIFCE.DLL"
"ThreadingModel"="Apartment"
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}\ProgID]
@="CTDCIFCE.DevConDevice.1"
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}\TypeLib]
@="{DA627420-2513-11D6-BC8A-00AA00A3AF32}"
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{285B5C41-CDBF-11d3-8DD4-00A0C98E9FB1}\VersionIndependentProgID]
@="CTDCIFCE.DevConDevice"
CTDCIFCE.DLL は %SYSTEMROOT%\SysWow64 以下に置かれてるので 32bit DLL でんな、つーか CtHelper.exe も 32bit アプリである。
プロセス内 COM サーバー (InprocServer32) かつスレッドモデル分離モデルはシングルスレッドとなる。
CoInitializeEx や CoCreateInstance でこれと矛盾する指定をしたらエラーになるはずである、知らんけど。
そんでレジストリから DLL とクラス の名前までは判っても、メンバ関数とその引数とか型までは判らんのだ。 C/C++ は静的型付け言語なのでコンパイル時にクラスとメソッドが定義されたヘッダファイルが無いと、ヒープ領域上の実行コードを関数ポインタとして実行する羽目になり可読性が死ぬのでな。
タイプライブラリとインタフェース記述言語 (IDL)
そんじゃインタフェースのメソッドをどうやって特定し実行するのかというと、ヘッダファイルを作成するのである。
ただし Visual C/C++ はタイプライブラリという .tlb を拡張子に持つバイナリファイルを #import ディレクティブで読み込むと自動的にヘッダファイルが生成される言語拡張がされているので、開発者キットがあれば意識することは無い。 しかし今回のケースはサードパーティーの製品で Windows SDK(Platform SDK) に含まれるはずもなくこの手も使えないのである。
じゃあどうすんだとなるが DLL からインタフェース記述言語 (IDL) を抽出し、そいつを IDL コンパイラ使ってタイプライブラリあるいはヘッダファイルを生成することが可能なのだ。
IDL ときくと突然 CORBA フフフフーン と鼻歌歌ってしまう俺であるが、COM の ILD は通称 MIDL という独自仕様で Common Object Request Broker Architecture (CORBA) の IDL とは似て非なるものである。 CORBA で思い出すのは新人時代に半年ほど会社に住む羽目になったとある大炎上案件なのだが昔話は割愛、当時コンビニで買った大量の下着類を使い切るのに 20 年かかったとだけ。
DLL から IDL ファイルを抽出するには俺の記憶が正しければ COM/OLE Viewer なるものが Visual Studio に付属してたはずなのだが、Community のダウンロードにそれらしきものが見当たらないのである。 しばし探し回ったらどうやら Windows SDK (旧名 Windows Platform SDK) の方に含まれてんのね、スタートメニューにショートカットくらい作れやクソが。
取得した IDL の抜粋がこちら、クソ長いので雰囲気だけ感じとればいい。
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: CTDCIFCE.DLL
...
library CTDCIFCELib
{
...
// Forward declare all types defined in this typelib
...
interface IDevConDevice2;
...
[
uuid(285B5C41-CDBF-11D3-8DD4-00A0C98E9FB1),
helpstring("DecConDevice Class")
]
coclass DevConDevice {
[default] interface IDevConDevice;
interface IDevConDevice2;
};
[
odl,
uuid(B35133C1-CDBE-11D3-8DD4-00A0C98E9FB1),
helpstring("IDevConDevice Interface")
]
interface IDevConDevice : IUnknown {
[helpstring("method GetNumDevices")]
HRESULT _stdcall GetNumDevices(long* pdwNumDevices);
[helpstring("method GetFirstDevice")]
HRESULT _stdcall GetFirstDevice(long* pdwDevice);
[helpstring("method GetNextDevice")]
HRESULT _stdcall GetNextDevice(
long dwCurrentDevice,
long* pdwNextDevice);
[helpstring("method GetDeviceInfo")]
HRESULT _stdcall GetDeviceInfo(
long dwDevice,
long* pDeviceInfo);
...
};
[
odl,
uuid(C3ED4631-2768-43E0-BEBC-1C399989E666),
helpstring("IDevConDevice2 Interface")
]
interface IDevConDevice2 : IUnknown {
[helpstring("method GetNumDevices")]
HRESULT _stdcall GetNumDevices(long* pdwNumDevices);
[helpstring("method GetFirstDevice")]
HRESULT _stdcall GetFirstDevice(long* pdwDevice);
[helpstring("method GetNextDevice")]
HRESULT _stdcall GetNextDevice(
long dwCurrentDevice,
long* pdwNextDevice);
[helpstring("method GetDeviceInfo")]
HRESULT _stdcall GetDeviceInfo(
long dwDevice,
long* pDeviceInfo);
...
};
...
CLSID に対応するクラスが DevConDevice クラスで、REFIID に対応するインタフェースが IDevConDevice2 であることが判った。
CoCreateInstance で返されるのはこの IDevConDevice2 なのでここで定義されてるメソッドを呼べばいい。
ただ Ghidra の出力からはどのメソッド呼んでるかはちょっとよくわからない、なんせ Ghidra はインタフェース定義を持ってないので疑似 C/C** ソース上ではヒープ領域上の実行コードを関数ポインタとして呼んでるようにしか出力されないからね。
HVar1 = CoCreateInstance(&CLSID_01001510,(LPUNKNOWN)0x0,1,&IID_01001460,&local_230);
if (-1 < HVar1) {
local_234 = 0;
(**(code **)(*local_230 + 0xc))(local_230,&local_234);
...
引数が 2 つあるけど C++ クラスの非静的メソッドの呼び出しになるから第一引数は this になるはずで実際にインタフェースの先頭ポインタ渡してる、しかし IDL 読んでも引数が 1 つのメソッドが多すぎてこれじゃどれ呼んでるのかわからんのだ。
先頭からのオフセットが 0xc(12) になってるから 32bit の 4 バイトアライメントで GetDeviceInfo を呼んでると思ったのだが引数の数が合わねえ。
困ったことに俺は C++ 全く知らんから vtable 周りの知識がないし実際にコード書いて Ghidra で喰わせてオフセットが 0xc になるか確認せんとならん。
IDL をタイプライブラリにコンパイルする
とりあえずその試行錯誤をする前に IDL をタイプライブラリに変換しておく必要がある。 Windows SDK に midl.exe という IDL コンパイラがあるのでそいつに喰わせてやればいい、でもなんかエラー出ますね…
C:\Users\tnozaki\Desktop>midl CTDCIFCE.IDL
Microsoft (R) 32b/64b MIDL Compiler Version 8.01.0628
Copyright (c) Microsoft Corporation. All rights reserved.
Processing .\CTDCIFCE.IDL
CTDCIFCE.IDL
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\oaidl.idl
oaidl.idl
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\objidl.idl
objidl.idl
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\unknwn.idl
unknwn.idl
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\shared\wtypes.idl
wtypes.idl
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\shared\wtypesbase.idl
wtypesbase.idl
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\shared\basetsd.h
basetsd.h
Processing C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\shared\guiddef.h
guiddef.h
.\CTDCIFCE.IDL(187) : error MIDL2025 : syntax error : expecting a type specification near "__MIDL_IWinTypes_0009"
.\CTDCIFCE.IDL(188) : error MIDL2026 : cannot recover from earlier syntax errors; aborting compilation
エラーの出る行を見てみると __MIDL_IWinTypes_0009 が typedef される前に使われとる。
typedef struct tag_RemotableHandle {
long fContext;
__MIDL_IWinTypes_0009 u;
} _RemotableHandle;
typedef union tag__MIDL_IWinTypes_0009 {
long hInproc;
long hRemote;
} __MIDL_IWinTypes_0009;
他の部分も併せて修正。
--- CTDCIFCE.IDL.orig 2026-10-02 05:57:40.748495300 +0900
+++ CTDCIFCE.IDL 2026-10-02 05:57:44.007336700 +0900
@@ -180,13 +180,6 @@ library CTDCIFCELib
typedef [public]
_RemotableHandle* wireHWND;
- typedef struct tag_RemotableHandle {
-
-long fContext;
-
-__MIDL_IWinTypes_0009 u;
- } _RemotableHandle;
-
typedef union tag__MIDL_IWinTypes_0009 {
long hInproc;
@@ -194,6 +187,13 @@ long hInproc;
long hRemote;
} __MIDL_IWinTypes_0009;
+ typedef struct tag_RemotableHandle {
+
+long fContext;
+
+__MIDL_IWinTypes_0009 u;
+ } _RemotableHandle;
+
[
odl,
uuid(59760C60-1DAE-11D3-BDB8-00C0F02DC1B8),
@@ -350,6 +350,30 @@ long hRemote;
long* pdwSize);
};
+ typedef struct tag_tagCallbackExtraInfoStruct {
+
+unsigned long dwMessage;
+
+unsigned long dwArgument;
+
+unsigned long dwUser;
+
+long* pIEfxPlugin;
+
+GUID guidSource;
+ } _tagCallbackExtraInfoStruct;
+
+ typedef struct tag_tagAuxSendInfo {
+
+long dwNumAuxSends;
+
+long dwLowerLimit;
+
+long dwUpperLimit;
+
+long dwAuxBusSend[8];
+ } _tagAuxSendInfo;
+
[
odl,
uuid(E2985081-7BE0-11D3-A720-005004070A24),
@@ -769,30 +793,6 @@ long hRemote;
long dwReserved);
};
- typedef struct tag_tagCallbackExtraInfoStruct {
-
-unsigned long dwMessage;
-
-unsigned long dwArgument;
-
-unsigned long dwUser;
-
-long* pIEfxPlugin;
-
-GUID guidSource;
- } _tagCallbackExtraInfoStruct;
-
- typedef struct tag_tagAuxSendInfo {
-
-long dwNumAuxSends;
-
-long dwLowerLimit;
-
-long dwUpperLimit;
-
-long dwAuxBusSend[8];
- } _tagAuxSendInfo;
-
[
odl,
uuid(52CE05C9-FD69-4329-A53E-3AA2E5F0ECEC),
@@ -1539,6 +1539,15 @@ unsigned char szMidiDeviceShortname[64];
HRESULT _stdcall GetUsedHostPatchMemory(long* pdwUsedMem);
};
+ typedef struct tag__MIDL___MIDL_itf_ctdcifce_0001_0064_0001 {
+
+unsigned short wMIDIBankNum;
+
+unsigned short wMIDIPresetNum;
+
+unsigned char szPresetName[32];
+ } __MIDL___MIDL_itf_ctdcifce_0001_0064_0001;
+
[
odl,
uuid(B36151C1-6F76-11D3-8DD4-00A0C98E9FB1),
@@ -1679,15 +1688,6 @@ unsigned char szMidiDeviceShortname[64];
HRESULT _stdcall UnadviseDevice(wireHWND hCallbackWnd);
};
- typedef struct tag__MIDL___MIDL_itf_ctdcifce_0001_0064_0001 {
-
-unsigned short wMIDIBankNum;
-
-unsigned short wMIDIPresetNum;
-
-unsigned char szPresetName[32];
- } __MIDL___MIDL_itf_ctdcifce_0001_0064_0001;
-
[
odl,
uuid(41462E75-42A1-40BD-AE12-51752277ECD5),
これで無事に CTDCIFCE.tlb が生成されるようになった。
でも本当は Visual Project のプロジェクト(ソリューション)に CTDCIFCE.IDL を入れてビルド時にタイプライブラリ作ってくんねーかなと思うのだが、やり方についてはもう今日は調べる気力が残ってない。
MIDL 以外の IDL コンパイラを使う
それに俺の最終目的としては休止状態に対応した CtHelper.exe のオープンソースな実装を MinGW でコンパイルできるようにすることである。 なので MinGW は gcc にタイプライブラリを import する言語拡張に対応してないので、タイプライブラリよりヘッダファイルを吐かせたいのだよな。
MinGW には
- DLL から IDL ファイルを抽出する genidl
- IDL からヘッダファイルを生成する widl
があるそうだ、Cygwin に含まれる MinGW には後者しかないようなのでとりあえず COM/OLE Viewer で取得した IDL を喰わせてみたのだが中身が空っぽになるのだ。
- CTDCIFCE.h
/*** Autogenerated by WIDL 1.6 from CTDCIFCE2.IDL - Do not edit ***/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif
#include <rpc.h>
#include <rpcndr.h>
#ifndef COM_NO_WINDOWS_H
#include <windows.h>
#include <ole2.h>
#endif
#ifndef __ctdcifce_h__
#define __ctdcifce_h__
/* Forward declarations */
/* Headers for imported files */
#ifdef __cplusplus
extern "C" {
#endif
/* Begin additional prototypes for all interfaces */
/* End additional prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __ctdcifce_h__ */
- CTDCIFCE_i.c
/*** Autogenerated by WIDL 1.6 from CTDCIFCE2.IDL - Do not edit ***/
#include <rpc.h>
#include <rpcndr.h>
#include <initguid.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
15 秒ほど悩んだのだが COM/OLE Viewer の吐く IDL が UTF-16LE なのが原因っぽい。
ということで愛昆布こと iconv -f UTF-16LE -t UTF-8 でコード変換して再挑戦したのだが、importlib("stdole2.tlb") でエラーになってしまう。
import パスが必要になるのかそもそも .tlb を読めないのか、もうやる気が完全に失われてるので後回しとする。
というか genidl 使えば COM/OLE Viewer の後置参照エラーも解消できる気がしないでもないので、それを探しますかね。 これはまた後日やることにしよう。
CTDCIFCE.DLL 以外の COM 呼び出しを追跡する。
そんでもうひとつ別の DLL を呼び出してる箇所がある、こちらの CLSID/REFIID は以下の通り。
- CLSID …
{1446EF61-7128-11D3-82D2-0050DA0D96CA} - REFIID …
{7D041CC1-7D5C-11D3-82D2-0050DA0D96CA}
レジストリは以下の通り。
[HKEY_CLASSES_ROOT\CLSID\{1446EF61-7128-11D3-82D2-0050DA0D96CA}]
@="Live! 2K "
[HKEY_CLASSES_ROOT\CLSID\{1446EF61-7128-11D3-82D2-0050DA0D96CA}\InprocServer32]
@="C:\\Windows\\System32\\CTDPROXY.DLL"
"ThreadingModel"="Apartment"
[HKEY_CLASSES_ROOT\CLSID\{1446EF61-7128-11D3-82D2-0050DA0D96CA}\ProgID]
@="Emu10Kx.1"
[HKEY_CLASSES_ROOT\CLSID\{1446EF61-7128-11D3-82D2-0050DA0D96CA}\VersionIndependentProgID]
@="Emu10KX"
[HKEY_CLASSES_ROOT\Emu10KX]
@="Live! 2K "
[HKEY_CLASSES_ROOT\Emu10KX\CLSID]
@="{1446EF61-7128-11D3-82D2-0050DA0D96CA}"
[HKEY_CLASSES_ROOT\Emu10KX\CurVer]
@="Emu10Kx.1"
[HKEY_CLASSES_ROOT\Emu10Kx.1]
@="Live! 2K "
[HKEY_CLASSES_ROOT\Emu10Kx.1\CLSID]
@="{1446EF61-7128-11D3-82D2-0050DA0D96CA}"
CTDPROXY.DLL は %SYSTEMROOT%\System32 以下に置かれる 64bit DLL で、プロセス内 COM サーバー (InprocServer32) として動作しスレッドモデル分離モデルはシングルスレッドとなる。
32bit アプリケーションの CtHelper.exe とリンクはできないけど最初に書いた COM の魔法によって関数を呼ぶことが可能なわけ。
InprocServer32 じゃ呼べないっすね、LocalServer なりで別プロセスにしないと駄目ですわ、いちおう %SYSTEMROOT%\SysWOW64\CTDPROXY.DLL は存在してるので動いてんのかこれ。
64bit DLL なのに System32 とか InprocServer32 とか 32 がつくの気持ち悪いのだけど System64 を生やしたりキー名を InprocServer64 にするとアプリケーション開発者に混乱が生じると判断し名前を変えなかったから仕方がない。 16bit からの移行時に System → System32 とした時に混乱した反省ですかね知らんけど。
これは N なんかでも 64bit ネイティブライブラリは /lib に置かれ 32bit 互換ライブラリ /lib/i386 などに置かれ syscall 内でパスを変換するのでるのでやってる事は同じっすな、末尾に 32 とついてるから変に感じるだけで。 一方 Linux は System → System32 のように /lib64 を導入したのだがどうしてああなったんだっけ、もう覚えてないわ。
こっちも IDL を取り出そうと思ったのだが、なんか COM/OLE Viewer のタイプライブラリ一覧に出てこねえ。
ドライバのインストーラー inf で regsvr32.exe CTDPROXY.DLL し忘れてるのかと思ったけど、それならレジストリに登録情報もあるはずが無いし。
次回
ということで本日は時間切れである、また次回。