Creating a TIniFile in a custom class fails for an unknown reason - delphi-11-alexandria

I created a unit with my own class to manage my application settings. One of the members of the class is of type TIniFile which should be used to store the settings in the ini file.
In the constructor of the class, I call TIniFile.Create(SettingsFile) but it raises an exception (I tested it with try...except).
Why does TIniFile creation fail?
Here is my code:
unit SettingsUnit;
interface
{$M+}
uses
System.Classes, System.IniFiles, System.SysUtils;
type
TSettingsManager = class(TObject)
private
FSettingsFile: TIniFile;
FSettingsFileName: String;
FSize: Byte;
FBatteryTop: Integer;
FBatteryLeft: Integer;
FTransparentValue: Byte;
FMinBatteryPercent: Byte;
FShowPercentLabel: Boolean;
FShowPercentOnACPower: Boolean;
FStartWithWindows: Boolean;
public
constructor Create(const SettingsFile: String);
destructor Destroy; override;
procedure StartWithWindows(Value: Boolean);
procedure LoadSettings;
procedure RestoreDefaults;
procedure Apply;
published
property Size: Byte read FSize write FSize;
property BatteryTop: Integer read FBatteryTop write FBatteryTop;
property BatteryLeft: Integer read FBatteryLeft write FBatteryLeft;
property Transparent: Byte read FTransparentValue write FTransparentValue;
property MinBatteryPercent: Byte read FMinBatteryPercent write FMinBatteryPercent;
property ShowPercentLabel: Boolean read FShowPercentLabel write FShowPercentLabel;
property ShowPercentOnACPower: Boolean read FShowPercentOnACPower write FShowPercentOnACPower;
property StartingWithWindows: Boolean read FStartWithWindows;
end;
var
Settings: TSettingsManager;
implementation
uses
System.Win.Registry;
const
SettingsSection: String = 'BatteryFormSettings';
constructor TSettingsManager.Create(const SettingsFile: String);
begin
try
FSettingsFile := TIniFile.Create(SettingsFile);
except
RaiseLastOSError;
Exit;
end;
FSettingsFileName := SettingsFile;
LoadSettings;
inherited Create;
end;
procedure TSettingsManager.LoadSettings;
begin
with FSettingsFile do
begin
FSize := ReadInteger('BatteryFormSettings', 'FormSize', 1);
FBatteryTop := ReadInteger(SettingsSection, 'Top', 20);
FBatteryLeft := ReadInteger(SettingsSection, 'Left', 20);
FTransparentValue := ReadInteger(SettingsSection, 'TransparentValue', 255);
FMinBatteryPercent := ReadInteger(SettingsSection, 'MinBatteryPercent', 80);
FShowPercentLabel := ReadBool(SettingsSection, 'ShowPercentLabel', True);
FShowPercentOnACPower := ReadBool(SettingsSection, 'ShowPercentOnACPower', True);
FStartWithWindows := ReadBool(SettingsSection, 'StartWithWindows', False);
end;
end;
procedure TSettingsManager.Apply;
begin
with FSettingsFile do
begin
WriteInteger(SettingsSection, 'FormSize', FSize);
WriteInteger(SettingsSection, 'Top', FBatteryTop);
WriteInteger(SettingsSection, 'Left', FBatteryLeft);
WriteInteger(SettingsSection, 'TransparentValue', FTransparentValue);
WriteInteger(SettingsSection, 'MinBatteryPercent', FMinBatteryPercent);
WriteBool(SettingsSection, 'ShowPercentLabel', FShowPercentLabel);
WriteBool(SettingsSection, 'ShowPercentOnACPower', FShowPercentOnACPower);
end;
end;
procedure TSettingsManager.StartWithWindows(Value: Boolean);
const
KEY_SET_VALUE = $0002;
var
WinReg: TRegistry;
begin
try
WinReg := TRegistry.Create(KEY_SET_VALUE);
WinReg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', True);
if Value then
begin
if not WinReg.KeyExists('BatteryScreen') then
WinReg.WriteString('BatteryScreen', ChangeFileExt(FSettingsFileName, '.exe'));
FStartWithWindows := True;
end
else
begin
if WinReg.KeyExists('BatteryScreen') then
WinReg.DeleteKey('BatteryScreen');
FStartWithWindows := False;
end;
finally
WinReg.CloseKey;
WinReg.Free;
end;
end;
procedure TSettingsManager.RestoreDefaults;
begin
Size := 1;
BatteryTop := 20;
BatteryLeft := 20;
Transparent := 255;
MinBatteryPercent := 80;
ShowPercentLabel := True;
ShowPercentOnACPower := True;
StartWithWindows(False);
end;
destructor TSettingsManager.Destroy;
begin
FSettingsFile.Free;
inherited Destroy;
end;
end.
And I call him in my form unit like this:
implementation
uses SettingsUnit;
...
procedure TMyForm.FormCreate(Sender: TObject);
begin
Settings.Create(ChangeFileExt(Application.ExeName, '.ini'));
...
end;

