簡體   English   中英

使用文件 I/O 正確創建和運行 win32 服務

[英]Correctly creating and running a win32 service with file I/O

我已經基於此代碼示例編寫了一個非常簡單的服務應用程序。

應用程序作為其正常運行的一部分,假定在它找到的目錄中或其執行路徑中存在一個文件。

當我“安裝”服務,然后從控制面板中的服務管理器“啟動”服務時。 應用程序失敗,因為它找不到要打開和讀取的文件(即使該文件與已安裝的可執行文件位於同一目錄中)。

我的問題是當 Windows 服務運行時,預期的運行路徑應該是什么?

調用“CreateService”時,似乎只有二進制文件的路徑參數,而不是用於執行的路徑參數。 有沒有辦法指示應該從哪里執行二進制文件?

我在 windows vista 和 windows 7 上試過這個。遇到同樣的問題。

由於 Windows 服務在與普通用戶模式應用程序不同的上下文中運行,因此最好不要對工作目錄或相對路徑進行任何假設。 除了工作目錄的差異外,服務可以使用完全不同的權限集等運行。

使用服務所需文件的絕對路徑應該可以完全避免這個問題。 無論工作目錄如何,絕對路徑都將被解釋為相同,因此這應該使您的服務的工作目錄無關緊要。 有幾種方法可以解決這個問題:

  1. 硬編碼絕對路徑- 這可能是避免問題的最簡單方法,但它也是最不靈活的。 這種方法可能適用於基本的開發和測試工作,但在其他人開始使用您的程序之前,您可能想要更復雜的東西。
  2. 將絕對路徑存儲在環境變量中- 這為您提供了額外的靈活性,因為現在可以將路徑設置為任意值並根據需要進行更改。 由於服務可以作為具有不同環境變量集的不同用戶運行,因此這種方法仍然存在一些問題。
  3. 在注冊表中存儲絕對路徑- 這可能是最簡單的方法。 從注冊表中檢索路徑將為所有用戶帳戶提供相同的結果,而且這在安裝時設置起來相對容易。

默認情況下,Windows 服務的當前目錄是 System32 文件夾。

一個有前途的解決方案是創建一個環境變量,保留輸入位置的完整路徑,並在運行時從該變量中檢索路徑。

如果您使用與二進制相同的路徑,則可以讀取二進制路徑並相應地修改它。 但這是相當快速的修復而不是設計的解決方案。 如果我是你,我要么創建系統范圍的環境變量並在那里存儲值,要么(甚至更好)使用 Windows 注冊表來存儲服務配置。

筆記:

您需要使用AdjustTokenPrivileges 函數為自己添加一些權限,您可以在ModifyPrivilege函數中查看此處的示例。

還要確保使用 HKEY_LOCAL_MACHINE 而不是 HKEY_CURRENT_USER。 服務在不同的用戶帳戶下運行,因此它的 HKCU 將與您在注冊​​表編輯器中看到的不同。

今天我解決了這個問題,因為我正在開發的一些軟件需要它。

正如上面的人所說; 您可以將目錄硬編碼到特定文件 - 但這意味着需要加載的任何配置文件都必須放在那里。

對我來說,這項服務安裝在超過 50,000 台計算機上。 我們將其設計為從運行服務可執行文件的目錄加載。

現在,這很容易作為非系統進程進行設置和實現(我的大部分測試都是作為非系統進程進行的)。 但問題是您使用的系統包裝器(我也使用過)使用 Unicode 格式(並依賴於它),因此傳統的方法行不通。

代碼的注釋部分應該解釋這一點。 我知道有一些冗余,但我在寫這篇文章時只想要一個工作版本。 幸運的是,您可以使用 GetModuleFileNameA 以 ASCII 格式處理它

我使用的代碼是:

char buffer[MAX_PATH]; // create buffer
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH); // Get file path in ASCII

std::string configLoc; // make string

for (int i = 0; i < strlen(buffer); i++) // iterate through characters of buffer
{
    if (buffer[i] == '\\') // if buffer has a '\' in it, replace with doubles
    {
        configLoc = configLoc + "\\\\"; // doubles needed for parsing. 4 = 2(str)
    }
    else
    {
        configLoc = configLoc + buffer[i]; // else just add char as normal
    }
}

// Complete location
configLoc = configLoc.substr(0, configLoc.length() - 17); //cut the .exe off the end
                                                          //(change this to fit needs)   
configLoc += "\\\\login.cfg"; // add config file to end of string

從這里開始,您可以簡單地將 configLoc 解析為新的 ifsteam - 然后處理內容。

使用此功能將服務的工作目錄調整為與其運行的 exe 的工作目錄相同。

void AdjustCurrentWorkingDir() {
    TCHAR szBuff[1024];
    DWORD dwRet = 0;
    dwRet = GetModuleFileName(NULL, szBuff, 1024); //gets path of exe

    if (dwRet != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
        *(_tcsrchr(szBuff, '\\') + 1) = 0; //get parent directory of exe

        if (SetCurrentDirectory(szBuff) == 0) {
            //Error
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM