Figure 2   CMultiThread—Modified but Bulky


// MultiThread.h : header file
// Copyright (C) 1997 by The Windward Group, All Rights Reserved

#ifndef MULTITHREAD_H
#define MULTITHREAD_H

#ifndef __AFXWIN_H__
    #error include 'stdafx.h' before including this file for PCH
#endif

#include <afxmt.h>

/////////////////////////////////////////////////////////////////////////////
class CMultiThread : public CWinThread
{
DECLARE_DYNCREATE(CMultiThread)
public:
    CMultiThread();
    virtual ~CMultiThread();

    BOOL CreateThread(DWORD dwCreateFlags = 0,      // masks
                                                    // CWinThread::CreateThread
                      UINT nStackSize = 0,
                      LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL,
                      UINT nMilliSecs = INFINITE);  // upper time limit to wait

    BOOL InitInstance() {return TRUE;}
    void KillThread2();
    int Run();

protected:
    CEvent*        m_pWorkEvent;              // do work event
    CEvent*        m_pExitEvent;              // used to synchronize destruction
    int            m_nCycleTime;              // do work cycle time
    BOOL           m_bEndThread;              // end the thread ?

    virtual void StartWork() {}               // override to do startup
    virtual void DoWork() {}                  // override to do work
    virtual void EndWork() {}                 // override to do shutdown

    CEvent* GetEvent() const {return m_pWorkEvent;}    // cycle control event
    int GetCycleTime() const {return m_nCycleTime;}
    void SetCycleTime(int nMilliSecs) {m_nCycleTime = nMilliSecs;}
};
#endif


// MultiThread.cpp : implementation file
// Copyright (C) 1997 by The Windward Group, All Rights Reserved

#include "stdafx.h"
#include "MultiThread.h"

IMPLEMENT_DYNCREATE(CMultiThread, CWinThread)

/////////////////////////////////////////////////////////////////////////////
// CMultiThread

CMultiThread::CMultiThread()
{
    // Create a non-signaled, manual-reset event to synchronize destruction
    m_pExitEvent = new CEvent(FALSE, TRUE);
    ASSERT(m_pExitEvent);

    // Create a non-signaled, auto-reset event to wait on for work cycle
    m_pWorkEvent = new CEvent();
    ASSERT(m_pWorkEvent);
}

CMultiThread::~CMultiThread()
{
    delete m_pWorkEvent;
    delete m_pExitEvent;
}

BOOL CMultiThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize, 
                                LPSECURITY_ATTRIBUTES lpSecurityAttrs, 
                                UINT nMilliSecs)
{
    m_nCycleTime = nMilliSecs;
    m_bEndThread = FALSE;

    // Start second thread
    return CWinThread::CreateThread(dwCreateFlags, nStackSize, lpSecurityAttrs);
}

void CMultiThread::KillThread2()
{
    // Start up the other thread so it can complete.
    // When it does, it will set the exit event and the object can be destructed.
    m_bEndThread = TRUE;
    m_pWorkEvent->SetEvent();
    CSingleLock csl(m_pExitEvent);
    csl.Lock();                        // wait for 2nd thread to finish
    csl.Unlock();                        
}

int CMultiThread::Run()
{
    CSingleLock csl(m_pWorkEvent);     // synch on the work event
    StartWork();                       // do derived startup
    while (!m_bEndThread)              // loop until we're done
    {
        csl.Lock(m_nCycleTime);        // wait for event or timeout
        csl.Unlock();            
        if (!m_bEndThread) DoWork();   // and then do some work
    }

    EndWork();                         // do derived shutdown
    m_pExitEvent->SetEvent();          // set not waiting signal
    AfxEndThread(0, FALSE);            // end the thread, but do not delete it
    return 0;
}

Figure 3   CThinThread


// ThinThread.h : header file
// Copyright (C) 1997 by The Windward Group, All Rights Reserved

#ifndef THINTHREAD_H
#define THINTHREAD_H

#ifndef __AFXWIN_H__
    #error include 'stdafx.h' before including this file for PCH
#endif

#include <afxmt.h>

/////////////////////////////////////////////////////////////////////////////
class CThinThread
{
public:
    CThinThread();
    virtual ~CThinThread();

