Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Window handle for a console program 2

Status
Not open for further replies.

PavelGur

Programmer
Sep 21, 2001
75
I need to post messages from one console program to another that I’ve started with CreateProcess.. I need to find the handle to the window of new program to do it. I tried
HWND myhwnd;
while ( NULL == myhwnd)
{
myhwnd = ::FindWindowEx ( NULL, NULL, NULL, szMyPath );
}
However it always returns NULL. I also tried to find handle of the window for parent process with the same result.

I can see both windows in Spy++ with titles equal to program paths. The handles are there as well. But how I can get them programmatically?
Thanks, Pavel.
 
Console programm, I suppose, is not owner of its window. Try to send messages directly to the thread - PostThreadMessage(). Thread ID is returned from CreateProcess() in field dwThreadId of PROCESS_INFORMATION parameter.
 
Use the API GetConsoleWindow( ) if your os is Windows 2000 or above (not supported in NT4 and 9x/ME). If your os does not meet the requirements, let me know, i have some code somewhere but must search for it first.

Marcel
 
Thanks guys, I looked into this deeper and though I was able to I find the handle to console window with the function that first sets the window title and then calls FindWindow I kind of got convinced that mingis is right about PostThreadMessage rather than PostMessage.
I found the article claiming that PostMessage is useful to send messages the classes derived from CWnd while PostThreadMessage is for classes derived from CWinApp. Since console application is using CWinApp class it seems that PostThreadMessage is the one to use.
I have a problem however in compilation of sending program. There are two versions of PostThreadMessage. One is from SDK and another from CWinThread. I need the one from SDK because it specifies thread handle as a first parameter. However my sending console program defaults to CWinThread. I probably need to change some settings in it.
Do you know how I can overcome this problem?
Thanks, Pavel.
 
I think I've jumped to conclusion too fast. It probably has nothing to do with SDK vs MFC. The way I coded it:
PostThreadMessage(pi.hThread, WM_MY_DATA,
(WPARAM)MessText.GetBuffer(MessText.GetLength()), 0);
The compile diagnostic:
error C2664:'PostThreadMessageA' : cannot convert parameter 1 from 'void *' to 'unsigned long'
The first parameter of SDK function is DWORD idTread. The pi.hThread should be DWORD thread number. However it seems to be interpreted as a pointer.
The CreateProcess coded as
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};
if (CreateProcess(NULL, szSendMess.GetBuffer(szSendMess.GetLength()),NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,NULL, NULL, &si, &pi ))
WaitForInputIdle(pi.hProcess,3000);

I tried to force cast
PostThreadMessage((DWORD)pi.hThread, WM_MY_DATA,
(WPARAM)MessText.GetBuffer(MessText.GetLength()), 0);
It compiles but does not work.
Thanks again, Pavel.
 
Sorry, I found what the problem was. It was my fault. The first parameter is ThreadID not ThreadHandle. It compiles and works now. I need to find out how to receive it in the called program. This still does not work.
Thanks again, Pavel.
 
Pavel,

First thing you wrote was : "I need to post messages from one console program to another that I’ve started with CreateProcess.." and now (a little bit late - apologies) I realize what you really want: some interprocess communication between two console programs.
I don't think a console program can access Windows messages sent to the console. Even when you succeed to send a message to a console, the program which is using it will not be able to receive it, unless you're going to subclass the console Window Procedure. Alse realize, only the values of wParam and lParam can be received correct by an other process, but not the string or struct they possible point to.

I suggest you use an other way of inter-process communication, for example:
- socket
- named pipe
- named event / mutex / semaphore
If you write in more detail what you exactly want, I can give better advice and maybe, code.

Marcel
 
Marcel, thank you very much for answering my posts. Here is what I need to accomplish. I will receive real time data in the callback from Internet. The callback function can not wait for me to put data into DB. I need quickly to copy data and post it to the queue of another program that will take care of the data. The sample program I have from the vendor posts data to its GUI thread and then displays it in the window as fast moving numbers. I need to put this data in the DB for further analysis.

