Getting a slice of inactivity in a managed bean under an unmanaged host

I have a managed component written in C # that is hosted by an inherited Win32 application as an ActiveX control. Inside my component, I need to get what usually will be Application.Idle, i.e. Get a time slice of the processing time in standby mode user interface thread (it should be the main user interface thread).

However, this hosted script Application.Idledoes not start because there is no controlled message loop (i.e. no Application.Run).

Unfortunately, the host also does not implement IMsoComponentManager, which may be suitable for what I need. And a long nested message loop (s Application.DoEvents) is not an option for many reasons.

So far, the only solution I can come up with is to use simple Win32 timers . According to http://support.microsoft.com/kb/96006 , it WM_TIMERhas one of the lowest priorities, followed only by one WM_PAINT, which should be as close to me as possible.

Are there any other options for this scenario?

Here is the prototype code:

// Do the idle work in the async loop

while (true)
{
    token.ThrowIfCancellationRequested();

    // yield via a low-priority WM_TIMER message
    await TimerYield(DELAY, token); // e.g., DELAY = 50ms

    // check if there is a pending user input in Windows message queue
    if (Win32.GetQueueStatus(Win32.QS_KEY | Win32.QS_MOUSE) >> 16 != 0)
        continue;

    // do the next piece of the idle work on the UI thread
    // ...
}       

// ...

static async Task TimerYield(int delay, CancellationToken token) 
{
    // All input messages are processed before WM_TIMER and WM_PAINT messages.
    // System.Windows.Forms.Timer uses WM_TIMER 
    // This could be further improved to re-use the timer object

    var tcs = new TaskCompletionSource<bool>();
    using (var timer = new System.Windows.Forms.Timer())
    using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: true))
    {
        timer.Interval = delay;
        timer.Tick += (s, e) => tcs.TrySetResult(true);
        timer.Enabled = true;
        await tcs.Task;
        timer.Enabled = false;
    }
}

I do not think it Task.Delayis suitable for this approach, since it uses kernel timer objects that are independent of the message loop and its priorities.

. : WH_FOREGROUNDIDLE/ForegroundIdleProc. , .

, , Win32 WPF Dispatcher, .. Dispatcher.BeginInvoke(DispatcherPriority.Background, ...):

+4
1

, WH_FOREGROUNDIDLE/ForegroundIdleProc . Application.Idle: hook , , GetMessage .

. , -, , , WM_TIMER. , , Spy ++, .

ForegroundIdleProc ( Application.Idle, ), WM_TIMER . WM_TIMER, . , ForegroundIdleProc , .

, , ForegroundIdleProc , (.. ). , async/await, . .

+2

All Articles