C++ Builder XE4, 10.2 Tokyo > TStreamWriter > clWhite cannot be copied - c++builder

My environment:
RadStudio 10.2 Tokyo (and also XE4)
I was implementing a copy property method to copy TShape properties.
Following is what I implemented:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// set Shape1 color to [clWhite]
Shape1->Brush->Color = clRed; // clWhite
Shape2->Brush->Color = clAqua;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
// to Keep original names
String orgName_src = srcCtrl->Name;
String orgName_dst = dstCtrl->Name;
// copy properties
TMemoryStream *strm = new TMemoryStream;
Shape1->Name = L""; // to avoid source collision
try {
strm->WriteComponent(srcCtrl);
strm->Position = 0;
strm->ReadComponent(dstCtrl);
}
__finally
{
delete strm;
}
srcCtrl->Name = orgName_src;
dstCtrl->Name = orgName_dst;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
copyProperties((TControl *)Shape1, (TControl *)Shape2);
// shift to avoid position-overlapping
Shape2->Left = Shape1->Left + 150;
}
//---------------------------------------------------------------------------
The code seems work fine.
But there is a single case, in which the code does not work.
i.e. when the Brush->Color = clWhite for Shape1.
This bug? can be reproduced also for XE4.
I wonder why only the clWhite has this kind of bug? Other colors does not have this kind of bug.

There is no bug in the streaming. It is operating as designed. You are simply using it in a way that it is not intended for.
clWhite is the declared default value of the TBrush.Color property. The DFM streaming system does not stream properties that are currently set to their default values, unless those properties are declared as nodefault or stored=true. TBrush.Color is neither. So the current Brush.Color value will not be streamed when it is set to clWhite.
Consider using the RTTI system directly instead of using the DFM system to copy properties from one object to another. Then you can copy property values regardless of defaults, if you choose to do so. And you can opt to ignore the Name property without having to (re)store it each time.
For example:
#include <System.TypInfo.hpp>
void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
PTypeInfo pDstTypeInfo = static_cast<PTypeInfo>(dstCtrl->ClassInfo());
PPropList srcPropList;
int srcPropCount = GetPropList(srcCtrl, srcPropList);
try
{
for (int i = 0; i < srcPropCount; ++i)
{
PPropInfo pSrcPropInfo = (*srcPropList)[i];
if (pSrcPropInfo->Name == "Name") continue;
PTypeInfo pSrcPropTypeInfo = *(pSrcPropInfo->PropType);
if (pSrcPropTypeInfo->Kind == tkClass)
{
PPropInfo pDstPropInfo = GetPropInfo(pDstTypeInfo, pSrcPropInfo->Name, TTypeKinds() << tkClass);
if (pDstPropInfo)
{
TPersistent *pDstObj = static_cast<TPersistent*>(GetObjectProp(dstCtrl, pDstPropInfo, __classid(TPersistent)));
if (pDstObj)
{
TPersistent *pSrcObj = static_cast<TPersistent*>(GetObjectProp(srcCtrl, pSrcPropInfo, __classid(TPersistent)));
pDstObj->Assign(pSrcObj);
}
}
}
else
{
PPropInfo pDstPropInfo = GetPropInfo(pDstTypeInfo, pSrcPropInfo->Name);
if (pDstPropInfo)
{
Variant value = GetPropValue(srcCtrl, pSrcPropInfo);
SetPropValue(dstCtrl, pDstPropInfo, value);
}
}
}
}
__finally
{
FreeMem(srcPropList);
}
}
Alternatively:
#include <System.Rtti.hpp>
void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
TRttiContext ctx;
TRttiType *pSrcType = ctx.GetType(srcCtrl->ClassInfo());
TRttiType *pDstType = ctx.GetType(dstCtrl->ClassInfo());
DynamicArray<TRttiProperty*> srcProps = pSrcType->GetProperties();
for (int i = 0; i < srcProps.Length; ++i)
{
TRttiProperty *pSrcProp = srcProps[i];
if (pSrcProp->Name == L"Name") continue;
if (pSrcProp->PropertyType->TypeKind == tkClass)
{
TRttiProperty *pDstProp = pDstType->GetProperty(pSrcPropInfo->Name);
if ((pDstProp) && (pDstProp->PropertyType->TypeKind == tkClass))
{
TPersistent *pDstObj = dynamic_cast<TPersistent*>(pDstProp->GetValue(dstCtrl).AsObject());
if (pDstObj)
{
TPersistent *pSrcObj = dynamic_cast<TPersistent*>(pSrcProp->GetValue(srcCtrl).AsObject());
pDstObj->Assign(pSrcObj);
}
}
}
else
{
TRttiProperty *pDstProp = pDstType->GetProperty(pSrcPropInfo->Name);
if (pDstProp)
{
TValue value = pSrcProp->GetValue(srcCtrl);
pDstProp->SetValue(dstCtrl, value);
}
}
}
}

Related

Confused with error -- E2193 Too few parameters in call to '_fastcall TComponent::GetComponent(int)

I am trying to programmatically remove a selected component from its parent container using the code below (I may be using found incorrectly, but that is not the problem, suggestions are welcome):
void __fastcall TScrollControlsListContainer::RemoveItem(TWinControl* ctrl)
{
// find control in vector containing TWinControl(s)
std::vector<FWinControl*>::iterator found = std::find_if(FWinControls.begin(), FWinControls.end(), IsCtrl(ctrl));
if(found != FWinControls.end())
{
Components->RemoveComponent(dynamic_cast<TComponent*>(found));
//[bcc32 Error] ScrollControlsListContainer.cpp(208): E2193 Too few parameters in call to '_fastcall TComponent::GetComponent(int)' Full parser context
FWinControls.erase(found);
}
}
I am confused about the error, as only one parameter is required according to the help file example below:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int I;
TComponent *Temp;
Memo1->Lines->Add("Components removed: ");
Form2->Memo1->Lines->Add("Components added: ");
for (I = ComponentCount - 1; I >= 0; I--)
{
Temp = Components[I];
// Move only the components that are not controls.
if (dynamic_cast<TControl *>(Temp) == NULL)
{
RemoveComponent(Temp);
Memo1->Lines->Add(Temp->Name);
Form2->InsertComponent(Temp);
Form2->Memo1->Lines->Add(Temp->Name);
}
}
}
What am I missing here?

reinit.pas translated to C++

I have semi-successfully translated reinit.pas to C++ to use it in my project. The part where int __fastcall LoadNewResourceModule(LCID locale); is called works fine, in fact I can even call it prior to Application->Initialize() and it will load the proper language at startup. However, the part that calls void __fastcall ReinitializeForms(void); does not work, and gives a runtime error:
Resource TControl not found
Here is the dirty version of .cpp, and .h, I've yet to clean it up and comment it properly, at this point the thing just has to work fully. Please help me sort this out.
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <SysInit.hpp>
#include <Vcl.Forms.hpp>
#pragma hdrstop
#include "reinit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
class TAsInheritedReader : public TReader
{
public:
void __fastcall ReadPrefix(TFilerFlags &_flags, int &_aChildPos);
inline __fastcall TAsInheritedReader(TStream* Stream, int BufSize) : TReader(Stream, BufSize) {}
};
//---------------------------------------------------------------------------
void __fastcall TAsInheritedReader::ReadPrefix(TFilerFlags &_flags, int &_aChildPos)
{
TReader::ReadPrefix(_flags, _aChildPos);
_flags = _flags << ffInherited;
}
//---------------------------------------------------------------------------
int __fastcall SetResourceHInstance(int _newInstance)
{
PLibModule CurModule = LibModuleList;
while(CurModule != NULL) {
if (reinterpret_cast<void*>(CurModule->Instance) == HInstance) {
if (CurModule->ResInstance != CurModule->Instance) {
FreeLibrary(reinterpret_cast<HMODULE>(CurModule->ResInstance));
CurModule->ResInstance = _newInstance;
return _newInstance;
}
CurModule = CurModule->Next;
}
}
return 0;
}
//---------------------------------------------------------------------------
int __fastcall LoadNewResourceModule(LCID locale)
{
wchar_t FileName[260];
PChar P;
wchar_t LocaleName[4];
int NewInst = 0;
GetModuleFileName(HInstance, FileName, sizeof(FileName));
GetLocaleInfo(locale, LOCALE_SABBREVLANGNAME, LocaleName, sizeof(LocaleName));
P = PChar(&FileName) + lstrlen(FileName);
while((*P != L'.') && (P != reinterpret_cast<PChar>(&FileName))) {
--P;
}
if (P != reinterpret_cast<PChar>(&FileName)) {
++P;
if (LocaleName[0] != L'\0') {
NewInst = reinterpret_cast<int>(LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE));
if (NewInst == 0) {
LocaleName[2] = L'\0';
lstrcpy(P, LocaleName);
NewInst = reinterpret_cast<int>(LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE));
}
}
}
if (NewInst != 0) {
return SetResourceHInstance(NewInst);
}
return 0;
}
//---------------------------------------------------------------------------
bool __fastcall InternalReloadComponentRes(const String ResName, THandle HInst, TComponent* Instance)
{
//TResourceStream* ResStream = new TResourceStream;
//TAsInheritedReader* AsInheritedReader = new TAsInheritedReader;
if (HInst == 0) {
HInst = reinterpret_cast<THandle>(HInstance);
}
THandle HRsrc = reinterpret_cast<THandle>(FindResource((HMODULE)HInst, (LPCWSTR)ResName.w_str(), (LPCWSTR)RT_RCDATA));
if(HRsrc != 0) {
return false;
}
/* THIS IS THE OFFENDING LINE OF CODE THAT THROWS EXCEPTION
I checked HInst, it is not 0...
ResName = "TControl"
and it throws exception here for some reason
saying resource tcontrol not found
*/
TResourceStream* ResStream = new TResourceStream(HInst, ResName, RT_RCDATA);
try {
TAsInheritedReader* AsInheritedReader = new TAsInheritedReader(ResStream, 4096);
try {
Instance = AsInheritedReader->ReadRootComponent(Instance);
} __finally {
delete AsInheritedReader;
}
}
__finally {
delete ResStream;
}
return true;
}
//---------------------------------------------------------------------------
bool __fastcall InitComponent(TClass ClassType)
{
if ((ClassType->ClassName() == "TComponent") || (ClassType->ClassName() == "RootAncestor")) {
return false;
}
InitComponent(ClassType->ClassParent());
return InternalReloadComponentRes(ClassType->ClassName(), FindResourceHInstance(FindClassHInstance(ClassType)), (TComponent*)&ClassType);
}
//---------------------------------------------------------------------------
bool __fastcall ReloadInheritedComponent(TComponent* Instance)
{
return InitComponent(Instance->ClassType());
}
//---------------------------------------------------------------------------
void __fastcall ReinitializeForms(void)
{
for(int i=0; i<Screen->FormCount-1; i++) {
ReloadInheritedComponent(Screen->Forms[i]);
}
}
#ifndef _reinit_h
#define _reinit_h
#include <windows.h>
extern int __fastcall LoadNewResourceModule(LCID locale);
extern void __fastcall ReinitializeForms(void);
#endif
You don't really need to translate the code at all. You can use Delphi .pas units as-is in C++Builder projects. Simply add the .pas file to your project and compile it, the IDE will generate a .hpp file that you can then #include in your C++ code.
In any case, your translation is not correct in many places.
For instance, the original code wasn't written with Unicode in mind, but you are using Unicode strings in your code. Expressions like sizeof(FileName) and sizeof(LocaleName) are the wrong buffer sizes to pass to the Win32 APIs being used, which can potentially allow buffer overflows. The code was clearly expecting BYTE-sized narrow characters, not WORD-sized wide characters.
It also looks like the original code was not written with 64-bit in mind, either. It is using 32-bit integers in places where 64-bit integers would be needed (for resource handles, etc).
So, the original code needs some updating to support modern systems properly.
But also, your translation of InitComponent() is wrong. It is using strings where the original code is using metaclass references instead, and it is passing the wrong value in the last parameter of InternalReloadComponentRes(), which you have not even declared correctly.
And also, your loop in ReinitializeForms() is skipping the last TForm in the Screen->Forms[] array.
Now, that all being said, try something more like this:
ReInit.h
#ifndef REINIT_H
#define REINIT_H
void __fastcall ReinitializeForms();
NativeUInt __fastcall LoadNewResourceModule(unsigned long Locale);
#endif
ReInit.cpp
#include "ReInit.h"
#include <Windows.hpp>
#include <SysInit.hpp>
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Forms.hpp>
#include <memory>
class TAsInheritedReader : public TReader
{
public:
void __fastcall ReadPrefix(TFilerFlags &Flags, int &AChildPos) override;
};
void __fastcall TAsInheritedReader::ReadPrefix(TFilerFlags &Flags, int &AChildPos)
{
TReader::ReadPrefix(Flags, AChildPos);
Flags << ffInherited;
}
NativeUInt __fastcall SetResourceHInstance(NativeUInt NewInstance)
{
PLibModule CurModule = LibModuleList;
while (CurModule)
{
if (CurModule->Instance == HInstance)
{
if (CurModule->ResInstance != CurModule->Instance)
::FreeLibrary(reinterpret_cast<HMODULE>(CurModule->ResInstance));
CurModule->ResInstance = NewInstance;
return NewInstance;
}
CurModule = CurModule->Next;
}
return 0;
}
NativeUInt __fastcall LoadNewResourceModule(unsigned long Locale)
{
WCHAR FileName[MAX_PATH+1] = {};
::GetModuleFileNameW(reinterpret_cast<HMODULE>(HInstance), FileName, MAX_PATH+1);
WCHAR LocaleName[5] = {};
::GetLocaleInfoW(Locale, LOCALE_SABBREVLANGNAME, LocaleName, 5);
LPWSTR P = &FileName[lstrlenW(FileName)];
while ((*P != L'.') && (P != FileName)) --P;
HMODULE NewInst = nullptr;
if (P != FileName)
{
++P;
if (LocaleName[0] != L'\0')
{
// Then look for a potential language/country translation
lstrcpyW(P, LocaleName);
NewInst = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
if (!NewInst)
{
// Finally look for a language only translation
LocaleName[2] = L'\0';
lstrcpyW(P, LocaleName);
NewInst = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
}
}
}
if (NewInst)
return SetResourceHInstance(reinterpret_cast<NativeUInt>(NewInst));
return 0;
}
bool __fastcall InternalReloadComponentRes(const UnicodeString &ResName, NativeUInt HInst, TComponent* &Instance)
{
// avoid possible EResNotFound exception
if (HInst == 0) HInst = HInstance;
HRSRC HRsrc = FindResourceW(reinterpret_cast<HMODULE>(HInst), ResName.c_str(), MAKEINTRESOURCEW(10)/*RT_RCDATA*/);
if (!HRsrc) return false;
auto ResStream = std::make_unique<TResourceStream>(HInst, ResName, MAKEINTRESOURCEW(10)/*RT_RCDATA*/);
auto AsInheritedReader = std::make_unique<TAsInheritedReader>(ResStream.get(), 4096);
Instance = AsInheritedReader->ReadRootComponent(Instance);
return true;
}
bool __fastcall ReloadInheritedComponent(TComponent *Instance, TClass RootAncestor)
{
const auto InitComponent = [&Instance,RootAncestor](TClass ClassType) -> bool
{
auto InitComponent_impl = [&Instance,RootAncestor](TClass ClassType, auto& InitComponent_ref) -> bool
{
if ((ClassType == __classid(TComponent)) || (ClassType == RootAncestor)) return false;
bool Result = InitComponent_ref(ClassType->ClassParent(), InitComponent_ref);
return InternalReloadComponentRes(ClassType->ClassName(), FindResourceHInstance(FindClassHInstance(ClassType)), Instance) || Result;
}
return InitComponent_impl(ClassType, InitComponent_impl);
}
return InitComponent(Instance->ClassType());
}
void __fastcall ReinitializeForms()
{
int Count = Screen->FormCount;
for(int i = 0; i < Count; ++i)
{
ReloadInheritedComponent(Screen->Forms[I], __classid(TForm));
}
}

