Creating window in another thread(not main thread)
Asked Answered
G

3

10

I've got a function:

HWND createMainWindow(P2p_Socket_Machine * toSend){

    HWND hMainWnd = CreateWindow( 
        L"Class",/*(LPCWSTR) nameOfConference.c_str()*/L"Chat",  WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, 
    CW_USEDEFAULT, 0, 600,400, 
    (HWND)NULL, (HMENU)NULL, 
    /*(HINSTANCE)hlnstance*/NULL, NULL 
    ); 

    if (!hMainWnd) { 
        MessageBox(NULL, L"Cannot create main window", L"Error", MB_OK); 
        return 0; 
    }

    CreateWindowA("LISTBOX",NULL, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOTIFY|LBS_MULTIPLESEL,310,30,255,275,hMainWnd,(HMENU)List_Box,NULL,NULL);

    CreateWindowExA(NULL,"BUTTON", "Refresh", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,310,100,24,hMainWnd,(HMENU)Button_Refresh, NULL ,NULL);

    CreateWindowExA(NULL,"BUTTON", "Send", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,334,100,24,hMainWnd,(HMENU)Button_Send, NULL ,NULL);

    CreateWindowExA(NULL,"BUTTON", "New", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,354,100,24,hMainWnd,(HMENU)Button_New, NULL ,NULL);

    CreateWindowA("EDIT",0,WS_BORDER|WS_VISIBLE|WS_CHILD|ES_LEFT|ES_MULTILINE|WS_VSCROLL|WS_DISABLED,
    10,30,265,275,hMainWnd,(HMENU)Text_Box_Get,NULL,NULL);

    CreateWindowA("EDIT",0,WS_BORDER|WS_VISIBLE|WS_CHILD|ES_LEFT|ES_MULTILINE|WS_VSCROLL,
    10,320,265,45,hMainWnd,(HMENU)Text_Box_Send,NULL,NULL);

    SetWindowLongPtr(hMainWnd,GWLP_USERDATA,(LONG_PTR)toSend);

    ShowWindow(hMainWnd, SW_SHOW); 
    //UpdateWindow(hMainWnd);

    return hMainWnd;

}

And this is main part of my program:

int WINAPI WinMain(HINSTANCE hlnstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int 
nCmdShow) 
{
WNDCLASSEX wc; 
    wc.cbSize = sizeof(wc); 
    wc.style = CS_HREDRAW | CS_VREDRAW; 
    wc.lpfnWndProc = MyFunc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hlnstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = L"Class"; 
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
HWND toSend = createMainWindow(P2pSocket);

//some code

hThread = CreateThread(NULL, 0, ClientThread, 
            Message2, 0, &dwThreadId);

        if (hThread == NULL)
        {
            cout<<"Create thread filed";
            exit(10);
        }


    while (GetMessage(&msg, NULL, 0, 0)) { 

        TranslateMessage(&msg); 
        DispatchMessage(&msg);

    }

    return msg.wParam;   

When i call function createMainWindow() in main part of my program it works as it should, but when i run it in my thread(ClientThread) it doesn't work. i've read that i should create windows only in main thread. Is it true? And if it's true, what is the simplest way to call this function from another thead to be done in main thread?


Thanks everyone. Now i know the problem, but i'm stuck with solution. My client thread code is:

while(1){

    vector<HWND> AllHandlers;

    pair<string,string> Answer = Pointer->receiveMsgByUdp();

    if(!Pointer->isMyLocalAddress(Answer.first)){

        int type = messageUdpContentType(Answer.second);

        switch(type){

        case 0 :

            Pointer->sendMsgToIpUdp(Answer.first,"<?xml version='1.0'?><accepted/>");
            AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box);
            for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++)
                if(SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str())==LB_ERR)
                    SendMessageA(*j, LB_ADDSTRING, 0, (LPARAM)Answer.first.c_str());

            break;

        case 1 :
            AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box);
            for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++)
                if(SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str())==LB_ERR)
                    SendMessageA(*j, LB_ADDSTRING, 0, (LPARAM)Answer.first.c_str());

            break;

        case 2 :
            AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box);
            for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++)
                if((i = SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str()))!=LB_ERR)
                    SendMessageA(*j,LB_DELETESTRING, 0, (LPARAM)Answer.first.c_str());

            break;

        case 3 :

            userReply = MessageBoxW(NULL, L"Принять приглашение на конференцию?",
                    L"", MB_YESNO | MB_ICONQUESTION); 
            if (userReply==IDYES){

                //todo: Проверка на создание встречи, в которой уже состоишь
                string nameOfConf = fetchNameOfInviteConf(Answer.second);
                Pointer->createConference(nameOfConf);
                HWND toSendTo = createMainWindow(Pointer);
                Pointer->setHandlerInfo(nameOfConf,toSendTo);               
                Pointer->addNewMemberToConference_ServerType(nameOfConf,Answer.first);
                string toSend = string("<?xml version='1.0'?><inviteAccepted>") + nameOfConf + string("</inviteAccepted>");
                Pointer->sendMsgToIpUdp(Answer.first,toSend);

            }
            break;

        case 4 :

            string nameOfConf = fetchNameOfInviteAcceptConf(Answer.second);

            toSend.clear();
            Participants.clear();
            Participants = Pointer->getCurrentParticipants(nameOfConf);
            toSend+="<?xml version='1.0'?>";
            toSend+="<conference>";
            toSend+="<nameOfConference>";
            toSend+=nameOfConf;
            toSend+="</nameOfConference>";
            for(vector<string>::iterator i = Participants.begin();i!=Participants.end();i++){
                toSend+="<participant>" + *i + "</participant>";
            }
            toSend+="</conference>";



            Pointer->addNewMemberToConference_ClientType(nameOfConf,Answer.first);

            Pointer->sendToIpTcp(Answer.first,toSend);

            break;

    }

