从计划任务启动时如何让我的进程监听 WinEvents?

How can I get my process listening for WinEvents when started from a Scheduled Task?

我有一个用 C# 构建的 ClickOnce 应用程序。如您所知,ClickOnce 应用程序在设计上不能 运行 具有管理权限。但是,如果您需要执行需要提升权限的功能,您可以将另一个任意 EXE 与您的 ClickOnce 应用程序打包在一起,并让您的应用程序根据需要调用该单独的 EXE。

当然,问题是每次调用单独的应用程序时,它都会触发 UAC 提示,询问用户是否真的想 运行 提升权限。这个特定的应用程序处于一个非常封闭的环境中,我可以确保我始终希望提升权限部分为 运行。然而,当呈现给普通用户时,有时 UAC 提示可能看起来很可怕。

因此,为了缓解这种情况,我以一种只触发一次的方式构建了我的 EXE,然后在后台继续 运行ning 并监听 WinEvents。然后,我的主进程根据需要触发各种 WinEvent,我将其置于 AIA_START (0xA000) 和 AIA_END (0xAFFF) 之间的范围内,因为这些是社区保留事件。

还在我身边吗?到目前为止,我所描述的一切都完美无缺。但是,它仍然需要用户在每次启动应用程序时按一次 UAC 提示。我意识到我可以做得更好。

通过创建一个 Windows 计划任务来触发我的辅助 EXE,然后我只需要在 UAC 提示符下单击“是”——安装时。之后,我将能够简单地触发计划任务,进而以提升的权限启动 EXE。这似乎也有效。我可以验证我的 EXE 是否以管理权限启动,而且我每次都有效地绕过了 UAC 提示,这太棒了。

但是,既然我是从计划任务启动 EXE 而不是直接启动它,它似乎不再监听我触发的 WinEvents我的 ClickOnce 应用程序。或者,如果它正在倾听(我认为是这样),那么通信就会受到阻碍。我不太确定是什么阻碍了事情,因为看起来这两个进程仍在 Windows.

中的同一用户帐户下 运行ning

有谁知道为什么我的辅助进程在通过计划任务启动时似乎不再响应 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
}

根据要求,这是我创建计划任务的方法。我必须使用 XML 文件创建它,以便我可以将 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 来创建任务。此操作确实需要用户在 UAC 提示符下按 Yes,但显然创建是一次性操作,这意味着我可以在安装过程中自己完成。从那时起,我要做的就是调用schtasks.exe /Run /TN MySecondaryApp 来启动进程。因为 "Run with Highest Privileges" 属性 已经在任务上设置,所以这绕过了对任何更多 UAC 提示的需要。

就像我说的,我已经验证了这行得通。以这种方式 运行 时,WinEvents 侦听器似乎不起作用。我刚刚遇到 another SO question 说了一些关于进程的事情 运行 在不同的会话中 ning 将无法赢得 WinEvents,这并没有让我对此抱太大希望。

在您的原则对象中,使用以下元素:

 <LogonType>InteractiveToken</LogonType>

添加此 XML 标记会将计划任务更改为 "Run only when user is logged on" 选项,这与完整的 GUI 模式相关,您将再次看到 windows 消息。