Related

How to set two properties as a node and change the caption in design mode?

I'm trying to create a new component named CheckEdit as the following:
unit UnitName;
interface
uses
System.Classes, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.Controls;
type
TCheckEdit = class (TCustomControl)
private
FCheckBox : TCheckBox;
FEdit : TEdit;
FEnableCaption: TCaption;
FDisbleCaption: TCaption;
procedure SetIsActive(const Value: Boolean);
function GetIsActive : Boolean;
procedure ChBoxOnClick (Sender : TObject);
procedure SetDisbleCaption(const Value: TCaption);
procedure SetEnableCaption(const Value: TCaption);
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property IsActive : Boolean read GetIsActive write SetIsActive default False;
property EnableCaption : TCaption read FEnableCaption write SetEnableCaption;
property DisbleCaption : TCaption read FDisbleCaption write SetDisbleCaption;
property OnClick;
end;
procedure register;
implementation
procedure register;
begin
RegisterComponents('Standard', [TCheckEdit]);
end;
{ TCheckEdit }
procedure TCheckEdit.ChBoxOnClick(Sender: TObject);
begin
if FCheckBox.Checked then
IsActive := True
else
IsActive:= False;
end;
constructor TCheckEdit.Create(AOwner: TComponent);
begin
inherited;
FCheckBox := TCheckBox.Create(Self);
FCheckBox.Parent := Self;
FCheckBox.Align := alTop;
FCheckBox.Caption := Self.Name;
FCheckBox.OnClick := ChBoxOnClick;
FDisbleCaption := 'Disabled';
FEnableCaption := 'Enabled';
FCheckBox.Caption := FDisbleCaption;
FEdit := TEdit.Create(Self);
FEdit.Parent := Self;
FEdit.Align := alTop;
FEdit.Enabled := False;
Self.Height := 40;
Self.Width := 185;
Self.AutoSize := True;
end;
destructor TCheckEdit.Destroy;
begin
FEdit.Free;
FCheckBox.Free;
inherited;
end;
function TCheckEdit.GetIsActive: Boolean;
begin
if FCheckBox.Checked then
Result := True
else
Result := False;
end;
procedure TCheckEdit.SetDisbleCaption(const Value: TCaption);
begin
FDisbleCaption := Value;
end;
procedure TCheckEdit.SetEnableCaption(const Value: TCaption);
begin
FEnableCaption := Value;
end;
procedure TCheckEdit.SetIsActive(const Value: Boolean);
begin
FCheckBox.Checked := Value;
case Value of
True :
begin
FEdit.Enabled := True;
FCheckBox.Caption := FEnableCaption;
end;
False :
begin
FEdit.Enabled := False;
FCheckBox.Caption := FDisbleCaption;
end;
end;
end;
end.
Everything is working fine, but I want to make EnableCaption and DisableCaption in one node as TToggleSwitch have StateCaptions property, and when I change the caption it will change it in the CheckBox too.
I try to call Invalidate; in SetEnableCaption and SetDisbleCaption procedures, but that does not work.
How can I do that?
To be honest with you, I did not want to answer this question at first because you already have the answer in one of your questions here on SO
Create a button that accepts .PNG images as Glyph
the first class to be exact it is a TPersistent that exposed the Glyph coordinates in the TNCRSpeedButton.
I'm writing this because I said I hope you benefit well from it. and I can see you did not.
So this is your solution to your problem and you are more than welcome to ask any thing about how it is implemented.
unit UnitName;;
interface
uses
System.Classes, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.Controls;
type
TCheckEditCaptions = class(TPersistent)
private
FDisableCaption: TCaption;
FEnableCaption: TCaption;
FOnChange: TNotifyEvent;
function GetDisableCaption: TCaption;
function GetEnableCaption: TCaption;
procedure SetDisableCaption(const Value: TCaption);
procedure SetEnableCaption(const Value: TCaption);
public
procedure Assign(aValue: TPersistent); override;
published
property EnableCaption: TCaption read GetEnableCaption write SetEnableCaption;
property DisableCaption: TCaption read GetDisableCaption write SetDisableCaption;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
TCheckEdit = class (TCustomControl)
private
FCheckBox : TCheckBox;
FEdit : TEdit;
FCheckEditCaptions: TCheckEditCaptions;
procedure SetIsActive(const Value: Boolean);
function GetIsActive : Boolean;
procedure ChBoxOnClick (Sender : TObject);
procedure CheckEditCaptionsChanged(Sender : TObject);
procedure SetCheckEditCaptions(const Value: TCheckEditCaptions);
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property IsActive : Boolean read GetIsActive write SetIsActive default False;
property CheckEditCaptions : TCheckEditCaptions read FCheckEditCaptions write SetCheckEditCaptions;
property OnClick;
end;
procedure register;
implementation
procedure register;
begin
RegisterComponents('Samples', [TCheckEdit]);
end;
{ TCheckEdit }
procedure TCheckEdit.ChBoxOnClick(Sender: TObject);
begin
IsActive := FCheckBox.Checked;
end;
procedure TCheckEdit.CheckEditCaptionsChanged(Sender: TObject);
begin
SetIsActive(GetIsActive);
end;
constructor TCheckEdit.Create(AOwner: TComponent);
begin
inherited;
FCheckBox := TCheckBox.Create(Self);
FCheckBox.Parent := Self;
FCheckBox.Align := alTop;
FCheckBox.OnClick := ChBoxOnClick;
FCheckEditCaptions := TCheckEditCaptions.Create;
FCheckEditCaptions.FDisableCaption := 'Disabled';
FCheckEditCaptions.FEnableCaption := 'Enabled';
FCheckEditCaptions.OnChange := CheckEditCaptionsChanged;
FCheckBox.Caption := CheckEditCaptions.DisableCaption;
FEdit := TEdit.Create(Self);
FEdit.Parent := Self;
FEdit.Align := alTop;
FEdit.Enabled := False;
Self.Height := 40;
Self.Width := 185;
Self.AutoSize := True;
end;
destructor TCheckEdit.Destroy;
begin
FEdit.Free;
FCheckBox.Free;
FCheckEditCaptions.Free;
inherited;
end;
function TCheckEdit.GetIsActive: Boolean;
begin
Result := FCheckBox.Checked ;
end;
procedure TCheckEdit.SetCheckEditCaptions(const Value: TCheckEditCaptions);
begin
FCheckEditCaptions.Assign(Value);
end;
procedure TCheckEdit.SetIsActive(const Value: Boolean);
begin
FCheckBox.Checked := Value;
FEdit.Enabled := Value;
if Value then
FCheckBox.Caption := CheckEditCaptions.EnableCaption
else
FCheckBox.Caption := CheckEditCaptions.DisableCaption;
end;
{ TCheckEditCaptions }
procedure TCheckEditCaptions.Assign(aValue: TPersistent);
begin
if aValue is TCheckEditCaptions then begin
FEnableCaption := TCheckEditCaptions(aValue).FEnableCaption;
FEnableCaption := TCheckEditCaptions(aValue).FDisableCaption;
if Assigned(FOnChange) then
FOnChange(self);
end else
inherited;
end;
function TCheckEditCaptions.GetDisableCaption: TCaption;
begin
result := FDisableCaption;
end;
function TCheckEditCaptions.GetEnableCaption: TCaption;
begin
result := FEnableCaption;
end;
procedure TCheckEditCaptions.SetDisableCaption(const Value: TCaption);
begin
FDisableCaption := Value;
if Assigned(FOnChange) then
FOnChange(self);
end;
procedure TCheckEditCaptions.SetEnableCaption(const Value: TCaption);
begin
FEnableCaption := Value;
if Assigned(FOnChange) then
FOnChange(self);
end;
end.

