Hi,
I have never worked with threads before so I am wondering how to go about it. I have a windows application that runs just fine. It is just that when the user trigger a command that takes several minutes to complete, the window get the hour glass and the "white bar" if anything is covering the window and moved away during this computation time. If the window stays clear, the status field updates find thru out the process.

So, in my main program I have a WM_COMMAND case like this

case: HEAVY_DUTY
ComputeNow(hwnd);
break;

I wonder if I can just give a CreateThread() command prior to sending it to this computation. Or is there more to this? Any example that illustrates how to go about this would be great!

Thanks for your help!

Yes, Seakayaker, you need another thread for that. I'll list all the code for a small program below that should show all the ingredients in a GUI example. The context for this particular program is that I wanted to add a button to one of my Windows CE data collector programs that when clicked would start a thread to collect Gps lats & longs every second for anywhere from 10 to 180 seconds. I didn't want this to interfere with the other data collection activities and so I wanted a 'worker' thread in the background running while the GUI was being used for other purposes.

The program below is VC++ 6 but should compile with anything. Save tyhe file as Main.cpp if you want. The program opens an output.txt log file that will help you see what is going on. When you click the button a thread is created and a Windows timer started that runs for ten seconds and places a message in the listbox every second.

#include <windows.h>
#include <stdio.h>
#include <time.h>
#define IDC_LISTBOX   2000
#define IDC_BUTTON    2005
FILE*                 fp;


typedef struct GPSDATA
{
 double               dblLatitude;
 double               dblLongitude;
}GpsData;


typedef struct        WindowsEventArguments               
{
 HWND                 hWnd;                               
 WPARAM               wParam;                             
 LPARAM               lParam;                             
 HINSTANCE            hIns;                               
}WndEventArgs, *lpWndEventArgs;


void GetRawGps(double r_tic, double& dblLatitude, double& dblLongitude, double r_elv, double r_pdop, double r_ehe, double r_quality)
{
 srand(time(NULL));
 dblLatitude=rand();
 dblLongitude=rand();
}


void __stdcall fnTimerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
 double dblLatitude=0.0,dblLongitude=0.0,r_tic=0.0,r_elv=0.0,r_pdop=0.0,r_ehe=0.0,r_quality=0.0;
 unsigned int iNumReadingsDesired,iNum,iReturn=0;
 CRITICAL_SECTION m_memLock;
 GpsData* ptrGpsData=NULL;
 char szBuffer[16];
 HANDLE hThread;
 DWORD dwRet=0;
 
 fprintf(fp,"  Entering fnTimerProc()\n");
 InitializeCriticalSection(&m_memLock);  
 EnterCriticalSection(&m_memLock);
 GetRawGps(r_tic,dblLatitude,dblLongitude,r_elv,r_pdop,r_ehe,r_quality);
 fprintf(fp,"    dblLatitude=%f\tdblLongitude=%f\n",dblLatitude,dblLongitude);
 iNum=GetWindowLong(hwnd,0);
 iNum++;
 iNumReadingsDesired=GetWindowLong(hwnd,12);
 ptrGpsData=(GpsData*)GetWindowLong(hwnd,8);
 ptrGpsData[iNum].dblLatitude=dblLatitude;
 ptrGpsData[iNum].dblLongitude=dblLongitude;
 
 sprintf(szBuffer,"iNum=%u",iNum);
 SendMessage(GetDlgItem(hwnd,IDC_LISTBOX),LB_ADDSTRING,0,(LPARAM)szBuffer);
 if(iNum==iNumReadingsDesired)
 {
    unsigned int i;
    for(i=1;i<=iNumReadingsDesired;i++)
    {
        ptrGpsData[0].dblLatitude=ptrGpsData[0].dblLatitude+ptrGpsData[i].dblLatitude;
        ptrGpsData[0].dblLongitude=ptrGpsData[0].dblLongitude+ptrGpsData[i].dblLongitude;
    }
    ptrGpsData[0].dblLatitude=ptrGpsData[0].dblLatitude/iNumReadingsDesired;
    ptrGpsData[0].dblLongitude=ptrGpsData[0].dblLongitude/iNumReadingsDesired;
    fprintf(fp,"    ");
    fprintf(fp,"%s\n",szBuffer);
    hThread=(void*)GetWindowLong(hwnd,4);
    dwRet=CloseHandle(hThread);
    fprintf(fp,"    dwRet=%u\n",dwRet);
    iReturn=KillTimer(hwnd,NULL);
    fprintf(fp,"    iReturn(KillTimer())=%u\n",iReturn);
    if(iReturn)
       MessageBox(hwnd,"Successfully Destroyed Timer!","Success Message",MB_OK);
    else
       MessageBox(hwnd,"Failed To Destroy Timer!","Failure Message",MB_ICONERROR);
    SetWindowLong(hwnd,0,iNum);
    fprintf(fp,"  Leaving fnTimerProc()\n");
    LeaveCriticalSection(&m_memLock);
    return;
 }
 fprintf(fp,"    ");
 fprintf(fp,"%s\n",szBuffer);
 SetWindowLong(hwnd,0,iNum);
 LeaveCriticalSection(&m_memLock);
 fprintf(fp,"  Leaving fnTimerProc()\n");
}


