MFC Regular DLL
     
 

靜態載入 DLL

  首先在 VC 選擇 New 一個 Project ,選擇用 MFC 方式建立,但是此方式所建立的 DLL 會看不見 DllMain,取而代之的是 繼承 CWinApp 的類別,這跟一般的 MFC 很像,原本的 DLL_PROCESS_ATTACH被 InitInstance() 取而代之。建立步驟如下

我們選擇新增一個專案

[圖 1] 我們選擇新增一個專案

我們選擇建立 MFC 格式的 DLL 專案

[圖 2] 我們選擇建立 MFC 格式的 DLL 專案

我們選擇共用 MFC DLL 的形式

[圖 3] 我們選擇共用 MFC DLL 的形式

完成後我們可以看見,VC幫我們產生好的檔案

[圖 4] 完成後我們可以看見,VC幫我們產生好的檔案

完成後我們可以看見,VC幫我們產生好的類別

[圖 5] 完成後我們可以看見,VC幫我們產生好的類別

 

   
  //
// 注意!
//
// 如果這個 DLL 是動態地對 MFC DLL 連結,那麼從這個 DLL 匯出的任何會呼叫
// MFC 內部的函式,都必須在函式一開頭加上 AFX_MANAGE_STATE 巨集。
//
// 例如:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // 此處為正常函式主體
// }
//
// 這個巨集一定要出現在每一個函式中,才能夠呼叫 MFC 的內部。
// 這意味著它必須顯示為函式內的第一個陳述式,甚至必須在
// 任何物件變數宣告的前面,因為它們的建構函式可能會產生對
// MFC DLL 內部的呼叫。
//
// 如需詳細資訊,請參閱 MFC 技術提示 33 和 58。

//
   
    建立完成後,我們可以在我們主要的 .cpp 檔案裡面發現,有這一段敘述,這一個巨集是用來推送模組的全域資料。因此在每一個輸出函式的開始,你必須設定目前模組狀態為 DLL,別擔心重設模組狀態,因為 AFX_MANAGE_STATE 巨集實際上會在當物件過完生命週期時,重設為前一個狀態。

  因為 DLL 是給別人用的,所以我們最好另外建立一個 .h 的宣告檔案,將一些共用的東西存在那裡,至於要做哪些宣告,這也是很重要的,不要將別人用不到的也宣告進來,只需將要匯出的部分宣告出來即可。

我們增加一個匯出檔案定義宣告(假設為 DllCommon.h),定義內容如下:

   
  // 下列 ifdef 區塊是建立巨集以協助從 DLL 匯出的標準方式。
// 這個 DLL 中的所有檔案都是使用命令列中所定義 DLLFUNC_EXPORTS 符號編譯的。
// 任何使用這個 DLL 的專案都不應定義這個符號。這樣的話,原始程式檔中包含這檔案的任何其他專案
// 會將 DLLFUNC_API 函式視為從 DLL 匯入的,而這個 DLL 則會將這個巨集定義的符號視為匯出的。

#ifdef DLLFUNC_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
   
  

  然後在我們 DLL 的欲匯出函式的宣告處加入下面的宣告,用來宣告成匯出模式,並載入匯出函式定義宣告,我們什麼時候要使用 dllexport 的敘述,什麼時候又要使用 dllimport 的敘述呢?在 DLL 裡面,我們要將我們的函式匯出去,給其他應用程式使用,所以我們必須在 DLL 內部宣告 DLLFUNC_EXPORTS ,如此編譯器才知道我們要使用 dllexport 的敘述。

   
  #define DLLFUNC_EXPORTS
#include "DllCommon.h"
   

  使用 "#define DLLFUNC_EXPORTS" 是一種方法,但是如果我們定義一新的 Class 我們希望這一個 Class 能夠匯出,並且在 DLL 內部宣告視為 __declspec(dllexport) ,而應用程式端則會視為 __declspec(dllimport),我們有比較好的方法,我們改用前置處理器的宣告方式如下圖,我們把 DLL 的屬性頁叫出來,在前置處理器定義裡宣告 DLLFUNC_EXPORTS,我想這樣比較方便,因為我們可能有許多地方要匯出我們的 Class 或 Function。

設定我們 DLL 的屬性頁

[圖 6] 設定我們 DLL 的屬性頁

  匯出類別的宣告(.h)
 

#pragma once

#include "DllCommon.h"

