从C#编写的托管代码的Windows服务调用C++ dll(非托管代码)

6

我正在开发一个C++项目,其中包含不同的组件。我们需要将应用程序作为Windows服务启动。该项目是未经管理的C++代码。我编写了一个C# Windows服务和一个C风格的dll,该dll将具有启动不同组件的功能以及停止它们的另一个功能。该dll有两个文件,一个头文件和一个.cpp文件:

RTSS.h:
namespace PFDS
{
  extern "C" __declspec(dllexport) int runRTS(char*);
}

RTSS.cpp:

using namespace PFDS;
/* ... includes and declarations */
extern "C" __declspec(dllexport) int runRTS(char* service_name)
{
   g_reserved_memory = (char*) malloc(sizeof(char) * RESERVED_MEMORY_SIZE);
   _set_new_handler(memory_depletion_handler);
   // this function is from a C++ .lib which is included in
   // the linker input for the RTSS dll project setting.
   // SetUnhandledExceptionHandler("RTS");       
   return 0;
}

在Windows服务的ServiceBase子类中,我有以下内容:
[DllImport("RTSSd.dll")]
public static extern int runRTS(string serviceName);

protected override void OnStart(string[] args)
{
  try
  {
    // the bin directory has all dependencies (dlls needed)
    Environment.CurrentDirectory = "D:/work/projects/bin";
    eventLog1.WriteEntry("RTSWinService: Starting " + this.ServiceName);
    int result = runRTS(this.ServiceName);
    eventLog1.WriteEntry("Result of invoking runRTS = " + result);
  }
  catch (Exception e)
  {
    eventLog1.WriteEntry("Exception caught: " + e.ToString());
  }
}

此外,我有一个控制台测试应用程序,其中的主要代码类似于OnStart中的代码。当注释SetUnhandledException函数时,这两个应用程序都可以正常运行。但是当我取消注释该函数时,Windows控制台应用程序可以正常运行,但Windows服务输出以下异常:
System.DllNotFoundException: 无法加载DLL“RTSSd.dll”:找不到指定的模块。(来自HRESULT的异常:0x8007007E) 在RTSWS.RTSWinService.runRTS(String serviceName) 在D:\ work \ project \ ... \ RTSWinService.cs的RTSWS.RTSWinService.OnStart(String [] args):第39行
我在不同的论坛线程中读到,Windows服务在C:\WINDOWS\System32中启动,通过初始化DirectoryInfo并打印其完整名称可以证实。我尝试使用Environment.CurrentDirectory =“包含Windows服务可执行文件和dll的目录”更改默认启动目录,但没有成功。我还尝试更改进程目录,但也失败了。其他线程得出了这个结论link text,但真的是这样吗?可能有更简单的方法吗?
我应该注意到,SetUnhandledException函数是用C ++编写的,而不是C,并且许多其他需要调用的函数也是如此。所有所需的dll都放置在服务可执行文件旁边。非常感谢您的反馈。

健全性检查:打开依赖项查看器,查看您的 DLL 依赖于什么,然后确保您的 DLL 和其依赖的 DLL 都在您的路径上。 - Shog9
此外,您可以运行SysInternal的filemon来查看您的服务尝试加载哪些DLL。请记住,Windows服务在不同的用户会话(和桌面)中运行,因此具有自己的环境。并确保您的“d:”(或您安装到的任何位置)实际上是一个真正的本地路径,而不是网络路径或SUBST路径(已经完成了这个操作;-))。 - Chris O
进行了依赖性检查,但由于控制台应用程序可以无问题运行,我假设没有缺少的依赖项。顺便说一句,我遇到了在subst驱动器上安装服务的问题,而且很难将其移除 :) - vnammour
1
如果有人遇到类似的问题,当我将 Environment.CurrentDirectory 更改为我的 DLL 所在的目录时,它就可以工作了。 - fjsj
2个回答

2
当某些东西在控制台中运行但不作为服务运行时,我总是怀疑访问权限。请确保运行服务的用户具有对d:\work\projects\bin的读取/执行访问权限。
另一个建议是调用SetDllDirectory,这是一种比使用当前目录更直接地指定DLL位置的方法。请参见关于pinvoke dll搜索路径的此SO线程
您还可以使用SysInternal's ProcMon来观察系统尝试查找您的DLL。有时不是DLL本身出了问题,而是您的DLL所依赖的DLL出了问题,而ProcMon很适合解决这些问题。

非常感谢您的回答和评论。我很高兴它很快 :) - vnammour
我正在将服务作为本地服务运行。我也尝试使用用户身份运行它(我的帐户是此计算机上的管理员),以及本地系统,但结果并没有不同。我还从OnStart方法中调用了SetDllDirectory("D:\work\projects\bin"),返回值为true,但仍未解决问题。当我打开依赖项工具(depends.exe)并检查RTSSd.dll时,它会出现有关找不到IESHIMS.DLL和WER.DLL(可能是系统dll)的错误。但我认为如果有任何缺少的依赖项,控制台应用程序也会失败。 - vnammour
值得学习如何使用 procmon,因为如果显而易见的方法无法解决您的问题,您可能正在尝试解决错误的问题。Procmon 经常有助于查看是什么问题。 - Tony Lee
+1 推荐使用 ProcMon。这是更新后的下载链接:http://technet.microsoft.com/zh-cn/sysinternals/bb896645。 - fjsj

1

我只是想更新一下这个帖子。 事实证明,问题只存在于我的电脑上。相同的代码在另一台机器上运行得非常好,而且使用的是相同的Windows版本(Windows XP 32位,SP2)。在两台机器上,都使用了Visual Studio 2008来构建服务。 非常感谢所有的评论和答案。我很感激。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接