    BOOL CreateThread(DWORD dwCreateFlags = 0,
                      UINT nStackSize = 0,
                      LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL,
                      UINT nMilliSecs = INFINITE);    // upper time limit to wait

    HANDLE GetHandle() {return m_hThread2;}
    BOOL IsBusy()      {return m_b2ndThread;}
    void Stop()        {m_bEndThread = TRUE;}

protected:
    CEvent*        m_pWorkEvent;          // do work event
    CEvent*        m_pExitEvent;          // used to synchronize destruction
    int            m_nCycleTime;          // do work cycle time
    BOOL           m_bEndThread;          // end the thread ?
    BOOL           m_b2ndThread;          // 2nd thread active?
    HANDLE         m_hThread2;            // 2nd thread handle

    virtual void StartWork() {}           // override to do startup
    virtual void DoWork() = 0;            // override to do work
    virtual void EndWork() {}             // override to do shutdown

    CEvent* GetEvent() const {return m_pWorkEvent;}    // cycle control event
    int GetCycleTime() const {return m_nCycleTime;}
    void KillThread2();
    int Run();
    void SetCycleTime(int nMilliSecs) {m_nCycleTime = nMilliSecs;}
    static unsigned int __stdcall Start(void* pv);
};
#endif


// ThinThread.cpp : implementation file
// Copyright (C) 1997 by The Windward Group, All Rights Reserved

#include "stdafx.h"
#include "ThinThread.h"
#include <process.h>    /* _beginthread, _endthread */

/////////////////////////////////////////////////////////////////////////////
// ThinThread
CThinThread::CThinThread()
: m_b2ndThread(FALSE)
{
    // Create a signaled, manual-reset event to synchronize destruction
    m_pExitEvent = new CEvent(TRUE, TRUE);
    ASSERT(m_pExitEvent);

    // Create a non-signaled, auto-reset event to wait on for work cycle
    m_pWorkEvent = new CEvent();
    ASSERT(m_pWorkEvent);
}

BOOL CThinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize, 
                               LPSECURITY_ATTRIBUTES lpSecurityAttrs, 
                               UINT nMilliSecs)
{
    m_b2ndThread = TRUE;
    m_bEndThread = FALSE;
    m_nCycleTime = nMilliSecs;
    m_pExitEvent->ResetEvent();    // exit event is reset until we're done

    // Start second thread
    unsigned usThreadAddr;
    m_hThread2 = reinterpret_cast<HANDLE>
       (_beginthreadex(lpSecurityAttrs, nStackSize, Start,
       this, 1, &usThreadAddr));
    return reinterpret_cast<unsigned long> (m_hThread2);
}

CThinThread::~CThinThread()
{
    delete m_pWorkEvent;
    delete m_pExitEvent;
}

void CThinThread::KillThread2()
{
       // Start up the other thread so it can complete.
       // When it does, it will set the exit event and the object can
       // be destructed.
    m_bEndThread = TRUE;
    m_pWorkEvent->SetEvent();
    CSingleLock csl(m_pExitEvent);
    csl.Lock();                         // wait for 2nd thread to finish
    csl.Unlock();                        
}

int CThinThread::Run()
{
    CSingleLock csl(m_pWorkEvent);      // synch on the work event
    StartWork();                        // do derived startup

    while (!m_bEndThread)               // loop until we're done
    {
        csl.Lock(m_nCycleTime);         // wait for event or timeout
        csl.Unlock();            
        if (!m_bEndThread) DoWork();    // then do derived work
    }

    EndWork();                          // do derived shutdown
    m_pExitEvent->SetEvent();           // set not waiting signal
    m_b2ndThread = FALSE;
    CloseHandle(m_hThread2);
    _endthreadex(0);
    return 0;
}

unsigned int __stdcall CThinThread::Start(void* pv)
{
    CThinThread* pMT = static_cast<CThinThread*> (pv);
    return pMT->Run();
}