I’ve succeeded today in sending data to the console program and receiving it there. You are right about getting data pointers via wParam. That is exactly how vendor’s program does it and how I plan to do it. Here is how they receive the data before displaying it in window:
LRESULT CMainFrame::OnData(WPARAM w, LPARAM l)
{
MID_DATA* pData = (MID_DATA*)w;
So far for testing purposes only I was sending data the following way:
CString MessText = "This";
int i = 0;
while (i == 0)
i = PostThreadMessage(pi.dwThreadId, WM_MY_DATA,
(WPARAM)MessText.GetBuffer(MessText.GetLength()), 0);

On the receiving side I have:
if (GetMessage(&msg, NULL, 0, 0))
{
AfxMessageBox( "I've got the message", MB_OK, 0 );
char szTxt = (char)msg.wParam;
}
You are telling me that I will not be able to access the data via the pointer passed to the console program. You may be right. Then I will need to find other way to do it. I will highly appreciate if you help me with other suggestion.
Thanks again, Pavel.
 
Pavel,

Can't you do this with a second thread instead of another process? If yes, you don't need interprocess communication and it will be more simple.

Like this:
- Foregroundthread:
1. Wait for data to receive from the internet
2. Allocate memory and copy the data to the allocated block
3. Add the address of the allocated memory block and it's length to a queue
4. Respond "all done"
5. repeat from step 1

- Backgroundthread:
1. Wait for the queue to contain data
2. Remove the queue-entry for the data
3. Process the data into the db
4. Free the data-memory
5. repeat from step 1.

Queue :
- can be implemented as a fifo (first in first out) if the order of processing is important or as a lifo (last in first out, like the stack), which is easier to implement, if order of processing is not important.
- Can be implemented as an array of a struct containing a void-pointer to the data and an int containing the length of the data.
- Protect it with a mutex or critical-section to prevent an entry being written at the same time an entry is read.
- Let a semaphore keep track of the number of queue-entries: add 1 to the semaphore when an entry is written, subtract 1 from the semaphore when an entry is read. Let the backgroundthread wait for this semaphore before trying to read an entry from the queue. That will automatically halt the backgroundthread if the queue is empty, until it is filled again.

Marcel
 
Thanks again Marcel, you are terrific. I was thinking of this solution from the beginning and did not go this way because Microsoft scared me with article

.

In the paragraph Threads it is saying:
“When you need a background task, threads may not be your best solution. Effective idle handling of the event may be a better solution. It’s easier to understand the locality of reference of your program with one thread than with two or more.”

And later:
“Threads also present communication problems. You will have to manage the communication link between your threads, whether it be with a list of messages or by allocating and using shared memory. Managing your communication link will likely require synchronization to avoid race conditions and deadlock problems. Such complexity can easily turn into bugs and performance problems.”

Nevertheless I’m willing to try taking your word for it. My understanding is that I will need to create new thread for background process.

Right now I have one class that derived from CWinApp that is derived from CWinThread (a requirement for thread based classes) and second class derived from CFrameWnd that derived from CWnd. So I assume I have just one thread now and will need the second background thread.

Then I will need to set up input queue for it. And then I will need to copy data from the Internet data receiving queue to the background queue. After this on the background I will be able to read fifo (perfectly OK for my task) and store data in DB.

Am I correct here?

Thanks again, Pavel.
 
Pavel,

Each process has at least one thread. Starting another process automatically means starting another thread, which is also some sort of multithreading, but with a lot of difficulties and overhead (meaning time !!) when they need to communicate with each other.

Because we are talking about a console program here, you can not use the idle message the msdn article is talking about, at least I should not know how to do that.

That leaves us with only two other possibilities: multithreading or async writing the data to the database.

From these two possibilities the foreground - background thread seems the best solution to me. A fifo class can handle all the required synchronization.

I don't know MFC so I can't tell you how to start a thread in MFC. Maybe you know yourself or someone else can answer that.

Here is a fifo implementation. You can use it direct or change it to your specific needs. It handles all the synchronization. You need one global instance it. The foreground thread calls AddToFifo, the background thread calls GetItem.

mutex.h:
Code:
#ifndef __M_U_T_E_X__
#define __M_U_T_E_X__


#define MUTEX_OK                        0
#define MUTEX_INITIALIZATION_ERROR  30000
#define MUTEX_TIMEOUT               30001
#define MUTEX_NOT_OWNER             30002
#define MUTEX_TO_BE_DESTROYED       30003


#define CheckMutexInitialization                \
  if ( !mutex.fInitOK )                         \
     { return MUTEX_INITIALIZATION_ERROR; }     \
  DWORD tid = GetCurrentThreadId ( );

#define CheckMutexOwner                         \
  CheckMutexInitialization                      \
  if ( tid != mutex.dwOwningThread )            \
     { return MUTEX_NOT_OWNER; }

#define CheckMutexDestroy                       \
  CheckMutexInitialization                      \
  if ( mutex.dwDestroyThread != 0   &&          \
       mutex.dwDestroyThread != tid )           \
     { return MUTEX_TO_BE_DESTROYED; }

#define FreeMutex                               \
  mutex.nOwningCount = 0;                       \
  mutex.dwOwningThread = 0;                     \
  ReleaseMutex ( mutex.hMutex );                \
  return MUTEX_OK;

typedef struct
{
  BOOL   fInitOK;         // class goed geinitialiseerd ?
  HANDLE hMutex;          // Mutex-handle
  DWORD  dwOwningThread;  // Eigenaar van de mutex
  DWORD  dwDestroyThread; // thread van de destructor
  DWORD  nOwningCount;    // Aantal malen dat de mutex vrijgegeven moet worden
} mutexstruct;


class mutexclass
{
  private :       mutexstruct mutex;

            void  InitMutex   ( BOOL fOwner );

  public  :       mutexclass  ( );
                  mutexclass  ( BOOL fOwner );
                  ~mutexclass ( );
            BOOL  fClassOK    ( );
            DWORD BecomeOwner ( );
            DWORD BecomeOwner ( DWORD WaitTime );
            DWORD Release     ( );
            DWORD ReleaseAll  ( );
            DWORD fOwn        ( );
};

#endif  // ifndef __M_U_T_E_X__

mutex.cpp:
Code:
#include <windows.h>
#include "mutex.h"


mutexclass::mutexclass ( )
{
  InitMutex ( FALSE ); }


mutexclass::mutexclass ( BOOL fOwner )
{
  InitMutex ( fOwner ); }


mutexclass::~mutexclass ( )
{
  if ( mutex.hMutex == NULL )
     { return; }
  mutex.dwDestroyThread = GetCurrentThreadId ( );
  BecomeOwner ( );
  ReleaseAll  ( );
  CloseHandle ( mutex.hMutex );
  memset ( &mutex, 0, sizeof ( mutex )); }


void mutexclass::InitMutex ( BOOL fOwner )
{
  memset ( &mutex, 0, sizeof ( mutex ));
  mutex.hMutex = CreateMutex ( NULL, fOwner, NULL );
  if ( mutex.hMutex == NULL )
     { return; }
  if ( fOwner )
     { mutex.dwOwningThread = GetCurrentThreadId ( );
       mutex.nOwningCount = 1; }
  mutex.fInitOK = TRUE; }


BOOL mutexclass::fClassOK ( )
{
  return mutex.fInitOK; }


DWORD mutexclass::BecomeOwner ( DWORD WaitTime )
{
  CheckMutexDestroy
  if ( tid == mutex.dwOwningThread )
     { mutex.nOwningCount++;
       return MUTEX_OK; }
  DWORD dwWaitStatus  = WaitForSingleObject ( mutex.hMutex, WaitTime );
  if ( dwWaitStatus == WAIT_TIMEOUT )
     { return MUTEX_TIMEOUT; }
  if ( mutex.dwDestroyThread != 0 &&
       mutex.dwDestroyThread != tid )
     { ReleaseMutex ( mutex.hMutex );
       return MUTEX_TO_BE_DESTROYED; }
  mutex.dwOwningThread = tid;
  mutex.nOwningCount   = 1;
  return MUTEX_OK; }


DWORD mutexclass::BecomeOwner ( )
{
  return BecomeOwner ( INFINITE ); }


DWORD mutexclass::Release ( )
{
  CheckMutexOwner
  if ( mutex.nOwningCount > 1 )
     { mutex.nOwningCount--;
       return MUTEX_OK; }
  FreeMutex }


DWORD mutexclass::ReleaseAll ( )
{
  CheckMutexOwner
  FreeMutex }


DWORD mutexclass::fOwn ( )
{
  CheckMutexOwner
  return MUTEX_OK; }

sem.h:
Code:
#ifndef __S_E_M__
#define __S_E_M__


#define SEM_OK                        0
#define SEM_INITIALIZATION_ERROR  30100
#define SEM_TIMEOUT               30101
#define SEM_TO_BE_DESTROYED       30103


#define CheckSemInitialization                  \
  if ( !sem.fInitOK )                           \
     { return SEM_INITIALIZATION_ERROR; }       \
  DWORD tid = GetCurrentThreadId ( );


#define CheckSemDestroy                         \
  CheckSemInitialization                        \
  if ( sem.dwDestroyThread != 0   &&            \
       sem.dwDestroyThread != tid )             \
     { return SEM_TO_BE_DESTROYED; }


typedef struct
{
  BOOL   fInitOK;         // class goed geinitialiseerd ?
  HANDLE hSem;            // sem-handle
  DWORD  MaxCount;        // Maximum waarde
  DWORD  CurrentCount;    // Huidige waarde
  DWORD  nThreadsWaiting; // Aantal wachtende threads
  DWORD  dwDestroyThread; // thread van de destructor
} semstruct;


class semclass
{
  private :       semstruct sem;

            void  InitSem       ( DWORD MaxAantal, DWORD Initial );

  public  :       semclass      ( DWORD MaxAantal, DWORD Initial );
                  semclass      ( DWORD MaxAantal );
                  ~semclass     ( );
            DWORD PassSemaphore ( DWORD dwWaitTime );
            DWORD PassSemaphore ( );
            DWORD Release       ( );
            BOOL  fClassOK      ( );
};

#endif  // ifndef __S_E_M__

sem.cpp:
Code:
#include <windows.h>
#include "sem.h"


semclass::semclass ( DWORD MaxAantal )
{
  InitSem ( MaxAantal, 0 ); }


semclass::semclass ( DWORD MaxAantal, DWORD Initial )
{
  InitSem ( MaxAantal, Initial ); }


semclass::~semclass ( )
{
  if ( sem.hSem == NULL )
     { return; }
  sem.dwDestroyThread = GetCurrentThreadId ( );
  while ( sem.nThreadsWaiting )
     { ReleaseSemaphore ( sem.hSem, 1, NULL );
       Sleep ( 10 ); }
  CloseHandle ( sem.hSem );
  memset ( &sem, 0, sizeof ( sem )); }


void semclass::InitSem ( DWORD MaxAantal, DWORD Initial )
{
  memset ( &sem, 0, sizeof ( sem ));
  sem.hSem = CreateSemaphore ( NULL, (LONG)Initial, (LONG)MaxAantal, NULL );
  if ( sem.hSem == NULL )
     { return; }
  sem.fInitOK = TRUE; }


BOOL semclass::fClassOK ( )
{
  return sem.fInitOK; }


DWORD semclass::PassSemaphore ( DWORD WaitTime )
{
  CheckSemDestroy
  sem.nThreadsWaiting++;
  DWORD dwWaitStatus  = WaitForSingleObject ( sem.hSem, WaitTime );
  sem.nThreadsWaiting--;
  if ( dwWaitStatus == WAIT_TIMEOUT )
     { return SEM_TIMEOUT; }
  if ( sem.dwDestroyThread != 0 &&
       sem.dwDestroyThread != tid )
     { return SEM_TO_BE_DESTROYED; }
  return SEM_OK; }


DWORD semclass::PassSemaphore ( )
{
  return PassSemaphore ( INFINITE ); }


DWORD semclass::Release ( )
{
  CheckSemInitialization
  ReleaseSemaphore ( sem.hSem, 1, NULL );
  return SEM_OK; }

fifo.h:
Code:
#ifndef __F_I_F_O__
#define __F_I_F_O__

#define DEFAULT_MAX_ITEMS 1000
#include "mutex.h"
#include "sem.h"

#define FIFO_OK                       0
#define FIFO_INITIALIZATION_ERROR 30200
#define FIFO_QUEUE_FULL_ERROR     30201

typedef struct
{
  mutexclass *mutex;   // Mutex voor multi-thread access
  semclass   *sem;     // Semafoor om threads te laten wachten als de
                       // queue leeg is
  DWORD  MaxAantal;    // Queue-grootte
  DWORD  Aantal;       // Aantal gequeuede items
  DWORD  CurrentIndex; // Eerste op te halen item
  void  *Queue[1];     // Queue, wegens variabele tabel achteraan in
                       // de structure laten staan
} fifostruct;


class fifoclass
{
  private : fifostruct *fifo;

            void  InitFifo   ( DWORD QueueSize );

  public  :       fifoclass  ( );
                  fifoclass  ( DWORD QueueSize );
                  ~fifoclass ( );
            BOOL  fInitOK    ( );
            DWORD AddToFifo  ( void *Param, BOOL fHighPriority );
            DWORD GetItem    ( void **Param );
};

#endif  // ifndef __F_I_F_O__

fifo.cpp:
Code:
#include <windows.h>
#include <malloc.h>
#include "fifo.h"

fifoclass::fifoclass ( )
{
  InitFifo ( DEFAULT_MAX_ITEMS ); }


fifoclass::fifoclass ( DWORD QueueSize )
{
  InitFifo ( QueueSize ); }


fifoclass::~fifoclass ( )
{
  if ( fifo == NULL )
     { return; }
  if ( fifo->mutex != NULL )
     { delete fifo->mutex;
       fifo->mutex = NULL; }
  if ( fifo->sem   != NULL )
     { delete fifo->sem;
       fifo->sem   = NULL; }
  free ( fifo );
  fifo = NULL; }


void fifoclass::InitFifo ( DWORD QueueSize )
{
  fifo = NULL;
  if ( !QueueSize )
     { return; }
  DWORD nBytes = sizeof ( fifostruct ) + ( QueueSize * sizeof ( void * ));
  fifo = (fifostruct *)( malloc ( nBytes ));
  if ( fifo == NULL )
     { return; }
  memset ( fifo, 0, nBytes );
  fifo->MaxAantal = QueueSize;
  fifo->mutex     = new mutexclass;
  fifo->sem       = new semclass ( QueueSize ); }


BOOL fifoclass::fInitOK ( )
{
  if ( fifo == NULL )
     { return FALSE; }
  if ( fifo->mutex == NULL || fifo->sem == NULL )
     { return FALSE; }
  return TRUE; }


DWORD fifoclass::AddToFifo ( void *Param, BOOL fHighPriority )
{
  if ( !fInitOK ( ))
     { return FIFO_INITIALIZATION_ERROR; }
  if ( fifo->Aantal >= fifo->MaxAantal )
     { return FIFO_QUEUE_FULL_ERROR; }
  DWORD Erc = fifo->mutex->BecomeOwner ( );
  if ( Erc != MUTEX_OK )
     { return Erc; }
  if ( fifo->Aantal == 0 )
     { fifo->Queue[0] = Param;
       fifo->CurrentIndex = 0;
       fifo->Aantal = 1; } else
     { if ( fHighPriority )
          { // Vooraan toevoegen
            if ( fifo->CurrentIndex == 0 )
               { fifo->CurrentIndex = fifo->MaxAantal - 1; } else
               { fifo->CurrentIndex--; }
            fifo->Queue[fifo->CurrentIndex] = Param; } else
          { // Achteraan toevoegen
            DWORD Index = ( fifo->CurrentIndex + fifo->Aantal )
                        % fifo->MaxAantal;
            fifo->Queue[Index] = Param; }
       fifo->Aantal++; }
  Erc = fifo->mutex->Release ( );
  if ( Erc != MUTEX_OK )
     { return Erc; }
  Erc = fifo->sem->Release ( );
  if ( Erc != SEM_OK )
     { return Erc; }
  return FIFO_OK; }


DWORD fifoclass::GetItem ( void **Param )
{
  if ( !fInitOK ( ))
     { return FIFO_INITIALIZATION_ERROR; }
  DWORD Erc = fifo->sem->PassSemaphore ( );
  if ( Erc != SEM_OK )
     { return Erc; }
  Erc = fifo->mutex->BecomeOwner ( );
  if ( Erc != MUTEX_OK )
     { return Erc; }
  *(Param) = fifo->Queue[fifo->CurrentIndex];
  fifo->CurrentIndex++;
  if ( fifo->CurrentIndex >= fifo->MaxAantal )
     { fifo->CurrentIndex = 0; }
  fifo->Aantal--;
  Erc = fifo->mutex->Release ( );
  if ( Erc != MUTEX_OK )
     { return Erc; }
  return FIFO_OK; }

Marcel
 
Thank you Marcel. I will probably do it in MFC.
Pavel.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top