从计划任务启动时如何让我的进程监听 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 消息。
我有一个用 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 消息。