首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC++

ActiveX 控制項動態載入 DLL 並呼叫函數完整實作

     編輯:VC++

專案背景

將 POS 機廠商提供的 Police.dll(無標頭檔、僅百字說明)封裝進 ATL ActiveX 控制項,供 IE 網頁呼叫操作硬體。

關鍵技術:動態載入與函數指標

無標頭檔時,以 LoadLibrary + GetProcAddress 取得函數位址,轉型為正確呼叫慣例(__stdcall)的函數指標。

介面設計 (IPosCtrl)

方法功能
Init(short nCom)載入 DLL、開啟 COM 埠
Quit()關閉 COM 埠、卸載 DLL
ModifyPosDT(BSTR bstrDT)設定 POS 機時間
EmptyPos()清空 POS 機資料

核心實作 (CPosCtrl)

DLL 函數指標定義

#pragma data_seg("mydata")
static HINSTANCE DLLInst = NULL;
#pragma data_seg()

HANDLE (__stdcall *Init_comm)(LPSTR);
BYTE   (__stdcall *Exit)(HANDLE);
BYTE   (__stdcall *Protocol)(HANDLE, BYTE, BYTE, BYTE*, BYTE*, BYTE*);
BYTE   (__stdcall *Pipe_control)(HANDLE, BYTE);
BYTE   (__stdcall *Dev_escape)(HANDLE);

#pragma data_seg("mydata") 讓 DLLInst 處於共享區段,多進程載入同一 ActiveX 時共用 DLL 句柄(視需求決定是否需要)。

載入/卸載 DLL

void CPosCtrl::InitDll() {
    DLLInst = LoadLibrary("police.dll");
    if (DLLInst) {
        Init_comm = (HANDLE(__stdcall*)(LPSTR))GetProcAddress(DLLInst, "Init_comm");
        Exit      = (BYTE(__stdcall*)(HANDLE))GetProcAddress(DLLInst, "Exit");
        Protocol  = (BYTE(__stdcall*)(HANDLE,BYTE,BYTE,BYTE*,BYTE*,BYTE*))GetProcAddress(DLLInst, "Protocol");
        Pipe_control = (BYTE(__stdcall*)(HANDLE,BYTE))GetProcAddress(DLLInst, "Pipe_control");
        Dev_escape   = (BYTE(__stdcall*)(HANDLE))GetProcAddress(DLLInst, "Dev_escape");
    }
    else {
        MessageBox(NULL, "載入 DLL 失敗!", "錯誤", MB_OK|MB_ICONERROR);
        exit(0);
    }
}

void CPosCtrl::ExitDll() {
    if (DLLInst) FreeLibrary(DLLInst);
}

介面方法實作

STDMETHODIMP CPosCtrl::Init(short nComNo) {
    InitDll();
    char str[20]; sprintf(str, "COM%d", nComNo);
    hCom = (*Init_comm)(str);
    MessageBox(NULL, "開啟 COM 埠成功", "提示", MB_OK);
    return S_OK;
}

STDMETHODIMP CPosCtrl::Quit() {
    (*Exit)(hCom);
    ExitDll();
    MessageBox(NULL, "關閉 COM 埠成功", "提示", MB_OK);
    return S_OK;
}

STDMETHODIMP CPosCtrl::ModifyPosDT(BSTR bstrDT) {
    return SetPosDateTime((char*)bstrDT) ? S_OK : S_FALSE;
}

STDMETHODIMP CPosCtrl::EmptyPos() {
    ClearPos();
    return S_OK;
}

硬體協議封裝:ASCII 轉 BCD、多路控制

void CPosCtrl::ASCII_TO_BCD(char* cSource, char* cResult, int iSourceLen, int iResultLen) {
    int iBegin = (iSourceLen % 2) ? 1 : 0;
    if (iBegin) cResult[0] = cSource[0] - '0';
    for (int i = iBegin; i < iResultLen; i++)
        cResult[i] = (cSource[2*i-iBegin]-'0')*16 + (cSource[2*i-iBegin+1]-'0');
    cResult[iResultLen] = '\0';
}

BOOL CPosCtrl::SetPosDateTime(char* pchDateTime) {
    unsigned char cInput[257], cOutput[256];
    BYTE byOutput, retVal;
    for (int n=1, i=0; i<8; i++, n<<=1) {  // 8 路裝置
        (*Pipe_control)(hCom, n);
        if (MessageBox(NULL, "確定修改時間?", "確認", MB_YESNO) == IDNO) return FALSE;
        ASCII_TO_BCD(pchDateTime, (char*)cInput, 10, 5);
        retVal = (*Protocol)(hCom, 0x85, 5, cInput, &byOutput, cOutput); // Com_DownDateTime
        MessageBox(NULL, retVal ? "修改成功" : "修改失敗", "結果", MB_OK);
    }
    (*Protocol)(hCom, 0x86, 5, cInput, &byOutput, cOutput); // Com_Exit
    (*Dev_escape)(hCom);
    (*Pipe_control)(hCom, 0);
    return TRUE;
}

部署與註冊

  1. 編譯產生 PosActiveX.dll。
  2. 註冊:regsvr32 C:\Path\PosActiveX.dll。
  3. 關鍵:Police.dll 必須置於系統目錄:
    • Win9x:Windows\System
    • NT/2000/XP:WINNT\System32 (或 System32)
    否則 LoadLibrary 找不到 DLL。

網頁端呼叫範例

<object id="posCtrl" classid="clsid:YOUR-CLSID" width=0 height=0></object>
<script>
    posCtrl.Init(1);           // 開啟 COM1
    posCtrl.ModifyPosDT("202401011200"); // 設定時間
    posCtrl.EmptyPos();        // 清空資料
    posCtrl.Quit();            // 關閉
</script>

注意事項與最佳實踐

  • 錯誤處理:每個 GetProcAddress 應檢查 NULL,避免呼叫空指標崩潰。
  • 字串轉換:BSTR 為 Unicode,傳給 ANSI DLL 需 WideCharToMultiByte 或 _bstr_t 轉換,範例直接轉型有風險。
  • 執行緒安全:ActiveX 可能多執行緒呼叫,共用 hCom 需關鍵區保護。
  • 資源清理:Quit 必須確保被呼叫(頁面卸載 onbeforeunload 呼叫),否則 COM 埠、DLL 句柄洩漏。
  • 安全性:簽署 CAB 檔、標記 Safe for Scripting/Initialization 才能在 IE 低安全性區域執行。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved