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

MFC 對話框型 NT 服務程式框架:開機自動執行與系統托盤整合

     編輯:關於VC++

專案概述

本框架實現一個同時具備 MFC 對話框介面與 NT 服務身分的應用程式,可在使用者登入前於開機階段自動啟動。整合系統托盤功能,支援安裝/移除服務、除錯模式、優雅關機與 Explorer 重啟時的圖示自動恢復。

核心架構特點

  • 雙重身分:既是 GUI 應用程式,也是標準 NT 服務
  • 資源字串配置:服務名稱、顯示名稱、依賴服務皆在 String Table 定義 (IDS_SZSERVICENAME 等)
  • 命令列參數:-install 安裝、-remove 移除、-debug 除錯模式
  • WM_ENDSESSION 處理:解決 MFC 對話框在使用者登出/重新登入後失去回應的關鍵問題
  • 工作目錄固定:啟動時 SetCurrentDirectory 至模組所在目錄,便於相對路徑存取
  • 系統托盤強化:處理選單失焦自動隱藏、Explorer 崩潰重建時圖示自動恢復

關鍵技術細節

服務安裝與控制


// 服務安裝邏輯 (簡化)
if (strcmp(cmdParam, "-install") == 0) {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
    SC_HANDLE svc = CreateService(scm, 
        serviceName, displayName,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
        exePath, NULL, NULL, dependencies, NULL, NULL);
}
// 啟動/停止:net start/stop ServiceName 或服務管理員

MFC 對話框與服務主執行緒協調


// 服務主函數
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv) {
    // 註冊控制處理器
    g_statusHandle = RegisterServiceCtrlHandler(serviceName, ServiceCtrlHandler);
    // 初始化完成回報
    SetServiceStatus(g_statusHandle, &status);
    
    // 建立 MFC 應用程式物件並進入訊息迴圈
    CWinApp* pApp = AfxGetApp();
    pApp->InitInstance();  // 包含對話框建立
    pApp->Run();           // 訊息迴圈
}

// 控制處理器
void WINAPI ServiceCtrlHandler(DWORD ctrlCode) {
    switch (ctrlCode) {
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
            // 發送 WM_CLOSE 給主視窗
            PostMessage(hMainWnd, WM_CLOSE, 0, 0);
            break;
    }
}

WM_ENDSESSION 關鍵處理


void CMyDialog::OnEndSession(BOOL bEnding) {
    if (bEnding) {
        // 系統關機/登出時,必須正確關閉資源
        // 否則使用者重新登入後對話框將失去回應
        CleanupResources();
    }
    CDialog::OnEndSession(bEnding);
}

系統托盤實現要點

  • 使用 Shell_NotifyIcon(NIM_ADD, ...) 註冊圖示
  • 自訂訊息 WM_TRAYNOTIFY 處理滑鼠事件
  • 右鍵選單動態更新:視窗可見時顯示「隱藏」,否則顯示「顯示」
  • TaskbarCreated 訊息處理:Explorer 重啟時重新註冊圖示

防止誤關閉機制


// 系統選單與關閉鈕均改為隱藏
void CMyDialog::OnSysCommand(UINT nID, LPARAM lParam) {
    if ((nID & 0xFFF0) == SC_CLOSE) {
        AnimateWindow(1000, AW_HIDE | AW_BLEND); // 淡出動畫
        ShowWindow(SW_HIDE);
        return;
    }
    CDialog::OnSysCommand(nID, lParam);
}

// 真正退出僅透過托盤選單「退出」項
void CMyDialog::OnTrayExit() {
    if (AfxMessageBox("確定退出?", MB_YESNO) == IDYES) {
        ::PostQuitMessage(0);
    }
}

網路依賴與認證處理

服務在使用者登入前啟動,若需存取網路共享:

  1. 將「網路連線」服務加入依賴清單 (IDS_SZDEPENDENCIES)
  2. 使用 WNetAddConnection2 提供明確使用者名稱/密碼

關於視窗工作站的疑問

文獻常建議呼叫 OpenWindowStation("winsta0") 以取得互動式桌面存取權,但本框架未顯式呼叫仍能正常顯示介面,具體原因有待深入研究 (可能與服務類型 SERVICE_INTERACTIVE_PROCESS 相關)。

授權聲明

本程式碼為業餘時間開發,雖曾用於商業專案,但版權歸屬個人,不屬任何公司。自由複製、修改、發布,無需授權。

測試環境

  • Windows 2000 Professional
  • Visual C++ 6.0 + SP5
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved