如何从Scheduled Task启动时让我的进程监听WinEvents?

我有一个build立在C#中的ClickOnce应用程序。 如您所知,按devise,ClickOnce应用程序无法以pipe理权限运行。 但是,如果需要执行需要提升特权的函数,则可以将另一个任意EXE与ClickOnce应用程序一起打包,并根据需要将应用程序调用到单独的EXE。

麻烦的是,当你每次调用单独的应用程序时,都会触发UAC提示,询问用户是否真的想用提升的特权运行。 这个特定的应用程序是在一个非常封闭的环境中,我可以确保我总是希望提升权限部分运行。 但是,当提供给普通用户时,有时UAC提示可能会显得令人恐惧。

所以为了缓解这个问题,我build立了一个EXE,它只会触发一次,然后继续在后台运行并监听WinEvents 。 然后我的主进程根据需要触发各种WinEvent,我把它放在AIA_START( 0xA000 )和AIA_END( 0xAFFF )之间,因为这些是社区保留事件。

还在我这儿? 到目前为止,我所描述的已经很完美了。 但是,它仍然需要用户在每次启动应用程序时都按一次“是”进入UAC提示。 我意识到我可以做得更好。

通过创build一个启动辅助EXE的Windows Scheduled Task,我只需要在UAC提示符上单击Yes,即可安装。 之后,我可以简单地触发计划任务,然后以提升的权限启动EXE。 这似乎也在工作。 我可以validation我的EXE是否具有pipe理权限启动,而且我已经有效地绕过了UAC提示,这很好。

然而 ,现在我从一个Scheduled Task启动EXE而不是直接启动EXE,看起来它不再是监听我在ClickOnce应用程序中触发的WinEvents。 或者,如果它正在倾听(我认为是这样),那么有一些东西阻碍了沟通。 我不太确定什么是阻碍的东西,因为看起来这两个进程仍在Windows下的同一用户帐户下运行。

有谁知道为什么我的辅助进程似乎不再响应WinEvents时,通过计划任务启动? 我该如何解决这个问题? 我意识到我可以使用不同的通信线路(在TCP端口上侦听,在共享文件上连续轮询等),但是因为我已经写了用于发送和侦听WinEvents的代码,所以我更愿意继续使用那。

如果您好奇,下面是我用来侦听WinEvents的代码的简要概述:

 public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); [DllImport("user32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); private void App_OnStartup(object sender, StartupEventArgs e) { var handler = new WinEventDelegate(MyHandlerMethod); SetWinEventHook(0xA000, 0xAFFF, IntPtr.Zero, handler, 0, 0, 0x0002); } private void MyHandlerMethod(IntPtr hook, uint evt, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { // handle the incoming WinEvents here } 

按照要求,这是我正在做什么来创build计划任务。 我必须使用XML文件创build它,以便可以将DisallowStartIfOnBatteries属性设置为false。 所以使用下面的XML文件…

 <?xml version="1.0" encoding="UTF-16"?> <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <RegistrationInfo> <Date>2015-03-20T11:10:15</Date> <Author>myuser</Author> </RegistrationInfo> <Triggers> <TimeTrigger> <StartBoundary>2014-01-01T00:00:00</StartBoundary> <Enabled>true</Enabled> </TimeTrigger> </Triggers> <Principals> <Principal id="Author"> <UserId>myuser</UserId> <LogonType>Password</LogonType> <RunLevel>HighestAvailable</RunLevel> </Principal> </Principals> <Settings> <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries> <AllowHardTerminate>true</AllowHardTerminate> <StartWhenAvailable>false</StartWhenAvailable> <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> <IdleSettings> <StopOnIdleEnd>true</StopOnIdleEnd> <RestartOnIdle>false</RestartOnIdle> </IdleSettings> <AllowStartOnDemand>true</AllowStartOnDemand> <Enabled>true</Enabled> <Hidden>false</Hidden> <RunOnlyIfIdle>false</RunOnlyIfIdle> <WakeToRun>false</WakeToRun> <ExecutionTimeLimit>P3D</ExecutionTimeLimit> <Priority>7</Priority> </Settings> <Actions Context="Author"> <Exec> <Command>C:\MySecondaryApp.exe</Command> </Exec> </Actions> </Task> 

…我打电话给schtasks.exe /Create /TN MySecondaryApp /XML task.xml /RU myuser /RP mypassword来创build任务。 此操作确实要求用户在UAC提示符下按“是”,但显然创build是一次性操作,这意味着我可以在安装过程中自行完成。 从那时起,我所要做的就是调用schtasks.exe /Run /TN MySecondaryApp来启动进程。 由于已经在任务上设置了“以最高权限运行”属性,因此绕过了需要更多UAC提示。

就像我说的,我已经证实这个工作。 当运行这种方式时,似乎不起作用的是WinEvents侦听器。 我刚刚遇到另外一个SO问题 ,它提到在不同的会话中运行的进程将无法胜出WinEvents,这并不会让我感到太有希望。

在你的主要对象中,使用下面的元素:

  <LogonType>InteractiveToken</LogonType> 

添加此XML标记会将计划任务更改为“仅在用户登录时运行”选项,该选项与完整的GUI模式相关,您将在其中再次看到您的Windows消息。