Object serializing via RTTI doesn't work anymore

I have 2 years old project which I am rebooting back to life. I have debug window which shows different information, and one part of them is that it serializes various google protobuf objects and shows them.
Here is how typical protobuf class looks like:
// Generated by the protocol buffer compiler. DO NOT EDIT!
// Source: message.proto
unit Poker.Protobufs.Objects.PingParams;
interface
uses
System.SysUtils,
{$IFNDEF FPC} System.Generics.Collections {$ELSE} Contnrs {$ENDIF},
pbOutput, Poker.Protobufs.Objects.Base, Poker.Protobufs.Reader, Poker.Types;
type
TPB_PingParams = class(TProtobufBaseObject)
private
const
kUptimeFieldNumber = 1;
var
FUptime: UInt32;
FHasBits: UINT32;
procedure set_has_Uptime;
procedure clear_has_Uptime;
procedure SetUptime(const AValue: UInt32);
public
constructor Create(const AFrom: TPB_PingParams; const ALightweight: Boolean = FALSE); overload;
destructor Destroy; override;
procedure LoadFromProtobufReader(const AProtobufReader: TProtobufReader; const ASize: Integer); override;
procedure MergeFrom(const AFrom: TPB_PingParams);
procedure Clear;
function IsInitialized: Boolean; override;
// required uint32 Uptime = 1;
function has_Uptime: Boolean;
procedure clear_Uptime;
property Uptime: UInt32 read FUptime write SetUptime;
end;
TPB_PingParamsList = class(TObjectList<TPB_PingParams>)
procedure Assign(const APB_PingParamsList: TList<TPB_PingParams>);
end;
implementation
uses
pbPublic;
constructor TPB_PingParams.Create(const AFrom: TPB_PingParams; const ALightweight: Boolean = FALSE);
begin
inherited Create(ALightweight);
MergeFrom(AFrom);
end;
destructor TPB_PingParams.Destroy;
begin
inherited;
end;
procedure TPB_PingParams.LoadFromProtobufReader(const AProtobufReader: TProtobufReader; const ASize: Integer);
var
tag, field_number, wire_type, endpos: Integer;
begin
endpos := AProtobufReader.getPos + ASize;
while (AProtobufReader.getPos < endpos) and
(AProtobufReader.GetNext(tag, wire_type, field_number)) do
case field_number of
kUptimeFieldNumber: begin
Assert(wire_type = WIRETYPE_VARINT);
FUptime := AProtobufReader.readUInt32;
set_has_Uptime;
end;
else
AProtobufReader.skipField(tag);
end;
end;
procedure TPB_PingParams.MergeFrom(const AFrom: TPB_PingParams);
begin
if AFrom.has_Uptime then
SetUptime(AFrom.Uptime);
end;
function TPB_PingParams.IsInitialized: Boolean;
begin
if (FHasBits and $1) <> $1 then
Exit(FALSE);
Exit(TRUE);
end;
procedure TPB_PingParams.clear_Uptime;
begin
FUptime := 0;
clear_has_Uptime;
end;
function TPB_PingParams.has_Uptime: Boolean;
begin
result := (FHasBits and 1) > 0;
end;
procedure TPB_PingParams.set_has_Uptime;
begin
FHasBits := FHasBits or 1;
end;
procedure TPB_PingParams.clear_has_Uptime;
begin
FHasBits := FHasBits and not 1;
end;
procedure TPB_PingParams.SetUptime(const AValue: UInt32);
begin
if not Lightweight then
Assert(not has_Uptime);
FUptime := AValue;
if not Lightweight then
ProtobufOutput.writeUInt32(kUptimeFieldNumber, AValue);
set_has_Uptime;
end;
procedure TPB_PingParams.Clear;
begin
if FHasBits = 0 then
Exit;
clear_Uptime;
end;
procedure TPB_PingParamsList.Assign(const APB_PingParamsList: TList<TPB_PingParams>);
var
pbobj: TPB_PingParams;
begin
Clear;
for pbobj in APB_PingParamsList do
Add(TPB_PingParams.Create(pbobj, TRUE));
end;
end.
And my serialization function:
function SerializeObject(const AObject: TObject): String;
var
t: TRttiType;
p: TRttiProperty;
properties: TArray<TRttiProperty>;
method: TRttiMethod;
begin
result := '';
if not Assigned(AObject) then
Exit;
t := TRttiContext.Create.GetType(AObject.ClassType);
properties := t.GetProperties;
for p in properties do
begin
method := t.GetMethod(Format('has_%s', [p.Name]));
if (Assigned(method)) and
(method.Invoke(AObject, []).AsBoolean) then
result := result + Format('%s: %s; ', [p.Name, ValueToStr(p, p.GetValue(AObject))]);
end;
end;
It is specifically designed to serialize fields that begin with has_ in protobuf objects. Now, I didn't change anything in the code over last 2 years, and this was working before. But now it doesn't. Line properties = t.GetProperties returns empty array for my protobuf classes.
My guess is that I had some globally defined compiler directive which allowed me to serialize public methods in the class. But I cannot figure out which one.
If I put {$M+} in front of my protobuf classes, and move methods to published, it works (kinda). But this worked before just like it is shown in the sources, without any {$M+} directives or similar. So I'm curious what I miss.
Compiler is same as before, XE2.

