//////////////////////////trydll2.h///
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the TRYDLL2_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// TRYDLL2_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef TRYDLL2_EXPORTS
#define TRYDLL2_API __declspec(dllexport)
#else
#define TRYDLL2_API __declspec(dllimport)
#endif
// This class is exported from the trydll2.dll
class TRYDLL2_API CTrydll2 {
public:
CTrydll2(void);
virtual void diplay();
virtual int fnp(int i);
// TODO: add your methods here.
};
//extern TRYDLL2_API int nTrydll2;//放在這裡不會重定義!!
extern "C"
{
TRYDLL2_API int nTrydll2;
TRYDLL2_API CTrydll2 * GetInstance();
TRYDLL2_API int fnTrydll2(void);
TRYDLL2_API int fnp(int para);
};
、、、、、、、、trydll2.cpp、、、、、、、、、、、、、、、、、、、、、
#include "stdafx.h"
#include "trydll2.h"
//..............................
#include <iostream>
using namespace std;
///...............................
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// This is an example of an exported variable
//TRYDLL2_API int nTrydll2=0;//將導致重定義
extern "C" TRYDLL2_API int nTrydll2=0;
// This is an example of an exported function.
TRYDLL2_API int fnTrydll2(void)
{
cout<<"fnTrydll2 /n";
return 42;
}
TRYDLL2_API CTrydll2 * GetInstance()
{
return new CTrydll2;
}
TRYDLL2_API int fnp(int para)
{
cout<<"fnp:"<<para<<endl;
return para=1;
}
// This is the constructor of a class that has been exported.
// see trydll2.h for the class definition
CTrydll2::CTrydll2()
{
return;
}
void CTrydll2::diplay()
{
cout<<"CTrydll2::diplay"<<endl;
}
int CTrydll2::fnp(int i)
{
cout<<"CTrydll2::fnp:" <<i<<endl;
i++;
return i;
}
、、、、、、、、、、、、、、、、、、、、、、、、、、、、
// UseDll.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "../trydll2.h" //必須加進來,因為要用類CTrydll2
//如果不包含上面這個檔案可以使用下面被注釋掉的程式碼片段
//begin。告訴編譯器這些東西將從外部倒入。這個部分相當包含標頭檔。。。
/*
class __declspec(dllimport) CTrydll2
{
public:
CTrydll2(void);
virtual void diplay();
virtual int fnp(int i);
// TODO: add your methods here.
};
*/
//__declspec(dllimport) int nTrydll2;
//end。。。..
#include "windows.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
HINSTANCE hi=::LoadLibrary("E://lianxi//DLL_LIB_DCOM_COM//trydll2//Debug//trydll2.dll");
if( hi == NULL )
{
cout<<"LoadLibrary error/n";
return 0;
}
//fnTrydll2:別忘了C++預設編譯器會對函數進行改名
//所以你應該強制什麼聲明不應該改名
//但是這樣的話 重載就有麻煩了。。因為重載就是應用了改名的方法
//.......fn.................
FARPROC lpfn= ::GetProcAddress( hi , "fnTrydll2" );
if( lpfn == NULL )
{
cout<<"GetProcAddress fnTrydll2 error/n";
return 0;
}
lpfn();
//FARPROC == int (*lpfn)(void )
typedef int (*LPFNP)(int );
LPFNP lpfnP=(LPFNP)::GetProcAddress( hi , "fnp" );
if ( lpfnP == NULL)
{
cout<<"GetProcAddress fnp error/n";
}
else
{
lpfnP(15);
}
//......variable................
int *pi = (int *)::GetProcAddress(hi,"nTrydll2");
if( pi==NULL)
{
cout<<"cann't find nTrydll2 error/n";
//return 0;
}
//nTrydll2=100; //即使在dll中聲明為extern "C"來禁止改名,但是在用戶端連結時還是
//會試圖去連結,所以總是連結失敗,如果沒有頂部的__declspec(dllimport) int nTrydll2;
//則根本無法編譯
//.............class........
CTrydll2 *pt = NULL;
FARPROC lpfn2 = ::GetProcAddress(hi ,"GetInstance");
if ( lpfn2 == NULL )
{
cout<<"GetProcAddress GetInstance error/n";
return 0;
}
pt = (CTrydll2 *)lpfn2();
pt->diplay(); //串連時錯誤,本質上顯示利用dll匯出的東西,使不應該對此進行連結的
pt->fnp(10); ////串連時錯誤,所以要使用需函數,因為虛函數時運行時確定的,編譯時間產生
//vtable,但是在連結時就不會試圖去連結了。
//fnTrydll2();
/*
怎麼使用匯出的類或者變數呢??
*/
::FreeLibrary( hi );
return 0;
}
、、
、、請注意 看看上面代碼的注釋
這裡有一點要做說明:能夠顯式使用dll匯出的類完全是因為 vtable的存在!!這正是決定性因素。作為比較的道出的全域變數就不能使用。因為在連結時會發生錯誤。而vtable是運行時決定的,所以避開了連結的檢查。
好了,該想想 為什麼c++中介面最好只有vitual fn我想和上面有關係。這要從com說起。本質上c++沒有介面這種概念但允許存在。com中利用介面的vtable屬性,避開了 連結時的檢查而直接進行運行時的決定。
呵呵 。