About Async Tasks and Disposal

2019-08-09 23:22发布

问题:

In my MainWindow I have a button that can be used to open a Process (native OpenProcess call) and perform some checks on it's memory, but the method called on Click is asynchronous:

<Button Content="Attach" Click="OnClickAttach"/>

private async void OnClickAttach(Object sender, RoutedEventArgs e)
{
    AttachmentResult result = await m_ViewModel.Attach();

    switch (result)
        // Different MessageBox depending on the result.
}

Now, let's see the ViewModel portion of code...

// MemoryProcess class is just a wrapper for Process' handle and memory regions.
private MemoryProcess m_MemoryProcess;

public async Task<AttachmentResult> Attach()
{
    AttachmentResult result = AttachmentResult.Success;
    MemoryProcess memoryProcess = NativeMethods.OpenProcess(m_SelectedBrowserInstance.Process);

    if (memoryProcess == null)
        result = AttachmentResult.FailProcessNotOpened;
    else
    {
        Boolean check1 = false;
        Boolean check2 = false;

        foreach (MemoryRegion region in memoryProcess)
        {
            // I perform checks on Process' memory regions and I eventually change the value of check1 or check2...
            await Task.Delay(1);
        }

        if (!check1 && !check2)
        {
            NativeMethods.CloseHandle(memoryProcess.Handle);
            result = AttachmentResult.FailProcessNotValid;
        }
        else
        {
            // I keep the Process opened for further use. I save it to a private variable.
            m_MemoryProcess = memoryProcess;
            m_MemoryProcess.Check1 = check1;
            m_MemoryProcess.Check2 = check2;
        }
    }

    return result;
}

Now... here comes the problem. When the user closes the application, if a Process is opened, I must properly close its handle. So in my MainWindow I have the following code:

protected override void OnClosing(CancelEventArgs e)
{
    m_ViewModel.Detach();
    base.OnClosing(e);
}

And in my ViewModel I have the following code:

public void Detach()
{
    if (m_MemoryProcess != null)
    {
        if (m_MemoryProcess.Check1)
            // Do something...

        if (m_MemoryProcess.Check2)
            // Do something...

        NativeMethods.CloseHandle(m_MemoryProcess.Handle);
        m_MemoryProcess = null;
    }
}

The Attach() method can take very long time, more than 2 minutes sometimes. I need to find a solution for the following issues:

  • If the user closes the application while Attach() method is running and before memoryProcess is saved to the private variable, the Process handle will not be closed.
  • If I save the MemoryProcess instance to the private variable just at the beginning of the Attach() method, there is a risk for the user to get a NullReferenceException if he closes the application while the Attach() method is processing its foreach loop.
  • I absolutely don't want to make the user wait for Attach() method to complete before letting him close the application. That's horrible.

How can I do this?

回答1:

IMO, if you do not explicitly and specifically target to create separate detached/independent processes like, for example, through:

  • using PInvoke.CreateProcess
  • using

    (new System.Management.ManagementClass("Win32_ProcessStartup"))
    .Properties["CreateFlags"].Value = 8;
    
  • or maintaining child process alive upon app closing by launching them through separate shell scripts or other processes remaining to run after app closing;

  • creating a new thread in another independent process using CreateRemoteThread
  • etc.

or finding already run independently processes, you don't need to and probably should not "close" or dispose spawned by app processes. Windows (operting system) will close any unclosed spawned by app processes.

Also, I believe that it is impossible to execute any code in an application once it has started exiting or being closed.

PS (off-topic comment):
I do not even see that you close (really one should kill) or dispose your processes in your code...