class DLLFUNC_API SampleClass
{
public:
  SampleClass(void);
  ~SampleClass(void);
  int GetVersion(void);
};

   
  匯出類別的程式碼(.cpp)
 

#include "StdAfx.h"
#include "sampleclass.h"

SampleClass::SampleClass(void)
{
}

SampleClass::~SampleClass(void)
{
}

int SampleClass::GetVersion(void)
{
  return 0x5678;
}

  同理,我們要匯出我們的變數或者一個函是我們可以這樣做,這樣做的另一個好處就是不必為每一個 .cpp 檔案去定義 DLLFUNC_EXPORTS ,而且標頭檔又可以共用而不會有問題出現。下面就是以函式與變數的範例。

  匯出檔案定義宣告(假設為 DllFunc.h),定義內容如下:
  // 下列 ifdef 區塊是建立巨集以協助從 DLL 匯出的標準方式。
// 這個 DLL 中的所有檔案都是使用命令列中所定義 DLLFUNC_EXPORTS 符號編譯的。
// 任何使用這個 DLL 的專案都不應定義這個符號。這樣的話,原始程式檔中包含這檔案的任何其他專案
// 會將 DLLFUNC_API 函式視為從 DLL 匯入的,而這個 DLL 則會將這個巨集定義的符號視為匯出的。

#ifdef DLLFUNC_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif

extern DLLFUNC_API int iDllVersion;   //宣告匯出變數

DLLFUNC_API int fnDllFunction(void);      //宣告匯出函數

   
  然後我們撰寫我們的程式(.cpp)
 

DLLFUNC_API int iDllVersion = 0;

// 這是匯出之函式的範例。
DLLFUNC_API int fnDllFunction(void)
{
 return 0x1234;
}

 

  現在我們使用靜態連接方式來使用我們的 DLL ,首先我們建立一個應用程式,並將我們的產生出來的 .lib 加入,並在我們應用處載入我們 DLL 的匯出定義檔(.h) ,應用範例如下:

   
 

#include "../include/SampleClass.h"
#include <DllCommon.h>

...

void CDllTestApp::OnTestDLL02()
{
  // TODO: 在此加入您的命令處理常式程式碼
  SampleClass Sample;
  int abc=0;
  abc = Sample.GetVersion();  // 使用 DLL 裡的 Class
  abc = iDllVersion;       // 使用 DLL 裡的變數
  abc = fnDllFunction();     // 使用 DLL 裡的函數
}

  程式參考 StaticLink.rar

 

  如果我們要混和使用 C 與 C++ 來撰寫模組時,我們可以使用 extern "C" 修飾字來修飾要輸出的 C++ 符號,而且如果我們輸出 C++ 類別的話,我們的應用程式端也要使用相同的編譯器。縱使我們使用純粹的 C 語言來撰寫程式碼,也會因為不同的 C 編譯器會產生不同的符號,Microsoft 的 C 編譯器也一樣會修飾 C 函式的符號。當我們使用 __stdcall (WINAPI) 的呼叫協定來修飾我們的 C 函式時,就會發生這一個問題,不幸的是這一個是我們最常使用的呼叫協定型式。(詳見Windows 程式設計開發指南 P820)

  如果我的的程式要給 C 和 C++ 使用的話,我們宣告方式可以加上 extern "C" 語法來修飾,片段程式如下:

   
 

#ifdef __cplusplud
extern "C" {
#endif // __cplusplud

其它定義...

extern DLLFUNC_API int iDllVersion;

DLLFUNC_API int fnDllFunction(void);

#ifdef __cplusplud
}
#endif // __cplusplud

 

   
  接下來看看我們DLL的程式碼(.cpp)
 

...

DLLFUNC_API int fnDllFunction(void)
{
 return 0x1234;
}

...

 

 

動態載入 DLL

  當我們的 DLL 寫好以後,我們要怎麼載入我們的 DLL 呢?使用動態載入的方式,我們可以不必管我們匯入什麼函式,也就是說我們可以不必理會 DLL 端宣告的匯入匯出動作,當然 DLL 端必須有匯出的動作,只是我們應用端改變載入方式而已,他的應用大致如下:

1.
LoadLibrary

HINSTANCE hinstDll = LoadLibrary("DllTemplate.dll");

將指定的行程載入到行程空間,如果成功的話,將會傳回被映射的可執行模組之 Handle

2.
GetProcAddress

FARPROC pfn = GetProcAddress(hDll,"fnDllFunction");

