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.
Related
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.
I have met a really weird behaviour using Delphi and DLL.
I want to learn how to create and use DLL's. To do so, I created two files: one containing the DLL, and the other one calling it.
The DLL retrieves informations from a *.ini file (the GetInfos function). When calling it in a form, I get the following error: "Access violation at address XXXXXXXX in "dlltest.dll" module. Read of address 00000000.
I wanted to try some other things, so I created a simple procedure, that just display a message.
The interesting thing is that when I call this procedure (which only purpose is to display a message), I no longer get the Access violation error.
I really do not understand how it can make my code work. I'd really like if someone could give me some explanation. I am using Delphi 10.3 Rio Version 26.0.36039.7899
Here is the code I produced :
dlltest:
library dlltest;
uses
System.SysUtils, System.Classes, System.IniFiles, Winapi.Windows, vcl.dialogs;
var
// Ini file var
iniConfig: TInifile;
procedure SayHello;
var
hello: PChar;
begin
hello := 'Hello world!';
ShowMessage(hello);
end;
// -----------------------------------------------------------------------------
// Retrieving .ini file
function GetIni: TInifile;
begin
result := TInifile.Create('.\monitoring.ini');
end;
// -----------------------------------------------------------------------------
function GetInfos: ShortString;
begin
iniConfig := GetIni;
try
result := iniConfig.ReadString('filename', 'start', 'start');
finally
iniConfig.Free;
end; { try }
end; { function GetInfos }
exports
SayHello, GetInfos;
begin
end.
Using dll:
unit testUtilisationDll;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils,
System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
Form1: TForm1;
procedure SayHello; StdCall; external 'dlltest.dll';
function GetInfos: ShortString; StdCall;
external 'dlltest.dll';
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
SayHello;
ShowMessage(GetInfos(self));
end;
end.
When you export the functions, they are using the default calling convention (Register), whereas when you import them, you are telling the import that they're using StdCall calling convention.
The normal case for DLL exported functions is StdCall convention, so you should declare your functions to use this calling convention in your .DLL source:
function GetInfos: ShortString; stdcall;
and
procedure SayHello; stdcall;
Another problem is that when you call GetInfos:
ShowMessage(GetInfos(self));
you are passing it a parameter (self), but your declaration of the import:
function GetInfos: ShortString; StdCall; external 'dlltest.dll';
doesn't list any parameters. You won't be able to compile the program as it is shown in your question...
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.
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.
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.