【问题标题】:How can I get my process listening for WinEvents when started from a Scheduled Task?从计划任务启动时,如何让我的进程监听 WinEvents?
【发布时间】:2015-03-24 14:34:46
【问题描述】:

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

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

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

还和我在一起吗?到目前为止,我所描述的效果很好。但是,它仍然需要用户在每次启动应用程序时按一次是到 UAC 提示。我意识到我可以做得更好。

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

但是,现在我从计划任务启动 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
}

根据要求,我正在执行以下操作来创建计划任务。我必须使用 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,但显然 create 是一次性操作,这意味着我可以在安装过程中自己完成。从那时起,我所要做的就是打电话给schtasks.exe /Run /TN MySecondaryApp 来开始这个过程。因为已经在任务上设置了“以最高权限运行”属性,所以这绕过了对任何更多 UAC 提示的需要。

就像我说的,我已经验证了它的工作原理。以这种方式运行时似乎不起作用的是 WinEvents 侦听器。我刚刚遇到another SO question,它说在不同会话中运行的进程将无法在 WinEvents 中运行,这并没有让我对此抱有太大希望。

【问题讨论】:

  • 能看到定时任务吗?
  • @MatthewFrontino - 添加了有关计划任务的信息。
  • 在你的原则中试试这个:InteractiveToken IE:“仅在用户登录时运行”单选按钮。有一个无 GUI 模式的任务可能会导致您丢失 WinMessage。
  • 好吧,现在你有两个问题。谷歌“windows bypass uipi”以获得更好的方法。
  • @MatthewFrontino - 太棒了!将 LogonType 更改为 InteractiveToken(然后从我的 create 语句中删除 /RU 和 /RP 参数)就像一个魅力!如果您想提交它作为答案,我会接受。

标签: c# windows winapi clickonce interprocess


【解决方案1】:

在您的主体对象中,使用以下元素:

 <LogonType>InteractiveToken</LogonType>

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

【讨论】:

    猜你喜欢
    • 2018-01-01
    • 2013-10-15
    • 1970-01-01
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多