DWORD CollectGpsData(HWND hWnd)
{
 unsigned int iNum,iNumReadingsDesired;
 GpsData* ptrGpsData=NULL;
 int iReturn=0;
 
 fprintf(fp,"Entering CollectGpsData()\n");
 fprintf(fp,"  hWnd=%u\n",hWnd);
 SetTimer(hWnd,NULL,1000,(TIMERPROC)fnTimerProc);
 iNum=GetWindowLong(hWnd,0);
 iNumReadingsDesired=GetWindowLong(hWnd,12);
 while(iNum<iNumReadingsDesired)
 {
  iNum=GetWindowLong(hWnd,0);
  fprintf(fp,"  Got In While.  iNum=%u\n",iNum);
  Sleep(1000);
 };
 fprintf(fp,"Leaving CollectGpsData()\n\n"); 
 EnableWindow(GetDlgItem(hWnd,IDC_BUTTON),TRUE);
 ptrGpsData=(GpsData*)GetWindowLong(hWnd,8);
 fprintf(fp,"Averaged Latitude = %f\tAveraged Longitude = %f\n",ptrGpsData[0].dblLatitude,ptrGpsData[0].dblLongitude);
 ExitThread(TRUE);
 
 return TRUE;
}


long fnWndProc_OnCreate(lpWndEventArgs Wea)                         //Bytes    What's Stored There
{                                                                   //=============================================== 
 unsigned int iNumReadings=10;                                      //0  -  3  Count of Gps Readings Actually Taken
 GpsData* ptrGpsData;                                               //4  -  7  hThread                  
 HWND hCtrl;                                                        //8  - 11  Buffer address of GpsData to hold readings 
                                                                    //12 - 15  Number of readings desired
 fp=fopen("Output.txt","w");                           
 fprintf(fp,"Output.txt Opened In fnWndProc_OnCreate()\n");
 SetWindowLong(Wea->hWnd,0,0);
 SetWindowLong(Wea->hWnd,12,iNumReadings);
 fprintf(fp,"  Wea->hWnd=%u\n",Wea->hWnd);                                                    
 Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance; 
 ptrGpsData=(GpsData*)GlobalAlloc(GPTR,sizeof(GpsData)*(iNumReadings+1)); 
 SetWindowLong(Wea->hWnd,8,(long)ptrGpsData);

 hCtrl=CreateWindowEx(WS_EX_CLIENTEDGE,"listbox","",WS_CHILD|WS_VISIBLE|WS_VSCROLL,105,10,80,120,Wea->hWnd,(HMENU)IDC_LISTBOX,Wea->hIns,0); 
 hCtrl=CreateWindowEx(0,"button","Start Collecting GPS Data",WS_CHILD|WS_VISIBLE,45,140,200,30,Wea->hWnd,(HMENU)IDC_BUTTON,Wea->hIns,0); 
 fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");

 return 0;                                             
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)           
{
 HANDLE hThread;

 fprintf(fp,"Entering fnWndProc_OnCommand()\n");
 switch(LOWORD(Wea->wParam))
 {
  case IDC_BUTTON:
    SendMessage(GetDlgItem(Wea->hWnd,IDC_LISTBOX),LB_RESETCONTENT,0,0);
    SetWindowLong(Wea->hWnd,0,0);
    hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)CollectGpsData,Wea->hWnd,0,NULL);
    SetWindowLong(Wea->hWnd,4,(long)hThread);
    fprintf(fp,"  hThread=%u\n",hThread);
    EnableWindow(GetDlgItem(Wea->hWnd,IDC_BUTTON),FALSE);
    break;
 }
 fprintf(fp,"Leaving fnWndProc_OnCommand()\n\n");

 return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)            
{
 unsigned int blnFree=FALSE;
 GpsData* ptrGpsData; 
                                                     
 fprintf(fp,"Entering fnWndProc_OnClose()\n");
 ptrGpsData=(GpsData*)GetWindowLong(Wea->hWnd,8);
 blnFree=(unsigned int)GlobalFree(ptrGpsData);
 fprintf(fp,"  blnFree=%u\n",blnFree);
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(WM_QUIT);
 fprintf(fp,"Leaving fnWndProc_OnClose()\n");
 fclose(fp);

 return 0;
}


