tags:

views:

208

answers:

2

Hello,

I have a windows form that has a simple menu and performs a simple operation, I want to be able to create another windows form with all the functionality of a menu bar, message pump etc.. as a separate thread so I can then share the results of the operation to the second window.

I.E.

1) Form A opens Form B opens as a separate thread

2)Form A performs operation

3)Form A passes results via memory to Form B

4)Form B display results

I'm confused as to how to go about it, the main app runs fine but i'm not sure how to add a second window if the first one already exists. I think that using CreateWindow will allow me to make another window but again i'm not sure how to access the message pump so I can respond to certain events like WM_CREATE on the second window.

I hope it makes sense.

Thanks!

Edit:

I've attempted to make a second window and although this does compile, no windows show atall on build.

//////////////////////
// WINDOWS FUNCTION //
//////////////////////
LRESULT CALLBACK WindowFunc(HWND hMainWindow, UINT message, 
                            WPARAM wParam, LPARAM lParam)
{
    //Fields
    WCHAR buffer[256];
    struct DiceData storage;
    HWND hwnd;

    // Act on current message
    switch(message)    
    {
    case WM_CREATE:
        AddMenus(hMainWindow);

        hwnd = CreateWindowEx(
            0,
            "ChildWClass",
            (LPCTSTR) NULL,
            WS_CHILD | WS_BORDER | WS_VISIBLE,
            0,
            0,
            0,
            0,
            hMainWindow,
            NULL,
            NULL,
            NULL);

        ShowWindow(hwnd, SW_SHOW);

        break;

Any suggestions as to why this happens?

Edit 2:

This is everything there is, I've no idea if I'm implementing this right but I've tried to be consistent with the other window creation.

//////////////////
// WINDOWS MAIN //
//////////////////
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, 
                   LPSTR lpszArgs, int nWinMode)
{
    // Declaration of window class (used to register program), 
    // handle to window (returned by CreateWindow)
    // and windows message (holds messages received from windows)
    WNDCLASS wcl;
    WNDCLASS scl;
    HWND hwnd;
    MSG msg;

    // Name of window and window class
    LPCWSTR szWinName   = L"DiceRoller - Producer";
    LPCWSTR szClassName = L"DiceRollProd";

    LPCWSTR szCWinName = L"Dice - Consumer";
    LPCWSTR szCClassName = L"DiceRollCon";

    // Set up the windows class struct
    wcl.hInstance = hThisInst;
    wcl.lpszClassName = szClassName;
    wcl.lpfnWndProc = WindowFunc;
    wcl.style = 0;
    wcl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcl.lpszMenuName = NULL;
    wcl.cbClsExtra = 0;
    wcl.cbWndExtra = 0;
    wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

    // Set up the windows class struct
    scl.hInstance = hThisInst;
    scl.lpszClassName = szCClassName;
    scl.lpfnWndProc = WindowFunc;
    scl.style = 0;
    scl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    scl.hCursor = LoadCursor(NULL, IDC_ARROW);
    scl.lpszMenuName = NULL;
    scl.cbClsExtra = 0;
    scl.cbWndExtra = 0;
    scl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

    // Register the windows class
    if(!RegisterClass(&wcl))
    {
        return 0;
    }

    if(!RegisterClass(&scl))
    {
        return 0;
    }

    // Create the main window
    hwnd = CreateWindowEx(0,
        szClassName,
        szWinName,
        WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
        100,
        100,
        400,
        400,
        HWND_DESKTOP,
        NULL,
        hThisInst,
        NULL );


    // Show the main window
    ShowWindow(hwnd, nWinMode);
    UpdateWindow(hwnd);

    // Main message processing loop
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}



//////////////////////
// WINDOWS FUNCTION //
//////////////////////
LRESULT CALLBACK WindowFunc(HWND hMainWindow, UINT message, 
                            WPARAM wParam, LPARAM lParam)
{
    //Fields
    WCHAR buffer[256];
    struct DiceData storage;
    HWND hwnd;

    // Act on current message
    switch(message)    
    {
    case WM_CREATE:
        AddMenus(hMainWindow);

        hwnd = CreateWindowEx(
        0,
        "ChildWClass",
        (LPCTSTR) NULL,
        WS_CHILD | WS_BORDER | WS_VISIBLE,
        CW_USEDEFAULT,  // x location
        CW_USEDEFAULT,  // y location
        400,  // width
        300,  // height
        hMainWindow,
        NULL,
        NULL,
        NULL);

        ShowWindow(hwnd, SW_SHOW);
        UpdateWindow(hwnd);

        break;
+3  A: 

You should be able to create another, secondary window using CreateWindow(), and the standard GetMessage() DispatchMessage() loop should actually feed window events to both of the windows. Windows will somehow associate the thread that makes the calls on CreateWindow with the window events on those windows, and feed the events for both of them to it through its calls to GetMessage(). You shouldn't have to make a second thread to process the events for the second window.

Now, if you pass the same window class name to both calls to CreateWindow(), you should have two instances of the same window class, and the way you distinguish them is that, in the wndproc, DispatchMessage will give you the window handle to the relevant window. If you do that, be aware that any data that is static to the application will in effect become a shared resource between both of those windows. For example, if you statically declare an offscreen bitmap/device context to back your WM_PAINTs, you might suddenly find both your windows apparently painting in response to events on the one. The possibilities for getting this kind of entanglement are the price you pay for the benefit of easily sharing data between threads.

You might be able to get by using just the one thread, provided that the work you intend to do with it doesn't tie up processing of the message queue to the point where the user experiences laggy controls.

JustJeff
@JustJeff I've added what I think is what you were trying to get across although I am having problems implementing it.
Jamie Keeling
I just test ran some code similar to your post, but just used a plain old BUTTON for the child window. Verified that you should be able to create child windows in the scope of WM_CREATE like that, and that if you forget WS_CLIPCHILDREN on the parent window, your child window isn't accessible.
JustJeff
Just to double check, this will make a window identical to the first that I can add menus too right? I've seen web pages mentioning creating combo boxes and buttons from it which isn't what I'm after.
Jamie Keeling
i just used a button because that's an easy, predefined window class, and i didn't have to hunt around to find something. windows, dialogs, and controls are all pretty much equal as far as CreateWindow() and CreateWindowEx() are concerned.
JustJeff
I've added the WS_CLIPCHILDREN but I can only ever see one window, i'm not too sure where i'm going wrong.
Jamie Keeling
idk, i have virtually the same code and the child window is visible. clutching at straws now, but are you sure you registered the ChildWClass prior to the CreateWindow call?
JustJeff
I'm sure it has, would you be able to post what you have entirely to see if there's any inconsistencies, this make's no sense at all. I've stripped it down to it's bare bones now.
Jamie Keeling
Also, i've added a bit more in a second edit to see if it's glaringly obvious why it's not working. I really do appreciate your time on this @JustJeff
Jamie Keeling
+1  A: 

In your CreateWindow() call you are passing 0's into the location and size parameters.
Try revising your WM_CREATE case like so..

case WM_CREATE:
    AddMenus(hMainWindow);

    hwnd = CreateWindowEx(
        0,
        "ChildWClass",
        (LPCTSTR) NULL,
        WS_CHILD | WS_BORDER | WS_VISIBLE,
        CW_USEDEFAULT,  // x location
        CW_USEDEFAULT,  // y location
        400,  // width
        300,  // height
        hMainWindow,
        NULL,
        NULL,
        NULL);

    ShowWindow(hwnd, SW_SHOW);

    break;

Here's an interpretation of what I think you're trying to do, at least w/regard to the child window. (I still don't see why a second thread is needed, but one problem at a time..)

#include <windows.h>
LRESULT CALLBACK TheWndProc(HWND hwnd, UINT msg, WPARAM wP, LPARAM lP) {
    HDC hdc;
    HWND hwother;

    switch( msg ) {
    case WM_CREATE:
        hwother =
        CreateWindowEx(0, "TerribleClassName100405", "child title",
            WS_OVERLAPPEDWINDOW,
            100, 100, // location
            200, 160,  // size
            (HWND)hwnd,  // parent
            (HMENU)NULL,
            NULL,
            NULL);
        ShowWindow(hwother, SW_SHOW);

        break;

    case WM_CHAR: // quit app using escape key
        switch( LOWORD(wP) ) {
        case 27:
            SendMessage(hwnd, WM_CLOSE, 0, 0);
            return 0;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    }
    return DefWindowProc(hwnd, msg, wP, lP);
}

LRESULT CALLBACK AltWndProc(HWND hwnd, UINT msg, WPARAM wP, LPARAM lP) {
    HDC hdc;

    switch( msg ) {
    case WM_CREATE:
        break;

    case WM_CHAR: // quit app using X
        switch( LOWORD(wP) ) {
        case 'X':
            SendMessage(hwnd, WM_CLOSE, 0, 0);
            return 0;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    }
    return DefWindowProc(hwnd, msg, wP, lP);
}

void registerCustomWindows(HINSTANCE hInst) {
    WNDCLASSEX wc1;
    WNDCLASSEX wc2;

    wc1.lpszClassName = "TerribleClassName040914";
    wc1.hInstance = hInst;
    wc1.lpfnWndProc = TheWndProc;
    wc1.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc1.cbSize = sizeof(wc1);
    wc1.cbClsExtra = 0;
    wc1.cbWndExtra = 0;
    wc1.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc1.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wc1.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc1.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc1.lpszMenuName = NULL;
    RegisterClassEx(&wc1);

    wc2.lpszClassName = "TerribleClassName100405";
    wc2.hInstance = hInst;
    wc2.lpfnWndProc = AltWndProc;
    wc2.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc2.cbSize = sizeof(wc2);
    wc2.cbClsExtra = 0;
    wc2.cbWndExtra = 0;
    wc2.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc2.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wc2.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc2.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc2.lpszMenuName = NULL;
    RegisterClassEx(&wc2);
}

int WINAPI WinMain(HINSTANCE hThis, HINSTANCE hPrev, LPSTR cml, int iCS) {
    MSG msg;
    HWND hwnd;

    registerCustomWindows(hThis);

    hwnd = CreateWindowEx(0, "TerribleClassName040914", "two windows skeleton",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,   // location
        640, 480,   // size
        (HWND)NULL, // parent
        (HMENU)NULL,
        hThis,
        NULL);

    ShowWindow(hwnd, SW_SHOWNORMAL);
    UpdateWindow(hwnd);

    while( GetMessage(&msg, NULL, 0, 0) ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

I have that exact code in a file called demo.c, and use MS visual studio express 2005 to compile it using the line

cl demo.c user32.lib gdi32.lib

.. and I get two windows. Just to distinguish the fact I have two window classes, one of them quits via pressing ESC and the other upper case X.

Apologies for posting a 2nd response. My other one was a very generalized response and may just be OBE now that the OP has supplied more detail.

JustJeff
There's still only one window visible, it should work as your example and the Microsoft MSDN one are identical.
Jamie Keeling
Thank you for the solution, yours works fine. I'm going to re-examine mine and see why it's not working. Thank you very much for your patience.
Jamie Keeling