最近一个面试,面试官说他们现在的架构是通过IIS 部署的Web Service 调用Server 端的Windows Application 也就是exe。
面试拉跨之后自己尝试了一下这种实现方式,在这里记录一下自己遇到的坑,然后留一下查到的解决方案。
(PS:虽然不理解这种架构的使用场景,但也就是闲得无聊试试看。。。。)
简单写好一个Web Service 的demo 之后,Debug run 了一下,发现一切完美,exe 正常执行,页面打开顺利。
发布至IIS 后发现,访问Web Service 的Method,提示只允许local 调用,应该是Web.config 没有配置HttpGet,添加后解决。
再次尝试,发现Web Service 正常运行,正在等待Process 结束(Method 最后代码为process.WaitForExit()), 但没有exe 的页面弹出。
开始以为是防火墙或代码问题,检查之后发现不是。
打开任务管理器后发现,exe 的process 正常执行,但没有页面。
百度之后发现好多前辈都出过类似的问题,挨个方法尝试。
包括但不限于:
1. 修改IIS 权限
2. 修改IIS machine.config
3. IIS Admin Service, allow service to interact with desktop
以上方法均已失败告终。
转战至Google,刚开始发现的解决方案和百度的一毛一样,直到发现一个回复,回复中说:
Zoltán Zörgő 5-Nov-13 5:07am
As IIS is a service it is not running under a separate session - different from the one you are logged in, and under a different user account! So you can't see the application, even if it is really started. Still, there are only few situations when starting a process on server side from an asp.net application is wise - use this approach only when the application you start is command line application, and never requires user interaction to terminate (even with error condition).
大体上的意思是说:
Windows Service 和Login 桌面是在两个Session 中(此处只Process 的Session ID),而IIS 是一个Windows Service,因此通过IIS 起的Process 无法展示到当前User Login 的桌面中。
按照这个思路往下查询,发现是自Windows Vista 起,巨硬修改了Desktop 的显示,即Login,Windows Service 及UAC 分别处于三个桌面中,Process的SessionID 不同,无法直接相互切换。
在自己的IIS 服务器上打开任务管理器,在Colunms 中选中SessionID 发现,w3wp 的SessionID 为1,而winlogon Process 的SessionID 为0,符合发现的结果。
那么为什么本机Debug 好用呢?
想了一下,可能是Debug 时使用的时IIS Express, 这是个进程,有VS 启动,和winlogon Process 在同一个桌面的Session 中,因此可以成功调用exe 并展示页面。
那,是否有解决方案呢?
身边的C++ 同事说,可以通过获取SessionID,将执行exe 的process 挂到Winlogon 的Session 中解决。
按照同事的思路,Google 到一篇文章,成功解决了个问题,文章的链接为:
How to make windows service interactivity with desktop application
文章中关于这个问题的描述和解决方案都给出了明确的说明,在此就不多做解释了。
为了方便网络可能不那么好的小伙伴,单纯的把代码贴在下面,当然更推荐去阅读一下具体的内容~
代码中需要注意.Net Framework 的版本,我开始使用4.0,发现4.0 没有WindowsIdentity.RunImpersonated,改到4.8 后解决。
其次是
// retrieve the pri