正确创建并运行带有文件I / O的Win32服务

问题描述 投票:3回答:5

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

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

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

我的问题是Windows服务何时运行,应该是预期的运行路径?

调用'CreateService'时,似乎只有二进制文件的路径参数,而不是执行文件的路径参数。是否有某种方式指示应从何处执行二进制文件?

我已经在Windows Vista和Windows 7上尝试过。出现相同的问题。

c++ c winapi service
5个回答
6
投票

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

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

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

2
投票

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

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


2
投票

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

注意:

您需要使用AdjustTokenPrivileges function添加一些特权,您可以在here功能中看到一个示例ModifyPrivilege

也请务必使用HKEY_LOCAL_MACHINE,而不要使用HKEY_CURRENT_USER。服务在不同的用户帐户下运行,因此它的HKCU与您在注册​​表编辑器中看到的有所不同。


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-然后处理其内容。


0
投票

使用此功能可以将服务的工作目录调整为与正在运行的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
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.