Figure 5   CrefreshThread


 // RefreshThread.h : header file
 // Copyright (C) 1996 by CTB/McGraw-Hill, All Rights Reserved
 
 #ifndef REFRESHTHREAD_H
 #define REFRESHTHREAD_H
 
 #ifndef __AFXWIN_H__
     #error include 'stdafx.h' before including this file for PCH
 #endif
 
 #include <afxmt.h>
 #include "ThinThread.h"
 #include "ListCtrlEx.h"
 #include "OpUnitStatusMgrInc.h"
 using namespace OpUnitStatusRecordSQLParts;
 #include "PageBase.h"
 
 /////////////////////////////////////////////////////////////////////////////
 class CRefreshThread : public CThinThread, public CPageBase
 {
 public:
     CRefreshThread() {};
     virtual ~CRefreshThread() {KillThread2();}
 
     BOOL Go();
     void Set(HWND hActive, HWND hHistory,
              const CString& csFilter,
              const CUIntArray& sortFields, BOOL bActive=TRUE);
 
 protected:
     HWND m_hwndActive;
     HWND m_hwndHistory;
     CString m_csFilter;
     CUIntArray m_cuiaSortFields;
     BOOL m_bActiveTable;
     CListCtrlEx m_LCActive;
     CListCtrlEx m_LCHistory;
     OURecSet* m_pRecSet;
     int m_nRecord;
 
     virtual void StartWork();     // start work
     virtual void DoWork();        // continue doing work
     virtual void EndWork();       // end work
 };
 #endif
 
 
 // RefreshThread.cpp : implementation file
 // Copyright (C) 1997 by CTB/McGraw-Hill, All Rights Reserved
 
 #include "stdafx.h"
 #include "RefreshThread.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CRefreshThread
 
 void CRefreshThread::Set(HWND hActive, HWND hHistory, const CString& csFilter,
 const CUIntArray& sortFields, BOOL bActive)
 {
     m_hwndActive = hActive;
     m_hwndHistory = hHistory;
     m_csFilter = csFilter;
     m_bActiveTable = bActive;
     m_nRecord = 0;
 
     int size = sortFields.GetSize();
     m_cuiaSortFields.SetSize(size);
     for (int i = 0; i < size; i++)
         m_cuiaSortFields.SetAt(i, sortFields.GetAt(i));
 }
 
 BOOL CRefreshThread::Go()
 {
     // kick off CThinThread with no loop delay
     return CreateThread(0, 0, NULL, 0);
 }
 
 void CRefreshThread::StartWork()
 {
     // setup the listctrls
     m_LCActive.Attach(m_hwndActive);
     m_LCHistory.Attach(m_hwndHistory);
 
     m_LCActive.DeleteAllItems();
     m_LCHistory.DeleteAllItems();
 
     m_pRecSet = m_pStatMgr->GetAllOrderedRecords((m_bActiveTable ? 
         COpUnitStatusMgr::TABLE_ACTIVE : COpUnitStatusMgr::TABLE_ARCHIVED),
         m_cuiaSortFields, m_csFilter, FALSE, TRUE);
 
     // prepare the list control to consume mass quantities
     m_LCActive.SetItemCount(m_pRecSet->GetRecordCount());
 
     // insert the data from first database record directly into the list control
     if (m_pRecSet && m_pRecSet->GetFirstRecord(m_nRecord++, &m_LCActive));
     else m_bEndThread = TRUE;
 }
 
 void CRefreshThread::DoWork()
 {
     if (!m_pRecSet->GetNextRecord(m_nRecord++, &m_LCActive))
         m_bEndThread = TRUE;
 }
 
 void CRefreshThread::EndWork()
 {
     // release the record set
     m_pSPCSFactory->ReleaseOpUnitStatusRecordSet(m_pRecSet);
 
     // select first, repaint, and set focus
     SelectFirstItem(m_LCActive);
     UpdatesCompleted(m_LCActive, m_LCHistory);
 
     // cleanup the listctrls
     m_LCActive.Detach();
     m_LCHistory.Detach();
}

