[英]Correctly creating and running a win32 service with file I/O
我已經基於此代碼示例編寫了一個非常簡單的服務應用程序。
應用程序作為其正常運行的一部分,假定在它找到的目錄中或其執行路徑中存在一個文件。
當我“安裝”服務,然后從控制面板中的服務管理器“啟動”服務時。 應用程序失敗,因為它找不到要打開和讀取的文件(即使該文件與已安裝的可執行文件位於同一目錄中)。
我的問題是當 Windows 服務運行時,預期的運行路徑應該是什么?
調用“CreateService”時,似乎只有二進制文件的路徑參數,而不是用於執行的路徑參數。 有沒有辦法指示應該從哪里執行二進制文件?
我在 windows vista 和 windows 7 上試過這個。遇到同樣的問題。
由於 Windows 服務在與普通用戶模式應用程序不同的上下文中運行,因此最好不要對工作目錄或相對路徑進行任何假設。 除了工作目錄的差異外,服務可以使用完全不同的權限集等運行。
使用服務所需文件的絕對路徑應該可以完全避免這個問題。 無論工作目錄如何,絕對路徑都將被解釋為相同,因此這應該使您的服務的工作目錄無關緊要。 有幾種方法可以解決這個問題:
默認情況下,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.