wiki:インタフェースのCOM対応

山本さんのところにCOMが使用できないと書いてあったのが気になったので、調べてみました。結論から言うと、ちょっとした工夫 (hack)でvtbl経由での呼出はできましたが、引数のthisポインタがだめだめなので、実質的に使えないことに違いありませんでした。

実験台となるVisual C++プログラムです。

//td.cpp
//cl td.cpp /link /DLL /out:td.dll
#include <stdio.h>
#include <windows.h>

void msg(void* t, PCTSTR s);

class Test
{
public:
	virtual void WINAPI Proc1()
	{
		msg(this, "Proc1");
	}
	virtual void WINAPI Proc2()
	{
		msg(this, "Proc2");
	}
	virtual void WINAPI Proc3()
	{
		msg(this, "Proc3");
	}
	virtual void WINAPI Proc4()
	{
		msg(this, "Proc4");
	}
};

Test o;

extern "C" __declspec(dllexport)
void WINAPI GetTestObj(Test*& t)
{
	t = &o;
}

BOOL WINAPI DllMain(HINSTANCE, DWORD, void*)
{
	return TRUE;
}

void msg(void* t, PCTSTR s)
{
	char buf[1024];
	_snprintf(buf, sizeof buf, "%s, this: %p, &o: %p", s, t, &o);
	MessageBoxA(0, buf, "", MB_OK);
}

逆アセンブルしてみて気付いたのですが、ABではインタフェース型変数が保持しているアドレス値から12 (&h0c)を引いてやると、ABがvtblがあると思い込む位置に、VC++が設定した、ひいてはCOMで決められたvtblの位置が重なり、メソッド呼出が成功します。ただし実行してみれば分かりますが、正しいオブジェクトのアドレスとABから渡されたthisがまるで一致していません。上のDLLではthisからの参照を行っていないため、何事もなく実行できますが、普通はあっという間にアクセス違反になるしょう。

Interface ITest
	Sub Proc1()
	Sub Proc2()
	Sub Proc3()
End Interface

Declare Sub GetTestObj Lib "td" Alias "_GetTestObj@4" (ByRef t As Any)

Sub Test()
	Dim t = Nothing As ITest
	'DebugBreak()
	GetTestObj(t)

	SetDWord(VarPtr(t), GetDWord(VarPtr(t)) - &h0c)

	t.Proc1()
	t.Proc2()
	t.Proc3()
End Sub

Test()

たったこれだけでメソッド呼出ができたのですから、COM再対応もそう難しいものではないかと思います。次の2つの修正で、COMインタフェースを特別扱いすることなく対応できるようになるはずです。

まず、その前提として、想像と実験から、現在インタフェースオブジェクトが保持するアドレスが指す先は次のようになっていると仮定しています。

Type IntarfaceObject
	Nanika1 As DWord
	Nanika2 As DWord
	This As VoidPtr
	vtbl As VoidPtr
	'...まだあるかも
End Type

最初の修正として、今のインタフェースオブジェクトは、Nanika1を指すアドレス値となっているはずですが、これをvtblを指すように変えます。Nanika1, 2, Thisを読み取るには負のオフセットを使うことにすれば問題ありません。

もう1つは、インタフェースメソッドの呼出時に、インタフェースオブジェクトが保持しているアドレス値(= ObjPtr(t)相当)をそのままThisにあたる引数として渡すようにします。現在、呼出元が行っているインタフェースオブジェクトから基のThisポインタを取り出す操作は、サンクにするなどして追い出します。

とまあ、口を出すだけなら簡単なわけですが、実際に行うのは難いでしょうけど,いかがでしょうか。2007年10月17日01時08分 イグトランス

Last modified 17 years ago Last modified on Oct 17, 2007, 1:17:56 AM
Note: See TracWiki for help on using the wiki.