My first FMX Control (Trying to create a FMX TRadioGroup) - class not found error

Using Delphi XE6
I am trying to create an FMX RadioGroup control using a TGroupBox and TRadioButton. I can see both my TTestRadioGroup and TTestGroupButton controls in my IDE control pallette. I can drop a TTestRadioGroup on my form and set the items property and it will create the Radio buttons. However, when I run the application and call the form with the TTestRadioGroup control with its radio buttons, on it, I get a "cant find TTestGroupButton" message.
What have I done wrong?
My first test seems to work ok, as long as I am in design. When
unit TestComponent;
interface
uses {$IFDEF MSWINDOWS}Windows, {$ENDIF}
System.Classes, FMX.Edit, System.UITypes, System.Character, FMX.DateTimeCtrls,
System.SysUtils, FMX.Types, System.DateUtils, System.SysConst, FMX.Controls,
FMX.Pickers, FMX.Platform, FMX.Text, math, FMX.Consts, FMX.Forms, FMX.StdCtrls;
type
TTestRadioGroup = class;
TTestGroupButton = class(TRadioButton)
private
protected
public
constructor InternalCreate(RadioGroup: TTestRadioGroup);
destructor Destroy; override;
end;
TTestRadioGroup = class(TGroupBox)
private
FReading: Boolean;
FButtons: TList;
FItems: TStrings;
FItemIndex: Integer;
FColumns: Integer;
FUpdating: Boolean;
FButtonMargins: Integer;
fButtonSpacing: Integer;
function GetButtons(Index: Integer): TRadioButton;
procedure SetButtonMargins(Value: Integer);
procedure SetButtonSpacing(Value: Integer);
procedure SetColumns(Value: Integer);
procedure SetItemIndex(Value: Integer);
procedure SetItems(Value: TStrings);
procedure ItemsChange(Sender: TObject);
procedure SetButtonCount(Value: Integer);
procedure ButtonClick(Sender: TObject);
procedure UpdateButtons; //updates buttons list from Items list
procedure ArrangeButtons; //rearranges buttons on Groupbox based on new properties
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Buttons[Index: Integer]: TRadioButton read GetButtons;
published
property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
property Items: TStrings read FItems write SetItems;
property Columns: Integer read FColumns write SetColumns default 1;
property ButtonMargins: Integer read fButtonMargins write SetButtonMargins default 0;
property ButtonSpacing: Integer read fButtonSpacing write SetButtonSpacing default 0;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Test', [TTestRadioGroup, TTestGroupButton]);
end;
{ TTestGroupButton }
constructor TTestGroupButton.InternalCreate(RadioGroup: TTestRadioGroup);
begin
inherited Create(RadioGroup);
RadioGroup.FButtons.Add(Self);
Visible := False;
Enabled := RadioGroup.Enabled;
OnClick := RadioGroup.ButtonClick;
Parent := RadioGroup;
end;
destructor TTestGroupButton.Destroy;
begin
TTestRadioGroup(Owner).FButtons.Remove(Self);
inherited Destroy;
end;
{ TTestRadioGroup }
constructor TTestRadioGroup.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FButtons := TList.Create;
FItems := TStringList.Create;
TStringList(FItems).OnChange := ItemsChange;
FItemIndex := -1;
FColumns := 1;
end;
destructor TTestRadioGroup.Destroy;
begin
SetButtonCount(0);
TStringList(FItems).OnChange := nil;
FItems.Free;
FButtons.Free;
inherited Destroy;
end;
procedure TTestRadioGroup.ArrangeButtons;
var
I,Y: Integer ;
begin
if (FButtons.Count <> 0) and not FReading then
begin
try
Y:= 10;
for I := 0 to FButtons.Count - 1 do
with TTestGroupButton(FButtons[I]) do
begin
Position.X:= 10;
Position.Y:= Y;
Y:= Y + 10;
Visible := True;
end;
finally
end;
end;
end;
procedure TTestRadioGroup.UpdateButtons;
var
I: Integer;
begin
SetButtonCount(FItems.Count);
for I := 0 to FButtons.Count - 1 do
TRadioButton(FButtons[I]).Text := FItems[I];
if FItemIndex >= 0 then
begin
FUpdating := True;
TRadioButton(FButtons[FItemIndex]).isChecked := True;
FUpdating := False;
end;
ArrangeButtons;
Repaint;
end;
procedure TTestRadioGroup.ButtonClick(Sender: TObject);
begin
if not FUpdating then
begin
FItemIndex := FButtons.IndexOf(Sender);
Change;
Click;
end;
end;
procedure TTestRadioGroup.ItemsChange(Sender: TObject);
begin
if not FReading then
begin
if FItemIndex >= FItems.Count then
FItemIndex := FItems.Count - 1;
UpdateButtons;
end;
end;
procedure TTestRadioGroup.SetColumns(Value: Integer);
begin
if Value < 1 then Value := 1;
if Value > 16 then Value := 16;
if FColumns <> Value then
begin
FColumns := Value;
ArrangeButtons;
Repaint;
end;
end;
procedure TTestRadioGroup.SetItemIndex(Value: Integer);
begin
if FReading then FItemIndex := Value else
begin
if Value < -1 then Value := -1;
if Value >= FButtons.Count then Value := FButtons.Count - 1;
if FItemIndex <> Value then
begin
if FItemIndex >= 0 then
TRadioButton(FButtons[FItemIndex]).isChecked := False;
FItemIndex := Value;
if FItemIndex >= 0 then
TRadioButton(FButtons[FItemIndex]).isChecked := True;
end;
end;
end;
procedure TTestRadioGroup.SetItems(Value: TStrings);
begin
FItems.Assign(Value);
end;
procedure TTestRadioGroup.SetButtonCount(Value: Integer);
begin
while FButtons.Count < Value do
TTestGroupButton.InternalCreate(Self);
while FButtons.Count > Value do
TTestGroupButton(FButtons.Last).Free;
end;
procedure TTestRadioGroup.SetButtonMargins(Value: Integer);
begin
if fButtonMargins <> Value then
fButtonMargins:= Value;
ArrangeButtons;
end;
procedure TTestRadioGroup.SetButtonSpacing(Value: Integer);
begin
if fButtonSpacing <> Value then
fButtonSpacing:= Value;
ArrangeButtons;
end;
function TTestRadioGroup.GetButtons(Index: Integer): TRadioButton;
begin
Result := TRadioButton(FButtons[Index]);
end;
end.
What I think the first problem is, is that when you run the program it will attempt to load the design state of the objects using a copy of the FMX file. The problem with this is that it expects TTestGroupButton to have a standard Create constructor, which effectively yours does not, so it uses TRadioButton.Create instead, which means that at run time your InternalCreate never gets called.
There is a second problem too, to do with creating buttons on the fly, and indeed it is probably this that causes your first problem.
One way to attempt to address this might be to define an additional create. like this:
TTestGroupButton = class(TRadioButton)
private
protected
public
constructor InternalCreate(RadioGroup: TTestRadioGroup);
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
...
constructor TTestGroupButton.Create(AOwner: TComponent); override;
begin
if AOwner is TTestRadioGroup then
begin
InternalCreate( AOwner as TTestRadioGroup );
end
else
begin
inherited;
end;
end;
However it may be better to address the underlying problem which is that the buttons that you create are created on the fly both at design and at run time, so either do not create them at design time, or make sure that they are not saved at design time like this by setting the Stored value to FALSE.
constructor TTestGroupButton.InternalCreate(RadioGroup: TTestRadioGroup);
begin
inherited Create(RadioGroup);
RadioGroup.FButtons.Add(Self);
Visible := False;
Enabled := RadioGroup.Enabled;
OnClick := RadioGroup.ButtonClick;
Parent := RadioGroup;
Stored := FALSE; //////////// Make sure not saved in FMX file
end;

