1

I am trying to learn how to pass and handle messages in a VCL forms app. I've been digging the internet for some time and found this

Suppose I have a progress bar I want to update using messages (btw if there's any other better way, I am eager to hear it) So I made a simple project to test the stuff and here's what I have (RECEIVER is a name of a form with progress bar, SENDER is a button used to send messages, updBar is a function to update progress bar, and 123456 is a message ID I want to use): Unit1.cpp:

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TRECIEVER *RECIEVER;
//---------------------------------------------------------------------------
__fastcall TRECIEVER::TRECIEVER(TComponent* Owner)
    : TForm(Owner)
{
}
void __fastcall TRECIEVER::barUPD(TMessage& msg){
    BAR->StepIt();
}
//---------------------------------------------------------------------------
void __fastcall TRECIEVER::SENDERClick(TObject *Sender)
{
//BAR->StepIt();
PostMessage(FindWindow(0,(wchar_t*)"RECIEVER"),123456,0,0);
}

Unit1.h:

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ComCtrls.hpp>
//---------------------------------------------------------------------------
class TRECIEVER : public TForm
{
__published:    // IDE-managed Components
    TButton *SENDER;
    TProgressBar *BAR;
    void __fastcall SENDERClick(TObject *Sender);
private:    // User declarations
public:     // User declarations
    void __fastcall barUPD(TMessage& msg);
    __fastcall TRECIEVER(TComponent* Owner);
    BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(123456,TMessage,barUPD);
    END_MESSAGE_MAP(TForm)
};
//---------------------------------------------------------------------------
extern PACKAGE TRECIEVER *RECIEVER;
//---------------------------------------------------------------------------
#endif

As you can see I have defined both the handling function and appropriate message handler for my message. But when I look it through via debugger (after sending the message using the button), the execution point never seems to go to neither my function nor the handler line. Thanks in advance

Kromster
  • 7,181
  • 7
  • 63
  • 111
Gear54rus
  • 207
  • 4
  • 12
  • Have you examined the return value to FindWindow(0,(wchar_t*)"RECIEVER") to see if it actually finds anything? I am sure there is a good reason, but why the wchar_t* cast ?? – mathematician1975 Jun 21 '12 at 11:44
  • Also add the embarcadero tag to your question - you might get more response – mathematician1975 Jun 21 '12 at 11:49
  • Thanks for replies! Strange thing, maybe because of my noobiness even when I call FindWindow it still actually calls FindWindowW which accepts only wchar_t. I have a feeling it is specified somewhere in a project properties. I cant understand... HWND aaa=FindWindow(0,(wchar_t*)"RECIEVER"); and aaa is NULL, what am I doing wrong? – Gear54rus Jun 21 '12 at 11:58
  • Ive changed Findwindow call to HWND_BROADCAST and thing is working as intended. How do I find my form??? – Gear54rus Jun 21 '12 at 13:17
  • The "TCHAR maps to" setting in the Project Options controls whether `FindWindow()` maps to `FindWindowA()` or `FindWindowW()`. But either way, `(wchar_t*)"RECEIVER"` is just plain wrong. You are type-casting a `char[]` to a `wchar_t*`, which will not produce a valid string. You have to use `L"RECEIVER"` to produce a valid `wchar_t[]` instead. – Remy Lebeau Jun 21 '12 at 22:20
  • On another note, when using `FindWindow()` and not `FindWindowW()` directly (or any other API that is senstive to `TCHAR`, for that matter), you should use the `TEXT()` macro instead of hard-coding the `L` prefix for literals, eg: `TEXT("RECEIVER")`. – Remy Lebeau Jun 22 '12 at 01:52

1 Answers1

3

There are two problems with your code:

1) 123456 (0x1E240) is not a valid user-level message ID. Values above 0xFFFF are reserved by the OS. Custom messages MUST be in the WM_USER (0x0400 - 0x7FFF), WM_APP (0x8000 - 0xBFFF), or RegisterWindowMessage() (0xC000 - 0xFFFF) ranges.

2) you are passing a bad string pointer to FindWindow(). You are type-casting a char[] to a wchar_t*, which is an invalid type-cast. To specify that the string literal should use wchar_t instead of char, you have to prefix the literal with the L specifier instead. Or more generically, when using any TCHAR-senstive API (like FindWindow()), use the TEXT() macro instead.

