使用文件I/O正确创建和运行win32服务

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

本文关键字:创建 运行 win32 服务 文件      更新时间:2023-10-16

我已经基于这个代码示例编写了一个非常简单的服务应用程序。

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

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

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

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

我在windowsvista和windows7上试过这个。遇到同样的问题。

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

使用服务所需文件的绝对路径应该可以完全避免此问题。无论工作目录是什么,绝对路径都将被解释为相同的,因此这应该使服务的工作目录不相关。有几种方法可以做到这一点:

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

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

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

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

注:

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

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

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

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

对我来说,这项服务安装在50000台以上的计算机上。我们将其设计为从运行服务可执行文件的目录中加载。

现在,作为一个非系统流程来设置和实现这一点已经足够容易了(我的大部分测试都是作为一个无系统流程来完成的)。但问题是,您使用的系统包装器(我也使用过)使用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
        }
    }
}