the function receiveMsgByUdp() stops this thread until receives message. I apologize for lack of knowledge, but what functions can i use or another stuff to solve this. Should i rewrite my method receiveMsgByUdp() to be asynchronous or how can i call function createMainWindow() to be run on main thread?About the last variant : how can i do this in pure winapi, i couldn't found any simple examples. Can someone give code snippet. Thanks one more time)

Gessner answered 11/3, 2012 at 11:31 Comment(1)
Thanks every one. I managed to make this program to work. i used method by David Heffernan. And information on this link #7061186Gessner
V
9

You can indeed create windows in threads other than the main UI thread. However, those windows will have affinity to the thread that created them and you will need to run a message pump in each and every thread that creates windows.

So whilst you can do what you ask, Win32 is really designed to work with all windows in a process having affinity to the same thread. There's really nothing to be gained from creating multiple UI threads. All you will succeed in doing is making your life extraordinarily and needlessly complex.

Velodrome answered 11/3, 2012 at 11:47 Comment(8)
Yes, it's complex. But in my situation i can't find another solution. In non-main thread socket receive messages, and when i receive special messsage i need to create window. I don't know how to create it in main thread. I'm newbie in winapi, so maybe you will tell me how to manage to do this kind of trick?Gessner
This is a common scenario. What you do is send or post a message to the main thread and get the main thread to create the window. All GUI frameworks offer mechanisms to support this scenario.Velodrome
But if i need to do this in pure winapi? Will it be very complex?Gessner
Sorry, but what kind of message?Gessner
one defined by you in WM_APP rangeVelodrome
Or you can switch to an asynchronous socket operation. Or you can put the socket operations on a background thread.Kikelia
What if I like it when my life is extraordinarily and needlessly complex?Erna
@Erna That's fine. Do you disagree with what I wrote in this answer?Velodrome
S
5

You can create windows on "non-main" threads but be aware that those windows are attached to the creation thread, and you need to make sure to implement a message loop there and keep dispatching messages posted on the queue. If you don't do this, your windows are going to freeze.

See:

The system does not automatically create a message queue for each thread. Instead, the system creates a message queue only for threads that perform operations which require a message queue. If the thread creates one or more windows, a message loop must be provided; this message loop retrieves messages from the thread's message queue and dispatches them to the appropriate window procedures.

Standee answered 11/3, 2012 at 11:35 Comment(4)
So, all i need to do is to add a code to my non-main thread to dispatch messages. But there is one problem: in this thread i work with sockets, i receive messages, and some functions stop my loop until socket receives message. I think the only way to solve this is to create another thread in my non-main thread to dispach messages from window. What do you think?Gessner
@Roman In my view it would be bad advice to suggest that multiple UI threads is the best solution here. What's your opinion? Do you agree with me or not?Velodrome
You have choices for API to work with sockets, all you need is not avoid blocking calls where the execution is spending long periods of time within the API.Standee
@David I concur that UI on background thread is something OP might see as solution, but in fact it takes the application structure even farther from reasonable design. If a background thread UI is possible, it does not yet mean that it's a good idea. My suggestion would here be that it is fine to keep all communication code on background. At the very least, it does not interfere with UI, does not block UI, is not blocked by let's say message boxes etc. I would leave all UI on main thread and implement certain "interthread" communication between socket code and UI to connect the two.Standee
C
5

If you create a window in another thread, you will also need to implement a message loop on that thread since queued messages are posted to the message queue of thread which owns the window.

Carrnan answered 11/3, 2012 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.