How to correctly declare StreamLn [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I am trying to compile my program but I am getting this error:
Undeclared indentifier 'StreamLn'
i even tried to download PSock.dcu and put it into the library but it doesnt compile, it looks like its compactible with delphi 5,
unit ResourceInfo;
interface
uses
Classes, SysUtils, Windows;
type
TResourceInfo = class;
TDfmMode = ( dfmData, dfmResource, dfmASCII, dfmBinary);
TDfm = class
private
FOwner: TResourceInfo;
FName: string;
FData: TStream;
procedure SetName(const Value: string);
procedure SetOwner(const Value: TResourceInfo);
public
constructor Create(AOwner: TResourceInfo);
destructor Destroy; override;
function SaveToFile(FileName: TFileName; Mode: TDfmMode): Boolean;
property Data: TStream read FData;
property Name: string read FName write SetName;
property Owner: TResourceInfo read FOwner write FOwner;
end; {TDfm}
TResourceInfo = class(TComponent)
private
FActive: Boolean;
FDfms: TList;
FExeFileName: TFileName;
FModule: THandle;
FOnActivate: TNotifyEvent;
FOnDeactivate: TNotifyEvent;
procedure SetExeFileName(const Value: TFileName);
procedure SetActive(const Value: Boolean);
function GetDfms(Index: Cardinal): TDfm;
function GetDfmCount: Cardinal;
protected
procedure Clear;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function AddDfm(const Name: string; AData: TMemoryStream): Integer;
procedure DeleteDfm(const Name: string);
property DfmCount: Cardinal read GetDfmCount;
property Dfms[Index: Cardinal]: TDfm read GetDfms;
procedure EnumDfmNames;
property Module: THandle read FModule;
published
property Active: Boolean read FActive write SetActive;
property ExeFileName: TFileName read FExeFileName write SetExeFileName;
property OnActivate: TNotifyEvent read FOnActivate write FOnActivate;
property OnDeactivate: TNotifyEvent read FOnDeactivate write FOnDeactivate;
end; {TResourceInfo}
procedure Register;
implementation
uses
Winsock;
resourcestring
rsErrorLoadingExeFile = 'An error ocurred loading file %s, it may not be an executable module';
procedure Register;
begin
RegisterComponents('+HCU', [TResourceInfo]);
end; {Register}
{ TResourceInfo }
function TResourceInfo.AddDfm(const Name: string; AData: TMemoryStream): Integer;
var
FDfm: TDfm;
begin
FDfm := TDfm.Create(Self);
FDfm.Name := Name;
FDfm.Data.Size := AData.Size;
FDfm.Data.Seek(0, 0);
AData.Seek(0, 0);
FDfm.Data.CopyFrom(AData, AData.Size);
Result := FDfms.Add(FDfm);
end; {TResourceInfo.AddDfm}
constructor TResourceInfo.Create(AOwner: TComponent);
begin
inherited;
FActive := False;
FDfms := TList.Create;
FModule := 0;
end; {TResourceInfo.Create}
destructor TResourceInfo.Destroy;
begin
Clear;
FDfms.Free;
inherited;
end; {TResourceInfo.Destroy}
function CB_EnumDfmNameProc(hModule: THandle; lpszType, lpszName: PChar; lParam: Integer): Boolean; stdcall;
var
ms: TMemoryStream;
rs: TResourceStream;
Buffer: array of Byte;
begin
with TResourceInfo(lParam) do
begin
rs := TResourceStream.Create(TResourceInfo(lParam).Module, lpszname, lpszType);
try
ms := TMemoryStream.Create;
try
try
SetLength(Buffer, 4);
rs.Read(Buffer[0], SizeOf(Buffer));
if string(Buffer) = 'TPF0' then
begin
rs.Seek(0, 0);
ObjectBinaryToText(rs, ms);
ms.Seek(0, 0);
AddDfm(StrPas(lpszName), ms);
end;
except
raise;
end;
finally
ms.Free;
end;
finally
rs.free;
end;
end;
Result := True;
end; {CB_EnumDfmNameProc}
procedure TResourceInfo.EnumDfmNames;
begin
if FModule > 0 then
EnumResourceNames(FModule, RT_RCDATA, #CB_EnumDfmNameProc, Integer(Self));
end; {TResourceInfo.EnumDfmNames}
procedure TResourceInfo.DeleteDfm(const Name: string);
var
i: Cardinal;
begin
if FDfms.Count > 0 then
for i := Pred(FDfms.Count) downto 0 do
if UpperCase(TDfm(FDfms[i]).Name) = UpperCase(Name) then
begin
FDfms.Delete(i);
Break;
end;
end; {TResourceInfo.DeleteDfm}
procedure TResourceInfo.SetActive(const Value: Boolean);
begin
if FActive <> Value then
begin
if Value then
begin
if FModule > 0 then
FreeLibrary(FModule);
(* LOAD_LIBRARY_AS_DATAFILE
If this value is given, the function does a simple mapping of the file into the
address space. Nothing is done relative to executing or preparing to execute the
code in the mapped file. The function loads the module as if it were a data file.
You can use the module handle that the function returns in this case with the Win32
functions that operate on resources. Use this flag when you want to load a DLL in
order to extract messages or resources from it, and have no intention of executing
its code.If this value is not given, the function maps the file into the address
space in the manner that is normal for an executable module. The behavior of the
function is then identical to that of LoadLibrary in this regard. *)
FModule := LoadLibraryEx(PChar(FExeFileName), 0, LOAD_LIBRARY_AS_DATAFILE);
if not (FModule >= 32) then
raise Exception.CreateFmt(rsErrorLoadingExeFile, [FExeFileName]);
if Assigned(FOnActivate) then
FOnActivate(Self);
end
else
begin
Clear;
if FModule > 0 then
begin
FreeLibrary(FModule);
FModule := 0;
end;
if Assigned(FOnDeactivate) then
FOnDeactivate(Self);
end;
FActive := Value;
end;
end; {TResourceInfo.SetActive}
procedure TResourceInfo.SetExeFileName(const Value: TFileName);
begin
if FExeFileName <> Value then
FExeFileName := Value;
end; {TResourceInfo.SetExeFileName}
function TResourceInfo.GetDfms(Index: Cardinal): TDfm;
begin
Result := TDfm(FDfms[Index]);
end; {TResourceInfo.GetDfms}
function TResourceInfo.GetDfmCount: Cardinal;
begin
Result := FDfms.Count;
end; {TResourceInfo.GetDfmCount}
procedure TResourceInfo.Clear;
begin
if FDfms.Count > 0 then
while FDfms.Count > 0 do
FDfms.Delete(0);
end; {TResourceInfo.Clear}
{ TDfm }
constructor TDfm.Create(AOwner: TResourceInfo);
begin
inherited Create;
FData := TMemoryStream.Create;
FName := '';
SetOwner(AOwner);
end; {TDfm.Create}
destructor TDfm.Destroy;
begin
FData.Free;
inherited;
end; {TDfm.Destroy}
function TDfm.SaveToFile(FileName: TFileName; Mode: TDfmMode): Boolean;
function EndOfStream(Stream: TStream): Boolean;
begin
with Stream do
Result := Position = Size;
end; {EndOfStream}
var
fs: TFileStream;
ms: TMemoryStream;
s: string;
i, j: Byte;
begin
fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
try
FData.Seek(0, 0);
case Mode of
dfmASCII:
begin
ms := TMemoryStream.Create;
try
s := FName + ' RCDATA' + #13#10 + '{';
StreamLN(fs, s);
ObjectTextToBinary(FData, ms);
ms.Seek(0, 0);
while not EndOfStream(ms) do
begin
s := '''';
for i := 0 to 15 do
begin
if ms.Read(j, SizeOf(j)) = 0 then
Break;
s := Concat(s, Format('%2.2x', [j]));
if (i = 15) or EndOfStream(ms) then
s := Concat(s, '''')
else
s := Concat(s, ' ');
end;
if EndOfStream(ms) then
s := Concat(s, #13#10 + '}');
StreamLN(fs, s);
end;
finally
ms.Free;
end;
end;
dfmBinary:
ObjectTextToBinary(FData, fs);
end;
finally
fs.Free;
end;
end; {TDfm.SaveToFile}
procedure TDfm.SetName(const Value: string);
begin
if FName <> Value then
FName := Value;
end; {TDfm.SetName}
procedure TDfm.SetOwner(const Value: TResourceInfo);
begin
FOwner := Value;
end; {TDfm.SetOwner}
end.
How can I declare it successfully?
Appears to me that WinSock unit does not have an StreamLn function (as PowerSock's PSock.pas unit uses Winsock as imported unit).
The StreamLn function in PSock.pas just adds an CRLF sequence to the string passed as parameter before calling the TStream.WriteBuffer method of the passed TStream parameter.
Here's the google cache snapshot from the Powersock's source code of PSock.pas
You need to either implement this function, or add a unit where this function is declared to your uses section.

"Control has no parent" in Create ComboBox

In this code :
unit MSEC;
interface
uses
Winapi.Windows, Vcl.Dialogs, Vcl.ExtCtrls, System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls;
type
TMSEC = class(TWinControl)
private
FOpr :TComboBox;
public
constructor Create(AOwner: TComponent); override;
end;
implementation
const
DEF_OPERATIONS :array[0..3] of Char = ('+', '-', '*', '/');
constructor TMSEC.Create(AOwner: TComponent);
var i :Integer;
begin
inherited;
FOpr:= TComboBox.Create(Self);
with FOpr do begin
Parent:= Self;
Align:= alLeft;
Width:= DEF_OPERATIONS_WIDTH;
Style:= csDropDownList;
//error in next lines :
Items.Clear;
for i := Low(DEF_OPERATIONS) to High(DEF_OPERATIONS) do Items.Add(DEF_OPERATIONS[i]);
ItemIndex:= 0;
end;
end;
end.
When I change ComboBox items, the program breaks with the message :
'Control' has no parent.
How can I fix this error or initialize ComboBox items in another way?
TComboBox requires an allocated HWND in order to store strings in its Items property. In order for TComboBox to get an HWND, its Parent needs an HWND first, and its Parent needs an HWND, and so on. The problem is that your TMSEC object does not have a Parent assigned yet when its constructor runs, so it is not possible for the TComboBox to get an HWND, hense the error.
Try this instead:
type
TMSEC = class(TWinControl)
private
FOpr: TComboBox;
protected
procedure CreateWnd; override;
public
constructor Create(AOwner: TComponent); override;
end;
constructor TMSEC.Create(AOwner: TComponent);
begin
inherited;
FOpr := TComboBox.Create(Self);
with FOpr do begin
Parent := Self;
Align := alLeft;
Width := DEF_OPERATIONS_WIDTH;
Style := csDropDownList;
Tag := 1;
end;
end;
procedure TMSEC.CreateWnd;
var
i :Integer;
begin
inherited;
if FOpr.Tag = 1 then
begin
FOpr.Tag := 0;
for i := Low(DEF_OPERATIONS) to High(DEF_OPERATIONS) do
FOpr.Items.Add(DEF_OPERATIONS[i]);
FOpr.ItemIndex := 0;
end;
end;
Remy explained the problem well, but for a more general solution, you could create a descendant of TComboBox, for example:
type
TComboBoxSafe = class(TComboBox)
strict private
FSafeItemIndex: Integer;
FSafeItems: TArray<string>;
function GetSafeItemIndex: Integer;
function GetSafeItems: TArray<string>;
procedure SetSafeItemIndex(const AValue: Integer);
procedure SetSafeItems(const AValue: TArray<string>);
strict protected
procedure CreateWnd; override;
procedure DestroyWnd; override;
public
constructor Create(AOwner: TComponent); override;
property SafeItemIndex: Integer read GetSafeItemIndex write SetSafeItemIndex;
property SafeItems: TArray<string> read GetSafeItems write SetSafeItems;
end;
{ TComboBoxSafe }
constructor TComboBoxSafe.Create(AOwner: TComponent);
begin
inherited;
FSafeItemIndex := -1;
end;
procedure TComboBoxSafe.CreateWnd;
var
LOnChange: TNotifyEvent;
begin
inherited;
LOnChange := OnChange;
OnChange := nil;
try
Items.Text := string.Join(sLineBreak, FSafeItems);
ItemIndex := FSafeItemIndex;
finally
OnChange := LOnChange;
end;
end;
procedure TComboBoxSafe.DestroyWnd;
begin
FSafeItemIndex := ItemIndex;
FSafeItems := Items.ToStringArray;
inherited;
end;
function TComboBoxSafe.GetSafeItemIndex: Integer;
begin
if WindowHandle <> 0 then
Result := ItemIndex
else
Result := FSafeItemIndex;
end;
function TComboBoxSafe.GetSafeItems: TArray<string>;
begin
if WindowHandle <> 0 then
Result := Items.ToStringArray
else
Result := FSafeItems;
end;
procedure TComboBoxSafe.SetSafeItemIndex(const AValue: Integer);
begin
if WindowHandle <> 0 then
ItemIndex := AValue
else
FSafeItemIndex := AValue;
end;
procedure TComboBoxSafe.SetSafeItems(const AValue: TArray<string>);
begin
if WindowHandle <> 0 then
Items.Text := string.Join(sLineBreak, AValue)
else
FSafeItems := AValue;
end;

Resources