9.29.2007

Call DLL Function in class's way

CE 的DLL 有兩種使用方式 (ref : DLL有兩種)
一般使用自動link (ap load 時自動load)比較方便,因為幾乎和一般的library 一樣,沒有限制,所以整個class 可以export 出來,使用起來沒有限制 (不會因為是dll 而有特殊)。

但是另一種手動Load (ap 呼叫 LoadLibrary( )來load dll)就不一樣了。
要使用dll 裡export 的function ,要一一用GetProcAddress(inst,"functioname") 來取出。
所以,得到的是一個address。這樣,要 export 出 class 真是不可能了。(倒是codegguru有一篇hack...,示範怎麼作..)。

但是,還是有方法的。利用 address 的這個特性,將class 轉成pure function table (也就是都是virtual 的function),讓 ap 呼叫就可以了。

方法大概是這樣:

DLL 的部份:
Calc.h (借用那篇hack的code, 但是方法不一樣喔)..
class CCalcPI
{
public:
virtual int Add(int i,int j)=0;
};

class CCalc : public CCalcPI
{
public:
CCalc ();

int Add(int i,int j);
~CCalc();
private:
int use;
};
可以看到那個CCalcPI (Pure Interface)。就是要讓ap 使用的。
dll implement 的部份:
Calc.cpp
#include "stdafx.h"
#include "Calc.h"

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}

CCalc* inst=0;

CCalcPI* GetCalc()
{
if(inst==0)
inst = new CCalc();
return inst;
}

CCalc::CCalc()
{
use=0;
}

CCalc::~CCalc()
{
}

int CCalc::Add(int i,int j)
{
use++;
return i+j+use;
}
...順便implement一個singletone.
然後是dll 的def file:
Calc.def
LIBRARY   "Calc"

EXPORTS

GetCalc
ReleaseCalc

SECTIONS
.shared READ WRITE SHARED
export 出來的只有將constructor, destructor包裝起來的C function. (因為是singletone,所以destructor不必傳參數)。

使用:

App 要使用dll 裡的CCalc class,只能由CalcPI介面操作,必且由GetCalc( )取得CCalc class的實體:
    CCalcPI *calcp;
HINSTANCE hInst;
hInst = LoadLibrary(L"\\Windows\\Calc.dll");
if(hInst){

typedef CCalcPI* getcalc(void);

getcalc* fgetc= (getcalc*)GetProcAddress(hInst,L"GetCalc");
if(fgetc){
calcp = (*fgetc)();
int r = calcp->Add(1,2);

CString outstr;
outstr.Format(L"%d",r);
MessageBox(outstr);

CCalcPI* cp2=(*fgetc)(); // 看看singletone有沒有效 : by "use" variable
r=cp2->Add(1,2);
outstr.Format(L"%d",r);
MessageBox(outstr);
}
}

很奇怪,如果不是pure virtual class (CCalcPI),而是直接使用CCalc 的話,Linker會complain Add( ) function 找不到。

這一招其實是客戶教的....但是他的sample code把export function 直接放到class中,我不知道要怎麼作..

這一篇有類似的方法(好像更漂亮),但是我照作 link 還是有error。

3 則留言:

chou 提到...

因為CCalc是繼承CCalcPI,而CCalcPI內有一個virtual method.所以必須要implement CCalcPI內的virtual method,才可以直接call.要呼叫DLL要特別小心memory mapping,記的有些compiler不suuport thread for DLL. 大概是 linker 方式的差異而已

Checko 提到...

喔,是這樣呀。

但是我還是不知道為甚麼是pure virtual 才行。

chou 提到...

CCalcPI只是一個空模而已,可以稱為interface. 它可以做到多型機制,只要後面繼承他的class具有implement,這是物件導向的繼承和多型觀念.