Figure 6   CjobMgrImp


 // JobMgrImp.cpp : implementation file
 // Copyright (C) 1997 by CTB/McGraw-Hill, All Rights Reserved
 
 /////////////////////////////////////////////////////////////////////////////
 // internal thread step methods
 /////////////////////////////////////////////////////////////////////////////
 // do the operation
 BOOL CJobMgrImp::DoTheOp()
 {
     BOOL bRet = FALSE;
 
     if (SetParameters())        // set values in the shared parameter file
     {
         if (m_OpVals.bLocalOp)
         {    // local operations
             if (m_LocalOp.DoFunction(m_csIOFile))
             {    if (GetParameters()) bRet = TRUE;}
             else m_csError = errCreateProc;
         }
 
         else if (m_OpVals.csOp.GetLength())
         {                        // other operations
             STARTUPINFO si;
             PROCESS_INFORMATION pi;
 
             si.cb             = sizeof(STARTUPINFO);
             si.lpReserved     = NULL;
             si.lpDesktop      = NULL;
             si.lpTitle        = NULL;
             si.dwFlags        = 0;
             si.cbReserved2    = 0;
             si.lpReserved2    = NULL;
 
             if (CreateProcess(NULL,
                 (LPTSTR)(LPCTSTR)(m_OpVals.csOp + CString(" ") + m_csIOFile),
                 NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
             {
                 if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
                 {
                     if (GetParameters())
                         bRet = TRUE;
 
                     CloseHandle(pi.hThread);
                     CloseHandle(pi.hProcess);
                 }
                 else m_csError = errWaitProc;
             }
             else m_csError = errCreateProc;
         }
         else bRet = TRUE;        // no operation
     }
     else m_csError = errParamFile;
 
     TraceWork();
     return bRet;
 }

Figure 8   ClaunchThread


 // LaunchThread.h : header file
 // Copyright (C) 1997 by CTB/McGraw-Hill, All Rights Reserved
 
 #ifndef LAUNCHTHREAD_H
 #define LAUNCHTHREAD_H
 
 #ifndef __AFXWIN_H__
     #error include 'stdafx.h' before including this file for PCH
 #endif
 
 #include <afxmt.h>
 #include "ThinThread.h"
 #include "ListCtrlEx.h"
 #include "OpUnitStatusMgrInc.h"
 using namespace OpUnitStatusRecordSQLParts;
 #include "PageBase.h"
 
 /////////////////////////////////////////////////////////////////////////////
 class CLaunchThread : public CThinThread, public CPageBase
 {
 public:
     CLaunchThread() {};
     virtual ~CLaunchThread(){KillThread2();}
 
     BOOL Go();
     void Set(HWND hParent, HWND hList, HCURSOR hc,
              const CString& csLaunchee, const CString& csOpUnit);
 
 protected:
     HWND m_hwndParent;
     HWND m_hwndList;
     HCURSOR m_hCursor;
     CString m_csLaunchee;
     CString m_csOpUnit;
     CListCtrlEx m_LC;
 
     virtual void DoWork();    // do the work
 };
 #endif
 
 
 // LaunchThread.cpp : implementation file
 // Copyright (C) 1997 by CTB/McGraw-Hill, All Rights Reserved
 
 #include "stdafx.h"
 #include "LaunchThread.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CLaunchThread
 
 void CLaunchThread::Set(HWND hParent, HWND hList,  HCURSOR hc,
                         const CString& csLaunchee, const CString& csOpUnit)
 {
     m_hwndParent = hParent;
     m_hwndList = hList;
     m_hCursor = hc;
     m_csLaunchee = csLaunchee;
     m_csOpUnit = csOpUnit;
 }
 
 BOOL CLaunchThread::Go()
 {
     // kick off CThinThread with no loop delay
     return CreateThread(0, 0, NULL, 0);
 }
 
 void CLaunchThread::DoWork()
 {
     CString csResult = LaunchApp(m_csLaunchee, m_csOpUnit, m_hCursor);
 
     if (csResult.GetLength())
     {
         SetActiveState(csResult, m_csOpUnit);
         CListCtrlEx list;
         list.Attach(m_hwndList);
         SelectItemRow(list, 0, m_csOpUnit);
         list.Detach();
     }
 
     PostMessage(m_hwndParent, WM_COMMAND, (csResult.GetLength() ? 1 : 0), 
                 reinterpret_cast<int> (this));
     m_bEndThread = TRUE;
 }
 
 void CUpdatesPage::OnBegin() 
 {
     HCURSOR hCursorOld = SetCursor(AfxGetApp()->LoadCursor(IDC_PLEASEWAIT));
 
     // get selected OpUnit
     int row = m_lcOpUnitInfo.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
     if (row > -1)
     {
         CString csOpUnit = m_lcOpUnitInfo.GetItemText(row, UC_ORGTP);
         csOpUnit += ",";
         csOpUnit += m_lcOpUnitInfo.GetItemText(row, UC_STRUCTUREELEMENT);
         csOpUnit += ",";
         csOpUnit += m_lcOpUnitInfo.GetItemText(row, UC_OPUNIT);
 
         // launch the app
         m_iLaunchCount++;
         CLaunchThread* pThread = new CLaunchThread;
         pThread->Set(GetSafeHwnd(), m_lcOpUnitInfo.GetSafeHwnd(), hCursorOld, 
                   pLaunchName, csOpUnit);
         pThread->Go();
         m_lcOpUnitInfo.SetFocus();
     }
 }
 
 // launch a Winscore app
 CString CPageBase::LaunchApp(const CString& csAppName,
                              const CString& csOpUnit, HCURSOR hCursor)
 {
     CString csRet = "";
     CParamFile file;
     SOutputStrings strOut;
 
     if (csAppName.GetLength() && SetParameters(&file, csOpUnit))
     {
         STARTUPINFO si;
         PROCESS_INFORMATION pi;
 
         si.cb           = sizeof(STARTUPINFO);
         si.lpReserved   = NULL;
         si.lpDesktop    = NULL;
         si.lpTitle      = NULL;
         si.dwFlags      = 0;
         si.cbReserved2  = 0;
         si.lpReserved2  = NULL;
 
         if (CreateProcess(NULL,
                 (LPTSTR)(LPCTSTR)(csAppName + CString(" ") + file.GetName()),
                 NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
         {
             Sleep(3000);          // to show launch cursor
             SetCursor(hCursor);
 
             if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
             {
                 CloseHandle(pi.hThread);
                 CloseHandle(pi.hProcess);
                 file.GetOutputStrings(strOut);
                 strOut.csStatus.MakeLower();
                 if (strOut.csStatus == fpvSuccess)
                     csRet = CParamFile::Code2String(strOut.csRet1);
             }
         }
         else SetCursor(hCursor);
     }
 
     Sleep(1000);                  // to wait for completion of file usage
     CKeyValues key;
     if (key.GetRemoveParamFile()) file.Remove();
     return csRet;
 }
 
 BOOL CUpdatesPage::OnCommand(WPARAM wParam, LPARAM lParam) 
 {
     if (wParam > 1)
     {                             // message not sent by CLaunchThread
         CPropertyPage::OnCommand(wParam, lParam);
         return 0;
     }
                         // message sent by CLaunchThread
     LaunchDone(!wParam ? pLaunchName : "", lParam);
     m_iLaunchCount--;
     m_lcOpUnitInfo.SetFocus();
     return CPropertyPage::OnCommand(0, lParam);
 }
 
 // launch completion handler
 void CPageBase::LaunchDone(const CString& csAppName, LPARAM lParam)
 {
     if (csAppName.GetLength()) AfxMessageBox(pLaunchText + csAppName);
     delete (reinterpret_cast<CLaunchThread*> (lParam));
 }
 
 BOOL CSpcsView::CheckThreads() 
 {
 BOOL bRet = TRUE;
 
     CRefreshThread* pThread1 = m_pPropSheet->GetStatusPage()->GetThread();
     CRefreshThread* pThread2 = m_pPropSheet->GetOfflinePage()->GetThread();
 
     if (pThread1->IsBusy() || pThread2->IsBusy() || 
          m_pPropSheet->GetUpdatesPage()->IsLaunchActive() ||
          m_pPropSheet->GetBrowserPage()->IsLaunchActive() || 
           m_pPropSheet->GetReaderPage()->IsLaunchActive()) bRet = FALSE;
     return bRet;
}

Figure 9   CRequestHandlerThread and CrequestPacket


 // HandlerThread.h : header file
 // Copyright (C) 1997 by The Windward Group, All Rights Reserved
 
 #ifndef HANDLERTHREAD_H
 #define HANDLERTHREAD _H
 
 #ifndef __AFXWIN_H__
     #error include 'stdafx.h' before including this file for PCH
 #endif
 
 #include <afxmt.h>
 #include "ThinThread.h"
 
 /////////////////////////////////////////////////////////////////////////////
 class CRequestPacket : public CObject
 {
 public:
     int m_iPacketType;
     int m_iStatus;
     CString m_csInput;
     CString m_csOutput;
 };
 
 
 /////////////////////////////////////////////////////////////////////////////
 class CRequestHandlerThread : public CThinThread
 {
 public:
     CRequestHandlerThread() {};
     virtual ~CRequestHandlerThread() {KillThread2();}
 
     void AddRequest(CRequestPacket* pPkt);
     BOOL Go() {return CreateThread(0, 0, NULL, 0);}
     void Set(HWND hParent, int ID);
 
 protected:
     HWND m_hwndParent;
     int m_ID;
     CObList m_PktList;
     CCriticalSection m_CritSect;
 
     virtual void DoWork();              // do the work
 
     virtual void CheckInput() = 0;      // check packet input
     virtual void RetrieveData() = 0;    // get required data
     virtual void ProcessData() = 0;     // process the data
     virtual void UpdateState() = 0;     // update any state info
     virtual void LoadOutput() = 0;      // load packet output
 };
 #endif
 
 
 // HandlerThread.cpp : implementation file
 // Copyright (C) 1997 by The Windward Group, All Rights Reserved
 
 #include "stdafx.h"
 #include "HandlerThread.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CRequestHandlerThread
 
 void CRequestHandlerThread::Set(HWND hParent, int ID)
 {
     m_hwndParent = hParent;
     m_ID = ID;
 }
 void CRequestHandlerThread::AddRequest(CRequestPacket* pPkt)
 {
     CSingleLock csl(&m_CritSect);        // be safe
     csl.Lock();                          // when accessing packet list
     m_PktList.AddTail(pPkt);             // put the packet on the list
     csl.Unlock();
     GetEvent()->SetEvent();              // notify internal thread
 }
 
 void CRequestHandlerThread::DoWork()
 {
     while (!m_PktList.IsEmpty())         // get next packet
     {
         CSingleLock csl(&m_CritSect);    // be safe
         csl.Lock();                      // when accessing packet list
         CRequestPacket* pPkt = dynamic_cast<CRequestPacket*>
                                  (m_PktList.RemoveHead());
         csl.Unlock();
         
         CheckInput();
         RetrieveData();
         ProcessData();
         UpdateState();
         LoadOutput(); 
                                          // return packet to dispatcher
         PostMessage(m_hwndParent, WM_COMMAND,
                     m_ID, reinterpret_cast<int> (pPkt));
     }
}

Figure 10   CfileSearchThread


 // SearchThread.h : header file
 // Copyright (C) 1997 by The Windward Group, All Rights Reserved
 
 #ifndef SEARCHTHREAD_H
 #define SEARCHTHREAD_H
 
 #ifndef __AFXWIN_H__
     #error include 'stdafx.h' before including this file for PCH
 #endif
 
 #include <afxmt.h>
 #include "ThinThread.h"
 
 /////////////////////////////////////////////////////////////////////////////
 class CFileSearchThread : public CThinThread
 {
 public:
     CFileSearchThread() {};
     virtual ~CFileSearchThread() {KillThread2();}
 
     BOOL Go() {return CreateThread(0, 0, NULL, 0);}
     void Set(HWND hParent, int ID, const CString& csSearchRoot, 
             const CString& csSearchString);
 
 protected:
     HWND m_hwndParent;
     int m_ID;
     CString m_csSearchRoot;
     CString m_csSearchString;
 
     virtual void DoWork();    // do the work
 };
 #endif
 
 
 // SearchThread.cpp : implementation file
 // Copyright (C) 1997 by The Windward Group, All Rights Reserved
 
 #include "stdafx.h"
 #include "SearchThread.h"
 #include <direct.h>
 
 /////////////////////////////////////////////////////////////////////////////
 // CFileSearchThread
 
 void CFileSearchThread::Set(HWND hParent, int ID, const CString& csSearchRoot, 
 const CString& csSearchString)
 {
     m_hwndParent = hParent;
     m_ID = ID;
     m_csSearchRoot = csSearchRoot;
     m_csSearchString = csSearchString;
 }
 
 void CFileSearchThread::DoWork()
 {
     WIN32_FIND_DATA fd;
 
     _chdir(m_csSearchRoot);
     HANDLE hnd = FindFirstFile(m_csSearchString, &fd);
     if (hnd != INVALID_HANDLE_VALUE)
         PostMessage(m_hwndParent, WM_COMMAND,
                     m_ID, reinterpret_cast<int> (this));
     m_bEndThread = TRUE;
 }