简体   繁体   中英

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

I have a ClickOnce application built in C#. As you know, ClickOnce applications, by design, cannot run with administrative privileges. However, if you need to perform functions that require elevated privileges, what you can do is package another arbitrary EXE along with your ClickOnce application, and have your application call out to that separate EXE as needed.

The trouble, of course, is that each time you call out the separate application, it triggers a UAC prompt asking the user if they really want to run with elevated privileges. This particular application is in a very closed environment, and I can ensure that I always want the elevated privileges part to run. However, when presented to average users, sometimes that UAC prompt can seem scary.

So to mitigate this, I built my EXE in a way that it only fires once, then continues running in the background and listens for WinEvents . My main process then triggers various WinEvents as needed, which I put in the range between AIA_START ( 0xA000 ) and AIA_END ( 0xAFFF ), since those are community reserved events.

Still with me? So far, what I've described has worked perfectly. However it still requires the user to press Yes to a UAC prompt once every time you start the app. And I realized I could do better.

By creating a Windows Scheduled Task which fires my secondary EXE, I would then only need to click Yes on the UAC prompt, ever -- upon installation. Afterwards, I would simply be able to trigger the Scheduled Task to in turn launch the EXE with elevated privileges. And this seemed to be working too. I can verify that my EXE is launching with administrative rights, and I've effectively bypassed the UAC prompt every time, which is great.

However , now that I'm launching the EXE from a Scheduled Task rather than starting it directly, it appears it is no longer to listening the WinEvents I'm triggering in my ClickOnce application. Or, if it is listening (which I presume it is), there's something getting in the way of that communication. I'm not really sure what is obstructing things, as it appears both processes are still running under the same user account in Windows.

Does anyone know why my secondary process no longer seems to respond to WinEvents when started via a Scheduled Task? And how do I solve this? I realize I could use a different line of communication (listen on a TCP port, poll continuously on a shared file, etc.), but since I already have the code written to send and listen for WinEvents, I would much prefer to continue using that.

And if you're curious, here's a brief overview of the code I'm using to listen for 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
}

As requested, here's what I'm doing to create the Scheduled Task. I have to create it using an XML file so that I can set the DisallowStartIfOnBatteries property to false. So using the following XML file...

<?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>

...I call schtasks.exe /Create /TN MySecondaryApp /XML task.xml /RU myuser /RP mypassword to create the task. This operation does require the user to press Yes to the UAC prompt, but obviously the create is a one-time operation, meaning I can do it myself during the installation. From then on, all I have to do is call schtasks.exe /Run /TN MySecondaryApp to start the process. Because the "Run with Highest Privileges" property is already set on the task, this bypasses the need for any more UAC prompts.

And like I said, I've already verified that this works. What doesn't seem to work is the WinEvents listener when running this way. I just came across another SO question which said something about processes running in different sessions won't work win WinEvents, which doesn't leave me feeling too hopeful about this.

In your principle object, use the following element:

 <LogonType>InteractiveToken</LogonType>

Adding this XML tag changes the scheduled task to "Run only when user is logged on" option, which correlates to the full GUI mode where you will see your windows messages again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM