Domanda

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

È stato utile?

Soluzione

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(); 
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top