GetProcAddress 可接受字串與序號,但是不建議使用序號方式,至於字串有一個要注意的就是,他只支援 ANSI 字串,不能傳送 Unicode 字串給他,如果成功的話,將會傳回所指定的匯出函式的位置

3.
利用傳回的位置做我們的應用
int a;
if (pfn != NULL)
  a = pfn();
4.
FreeLibrary

FreeLibrary(hDll);

當我們不再使用 DLL 的時候,記得使用 FreeLibrary 來釋放 DLL。

 

  呼叫端片段程式如下:
 

void CDllTestApp::OnTestDLL01()
{
  // TODO: 在此加入您的命令處理常式程式碼
  HINSTANCE hDll = NULL;
  hDll = LoadLibrary("MFC_DLL.dll");

  if (hDll != NULL)
  {
    int a=0;
    FARPROC pfn = GetProcAddress(hDll,"fnMFC_DLL");
    if (pfn != NULL)
      a = pfn();
    FreeLibrary(hDll);
  }

}

   

 

  如果我們直接使用的話,我們可以發現,我們無法找到我們的 function entery ,這一個問題主要就是前面所說的修飾問題,要解決這一個問題有兩種方法,一種是更改 .def 來將函式匯出,另一種則是在宣告時我們使用 extern "C" 修飾字來修飾我們的匯出宣告,片段程式如下:

1. 1.使用修改匯出定義檔(XXX.def),若使用了 DEF 檔案的話,就不需要使用到 extern "C" 了。另外,除了可以藉此控制輸出名稱外,也可以控制序數,不過相對的也要對 DEF 檔案進行維護。匯出的函式,只要單純指定 __stdcall 就可,_declspec(dllexport) 與 extern "C" 語法都不需要。
  內容如下:
 

; MFC_DLL.def : 宣告 DLL 的模組參數。

LIBRARY "DllTemplate"

EXPORTS
fnDllFunction
; ...

   
2. 使用修飾字來修飾我們的匯出宣告的標頭檔(.h)
 

#ifdef DLLFUNC_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
//==================================

#ifdef __cplusplud
extern "C" {
#endif // __cplusplud
extern DLLFUNC_API int iDllVersion;

extern "C" DLLFUNC_API int fnDllTemplate(void); // extern "C" 可直接應用在單行敘述上
// 其實在此可以不必宣告,因為我們頭尾早已
// 包含在 extern "C" { ... } 敘述裡


#ifdef __cplusplud
}
#endif // __cplusplud

  此種方式好像還是會有修飾問題,無法解決

 

  重新編譯我們的 DLL ,我們就完成了我們的 DLL 動態載入的應用了,但是如果我們的函式有參數要傳遞,那麼我們要怎麼樣去應用呢,這不再只是將我們抓到的指標直接拿來應用了,我們還必須透過一個轉型的動作,將指標轉成我們函數宣告型態,這樣我們才可以正確的將參數帶入,呼叫端範例如下:

  DLL 的標頭檔(.h)
 

// 下列 ifdef 區塊是建立巨集以協助從 DLL 匯出的標準方式。
// 這個 DLL 中的所有檔案都是使用命令列中所定義 DLLFUNC_EXPORTS 符號編譯的。
// 任何使用這個 DLL 的專案都不應定義這個符號。這樣的話,原始程式檔中包含這檔案的任何其他專案
// 會將 DLLFUNC_API 函式視為從 DLL 匯入的,而這個 DLL 則會將這個巨集定義的符號視為匯出的。

#ifdef DLLFUNC_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
//==================================

#ifdef __cplusplud
extern "C" {
#endif // __cplusplud
extern DLLFUNC_API int iDllVersion;

extern "C" DLLFUNC_API int fnDllFunction(int iValue);

#ifdef __cplusplud
}
#endif // __cplusplud

   
  呼叫端的片段應用程式(.cpp)
 

void CDllTestApp::OnTestDLL01()
{
  // TODO: 在此加入您的命令處理常式程式碼
  HINSTANCE hDll = NULL;
  hDll = LoadLibrary("MFC_DLL.dll");

  int ( _cdecl *fnDllTemplate) (int iValue);

  if (hDll != NULL)
  {
    int a=0;
    FARPROC pfn = GetProcAddress(hDll,"fnDllFunction");
    if (pfn != NULL)
    {
      fnMFC_DLL = (int ( _cdecl *)(int)) pfn ;
      a = (fnDllFunction(123));
    }
    FreeLibrary(hDll);
  }

}

 

  從範例程式我們可以看見,我們先宣告一個我們的函數指標變數,然後等我們擷取到函式位址時,我們利用強迫轉型,將函式位址轉給我們的函數指標,接著我們就可以直接使用我們的函式了,並且可以傳入參數,當然我們呼叫端的定義名稱可以不必與 DLL 端的定義取相同的名稱,不過在這使用相同名稱,避免混淆。

 

  extern "C" DLLFUNC_API int fnDllFunction(int iValue); 我們在 DLL 宣告的函數
  int ( _cdecl *fnDllFunction) (int iValue); 我們在呼叫端的函數指標宣告,在這我們可以發現,我們有使用 _cdecl ,在實際應用上,我們未必宣告成這樣,也可以是 _stdcall _fastcall 這完全根據我們 DLL 的宣告而定,但是在上面我們明明沒有宣告,那到底在哪裡宣告了呢?我們打開我們 DLL 的屬性頁,如下圖,我們可以發現我們使用 __cdecl 為我們的預設值,在這的函是可以被我們的外面函式所覆寫 __stdcall 是使用在呼叫 Win32 API 與 DLL 行程的時候,Stack 會由被呼叫端來清除。若不使用 __stdcall 的話,在混合使用多種語言時程式將容易發生問題。
  fnDllFunction = (int ( _cdecl *)(int)) pfn ; 將我們抓到的位置強制轉型給我們的函數指標
     

 

  當我們改用 __stdcall 方式時,由於 C++ 會修飾外部 Symbol ,因此取名的慣例不太一樣,這個慣例也會因為開發環境版本不一樣兒有所不同。而引起一些麻煩的問題。因此我們不採行 extern "C" 語法,而採用 C++ 修飾名詞的方法。

   
 

#ifdef __cplusplud
extern "C" {
#endif // __cplusplud
extern DLLFUNC_API int iDllVersion;

int DLLFUNC_API __stdcall fnDllFunction(int iValue);

#ifdef __cplusplud
}
#endif // __cplusplud

   

  我們觀看其它程式的時候,我們也常看見非使用 __cdecl or __stdcall 等宣告的方式,如 WINAPI 、CALLBACK ...的宣告方式,我們可以從下面的定義檔發現,其實他是透過巨集宣告成不一樣的名子, 也就是說我們常使用 __stdcall 方式,只是換成我們比較習慣或熟悉的語法而已,詳細參考 windef.h,片段參考如下。

  windef.h
 

#ifdef _MAC
...

#elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#else
#define CALLBACK
#define WINAPI
#define WINAPIV
#define APIENTRY WINAPI
#define APIPRIVATE
#define PASCAL pascal
#endif
   

 

 

 

 

VC 的慣例呼叫預設值

[圖 7] VC 的慣例呼叫預設值

VC 的慣例呼叫的選項

[圖 8] VC 的慣例呼叫的選項

  但是我們所寫的 DLL 每次要自己去寫載入的 DLL 檔案與程式入口以及宣告這些,還要轉記住要怎麼轉型似乎有些麻煩,所以我們可以使用巨集宣告在標頭檔裡面,這樣介面比較統一與方便我們使用。

  DLL Define.h
  // 下列 ifdef 區塊是建立巨集以協助從 DLL 匯出的標準方式。
// 這個 DLL 中的所有檔案都是使用命令列中所定義 DLLFUNC_EXPORTS 符號編譯的。
// 任何使用這個 DLL 的專案都不應定義這個符號。這樣的話,原始程式檔中包含這檔案的任何其他專案
// 會將 DLLFUNC_API 函式視為從 DLL 匯入的,而這個 DLL 則會將這個巨集定義的符號視為匯出的。
// 記得 DLL 要定義 DLLFUNC_EXPORTS

#pragma once
#ifdef DLLFUNC_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
//============================================================================
//--- For function ---
#define DLLFUNC(type) DLLFUNC_API type WINAPI //#define DLLFUNC(type) DLLFUNC_API type __stdcall
// Samele :
// DLLFUNC(int) fnTestFunc(int iValue);
//============================================================================
//--- For Variable ---

#define DLLVAR(type) DLLFUNC_API type
// Sample :
// Define(.h)
// extern DLLVAR(DWORD) dwTestValue;
// extern DLLFUNC_API DWORD dwTestValue;
// (.cpp)
// DLLVAR(DWORD) dwTestValue;
// DLLFUNC_API DWORD dwTestValue;
   
  Output Function define.h
  #pragma once

