knightOfSpring
knightOfSpring

Reputation: 359

Creating window in another thread(not main thread)

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)

Upvotes: 10

Views: 16050

Answers (4)

Sagan
Sagan

Reputation: 112

You could also send a custom WM message back to your main window so that it creates a new window for you(with some defining params as WPARAM and LPARAM maybe) and get the HWND as LRESULT to that message. This worked for me in my simple case.

You can

#define WM_CREATE_MY_WINDOW (WM_APP+1)

inside your main WndProc

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
   if(msg==WM_CREATE_MY_WINDOW){
      // Create the window that you need, maybe defined by the content of WPARAM and LPARAM
      // and return its handle as result.
      return CreateWindowEx(0, L"EDIT", L"Edit control", WS_CHILD/*styles you need*/, 
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        hWnd/*or other parent passed in params*/, nullptr, hInstance, nullptr);
   }
}

Then you pass the HWND of main window to the thread along with your Message2 parameter which looks like a function pointer, in a struct.

struct HWNDParam{
   HWND hWnd = nullptr;
   void* param = nullptr;
   HWNDParam(HWND hWnd, void* param){
      this->hWnd = hWnd;
      this->param = param;
   }
}

In your win main you pass pointer to that struct to get both your Message2 and HWND.

int WINAPI WinMain(HINSTANCE hlnstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int nCmdShow) {

   ...
   HWND toSend = createMainWindow(P2pSocket);

   HWNDParam* pHWndParam = new HWNDParam(toSend/*your main wnd*/, Message2);
   hThread = CreateThread(NULL, 0, ClientThread, 
        pHWndParam, 0, &dwThreadId);

   ...
   
}

And inside your thread

DWORD WINAPI ClientThread(LPVOID lpParam){
   HWNDParam* pHWndParam = (HWNDParam*)lpParam;
   
   // Send message to your main thread to create the window for you
   HWND hWndNew = (HWND)SendMessage(pHWndParam->hWnd, WM_CREATE_MY_WINDOW, 
      0/*some params maybe*/, 0/*some params maybe*/);
   
   // And delete the data when not necessary anymore
   delete pHWndParam;
}

Upvotes: 0

Ben Russell
Ben Russell

Reputation: 1423

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.

Upvotes: 6

David Heffernan
David Heffernan

Reputation: 612993

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.

Upvotes: 10

Roman Ryltsov
Roman Ryltsov

Reputation: 69672

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.

Upvotes: 6

Related Questions