【问题标题】:Correctly creating and running a win32 service with file I/O使用文件 I/O 正确创建和运行 win32 服务
【发布时间】:2012-08-28 13:10:05
【问题描述】:

我已经基于这个code example 编写了一个非常简单的服务应用程序。

应用程序作为其正常运行的一部分假定在它找到的目录或其执行路径中存在一个文件。

当我“安装”服务并随后从控制面板中的服务管理器“启动”服务时。应用程序失败,因为它找不到要打开和读取的文件(即使该文件与已安装的可执行文件位于同一目录中)。

我的问题是,当一个 Windows 服务运行时,预期的运行路径应该是什么?

调用“CreateService”时,似乎只有二进制文件的路径参数,而不是执行。有什么方法可以指出应该从哪里执行二进制文件?

我在 windows vista 和 windows 7 上试过这个。遇到同样的问题。

【问题讨论】:

  • 没有。 GetModuleFileName(NULL, ...) 仍然可以找到 EXE 的位置。

标签: c++ c winapi service


【解决方案1】:

今天我解决了这个问题,因为我正在开发的一些软件需要它。

正如上面的人所说;您可以将目录硬编码为特定文件 - 但这意味着任何需要加载的配置文件都必须放在那里。

对我来说,这项服务安装在超过 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 - 然后处理内容。

【讨论】:

    【解决方案2】:

    使用该功能可以将服务的工作目录调整为与其运行的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
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果你使用与二进制相同的路径,你可以只读取二进制路径并相应地修改它。但这是相当快速的解决方案,而不是设计的解决方案。如果我是你,我会创建系统范围的环境变量并在那里存储值,或者(甚至更好)使用 Windows 注册表来存储服务配置。

      注意:

      您需要使用AdjustTokenPrivileges function 为自己添加一些权限,您可以在ModifyPrivilege 函数中查看示例here

      还要确保使用 HKEY_LOCAL_MACHINE 而不是 HKEY_CURRENT_USER。服务在不同的用户帐户下运行,因此它的 HKCU 将与您在注册​​表编辑器中看到的不同。

      【讨论】:

        【解决方案4】:

        由于 Windows 服务的运行环境与普通用户模式应用程序不同,因此最好不要对工作目录或相对路径做出任何假设。除了工作目录的差异之外,服务还可以使用完全不同的权限集等运行。

        对您的服务需要的文件使用绝对路径应该可以完全避免这个问题。无论工作目录如何,绝对路径都将被解释为相同的,因此这应该使您的服务的工作目录无关紧要。有几种方法可以解决这个问题:

        1. 硬编码绝对路径 - 这可能是避免问题的最简单方法,但也是最不灵活的方法。这种方法可能适用于基本的开发和测试工作,但在其他人开始使用您的程序之前,您可能需要一些更复杂的东西。
        2. 将绝对路径存储在环境变量中 - 这为您提供了额外的灵活性,因为现在可以将路径设置为任意值并根据需要进行更改。由于服务可以作为具有不同环境变量集的不同用户运行,因此这种方法仍然存在一些问题。
        3. 在注册表中存储绝对路径 - 这可能是最简单的方法。从注册表中检索路径将为所有用户帐户提供相同的结果,而且在安装时设置相对容易。

        【讨论】:

        • 似乎可以做到,但只需更改当前路径,这里有一个 C# 解决方案:stackoverflow.com/questions/884963/… 但我不知道从哪里开始找到 c/c++ winapi 等效项。跨度>
        • 不需要更改当前路径。与其更改为特定路径然后访问位于该处的文件,不如将路径和文件名组合成绝对路径并直接访问该文件。它的步骤更少,API 调用更少,并且避免了更改工作目录可能导致的任何不可预见的后果。
        【解决方案5】:

        默认情况下,Windows 服务的当前目录是 System32 文件夹。

        一个有前途的解决方案是创建一个环境变量来保存输入位置的完整路径,并在运行时从该变量中检索路径。

        【讨论】:

        • 那么当应用程序作为服务运行时,它不是从与可执行路径相同的目录运行的?
        • 在哪里可以找到这个 win32 目录?是在 c:\windows
        • 抱歉,system32 位于 C:\Windows\System32
        猜你喜欢
        • 2020-05-09
        • 2012-11-25
        • 1970-01-01
        • 2012-03-01
        • 2015-03-03
        • 1970-01-01
        • 2011-09-07
        • 2012-07-03
        • 1970-01-01
        相关资源
        最近更新 更多