#include "DllCommon.h"


#ifdef __cplusplud
extern "C" {
#endif // __cplusplud

DLLFUNC(int) fnTestFunc(int iValue);

extern DLLFUNC_API DWORD g_dwTestValue; //extern DLLVAR(DWORD) dwTestValue;

#ifdef __cplusplud
}
#endif // __cplusplud

    

我們可以發現我們變數有額外使用 extern 的定義,這是外部宣告,避免重複定義,因為我們的標頭檔是共用的,所以我們必須稍微注意。

 

  Function.cpp
  DLLFUNC(int) fnTestFunc(int iValue)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState());

  CDialog dlgBuild;
  dlgBuild.DoModal();

return 0;
}

DLLFUNC_API DWORD g_dwTestValue = 10; // DLLVAR(DWORD) g_dwTestValue = 10;

    在剛開始的時候我們有提過 AFX_MANAGE_STATE 這一個巨集,這一個巨集是用來推送模組的全域資料。因此在每一個輸出函式的開始,你必須設定目前模組狀態為 DLL,別擔心重設模組狀態,因為 AFX_MANAGE_STATE 巨集實際上會在當物件過完生命週期時,重設為前一個狀態。在這一個匯出函式我們有使用到 MFC 的類別,所以我們在程式一開始的地方我們就加上這一個敘述。

 

  我們真正的應用,我們必須定義 DEF 檔,避免找不到匯出的函式,但是如果要為我們每一個匯出函式定義一個名稱,這可是一件累人的苦差事,除了將來維護是一個問題,呼叫端也要注意其關係性,並且為每一個要使用到的函式一一建立指標變數,是不是有別的方法可以簡化這些呢?其實還有一個不錯的方法,適用於大量的輸出函數,那就是建立一個 Function Table 的方法,我們替我們的函式建立一個結構( struct ) ,然後我們一次將這一個結構匯出,使用時只要宣告一個結構指標,就可以使用,就如同我們常使用的 Class 一樣,底下就是一個完整的範例,除了匯出一般函式,也會出結構方式的範例,並將 DLL 要用的一些基本機制加入並說明。

  DllCommon.h
 

#if !defined(_ORIGINALITY_DLL_COMMON_H_)
#define _ORIGINALITY_DLL_COMMON_H_


//============================================================================
// Originality Export/Import Define
//============================================================================
// 下列 ifdef 區塊是建立巨集以協助從 DLL 匯出的標準方式。
// 這個 DLL 中的所有檔案都是使用命令列中所定義 DLLFUNC_EXPORTS 符號編譯的。
// 任何使用這個 DLL 的專案都不宣告 DLLFUNC_EXPORTS 這個符號。這樣的話,只要
// 程式檔中包含這標頭檔案的任何其他專案會將 DLLFUNC_API 函式視為要從 DLL 匯
// 入,而只有宣告 DLLFUNC_EXPORTS 這個符號的 DLL 專案程式,會將這個巨集定義
// 的符號視為匯出的。
// ※記得 DLL 要定義 DLLFUNC_EXPORTS
#ifdef DLLFUNC_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
//============================================================================
//--- For function ---
#define DLLFUNC(type) DLLFUNC_API type WINAPI //#define DLLFUNC(type) DLLFUNC_API type __stdcall
// Samele :
// DLLFUNC(int) fnTestFunc(int iValue);
//============================================================================
//--- For Variable ---
#define DLLVAR(type) DLLFUNC_API type
// Sample :
// Define(.h)
// extern DLLVAR(DWORD) dwTestValue;
// extern DLLFUNC_API DWORD dwTestValue;
// (.cpp)
// DLLVAR(DWORD) dwTestValue;
// DLLFUNC_API DWORD dwTestValue;

#endif //_ORIGINALITY_DLL_COMMON_H_

   
 

#if !defined(_ORIGINALITY_COMMON_MESSAGE_H_)
#define _ORIGINALITY_COMMON_MESSAGE_H_

//============================================================================
// Module Register Message Define
// Samele :
// UINT g_uiOriginalityMessage = RegisterOriginalityMessage(); // For All Application
// UINT g_uiModuleMessage = RegisterWindowMessage((LPTSTR)_T("Originality Module Message"));
//============================================================================
#define RegisterOriginalityMessageName "OriginalityMessage" // Originality Message For All Application
#define RegisterOriginalityMessage() RegisterWindowMessage (RegisterOriginalityMessageName)

#pragma pack(4)

typedef struct _VERSION_FOR_ALL_
{
union
{
struct _VER
{
BYTE bMajor;
BYTE bMinor;
BYTE bRelease;
BYTE bBuild;
} Version;
DWORD dwVersion;
};
} VERSION_FOR_ALL, *PVERSION_FOR_ALL;

#define VERSION2DWORD(Major,Minor,Release,Build) MAKELONG(MAKEWORD(Major,Minor),MAKEWORD(Release,Build))
// return < 0, caller version is less than DLL
// = 0, caller version is the same as DLL
// > 0, caller version is great than DLL
__inline int CheckVersion (PVERSION_FOR_ALL pCaller, PVERSION_FOR_ALL pDll)
{
if (pCaller->Version.bMajor == pDll->Version.bMajor &&
pCaller->Version.bMinor == pDll->Version.bMinor &&
pCaller->Version.bRelease == pDll->Version.bRelease &&
pCaller->Version.bBuild == pDll->Version.bBuild)
return 0;
if (pCaller->Version.bMajor > pDll->Version.bMajor ||
(pCaller->Version.bMajor == pDll->Version.bMajor && pCaller->Version.bMinor > pDll->Version.bMinor) ||
(pCaller->Version.bMajor == pDll->Version.bMajor && pCaller->Version.bMinor == pDll->Version.bMinor &&
pCaller->Version.bRelease > pDll->Version.bRelease) ||
(pCaller->Version.bMajor == pDll->Version.bMajor && pCaller->Version.bMinor == pDll->Version.bMinor &&
pCaller->Version.bRelease > pDll->Version.bRelease && pCaller->Version.bBuild > pDll->Version.bBuild))
return 1;
else
return -1;
}

#pragma pack()

#endif //_ORIGINALITY_COMMON_MESSAGE_H_

   
 

#if !defined(_ORIGINALITY_TEMPLATE_LIBRARY_H_)
#define _ORIGINALITY_TEMPLATE_LIBRARY_H_

#include <windows.h>
#include <windowsx.h>
#include <OriginalityCommon.h>

#ifdef __cplusplud
extern "C" {
#endif // __cplusplud

#pragma pack(4)

//------------------------------------------------------------------------------------------------------
// ------------------------------- Search Library Version Info. -------------------------------
//------------------------------------------------------------------------------------------------------

#define TEMPLATE_LIBRARY_MAJOR 0 // Major
#define TEMPLATE_LIBRARY_MINOR 9 // Minor
#define TEMPLATE_LIBRARY_RELEASE 0 // Release
#define TEMPLATE_LIBRARY_BUILD 0 // Build

#define TemplateLibraryVersion VERSION2DWORD(DEVICE_SEARCH_MAJOR,DEVICE_SEARCH_MINOR,DEVICE_SEARCH_RELEASE,DEVICE_SEARCH_BUILD)

 

//========================================================================
//--- Struct ---
//========================================================================
typedef enum
{
DLL_RET_STATUS_SUCCESS = 1, // Okay
DLL_RET_STATUS_CANCELLED = 0, //
DLL_RET_STATUS_NO_MORE_DATA = 2,
DLL_RET_ERROR_FAIL = -1, // Invalid call
DLL_RET_ERROR_INCOMPATIBLE_VERSION = -2, // Version is incorrect (Incompatible Version)
DLL_RET_ERROR_BAD_PARAMETTER = -3, //
DLL_RET_ERROR_BAD_UI_PARAMETTER = -4, // UIparameter error
DLL_RET_ERROR_RESOURCE_NOT_ENOUGH = -5, //
DLL_RET_ERROR_INSUFFICIENT_MEMORY = -6, // Memory problem(Insufficient Memory)
DLL_RET_ERROR_INSUFFICIENT_RESOURCE = -7, // Resource problem(Insufficient Resource)
DLL_RET_ERROR_IO_ERROR = -8, // Read/Write error
DLL_RET_ERROR = -32767,
} DLL_TEMPLATE_LIBRARY_RETCODE;

 

typedef struct _TEMPLATE_LIBRARY_FUNCTS
{
DLL_TEMPLATE_LIBRARY_RETCODE (CALLBACK* pfnRegister)(PVERSION_FOR_ALL pVersion);// Regsiter
void (CALLBACK* pfnUnregister)();

} TEMPLATE_LIBRARY_FUNCS, *PTEMPLATE_LIBRARY_FUNCS;

 

//========================================================================
//--- Share for DLL ---
//========================================================================
#pragma data_seg(".DShare")
// add share data
#pragma data_seg()
//-------------------------------------------------------------------------------------------------------
// ---------- Functions ------------ //
//-------------------------------------------------------------------------------------------------------

//========================================================================
//--- Load DLL's Function Macro
//========================================================================
// Name of DLL
//#define DLL_TEMPLATE_LIBRARY_MODULE "DllTemplate.dll"

#if (defined _DEBUG)
#if (defined _UNICODE)
#define DLL_TEMPLATE_LIBRARY_MODULE _T("DllTemplateD.dll")
#define LIB_TEMPLATE_LIBRARY_MODULE "DllTemplateD.lib"
#elif (defined _MBCS)
#define DLL_TEMPLATE_LIBRARY_MODULE _T("DllTemplateMD.dll")
#define LIB_TEMPLATE_LIBRARY_MODULE "DllTemplateMD.lib"
#else
#define DLL_TEMPLATE_LIBRARY_MODULE _T("DllTemplateND.dll")
#define LIB_TEMPLATE_LIBRARY_MODULE "DllTemplateND.lib"
#endif // _UNICODE
#else
#if (defined _UNICODE)
#define DLL_DEVICE_SEARCH_MODULE _T("DllTemplate.dll")
#define LIB_DEVICE_SEARCH_MODULE "DllTemplate.lib"
#elif (defined _MBCS)
#define DLL_DEVICE_SEARCH_MODULE _T("DllTemplateM.dll")
#define LIB_DEVICE_SEARCH_MODULE "DllTemplateM.lib"
#else
#define DLL_DEVICE_SEARCH_MODULE _T("DllTemplateN.dll")
#define LIB_DEVICE_SEARCH_MODULE "DllTemplateN.lib"
#endif // _UNICODE
#endif // _DEBUG

// Used to get procedure address
#define DLL_TEMPLATE_LIBRARY_ENTRY "ServiceEntry"
// Calling convention and return parameter
typedef PTEMPLATE_LIBRARY_FUNCS (CALLBACK* PFNDLLENTRY)();

#pragma pack()
#ifdef __cplusplud
} /* Assume C declarations for C++ */
#endif // __cplusplud

#endif

 

   
   
 

// DllTemplate.cpp : 定義 DLL 的初始化常式。
//

#include "stdafx.h"
#include "DllTemplate.h"
#include "OriginalityCommon.h"
#include "DllCommon.h"
#include "TemplateLibrary.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//
//TODO: 如果這個 DLL 是動態地對 MFC DLL 連結,
// 那麼從這個 DLL 匯出的任何會呼叫
// MFC 內部的函式,都必須在函式一開頭加上 AFX_MANAGE_STATE
// 巨集。
//
// 例如:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // 此處為正常函式主體
// }
//
// 這個巨集一定要出現在每一個
// 函式中,才能夠呼叫 MFC 的內部。這意味著
// 它必須是函式內的第一個陳述式
// ,甚至必須在任何物件變數宣告前面
// ,因為它們的建構函式可能會產生對 MFC
// DLL 內部的呼叫。
//
// 請參閱 MFC 技術提示 33 和 58 中的
// 詳細資料。
//

// CDllTemplateApp

BEGIN_MESSAGE_MAP(CDllTemplateApp, CWinApp)
END_MESSAGE_MAP()

// CDllTemplateApp 建構

CDllTemplateApp::CDllTemplateApp()
{
// TODO: 在此加入建構程式碼,
// 將所有重要的初始設定加入 InitInstance 中
}

// 僅有的一個 CDllTemplateApp 物件

CDllTemplateApp theApp;

// CDllTemplateApp 初始設定

BOOL CDllTemplateApp::InitInstance()
{
CWinApp::InitInstance();

//(+)2007-12-31 Richard Chung
SetupDllObjects(); // Setup Function Table
//(-)2007-12-31 Richard Chung

return TRUE;
}

//==============================================================================
// Global Variables
//==============================================================================
TEMPLATE_LIBRARY_FUNCS g_DllObjects;
DLLVAR(DWORD) g_dwTestValue = 10;

DLLFUNC(int) fnTestFunc(int iValue)
{
//AFX_MANAGE_STATE(AfxGetStaticModuleState());
CString strTest=_T("1234");

return iValue;
}

