Using pointers and resourcestring - delphi

I'm trying to show the content of an array of constants attached to a Resourcestring; but it doesn't work properly (Showmessage should show 'Primavera') but you get a blank message and exception when you finish the application. The example code works well in Lazurus, for example. I'm missing something....
unit U_Translate;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
end;
Resourcestring
RS1 = 'Primavera'; RS2 = 'Verano'; RS3 = 'Otoño'; RS4 = 'Invierno';
Const
CEstacion: Array [1..4] of ^String = (#RS1,#RS2,#RS3,#RS4);
var Form1: TForm1;
implementation
$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage (CEstacion[1]^);
end;
end.

If you are using resourcestrings in a const array the values of the array are being initialized during unit initialization. At that point they are being loaded from the resources of the binary. The reason of them being empty can only mean you have some translation resource where you did not translate those resource strings yet.
Using resource strings that way also means that you you cannot just change the language at runtime and have the values being changed (unless you explicitly reinitialize the array but that requires quite some explicit low level code - see System._InitResStrings)
Another way would be to use an array of PResStringRec like this:
const
CEstacion: array [1..4] of PResStringRec = (#RS1,#RS2,#RS3,#RS4);
and then call it like this (when using resourcestrings the compiler usually inserts the call to LoadResString for you)
ShowMessage(LoadResString(CEstacion[1]));
What we would need as language extension would be to be able to declare array of resourcestring which the compiler would translate to an array of PResStringRec and insert the LoadResString calls just like for normal resourcestrings.

Perhaps it is necessary to use pointers in FreePascal/Lazarus, but there is no need to do that in Delphi:
resourcestring
RS1 = 'Primavera';
RS2 = 'Verano';
RS3 = 'Otoño';
RS4 = 'Invierno';
const
CEstacion: array[1..4] of string = (RS1, RS2, RS3, RS4);
...
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage(CEstacion[1]);
end;
That works fine. Now the array will not be modified once the application is running, but it can be localized for other languages when the app is not running and the different names will be used. Use the usual tools.

Related

How to use FocusEffect color in VCL Styles

FocusEffect is displayed as a color in the bitmap style designer however it is not defined in TStyleColor.
I can see from the source that FocusEffect relates to TseStyleColor.ktcFocusEffect and should be able to fetch it with TSeStyle(FSource).Colors[ktcFocusEffect] however to do this requires getting the style source which is not accessible from the style and to make it more difficult all of the Tse* definitions are in the implementation section of vcl.styles and therefore not accessible.
Due to this being a seemingly over complicated attack on the problem I presume I am going about it all wrong and there must be a way to access the custom style stuff without hacking around because if you were to create your own custom style with lots of newly defined colors etc. you would have to hack for each new entry.
I have a solution below but it seems excessive for what should be a trivial task.
unit StyleDirect;
interface
uses
Winapi.Windows, Vcl.Graphics;
type
TStyleDirect = class
public
class function FocusEffectColor: TColor;
end;
implementation
uses
System.Classes, System.SysUtils, Vcl.Direct2D, Winapi.D2D1, System.Types, Vcl.ImgList, Vcl.Consts, ZLib, StrUtils, Vcl.GraphUtil,
Winapi.Messages, Vcl.Controls, Vcl.Styles, Vcl.Forms, Vcl.Themes;
{$I StyleUtils.inc}
{$I StyleAPI.inc}
type
TStyleHelper = class helper for TCustomStyle
public
function GetSource: TObject;
end;
function TStyleHelper.GetSource: TObject;
begin
Result := Self.FSource;
end;
{ TStyleDirect }
class function TStyleDirect.FocusEffectColor: TColor;
begin
if not TStyleManager.IsCustomStyleActive then
Exit(GetSysColor(clHighlight));
var Style := TCustomStyle(TStyleManager.ActiveStyle);
var Source := Style.GetSource;
Result:=TSeStyle(Source).Colors[ktcFocusEffect];
end;
end.

Is it possible to use form attributes in separate unit?

I am creating a game using delphi and want to move some of my code to a separate unit, however this code uses attributes from a form. Is this possible?
I am creating a game using a VCL form application and currently have all my code for the game algorithm in form unit. There is nothing wrong with this, as in my program runs well, except it looks messy and I have been advised to put the algorithm code in a separate unit. I have tried moving the code into a new unit, however whatever I try syntax errors appear.
This is code in my main unit where Grid is TStringGrid from the form and GridSize is a procedure from my attempted second unit:
procedure TGame.NewGame;
begin
Grid.Width:=GridSize(Grid.ColCount);
Grid.Height:=GridSize(Grid.RowCount);
end;
This is the second unit code:
unit UGameGenerator;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.Menus,
Vcl.StdCtrls;
implementation
function GridSize(size: integer): integer;
begin
result:=291+36*(size-8);
end;
end.
EDIT:
This is code from the second unit:
procedure ClearGrid;
var
i,j: integer;
begin
for i := 0 to Grid.ColCount-1 do
begin
for j := 0 to Grid.RowCount-1 do
begin
Grid.Cells[i,j]:='';
end;
end;
end;
The compiler needs to find the declaration of GridSize somehow. To do that, follow this guide:
In the main form, add UGameGenerator to the uses list:
unit MainForm;
interface
uses
...,UGameGenerator; // Add here or in the implementation section
...
implementation
...
end.
In your UGameGenerator unit, expose all types/functions/procedures that is used in other program parts in the interface:
unit UGameGenerator;
interface
uses
...,...;
function GridSize(size: integer): integer;
implementation
function GridSize(size: integer): integer;
begin
result:=291+36*(size-8);
end;
end.
A tip when designing the separate unit, avoid using variables directly from other units. Instead pass them as parameters in procedure/function calls.
Otherwise you risk to have much trouble with circular references.
In your updated question, declare procedure ClearGrid( aGrid : TStringGrid); and pass the grid as a parameter.

Print complex number

Using embarcadero XE7 and System.VarCmplx - need to present a complex number as a string. Simple example where a complex number is created and the intent is to show it in the caption of the form. My problem is that I can not figure out how to get the complex number to a string - should be '1.23+4.56i'.
unit Unit57;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
System.VarCmplx,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm57 = class(TForm)
procedure FormCreate(Sender: TObject);
end;
var
Form57: TForm57;
implementation
{$R *.dfm}
procedure TForm57.FormCreate(Sender: TObject);
v : Variant;
begin
v := VarComplexCreate( 1.23, 4.56 );
// following does not work
Caption := v.AsString;
end;
end.
It is quite simple, actually:
v := VarComplexCreate(1.23, 4.56);
Caption := v;
On my German Windows, this shows
1,23 + 4,56i
No need for .AsString.
If you want custom formatting, you can use the real and imaginary parts directly, and do something like:
var
A, B: Extended;
...
A := v.Real;
B := v.Imaginary;
Caption := Format('%.3f+%.3fi', [A, B], TFormatSettings.Invariant);
That shows:
1.230+4.560i
Note: There is a public property AsString in the implementation, but apparently only the published properties can be accessed from code. I guess it was made public because it is not needed by the user anyway.
Note that your code doesn't compile. Although it looks as if you copied and pasted this from the Delphi editor, there is no proper var section in the procedure, so that can't compile. Please always use copy and paste.
There is actually a (as far as I can see) much better implementation of complex numbers, by Hallvard Vassbotn, using extended records instead of variants. It comes with the Samples that are (usually) installed together with Delphi. Just look in the <your samples dir>\Delphi\RTL\ComplexNumbers directory. This uses extended records, so it can be used like a normal value type, i.e. like a Double or an Integer.

Delphi code migration issues

I´m facing a problem between Delphi 2010 and Delphi Berlin (last update) during my code migration....
I made a simple code to demonstrante an strange behaviour...
I have an application that use TList (the former one) and TList (from Generics.Collections)
I know that this piece of code (below) doesn´t make any sense for you, but it´s for demonstration purposes
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TTest = class
Name: string;
constructor Create(Nome: string);
end;
TForm1 = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FList: TList;
end;
var
Form1: TForm1;
implementation
uses
System.Generics.Collections;
{$R *.dfm}
procedure TForm1.btn1Click(Sender: TObject);
var
tmpList: TList<TTest>;
begin
tmpList := TList<TTest>.Create;
tmpList.Add(TTest.Create('A'));
tmpList.Add(TTest.Create('B'));
tmpList.Add(TTest.Create('C'));
tmpList.Add(TTest.Create('D'));
tmpList.Add(TTest.Create('E'));
FList := TList(tmpList);
ShowMessage(TTest(FList[0]).Name);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FList := TList.Create;
end;
constructor TTest.Create(Nome: string);
begin
Name := Nome;
end;
end.
At Delphi 2010 the ShowMessage shows 'A' character, but on the Delphi Berlin it raises an Acess Violation
Both applications with Optimization set to False
FList := TList(tmpList);
This is the problem. The cast is simply wrong, because tmpList is not a TList.
Your code only compiles because of the cast, but the cast does not change the fact that the object on the right hand side is not of the type being casted to. All the cast does is stop the compiler from complaining and saving you from yourself. Your cast is a lie to the compiler, and the runtime error is the consequence.
This code might have worked in older versions, but only by chance. Your luck has changed.
Hard to know what to suggest for a fix. As you say, the code makes little sense. Every time you press the button, you leak a list. I'd suggest that you remove all the casts, stop using the non-Generic TList and use only Generic lists.
The class TList<T> is not castable to/from TList.
You cannot cast one to the other and expect sensible results any more than you could cast a TForm to TButton (for example).
In Delphi, typecasts of this form are unchecked, sometimes referred to as hard-casting. That is, the compiler will simply trust that you know what you are doing and will simply comply, but if the typecast is invalid then the results will be unpredictable.
For conversions between object reference types (and/or interface references) you can use a checked cast using the as operator:
FList := tmpList as TList;
If a checked cast is invalid (such as this one is) then the compiler will throw a runtime exception, alerting you to the mistake.
Why does the compiler even allow unchecked casts ?
In some cases unchecked casting can be useful and safely relied upon, within specific use cases. But outside of those specific conditions unchecked casts are at best trusting to luck or on specific compiler behaviours or RTL characteristics which may be subject to change.
e.g. the 32-bit trick of storing object references or other pointer values in an Integer variable. Such code may continue to work when recompiled for 64-bit, but now only as a matter of luck and only in some cases, since only a subset of possible 64-bit pointer values can safely be stored in a 32-bit Integer.
If you have code which is successfully hard-casting between TList and TList<T> then it worked only by luck, as a result of some particular behaviour of the compiler or RTL at that time.

Try to understanding Delphi Interfaces

I'm reading the Nick Hodges' book "Coding in Delphi" and I'm trying to understand the interface usage.
In a unit I've put asimple interface:
unit INameInterface;
interface
type
IName = interface
['{CE5E1B61-6F44-472B-AE9E-54FF1CAE0D70}']
function FirstName: string;
function LastName: string;
end;
implementation
end.
and in another unit I've put the implementation of this interface, according with the book sample:
unit INameImplementation;
interface
uses
INameInterface;
type
TPerson = class(TInterfacedObject, IName)
protected
function FirstName: string;
function LastName: string;
end;
implementation
{ TPerson }
function TPerson.FirstName: string;
begin
Result := 'Fred';
end;
function TPerson.LastName: string;
begin
Result := 'Flinstone';
end;
end.
At this point I've created a simple VCL form application in order to use the object I've created. The form code is this:
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, INameImplementation;
type
TfrmMain = class(TForm)
lblFirtName: TLabel;
lblLastName: TLabel;
txtFirstName: TStaticText;
txtLastName: TStaticText;
btnGetName: TButton;
procedure btnGetNameClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
Person: TPerson;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
txtFirstName.Caption := '';
txtLastName.Caption := '';
end;
procedure TfrmMain.btnGetNameClick(Sender: TObject);
begin
txtFirstName.Caption := ...
end;
end.
My question is this: how can I use the interface? The two funcions are declared as protected so how can I access them from the form? I've to define them as public, or should I use the INameInterface interface unit?
I'm terribly confused about interfaces!!!
Eros
Essentially there are three things for you to know, beyond what you have already demonstrated understanding.
1. How to call methods of an interface
If you have a reference to an interface, then you can call methods just as you would on a class reference:
var
Name: IName;
....
Writeln(Name.FirstName);
Writeln(Name.LastName);
2. How to obtain interface references
Typically you do this by instantiating a class that implements the interface you wish to use:
var
Name: IName;
....
Name := TPerson.Create;
// now you can use Name as before
There are other ways to obtain interface references, but let's leave those to one side for now.
3. How to pass around interfaces
You might not wish to create a new object every time you need to use an interface. So you can get other parties to pass you the interface to use. For instance interfaces can be passed as method parameters:
procedure Foo(Name: IName);
begin
// use Name as before
end;
You can obtain interface references via function calls and properties, etc.
The two functions are declared as protected so how can I access them from the form?
Well, they are declared protected in the implementing object. But you are not going to access them via the implementing object. You will access them via the interface. Which means that the visibility in the implementing object is not relevant from the perspective of the interface.
Your form unit references INameImplementation which is needed to create the object that implements the interface. You'll also need to use INameInterface so that your code can see the interface itself.
This example isn't very powerful yet because you can still see the implementing object's type. But imagine if that was hidden from you and all you could see was a function that returned an IName. It's when you reach this point that interfaces can achieve their potential.

Resources