I am writing a VCL componenet, TGIcon, to mimic the Icons in windows desktop, it has been working fine until I decided to add MouseEnter and MouseLeave events to the component. I followed guides from: Embarcadero Community
and here is my code (header):
class PACKAGE TGIcon : public TGraphicControl
{
private:
AnsiString FCaption;
TPngImage *FIcon, *FDIcon;
TFont *FFont;
TNotifyEvent FOnMouseEnter;
TNotifyEvent FOnMouseLeave;
void __fastcall CMMouseEnter(TMessage &Message);
void __fastcall CMMouseLeave(TMessage &Message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
END_MESSAGE_MAP(TGIcon)
protected:
virtual void __fastcall Paint();
void __fastcall SetCaption(AnsiString value);
void __fastcall SetIcon(TPngImage *value);
void __fastcall SetFont(TFont *value);
public:
__fastcall TGIcon(TComponent* Owner);
__fastcall ~TGIcon();
void __fastcall MakeGray(void);
__published:
__property AnsiString Caption = {read=FCaption, write=SetCaption, nodefault};
__property TPngImage *Icon = {read=FIcon, write=SetIcon};
__property TFont *Font = {read=FFont, write=SetFont};
__property Parent;
__property Enabled;
__property OnClick;
__property TNotifyEvent OnMouseEnter = {read=FOnMouseEnter, write=FOnMouseEnter};
__property TNotifyEvent OnMouseLeave = {read=FOnMouseLeave, write=FOnMouseLeave};
};
Whenever I try to place the component on a Form, the IDE (C++ Builder Starter) would crash to desktop. I have traced the source of problem to be the "BEGIN_MESSAGE_MAP...END_MESSAGE_MAP" part. If I comment out that part, the component works fine.
I used to have the same component working in C++Builder XE5 (Professional) but since that's owned by a company I no longer work with, I don't have the binary of the component, so I have to re-write it here. As far as I can remember, what I did is exactly the same as the one I wrote in XE5, that one works but this one would crash the IDE, no error message, no Access Violation, just plain CTD.
Can someone please help, is there anything I need to do to make this work in C++ Builder 10.1 (Berlin) Starter Edition? Is this a bug of C++Builder or is this what cannot be done in Starter Edition, that it only can be done in the 'paid' editions?? Or is this method already obsolete? If so please show me how the "modernized" C++ Builder do it.
Thanks in advance.
Your MESSAGE_MAP is terminated incorrectly. In the END_MESSAGE_MAP macro, you must specify the base class that your component derives from (TGraphicControl).
A MESSAGE_MAP is just a fancy way to override the virtual Dispatch() method, where:
BEGIN_MESSAGE_MAP declares and opens the overridden method, and opens a switch statement
MESSAGE_HANDLER (use VCL_MESSAGE_HANDLER instead if your project uses ATL) declares case statements for the switch
END_MESSAGE_MAP calls the Dispatch() method of the specified class for unhandled messages, closes the switch, and closes the overridden method.
Here are the declarations from sysmac.h:
#define BEGIN_MESSAGE_MAP virtual void __fastcall Dispatch(void *Message) \
{ \
switch (((PMessage)Message)->Msg) \
{
#define VCL_MESSAGE_HANDLER(msg,type,meth) \
case msg: \
meth(*((type *)Message)); \
break;
// NOTE: ATL defines a MESSAGE_HANDLER macro which conflicts with VCL's macro. The
// VCL macro has been renamed to VCL_MESSAGE_HANDLER. If you are not using ATL,
// MESSAGE_HANDLER is defined as in previous versions of BCB.
//
#if !defined(USING_ATL) && !defined(USING_ATLVCL) && !defined(INC_ATL_HEADERS)
#define MESSAGE_HANDLER VCL_MESSAGE_HANDLER
#endif // ATL_COMPAT
#define END_MESSAGE_MAP(base) default: \
base::Dispatch(Message); \
break; \
} \
}
So, this code:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
END_MESSAGE_MAP(TGIcon) // <-- error!
Gets translated by the preprocessor to this code, which is what the compiler sees:
virtual void __fastcall Dispatch(void *Message)
{
switch (((PMessage)Message)->Msg)
{
case CM_MOUSEENTER:
CMMouseEnter(*((TMessage *)Message));
break;
case CM_MOUSELEAVE:
CMMouseLeave(*((TMessage *)Message));
break;
default:
TGIcon::Dispatch(Message); // <-- recursive loop!
break;
}
}
As you can see, since you are specifying your own component class (TGIcon) instead of the base class (TGraphicControl) in END_MESSAGE_MAP, you are creating an endless recursion loop when the component receives an unhandled message. TGIcon::Dispatch() is calling TGIcon::Dispatch() again. It needs to call TGraphicControl::Dispatch() instead (so do your CMMouseEnter() and CMMouseLeave() methods):
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter)
MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave)
END_MESSAGE_MAP(TGraphicControl) // <-- fixed!
Related
The manual says that Synchronize is a member of TThread.
However it shows that you can call Synchronize directly. Other sources tell the same.
//Synchronize() performs actions contained in a routine as if they were executed from the main VCL thread
void __fastcall TCriticalThread::Execute()
{
...
Synchronize(UpdateCaption);
...
}
But if I do this, my compiler tells me "E2268 Call to undefined function 'Synchronize'". Of course I included the library:
#include <System.Classes.hpp>
On the other hand, TThread::Synchronize is found by the compiler, but it does not accept MainThreadID as parameter:
TThread::Synchronize(MainThreadID, MainForm->UpdateCaption );
PS: I am new to C++ Builder.
Synchronize() is a method of the RTL's TThread class. In all versions of C++Builder, TThread has a non-static version of Synchronize(), which is the version the code you showed is trying to call. That requires TCriticalThread to be derived for TThread, eg:
class TCriticalThread : public TThread
{
...
protected:
virtual void __fastcall Execute();
...
};
void __fastcall TCriticalThread::Execute()
{
...
Synchronize(UpdateCaption);
...
}
If that is not the case in your situation, TThread also has a static version of Synchronize() that can be used with threads that are not derived from TThread, eg:
void __fastcall TCriticalThread::Execute()
{
...
TThread::Synchronize(NULL, UpdateCaption);
...
}
I have been googling high and low and can't find a solution that will remove the warning, even when I use the using directive.
class TShowException_Form : public TForm {
__published: // IDE-managed Components
TButton *Send_Button;
TButton *Cancel_Button;
TLabel *Message_Label;
private: // User declarations
using TCustomForm::ShowModal;
//using TForm::ShowModal;
public: // User declarations
__fastcall TShowException_Form(TComponent* Owner);
int __fastcall ShowModal(System::Sysutils::Exception *E);
};
I want to hide the original virtual int __fastcall ShowModal(void) and expose a new one taking an Exception parameter.
But it still complaints on "hides virtual function":
[bcc32 Warning] TShowExceptionForm.h(32): '_fastcall TShowException_Form::ShowModal(Exception *)' hides virtual function '_fastcall TCustomForm::ShowModal()'
I have also tried using TForm::ShowModal; but with the same result.
Any ideas of how to solve this warning?
EDIT
I found out that it works perfectly well if I override the show() method instead:
class TShowException_Form : public TForm {
__published: // IDE-managed Components
TButton *Send_Button;
TButton *Cancel_Button;
TLabel *Message_Label;
private: // User declarations
using TForm::ShowModal;
using TForm::Show;
public: // User declarations
__fastcall TShowException_Form(TComponent* Owner);
int __fastcall Show(System::Sysutils::Exception *E);
};
So why isn't it working with ShowModal()?
bcc32 is, in many respects, not very compliant with the C++ standard. Whenever I find myself asking, "Why does this technique that I think should work in C++ not work in bcc32?", I usually assume it's yet another compiler bug.
The fact that Show works while ShowModal doesn't is interesting. Looking at Vcl.Forms.hpp shows the difference: Show is defined with HIDESBASE (a macro that expands to __declspec(hidesbase)).
Adding HIDESBASE to your ShowModal should work as well. You may also have to declare a virtual destructor, if you don't already have one, due to bcc32 compiler weirdness.
virtual __Fastcall ~TShowException_Form() {}
You must declare overloaded version as virtual too:
virtual int __fastcall ShowModal(System::SysUtils::Exception * E);
Does not know if C++Builder supports C++11, but if it does, try also to delete overload which you want to hide:
virtual int __fastcall ShowModal() = delete;
instead of placing it into private section.
You get the warning because what you are trying to do happens very often by mistake and is a serious and very hard to find bug if it is done by mistake. Maybe you should use a different name.
I have a few questions about DLL's. I tried a lot but I can not get the complete picture. Most examples are in C# etc.
With the wizard in VS2005 I created a unmanaged MFC regular DLL (must be MFC because of remaining code). Then I tried to import it in a VS2005 managed .NET C++ application. See code below.
mfc_main.h:
//---------------------------------------------------------
// mfc_main.h : main header file for the mfc_main DLL
//---------------------------------------------------------
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
class __declspec(dllexport) Cmfc_mainApp : public CWinApp
{
public:
Cmfc_mainApp();
// Overrides
public:
virtual BOOL InitInstance();
int SayHello(int j);
int init;
DECLARE_MESSAGE_MAP()
};
mfc_main.cpp:
//----------------------------------------------------------------
// mfc_main.cpp : Defines the initialization routines for the DLL.
//----------------------------------------------------------------
#include "stdafx.h"
#include "mfc_main.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
BEGIN_MESSAGE_MAP(Cmfc_mainApp, CWinApp)
END_MESSAGE_MAP()
Cmfc_mainApp::Cmfc_mainApp()
{
}
Cmfc_mainApp theApp;
BOOL Cmfc_mainApp::InitInstance()
{
CWinApp::InitInstance();
return TRUE;
}
int Cmfc_mainApp::SayHello(int j)
{
init = 12; // Comment this out the application works !!!!
return j * 6;
};
in application
[DllImport("mfc_main.dll",
EntryPoint = "?SayHello#Cmfc_mainApp##QAEHH#Z",
ExactSpelling = true)]
static int SayHello(int a);
......
private: System::Void button_Click(System::Object^ sender, System::EventArgs^ e)
{
int retval = SayHello(2);
}
My questions are:
1 - Why is it working without the init = 12 in the function SayHello and with the application crashes (error: Attempted to read or write protected memory)?
2 - Is in this case the InitInstance() executed although I don't call it (and why is there no ExitInstance)?
3 - Why do I see some examples giving the EntryPoint when using DLLImport and some don't?
4 - Can I give a delegate as parameter to a function in a MFC C++ DLL instead of a normal function pointer, to create a callback?
Methods cannot be P/Invoked. If you want to export a class from unmanaged DLL to be used in managed world, you have to flatten it, eg.
Create a constructor function, which looks like:
__declspec(dllexport) void * __stdcall MyClass_Create()
{
return new MyClass();
}
Create a destructor function, which looks like:
__declspec(dllexport) void * __stdcall MyClass_Destroy(MyClass * instance)
{
delete instance;
}
Flatten method calls. Let's suppose, that you have the following method in your class:
int MyClass::MyMethod(int i, double j) { ... }
Then you have to create a following function:
__declspec(dllexport) int __stdcall MyClass_MyMethod(MyClass * instance, int i, double j)
{
return instance->MyMethod(i, j);
}
Prepare P/Invoked external methods in C# (You already know how to do it, so I'll omit these)
Create instance of your class:
IntPtr instance = MyClass_Create();
Then call its method:
int i = MyClass_MyMethod(instance, 4, 2.0);
Finally, destroy the class:
MyClass_Destroy(instance);
Don't forget to add some error checking - I omitted it to keep the example clear.
New to C++ so sorry if this is a basic question! I am used to Java (oh yess! so easy).
My function below addMessages is called from another file, it will then actually run __fastcall TfrmRunning::Add(). As i could not get this working from the other file. the add is part of the TdrmRunning object)
How do I get the add messages to call the Add function?
This is from Running.cpp
void __fastcall TfrmRunning::Add()
{
lbMessages->Items->Add("Application Started at ");
}
//This is called from another file as i could not get the above function working
void addMessages(){
TfrmRunning::Add(); // this does not work
}
My Header file (Running.H)
class TfrmRunning : public TForm
{
__published: // IDE-managed Components
TImage *imgLogo;
TLabel *lblCopyRight;
TLabel *lblTitle;
TButton *btnExit;
TButton *btnViewType;
TListBox *lbMessages;
void __fastcall btnExitClick(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
void __fastcall Add();
private: // User declarations
public: // User declarations
__fastcall TfrmRunning(TComponent* Owner);
};
void addMessages();
Add() isn't a static function of TfrmRunning.
You'll need an object of type TfrmRunning to invoke it.
Try using
TObjetct *asd;
Add(asd);
I have create a simple MFC appwizard dialog project. I used the Class Wizard to create a new class called CMyDlg based on CDialog. Then I went to the Message Map screen and doubleclicked on the WM_INITDIALOG entry to automatically create a CMyDlg::OnInitDialog() handler.
The problem I have is that CMyDlg::OnInitDialog() will not call. I have put a breakpoint in there and it simply will not call. The parent dialog's OnInitDialog() method gets called, but it will not call the CMyDlg::OnInitDialog() method.
Is there something special than needs to be done?
I have managed to implement a workaround which is to send a message of my own from the parent dialog's OnInitDialog() method and have it handled in CMyDlg but.. I'm sure this is not the way to do it..
// MyDlg.cpp : implementation file
//
#include "stdafx.h"
#include "DeriveDlgTest.h"
#include "MyDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMyDlg dialog
CMyDlg::CMyDlg( UINT nIDTemplate, CWnd* pParent /*=NULL*/)
: CDialog(nIDTemplate, pParent)
{
// PDS: THIS GETS CALLED
}
CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMyDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
//{{AFX_MSG_MAP(CMyDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyDlg message handlers
BOOL CMyDlg::OnInitDialog()
{
// PDS: THIS DOES NOT GET CALLED
CDialog::OnInitDialog();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
#if !defined(AFX_MYDLG_H__ECC7F6AC_FEB3_419D_AFE2_6B6DE8196D74__INCLUDED_)
#define AFX_MYDLG_H__ECC7F6AC_FEB3_419D_AFE2_6B6DE8196D74__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// MyDlg.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// CMyDlg dialog
class CMyDlg : public CDialog
{
// Construction
public:
CMyDlg(CWnd* pParent = NULL); // standard constructor
CMyDlg( UINT nIDTemplate, CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CMyDlg)
enum { IDD = IDD_DERIVEDLGTEST_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CMyDlg)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MYDLG_H__ECC7F6AC_FEB3_419D_AFE2_6B6DE8196D74__INCLUDED_)
Thanks Guys. I've uploaded the dummy project to the link below. Try building the project and you will find that CMyDlg::OnInitDialog() is never called.
I removed the IDD enum and constructor as advised above but it didn't make any difference at all. There is not CMyDlg dlg; dlg.DoModal() call as the main dialog itself it derived from CMyDlg as opposed to the usual CDialog class.
I still haven't solved this issue so any help would be appreciated.
Cheers
link text
You derive
CDeriveDlgTestDlg from CMyDlg but inside CDeriveDlgTestDlg::OnInitDialog() you explicitly direct compiler to jump over base class and execute CDialog::OnInitDialog(), so CMyDlg::OnInitDialog() is never called.
You must not handle the WM_INITDIALOG message if you're using an MFC dialog.
The MFC CDialog class has a virtual method named OnInitDialog() which you must simply override and that one will get called.
You can create that method automatically from the "overrides" tab instead of the "window messages" tab in VS.
If you're using a Release build rather than Debug, you might have trouble setting breakpoints - they might get set on the wrong line, or ignored entirely. Either double check to see that you're using a Debug build, or find another way to determine that the code is or isn't being reached. I don't see anything obviously wrong with your code.
If you want to use CMyDlg as a base for other dialog classes, you cannot have the IDD set in your CMyDlg class. The IDD should be set on the class derived from CMyDlg.
So you should delete this:
enum { IDD = IDD_DERIVEDLGTEST_DIALOG };
and replace the standard constructor:
// in the .h file:
//CMyDlg(CWnd* pParent = NULL);
CMyDlg(LPCSTR szIDTemplate, CWnd* pParent = NULL );
// in the .cpp file:
CMyDlg::CMyDlg(LPCSTR szIDTemplate,CWnd* pParent /*=NULL*/)
: CDialog(szIDTemplate, pParent)
{
}
Edit: I just saw your link code. Have you noticed this in your derived class?
BOOL CDeriveDlgTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
You are calling CDialog::OnInitDialog(), not CMyDlg::OnInitDialog()!
In fact, you should replace all mentions of CDialog thar appear in CDeriveDlgTestDlg with CMyDlg. Do this and you're good to go.