//==============================================================================
// Function name : Register
// Description :
// Parameter : None
// Return :
// Note :
// Update : 2007-12-28 Richard Chung
//==============================================================================

DLLFUNC(DLL_TEMPLATE_LIBRARY_RETCODE) Register (PVERSION_FOR_ALL pVersion)
{
VERSION_FOR_ALL DllVersion;
DllVersion.dwVersion=VERSION2DWORD( TEMPLATE_LIBRARY_MAJOR, TEMPLATE_LIBRARY_MINOR,
TEMPLATE_LIBRARY_RELEASE, TEMPLATE_LIBRARY_BUILD);

if (CheckVersion(pVersion,&DllVersion) < 0)
return DLL_RET_ERROR_INCOMPATIBLE_VERSION;

return DLL_RET_STATUS_SUCCESS;
}

//==============================================================================
// Function name : Unregister
// Description :
// Parameter : None
// Return :
// Note :
// Update : 2007-12-28 Richard Chung
//==============================================================================
DLLFUNC(void) Unregister ()
{

}

//==============================================================================
// Function name : ServiceEntry
// Description : Export function table
// Parameter : None
// Return :
// Note :
// Update : 2007-12-28 Richard Chung
//==============================================================================
PTEMPLATE_LIBRARY_FUNCS WINAPI ServiceEntry ()
{
return &g_DllObjects;
}

//==============================================================================
// Function name :
// Description : Setup function table
// Parameter : None
// Return :
// Note :
// Update : 2007-12-28 Richard Chung
//==============================================================================
BOOL SetupDllObjects()
{
g_DllObjects.pfnRegister = Register;
g_DllObjects.pfnUnregister = Unregister;
return TRUE;
}

   
  DEF 定義檔
 

; DllTemplate.def : 宣告 DLL 的模組參數。

LIBRARY "DllTemplate"

EXPORTS
  ; 明確匯出可置於此
  ServiceEntry
  fnTestFunc

   
  呼叫端範例程式
 

void CMainFrame::OnCmdFunctest()
{
  // TODO: 在此加入您的命令處理常式程式碼
  //--- Use Function Table ---
  HINSTANCE hDllInstance; // DLL instance handle
  VERSION_FOR_ALL DllVersion; // DLL Version info
  PFNTEMPLATELIBRARYENTRY pfnEntry; // Entry Pointer
  PTEMPLATE_LIBRARY_FUNCS pfnFunc; // Function Table

  //--- DLL Register ---
  DllVersion.dwVersion=VERSION2DWORD( TEMPLATE_LIBRARY_MAJOR, TEMPLATE_LIBRARY_MINOR,
  TEMPLATE_LIBRARY_RELEASE, TEMPLATE_LIBRARY_BUILD);

  hDllInstance = NULL;
  pfnEntry = NULL;
  pfnFunc = NULL;
  VERIFY(hDllInstance = ::LoadLibrary(DLL_TEMPLATE_LIBRARY_MODULE) ); //發出 LoadLibrary 的函式將程式庫載入
  if (hDllInstance != NULL)
  {
    pfnEntry = (PFNTEMPLATELIBRARYENTRY) GetProcAddress (hDllInstance, DLL_TEMPLATE_LIBRARY_ENTRY); // 取得程式庫入口
    if (pfnEntry != NULL)
    {
      pfnFunc = pfnEntry();
      pfnFunc->pfnRegister(&DllVersion);
    }
    else
      return;
  }

  //--- Test Function ---
  if (hDllInstance != NULL)
  {

    typedef int (CALLBACK* PFNTEST)(int);
    PFNTEST pfnTest;

    FARPROC pfn = GetProcAddress (hDllInstance, "fnTestFunc"); // 取得程式庫入口
    if (pfn != NULL)
    {
      int a= 0;
      pfnTest = (PFNTEST)pfn;
      a = pfnTest(123);
    }
  }

  //--- Test Function 2 ---
  int (CALLBACK *pfnTestDLL) (int iValue);
  if (hDllInstance != NULL)
  {
    int a = 0;
    FARPROC pfn = 0;
    pfn = GetProcAddress(hDllInstance,"fnTestFunc");
    if (pfn != NULL)
    {
      pfnTestDLL = (int (CALLBACK *)(int)) pfn;
      a = pfnTestDLL(246);
    }
  }

  if (hDllInstance != NULL)
    FreeLibrary(hDllInstance);

}

 
     
 
版權所有 © 2009 Originality. All Rights Reserved.