long __stdcall fnWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) 
{                                                                        
 WndEventArgs wea;                                                
                                                                          
 switch (msg)                                                            
 {                                                                       
  case WM_CREATE:                                                        
    wea.hWnd=hwnd, wea.wParam=wParam, wea.lParam=lParam;                 
    return fnWndProc_OnCreate(&wea);
  case WM_COMMAND:
    wea.hWnd=hwnd, wea.wParam=wParam, wea.lParam=lParam;                 
    return fnWndProc_OnCommand(&wea);
  case WM_CLOSE:                                                         
    wea.hWnd=hwnd, wea.wParam=wParam, wea.lParam=lParam;                 
    return fnWndProc_OnClose(&wea);                                      
 }                                                                       
                                                                         
 return DefWindowProc(hwnd,msg,wParam,lParam);                           
}


int __stdcall WinMain(HINSTANCE hIns,HINSTANCE hPrevIns,LPSTR lpszArgument,int iShow)
{                                   
 char szClassName[]="Timer";       
 WNDCLASSEX wc;                     
 MSG messages;                     
 HWND hWnd;
                        
 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BACKGROUND,   wc.cbWndExtra=16;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0; 
 RegisterClassEx(&wc);
 hWnd=CreateWindow(szClassName,"Timer",WS_OVERLAPPEDWINDOW,525,350,300,225,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
  TranslateMessage(&messages);
  DispatchMessage(&messages);
 }

 return messages.wParam;
}

As you will note I don't use globals in my code; hopefully you're familiar with storing data that must persist across function calls/messages in a window's .cbWndExtra bytes. Particularly with threads globals are very bad.

You'll want to look up & study on the CreateThread() Api. Let me know if the program works for you. I wrote this as kind of a prototype for what I needed to do with my data collector CE code, and I believe it has some of the essential piecies you need.

The critical piecies are as follows. In fnWndProc_OnCommand() a thread is created with CreateThread() when the user clicks the single button on the form. When that occurs I disable the button so the user can't just keep clicking it to create new threads. The procedure named in CreateThread() that executes when the OS gives a time slice to the thread is 'CollectGpsData'..

CollectGpsData() creates a timer whose WndProc is fnTimerProc. That procedure will be called by Windows every second and a Gps point will be synthesized just through random numbers. CollectGpsData() will exit and re-enable the button after the required 10 readings have been collected. Its important to note that the application isn't 'tied up' while the thread is running. In fact, all the thread does really is check once every second on how many points have been collected and executes a sleep() which relinquishes the processor to other threads. Hope this helps.

This question has already been answered. Start a new discussion instead.