从.NET Windows服务调用本地DLL失败

3
我有一个第三方API,以本地dll的形式存在,我使用DllImport从C#调用它。这个本地dll依赖于第三方应用程序的打开状态。
当我正常运行代码时,API像预期那样驱动应用程序。然而,当我将相同的代码作为Windows服务运行时,即使是作为自己,API也会返回相同的(未记录的)错误代码,我在关闭应用程序时也看到了这种情况;进程资源管理器确认本地dll已正确从应用程序目录加载。
这可能是什么原因,并且我如何解决这个问题?

你尝试过使用一个虚拟的非托管 DLL 来加载以确定错误发生的位置吗:是在 P/Invoke 中还是在你自己的 DLL 中? - abatishchev
@abatishchev - 不,但这绝对不是服务的问题,我已经将服务剥离到只有p/invoke非托管dll。当Fadrian Sudaman说问题可能与应用程序和服务在不同会话中运行有关时,我认为他是正确的,或者类似于此的问题。第三方软件非常糟糕。 - satnhak
从控制台应用程序中使用p/invoke可以正常工作,但从Windows服务中无法工作? - abatishchev
@abatishchev - 是的,那就是情况。 - satnhak
当dll文件所在路径包含空格时,我遇到了问题。 - Andreas
3个回答

2
有点老,但在搜索结果中排名靠前。因此我认为我的数据仍然有用。
引用:
我有一个第三方API,它以本地dll的形式存在,我使用DllImport从C#中调用它。这个本地dll依赖于第三方应用程序的打开。
(t)rusty Office Interop DLL也是一样的。实际上,您正在启动所讨论的Office程序的后台实例。即使它不显示任何内容(设计假设/错误在工作中),后台实例也需要交互式会话。
自Windows Vista以来,服务不在交互式会话中运行。不建议使用覆盖。
这是不再使用Office Interop的(3?)原因之一。
可能的解决方法:
将以下英文文本翻译成中文:
  1. 停止使用服务。Windows任务计划程序可以完成几乎相同的工作,同时为您提供完整的交互式会话。即使是微软自己也开始将一些东西从服务中移出,尽可能地移到计划程序中。
  2. 将DLL访问移入一个辅助进程。该进程可以在交互式会话中运行。使用任何进程间通信方式在辅助程序和主服务之间进行通信。这种模式主要用于从x64程序中处理仅适用于x32的dll,但在这里也应该适用。无论是清单还是Process.Start()都有一种方法可以从服务中交互地启动程序。

根据其中一条评论,似乎您选择了选项2,但使用Web服务代替。 不幸的是,Web服务/ Web应用程序通常作为Windows服务运行。而且,在考虑它们运行在最严格的权限之一(因为它们可以通过网络访问)之前,这只会让您回到原点,甚至退步到方案0。


1

很难说,但我能想到三种可能性:

  • 您的服务具有需要选中“与桌面交互”选项的UI组件
  • Windows服务的工作目录为%WinDir%\system32(例如C:\windows\system32),而您的dll具有使用相对路径引用其他无法找到的资源的代码
  • 您的服务使用netpipe通信,应用程序在两个不同的会话中运行

我已经尝试了第一种方法,但没有效果。代码可以从任何位置运行而无需提供任何路径,因此排除了第二种方法。然而,第三种方法听起来很有可能 - 服务正在不同的会话/域中运行,这导致它无法工作。我该如何验证这一点,如果有的话,有哪些解决方法? - satnhak
很遗憾,没有很多解决方法。会话隔离是操作系统安全的一部分。您可以在stackoverflow或谷歌上搜索“netpipe会话隔离”等相关内容,并尝试建议的解决方法,但个人认为这可能是一段相当具有挑战性的旅程。 - Fadrian Sudaman
1
我认为我要采取的黑科技是将dll包装在一个 Web 服务中,在启动时运行,然后从Web服务调用。那几乎肯定会 work。我希望这个项目很快就会被取消 :) - satnhak
使用Web服务作为中介可以完成工作。这是一个不好的解决方案,但它是一个不好的软件,因此与整体架构非常匹配 :) - satnhak
看起来你刚刚证明了另一个遗留系统集成与WS的结合 :p - Fadrian Sudaman
如果这只是一个传统系统就好了。这是一个新的全球企业解决方案!就像我说的,我希望它很快被取消 :p - satnhak

0
如果您需要从服务中调用本地dll,在启动之前,请尝试像这样更改当前目录:
static void Main()
{
    System.Environment.CurrentDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
    System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);

    ServiceBase[] ServicesToRun;
    ServicesToRun = new ServiceBase[]
    {
        new XYZService()
    };
    ServiceBase.Run(ServicesToRun);
}

在此之后,该服务无需更改环境变量或将本地DLL移动到系统文件夹即可轻松找到本地DLL。


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