Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have a problem with AttachThreadInput and PeekMessage from User32.dll while multithreading. PeekMessage is not returning true in any case.

I am designing a Windows Form that uses OpenGL rendering on its buffers. OpenGL has to run on the same thread on which the Windows Form was created, and Windows Forms also use the same thread to process messages. Thus, I have a little conflict of interest.

I have an update loop on a separate thread, and when the frame has to be updated, the render loop which is situated on the main Windows Form and OpenGL thread is notified and it renders the frame. The rendering has to be in the form of a loop, and I tried calling BeginInvoke() or Invoke() from the update loop to call the rendering method, but the method was not invoked for some very odd and unknown reason. Thus, I had to create a method that loop checks whether rendering has to be done on the main thread, and if yes, then it renders using OpenGL.

So, the render loop works by checking if the frame needs updating. If it does, then it calls the OpenGL renderer and it renders all fine and dandy. It also handles calls Application.DoEvents() to process any Windows Form messages that have been queued on the thread.

A periodic run of Application.DoEvents() works just fine, however I felt that the algorithm would be more efficient if Application.DoEvents() is called when one or more messages are queued for Form processing, rather than just be checking at regular intervals, meaning that the algorithm would make sense more if it was based on need of processing rather than time intervals. This would also save some CPU usage.

So, I decided to check whether any messages are queued to the form from the update thread. This of course complicated matters, as PeekMessage cannot return the messages queued from another thread.

However I reasoned that the input messages from the main thread could be duplicated (or attached) to the input messages of the update thread. So, I created a control on my update loop and used:

AttachThreadInput(GetWindowThreadProcessId(this.Handle, IntPtr.Zero), GetWindowThreadProcessId(control.Handle, IntPtr.Zero), true);

to get a copy on the update thread of the input messages that occur on the Form. The attachment was successful, however:

PeekMessage(ref msg, control.Handle, 0, 0, 0x0001)

always returns false no matter what.

Here is a simplified version of my C# code:

public class GameWindow : Form {
....

//Runs on the separate update thread
private void UpdateLoop() {
    HighResolutionStopwatch updateWatch = new HighResolutionStopwatch();
    updateWatch.Start();
    double elapsed;
    Control control = new Control();
    control.CreateControl();
    AttachThreadInput(GetWindowThreadProcessId(this.Handle, IntPtr.Zero), GetWindowThreadProcessId(control.Handle, IntPtr.Zero), true);
    int controlHandle = (int) control.Handle;
    MSG msg = new MSG();
    do {
        if (PeekMessage(ref msg, controlHandle, 0, 0, 0x0001)) {
            MessageBox.Show("OK");
            needsProcessing = true;
            renderResetEvent.Set();
        }
        elapsed = updateWatch.ElapsedTicks;
        if (elapsed >= updateInterval) {
            updateWatch.Restart(elapsed - ((int) elapsed / updateInterval) * updateInterval);
            OnUpdate();
            needsRender = true;
            renderResetEvent.Set();
        } else if (VSync)
            Thread.Sleep(1);
    } while (IsShown);
    renderResetEvent.Set();
    AttachThreadInput(GetWindowThreadProcessId(this.Handle, IntPtr.Zero), GetWindowThreadProcessId(control.Handle, IntPtr.Zero), false);
    control.Dispose();
}

//Runs on the main Windows Form and OpenGL thread.
private void RenderLoop() {
    while (IsShown) {
        renderResetEvent.Reset();
        if (needsRender && IsGraphicsContextAvailable && !isPaused) {
            OnRender();
            needsRender = false;
        }
        if (needsProcessing) {
            Application.DoEvents();
            needsProcess = false;
        }
        renderResetEvent.Wait();
    }
}
....
}
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.