Using StyleHook in C++ Builder

I need to create a style hook for TEdit in C++ Builder XE7, in order to override the style color management, as the following Delphi example. Could somebody post a complete example in C++ Builder (hook unit, registration and example form)? Thanks!
A translation of the Delphi example would look something like this:
class TEditStyleHookColor : public TEditStyleHook
{
typedef TEditStyleHook inherited;
private:
void UpdateColors();
protected:
virtual void __fastcall WndProc(TMessage &Message);
public:
__fastcall TEditStyleHookColor(TWinControl *AControl);
};
#include <Vcl.Styles.hpp>
class TWinControlH : public TWinControl {};
__fastcall TEditStyleHookColor::TEditStyleHookColor(TWinControl *AControl)
: TEditStyleHook(AControl)
{
//call the UpdateColors method to use the custom colors
UpdateColors();
};
//Here you set the colors of the style hook
void TEditStyleHookColor::UpdateColors()
{
if (Control->Enabled)
{
Brush->Color = (static_cast<TWinControlH*>(Control)->Color; //use the Control color
FontColor = static_cast<TWinControlH*>(Control)->Font->Color;//use the Control font color
}
else
{
//if the control is disabled use the colors of the style
TCustomStyleServices *LStyle = StyleServices();
Brush->Color = LStyle->GetStyleColor(scEditDisabled);
FontColor = LStyle->GetStyleFontColor(sfEditBoxTextDisabled);
}
}
//Handle the messages of the control
void __fastcall TEditStyleHookColor::WndProc(TMessage &Message)
{
switch (Message.Msg)
{
case CN_CTLCOLORMSGBOX:
case CN_CTLCOLORSCROLLBAR:
case CN_CTLCOLORSTATIC:
{
//Get the colors
UpdateColors();
SetTextColor(reinterpret_cast<HDC>(Message.WParam), ColorToRGB(FontColor));
SetBkColor(reinterpret_cast<HDC>(Message.WParam), ColorToRGB(Brush->Color));
Message.Result = reinterpret_cast<LRESULT>(Brush->Handle);
Handled = true;
break;
}
case CM_ENABLEDCHANGED:
{
//Get the colors
UpdateColors();
Handled = false;
break;
}
default:
inherited::WndProc(Message);
break;
}
}
...
TStyleManager::Engine->RegisterStyleHook(__classid(TEdit), __classid(TEditStyleHookColor));
TStyleManager::Engine->RegisterStyleHook(__classid(TMaskEdit), __classid(TEditStyleHookColor));
TStyleManager::Engine->RegisterStyleHook(__classid(TLabeledEdit), __classid(TEditStyleHookColor));
TStyleManager::Engine->RegisterStyleHook(__classid(TButtonedEdit), __classid(TEditStyleHookColor));

How to know the Control is TLabel?

My Environment: C++ Builder XE4
how to copy all the TLabels parented with a TPanel on delphi to another TPanel?
I would like to implement above code in C++ Builder.
I do not know how to implement below in C++ Builder.
if ParentControl.Controls[i] is TLabel then
Are there any functions to get type as TLabel or some other?
You can use ClassType method as:
if(Controls[i]->ClassType() == __classid(TLabel))
{
...
}
Use dynamic_cast:
if (dynamic_cast<TLabel*>(ParentControl->Controls[i]) != NULL)
Here is a translation of that code:
void __fastcall CopyLabels(TWinControl *ParentControl, TWinControl *DestControl)
{
for(int i = 0; i < ParentControl->ControlCount; ++i)
{
if (dynamic_cast<TLabel*>(ParentControl->Controls[i]) != NULL)
{
TLabel *ALabel = new TLabel(DestControl);
ALabel->Parent = DestControl;
ALabel->Left = ParentControl->Controls[i]->Left;
ALabel->Top = ParentControl->Controls[i]->Top;
ALabel->Width = ParentControl->Controls[i]->Width;
ALabel->Height = ParentControl->Controls[i]->Height;
ALabel->Caption= static_cast<TLabel*>(ParentControl->Controls[i])->Caption;
//you can add manually more properties here like font or another
}
}
}
With that said, this would be slightly more efficient:
void __fastcall CopyLabels(TWinControl *ParentControl, TWinControl *DestControl)
{
int count = ParentControl->ControlCount;
for(int i = 0; i < count; ++i)
{
TLabel *SourceLabel = dynamic_cast<TLabel*>(ParentControl->Controls[i]);
if (SourceLabel != NULL)
{
TLabel *ALabel = new TLabel(DestControl);
ALabel->Parent = DestControl;
ALabel->SetBounds(SourceLabel->Left, SourceLabel->Top, SourceLabel->Width, SourceLabel->Height);
ALabel->Caption = SourceLabel->Caption;
//you can add manually more properties here like font or another
}
}
}
I found ClassName() method.
Following seems working.
static bool isTCheckBox(TControl *srcPtr)
{
if (srcPtr->ClassName() == L"TCheckBox") {
return true;
}
return false;
}

How to check if a component is TEdit type in c++builder?

How do I check through all the components of a form and verify that components are of type TEdit?
You can use the dynamic_cast operator.
Excuse me if I'm wrong, but won't embarcadero automatically add all form-component object pointers to the class definition ( in the header file )..
Such as:
class TFormSomeForm : public TForm
{
__published:
TEdit *SomeEditBox;
TEdit *AnotherEditBox;
...
}
Meaning that you can tell from the header which components are of type TEdit.
Or you can click on the components in the Design View and the Object Inspector will show the type.
My function sets Text property of all edits in a TWinControl and its children.
void __fastcall SetEditsText(TWinControl* winControl, UnicodeString editsText)
{
for (int c = 0; c < winControl->ControlCount; c++)
{
TControl* ctrl = winControl->Controls[c];
TWinControl* wc = dynamic_cast<TWinControl*>(ctrl);
// Check if it's grouping component
if (wc != NULL)
{
// Set edits of children
SetEditsText(wc, editsText);
}
else
{
if (ctrl->ClassType() == __classid(TEdit))
{
TEdit* ecomp = (TEdit*) ctrl;
ecomp->Text = editsText;
}
}
}
}
Using:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
SetEditsText(form1, ""); // Clear all edits
}

Resources