In addition, although not strictly an error, you should ue VCL_MESSAGE_HANDLER() instead of MESSAGE_HANDLER(), only because MESSAGE_HANDLER() is defined differently by ATL. If you are not using ATL in your project, you won't run into a problem, but it is better to use VCL_MESSAGE_HANDLER() just to make absolutely sure, and to document that the code is using the VCL's version of MESSAGE_HANDLER() and not some other version.

Try this:

Unit1.h:

#ifndef Unit1H 
#define Unit1H 
//--------------------------------------------------------------------------- 
#include <System.Classes.hpp> 
#include <Vcl.Controls.hpp> 
#include <Vcl.StdCtrls.hpp> 
#include <Vcl.Forms.hpp> 
#include <Vcl.ComCtrls.hpp> 
//--------------------------------------------------------------------------- 
#define WM_BAR_STEP_IT  (WM_USER+1)
//--------------------------------------------------------------------------- 
class TRECIEVER : public TForm 
{ 
__published:    // IDE-managed Components 
    TButton *SENDER; 
    TProgressBar *BAR; 
    void __fastcall SENDERClick(TObject *Sender); 
private:    // User declarations 
    void __fastcall barUPD(TMessage&); 
public:     // User declarations 
    __fastcall TRECIEVER(TComponent* Owner); 
    BEGIN_MESSAGE_MAP 
      VCL_MESSAGE_HANDLER(WM_BAR_STEP_IT, TMessage, barUPD); 
    END_MESSAGE_MAP(TForm) 
}; 
//--------------------------------------------------------------------------- 
extern PACKAGE TRECIEVER *RECIEVER; 
//--------------------------------------------------------------------------- 
#endif 

Unit1.cpp:

#include <vcl.h> 
#pragma hdrstop 

