Changes between Initial Version and Version 1 of インタフェースのCOM対応


Ignore:
Timestamp:
Oct 17, 2007, 1:17:56 AM (17 years ago)
Author:
イグトランス (egtra)
Comment:

新規作成

Legend:

Unmodified
Added
Removed
Modified
  • インタフェースのCOM対応

    v1 v1  
     1[http://dev.activebasic.com/dai/?p=561 山本さんのところ]にCOMが使用できないと書いてあったのが気になったので、調べてみました。結論から言うと、ちょっとした工夫 (hack)でvtbl経由での呼出はできましたが、引数のthisポインタがだめだめなので、実質的に使えないことに違いありませんでした。
     2
     3実験台となるVisual C++プログラムです。
     4{{{
     5//td.cpp
     6//cl td.cpp /link /DLL /out:td.dll
     7#include <stdio.h>
     8#include <windows.h>
     9
     10void msg(void* t, PCTSTR s);
     11
     12class Test
     13{
     14public:
     15        virtual void WINAPI Proc1()
     16        {
     17                msg(this, "Proc1");
     18        }
     19        virtual void WINAPI Proc2()
     20        {
     21                msg(this, "Proc2");
     22        }
     23        virtual void WINAPI Proc3()
     24        {
     25                msg(this, "Proc3");
     26        }
     27        virtual void WINAPI Proc4()
     28        {
     29                msg(this, "Proc4");
     30        }
     31};
     32
     33Test o;
     34
     35extern "C" __declspec(dllexport)
     36void WINAPI GetTestObj(Test*& t)
     37{
     38        t = &o;
     39}
     40
     41BOOL WINAPI DllMain(HINSTANCE, DWORD, void*)
     42{
     43        return TRUE;
     44}
     45
     46void msg(void* t, PCTSTR s)
     47{
     48        char buf[1024];
     49        _snprintf(buf, sizeof buf, "%s, this: %p, &o: %p", s, t, &o);
     50        MessageBoxA(0, buf, "", MB_OK);
     51}
     52}}}
     53逆アセンブルしてみて気付いたのですが、ABではインタフェース型変数が保持しているアドレス値から12 (&h0c)を引いてやると、ABがvtblがあると思い込む位置に、VC++が設定した、ひいてはCOMで決められたvtblの位置が重なり、メソッド呼出が成功します。ただし実行してみれば分かりますが、正しいオブジェクトのアドレスとABから渡されたthisがまるで一致していません。上のDLLではthisからの参照を行っていないため、何事もなく実行できますが、普通はあっという間にアクセス違反になるしょう。
     54{{{
     55Interface ITest
     56        Sub Proc1()
     57        Sub Proc2()
     58        Sub Proc3()
     59End Interface
     60
     61Declare Sub GetTestObj Lib "td" Alias "_GetTestObj@4" (ByRef t As Any)
     62
     63Sub Test()
     64        Dim t = Nothing As ITest
     65        'DebugBreak()
     66        GetTestObj(t)
     67
     68        SetDWord(VarPtr(t), GetDWord(VarPtr(t)) - &h0c)
     69
     70        t.Proc1()
     71        t.Proc2()
     72        t.Proc3()
     73End Sub
     74
     75Test()
     76}}}
     77たったこれだけでメソッド呼出ができたのですから、COM再対応もそう難しいものではないかと思います。次の2つの修正で、COMインタフェースを特別扱いすることなく対応できるようになるはずです。
     78
     79まず、その前提として、想像と実験から、現在インタフェースオブジェクトが保持するアドレスが指す先は次のようになっていると仮定しています。
     80{{{
     81Type IntarfaceObject
     82        Nanika1 As DWord
     83        Nanika2 As DWord
     84        This As VoidPtr
     85        vtbl As VoidPtr
     86        '...まだあるかも
     87End Type
     88}}}
     89最初の修正として、今のインタフェースオブジェクトは、Nanika1を指すアドレス値となっているはずですが、これをvtblを指すように変えます。Nanika1, 2, Thisを読み取るには負のオフセットを使うことにすれば問題ありません。
     90
     91もう1つは、インタフェースメソッドの呼出時に、インタフェースオブジェクトが保持しているアドレス値(= ObjPtr(t)相当)をそのままThisにあたる引数として渡すようにします。現在、呼出元が行っているインタフェースオブジェクトから基のThisポインタを取り出す操作は、サンクにするなどして追い出します。
     92
     93とまあ、口を出すだけなら簡単なわけですが、実際に行うのは難いでしょうけど,いかがでしょうか。2007年10月17日01時08分 イグトランス