#include "Unit1.h" 
//--------------------------------------------------------------------------- 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
TRECIEVER *RECIEVER; 
//--------------------------------------------------------------------------- 
__fastcall TRECIEVER::TRECIEVER(TComponent* Owner) 
    : TForm(Owner) 
{ 
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::barUPD(TMessage&)
{ 
    BAR->StepIt(); 
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::SENDERClick(TObject *Sender) 
{ 
    // this assumes the Form's Caption is set to "RECEIVER"
    // also specifying the class type for good measure...
    PostMessage(FindWindow(TEXT("TRECEIVER"), TEXT("RECIEVER")), WM_BAR_STEP_IT, 0, 0); 

    //Alternatively:
    //PostMessage(FindWindowW(ClassName().c_str(), Caption.c_str()), WM_BAR_STEP_IT, 0, 0); 
} 
//--------------------------------------------------------------------------- 

With that said, since the message is private to the app, there is no need to use FindWindow() at all, use the TForm::Handle property instead. And I would even go a step further by getting rid of the MESSAGE_HANDLER() altogether. The message is private to the internals of TRECEIVER, so that is where it should stay:

Unit1.h:

#ifndef Unit1H 
#define Unit1H 
//--------------------------------------------------------------------------- 
#include <System.Classes.hpp> 
#include <Vcl.Controls.hpp> 
#include <Vcl.StdCtrls.hpp> 
#include <Vcl.Forms.hpp> 
#include <Vcl.ComCtrls.hpp> 
//--------------------------------------------------------------------------- 
class TRECIEVER : public TForm 
{ 
__published:    // IDE-managed Components 
    TButton *SENDER; 
    TProgressBar *BAR; 
    void __fastcall SENDERClick(TObject *Sender); 
private:    // User declarations 
protected:
    void __fastcall WndProc(TMessage& Message); 
public:     // User declarations 
    __fastcall TRECIEVER(TComponent* Owner); 
}; 
//--------------------------------------------------------------------------- 
extern PACKAGE TRECIEVER *RECIEVER; 
//--------------------------------------------------------------------------- 
#endif 

Unit1.cpp:

#include <vcl.h> 
#pragma hdrstop 

#include "Unit1.h" 
//--------------------------------------------------------------------------- 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
TRECIEVER *RECIEVER; 
#define WM_BAR_STEP_IT  (WM_USER+1)
//--------------------------------------------------------------------------- 
__fastcall TRECIEVER::TRECIEVER(TComponent* Owner) 
    : TForm(Owner) 
{ 
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::WndProc(TMessage& Message)
{ 
    if (Message.Msg == WM_BAR_STEP_IT)
        BAR->StepIt(); 
    else
        TForm::WndProc(Message);
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::SENDERClick(TObject *Sender) 
{ 
    PostMessage(Handle, WM_BAR_STEP_IT, 0, 0); 
} 
//--------------------------------------------------------------------------- 

If you want other pieces of your app to post messages to the Revceiver, you could expose a public method for that:

Unit1.h:

#ifndef Unit1H 
#define Unit1H 
//--------------------------------------------------------------------------- 
#include <System.Classes.hpp> 
#include <Vcl.Controls.hpp> 
#include <Vcl.StdCtrls.hpp> 
#include <Vcl.Forms.hpp> 
#include <Vcl.ComCtrls.hpp> 
//--------------------------------------------------------------------------- 
class TRECIEVER : public TForm 
{ 
__published:    // IDE-managed Components 
    TButton *SENDER; 
    TProgressBar *BAR; 
    void __fastcall SENDERClick(TObject *Sender); 
private:    // User declarations 
protected:
    void __fastcall WndProc(TMessage& Message); 
public:     // User declarations 
    __fastcall TRECIEVER(TComponent* Owner); 
    void __fastcall PostBarStepIt();
}; 
//--------------------------------------------------------------------------- 
extern PACKAGE TRECIEVER *RECIEVER; 
//--------------------------------------------------------------------------- 
#endif 

Unit1.cpp:

#include <vcl.h> 
#pragma hdrstop 

#include "Unit1.h" 
//--------------------------------------------------------------------------- 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
TRECIEVER *RECIEVER; 
#define WM_BAR_STEP_IT  (WM_USER+1)
//--------------------------------------------------------------------------- 
__fastcall TRECIEVER::TRECIEVER(TComponent* Owner) 
    : TForm(Owner) 
{ 
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::WndProc(TMessage& Message)
{ 
    if (Message.Msg == WM_BAR_STEP_IT)
        BAR->StepIt(); 
    else
        TForm::WndProc(Message);
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::SENDERClick(TObject *Sender) 
{ 
    PostBarStepIt(); 
} 
//--------------------------------------------------------------------------- 
void __fastcall TRECIEVER::PostBarStepIt() 
{ 
    PostMessage(Handle, WM_BAR_STEP_IT, 0, 0); 
} 
//--------------------------------------------------------------------------- 

SomeOtherFile.cpp:

#include "Unit1.h"

void __fastcall TSomeOtherClass::SomeMethod()
{
    RECIEVER->PostBarStepIt(); 
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I have another question: I have created a form to serve as a model for multiple operations forms in my program (they will create themselves when a new operation has started and will run this new operation solely). I am creating them like this: TOpWindow* newOp = new TOpWindow(newOp); newOp->Show(); However when they are created I cant bring the main form back to front in any way... It gets focus, controls are usable, but it is always in the back, behind all these new forms... How do I fix that? Does it have sth to do with parents? – Gear54rus Jun 22 '12 at 01:59
  • By the way all the dynamically created forms have the same priority, I can select them and they come to front, unlike the main form which is always in the back – Gear54rus Jun 22 '12 at 02:26
  • Sounds like you have the MainForm set as the `PopupParent` of the operation Forms. Read up about the [PopupParent](http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.PopupParent) and [PopupMode](http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TForm.PopupMode) properties in the documentation. – Remy Lebeau Jun 22 '12 at 04:55
  • Thanks, read both now and the problem is that both forms PopupParent are nulls and PopupMode are pmNone, not Explicit or Auto. And yes, the situation itself looks like pmExplicit is set on the second form, but its NOT. Also what does pre-Delphi8 behavior mean? – Gear54rus Jun 22 '12 at 05:11
  • In pre-D8, forms did not handle z-ordering in relation to each other, so it was possible for modal forms to fall behind non-modal forms, making the app appear to be hung since users could not direct input to the hidden modal forms. The `PopupParent` property has added in D8 to fix that. – Remy Lebeau Jun 22 '12 at 05:20
  • Now that I learned what to google about I read this:http://winapi.freetechsecrets.com/win32/WIN32Z_Order00000014.htm | And then I made this (newOp is a window I want to fork for operation, TOpWindow it the model I want to use): TOpWindow* newOp; Application->CreateForm(__classid(TOpWindow),&newOp) ; SetWindowPos(CryptWindow->Handle,newOp->Handle,0,0,0,0,SWP_NOSIZE | SWP_NOMOVE); And still the situatin is the same... – Gear54rus Jun 22 '12 at 05:20