implement generic interface in delphi [duplicate] - object

I want to implement an interface, but I cannot declare it from TInterfacedObject, and in this case the documentation says that I must implement QueryInterface, _AddRef, _Release. But I am not sure if this is mandatory. I haven't really worked with interfaces so far... They say something about COM objects, but I don't even know what that means. I never intentionally used COM objects, maybe only if they are included in some pieces of code which I took from the internet. I tried an example (see below) and it's working without implementing those 3 methods. So...
How should I know if I must implemet them ?
If I must implement them, how can I do it ? I don't know nothing about those methods should do. Should I copy the implementation from TInterfacedObject ? (In fact, it has more than 3... those extra ones must be copied too ?)
Thanks !
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
IShellInterface = interface
['{55967E6B-5309-4AD0-B6E6-739D97A50626}']
procedure SetPath(const APath: String);
function GetPath: String;
end;
TDriveBar = class(TCustomPanel, IShellInterface)
private
FDriveLink: IShellInterface;
FPath: String;
public
procedure SetPath(const APath: String);
function GetPath: String;
property DriveLink: IShellInterface read FDriveLink write FDriveLink;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
DriveBar1, DriveBar2: TDriveBar;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TDriveBar.SetPath(const APath: String);
begin
FPath:= APath;
Caption:= APath;
end;
function TDriveBar.GetPath: String;
begin
Result:= FPath;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown:= True;
DriveBar1:= TDriveBar.Create(Form1);
DriveBar1.Parent:= Form1;
DriveBar1.SetBounds(20, 20, 250, 40);
DriveBar1.SetPath('Drive Bar 1');
DriveBar2:= TDriveBar.Create(Form1);
DriveBar2.Parent:= Form1;
DriveBar2.SetBounds(20, 80, 250, 40);
DriveBar2.SetPath('Drive Bar 2');
DriveBar1.DriveLink:= DriveBar2;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
DriveBar1.DriveLink.SetPath('D:\Software\Test');
Caption:= DriveBar2.GetPath;
end;
end.

Any class that supports interfaces must implement all three methods:
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
If base class you inherit from has those methods implemented, then you don't need to implement them. If the base class does not have them, your code will not compile.
Different classes have different implementations of those methods and primary and the most important difference is whether automatic reference counting is enabled or not.
If the reference counting is enabled, then you should always store instances of such class in interface reference (variable type must be interface), if it is disabled, they you can use object reference.
If enabled ARC will automatically manage memory of such instance, and if disabled you need to release such instance manually.
For example, class where reference counting is enabled is TInterfacedObject and class where it is disabled for most uses is TComponent (except in cases where it serves as COM object container).
Basically, in most simple scenario, if the _AddRef and _Release just return -1 then reference counting will be disabled. If you need to implement reference counting, then the best template to copy code or inherit from is TInterfacedObject.
Purpose of QueryInterface is adding functionality for Support function and querying interface for supported GUIDs. If you are not sure what to put there, you can always use TInterfacedObject implementation regardless of whether you have ARC enabled or not.
It is also possible to have class that can have ARC disabled or enabled depending on same value passed to the constructor. Example of such class is TXMLDocument where if nil is passed as Owner enables ARC and otherwise ARC is disabled.
In your example, TCustomPanel inherits from TComponent that already has those methods implemented, so you don't have to do a thing.

Related

Properties of published nested components are not saved, events are not visible in object inspector

I created a component with following contents:
TEditLine = class(TCustomControl)
strict private
FCaptionLabel: TLabel;
FUnitLabel: TLabel;
FEdit: TMyEdit;
end;
Then I started to propagate necessary properties of nested components by creating of corresponding getter/setter pairs.
Suddenly I thought, it may be easier to publish these nested components themselves as read-only properties so that when some new property or method gets introduced to one of these components, no changes in container component interface are required:
TEditLine = class(TCustomControl)
strict private
FCaptionLabel: TLabel;
FUnitLabel: TLabel;
FEdit: TMyEdit;
published
property CaptionLabel: TLabel read FCaptionLabel;
property UnitLabel: TLabel read FUnitLabel;
property Edit: TMyEdit read FEdit;
end;
When I put the component on a form, I see CaptionLabel, UnitLabel and Edit in Object Inspector, but that is all I can achieve.
Properties of nested components are not saved to DFM.
Their events are not visible in object inspector.
Is it generally a good idea to do so? How I solve the two issues listed above?
TComponent-based properties are treated as references to external components by default, unless you call SetSubComponent(True) on the objects that back them, eg:
TEditLine = class(TCustomControl)
strict private
FCaptionLabel: TLabel;
FUnitLabel: TLabel;
FEdit: TMyEdit;
public
constructor Create(AOwner: TComponent); override;
published
property CaptionLabel: TLabel read FCaptionLabel;
property UnitLabel: TLabel read FUnitLabel;
property Edit: TMyEdit read FEdit;
end;
...
constructor TEditLine.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
...
FCaptionLabel := TLabel.Create(Self);
FCaptionLabel.Parent := Self;
FCaptionLabel.SetSubComponent(True); // <-- ADD THIS
...
FUnitLabel := TLabel.Create(Self);
FUnitLabel.Parent := Self;
FUnitLabel.SetSubComponent(True); // <-- ADD THIS
...
FEdit := TMyEdit.Create(Self);
FEdit.Parent := Self;
FEdit.SetSubComponent(True); // <-- ADD THIS
...
end;

"Acess violation" when using a scripted constant

I need to implement a scripted constant to retrieve a drive of user-selected path.
I tried the code bellow, but I'm getting:
Acess violation ate adress 0043D8E0.Read of address 01CDE694
[Code]
procedure InitializeWizard;
begin
Page := CreateInputDirPage(
wpSelectDir,'Select local', 'Where will be stored?', '', False, 'New Folder');
Page.Add('Local Dserver (APP)');
Page.Add('Local Images (Storage)');
Page.Values[0] := ('F:\TEST1');
Page.Values[1] := ('G:\TEST2');
end;
function ExtractFileDrive(const FileName: string): String;
begin
Result := ExtractFileDrive(Page.Values[0]);
end;
[Run]
Filename: {code:ExtractFileDrive|0}\postgresql-9.4.5-1-windows-x64.exe
Your ExtractFileDrive scripted constant shadows the ExtractFileDrive support function. So you recursively call your ExtractFileDrive function from itself ad infinitum, until the stack overflows.
Just rename your function to anything else.
And once doing that, rename its argument, as the FileName is confusing. And also remove the scripted constant parameter in its use, as you actually do not use it.
[Code]
function GetFileDrive(Param: string): String;
begin
Result := ExtractFileDrive(Page.Values[0]);
end;
[Run]
Filename: {code:GetFileDrive}\postgresql-9.4.5-1-windows-x64.exe

FireMonkey Component Execution Error

I tried to create a very simple component(Tgraph) using the FireMonkey platform (XE7). First of all I create two new classes:
1) TGraph (anchestor type TLayout);
2) TMyPlot1D(anchestor type Tpanel);
I saved two units and created a package called 'MyPackage'. I compiled and Installed it in the "Samples" page. I opened a new Firemonkey project and drag and drop the TGraph instance in the form. Everything works well. At designtime,I can see the component as defined, and all the relevant units are visible from the main unit. The relevant code is in the following:
First Class
unit UMyPlot;
interface
uses
System.SysUtils, System.Classes, FMX.Types,
FMX.Controls, FMX.StdCtrls;
type
TMyPlot1D = class(TPanel)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyPlot1D]);
end;
end.
Second Class
unit UMyGraph;
interface
uses
System.SysUtils, System.Classes, FMX.Types, FMX.Controls, FMX.Layouts,
UMyPlot;
type
TMyGraph = class(TLayout)
private
Plot : TmyPlot1D;
public
constructor create(Aowner:TComponent); override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyGraph]);
end;
constructor TMyGraph.create(Aowner: TComponent);
begin
inherited;
Plot := TMyPlot1D.Create(Self);
plot.Parent := Self;
end;
end.
The problem is shown when I try to run my application.
I got the following error:
"Exception EClassNotFound in module Project1.exe at 000A51FA. Class TmyPlot1D not Found". The failed function seems to be the Application.RealCreateForms.
If I drag and drop only the TmyPlot1D instance, it works (of course) both in designtime and in runtime!
Any ideas?
Thanks in advance
In your TMyGraph.Create you are creating a child object, Plot. This behaviour happens at both design time and at runtime.
At runtime there's no problem, but the problem is occurring because when you save your design time form the children of the component are also streamed out to the FMX file.
When you run your app it streams the form in and attempts to stream in the TMyGraph and the TMyPlot1D child object which was created at design time, which fails. Even if it succeeded you would have a problem because you would have both the TMyPlot1D created at design time and the one created at run time.
You can solve this by setting the Stored := False for any children you create at design time, so your Create method will look like this:
constructor TMyGraph.Create(Aowner: TComponent);
begin
inherited;
Plot := TMyPlot1D.Create(Self);
Plot.Parent := Self;
Plot.Stored := False;
end;
Now we come to the reason why the class if failing to be read in by the streaming system. In FMX you need to call RegisterFMXClasses (Classes unit) to enable a class to be streamed into a form. You need to put this in the initialization section at the end of your units (before the final end.), e.g.:
initialization
RegisterFMXClasses([TMYGraph]);
end.
this command is very vital:
Tcomponent.Stored := true;

Inno Setup script: Identifier expected error

Please help me with the following code:
[Code]
var
AppVersionNumber: Integer;
DBVersionNumber: Integer;
function GetAppVersion:Integer;
var
AppVersion: AnsiString;
begin
ExtractTemporaryFile('info.dat');
LoadStringFromFile(ExpandConstant('{tmp}\info.dat'), AppVersion);
AppVersionNumber := StrToInt(Copy(AppVersion, 1, 6));
DBVersionNumber := StrToInt(Copy(AppVersion, 7, 12));
GetAppVersion := AppVersionNumber; <== here is error
end;
I don't understand what's wrong here. The Inno setup says there is "Identifier expected" error. If I try to replace on this line GetAppVersion to Result (why? I don't know but I saw it in examples) it says that "Invalid prototype for GetAppVersion".
What's wrong with this code? Please help
Result := is needed to specify the result/return value of a function (you don't assign it to the function name like VB)
The "Invalid Prototype" error is most likely because you're using it in a {code:...} constant that requires the called function to have a single string parameter at all times.
Try using this taken from the help file:
function GetAppVersion(Param: String): String;

Error in the AssemblyInfo file

I get the error "Error 1(E4) "end." or implementation section members (types or methods) expected."
Nowhere on the internet I can find information about this error.
I get this error because of this line of the AssemblyInfo.pas file:
Implementation
SomeMethod();
end.
I work in Delphi Prism.
That's not valid inside implementation.
The Pascal (which Delphi Prism is based loosely on) unit consists of a couple of sections. The interface section provides the same functionality as the C/C++ header file; it exposes the public content to users of the code unit.
The implementation is like the C/C++ source file that the header exposes. It's where you actually implement the content the interface unit made available. Therefore, it should contain the actual code for the methods and functions.
A quick example (Delphi code, but is pretty similar):
unit Test.NyClass;
interface
// Defines types and so forth that, if exposed via the proper declaration, can be seen outside
// this unit simmply by adding this unit to the uses clause of the calling code.
uses
SysUtils;
type
TMyClass=class(TObject)
FMyNumber: Integer; // protected members (no specifier, so defaults to protected)
FMyString: String;
private
function GetMyNumber: Integer; // Getters
function GetMyString: string;
procedure SetMyNumber(const Value: Integer); // Setters
procedure SetMyString(const Value: string);
published
property MyNumber: Integer read GetMyNumber write SetMyNumber; // properties exposed to class users
property MyString: string read GetMyString write SetMyString;
end;
implementation
// Actually provides the implementation for the getters/setters, any additional methods,
// types not needed outside this implementation section, etc.
// Optional uses clause. Add units here you only need access to in the implementation code;
// this prevents circular references ("Unit A uses Unit B which uses Unit A").
uses
SomeOtherUnit;
// Implementation of the getters and setters declared for the properties above. Outside code
// can't call these directly (they were declared as private), but they're called automatically
// when the corresponding property is referenced.
function TMyClass.GetMyNumber: Integer;
begin
Result := FMyNumber;
end;
function TMyClass.GetMyString: string;
begin
Result := FMyString;
end;
procedure TMyClass.SetMyNumber(const Value: Integer);
begin
if FMyNumber <> Value then
FMyNumber := Value;
end;
procedure TMyClass.SetMyString(const Value: string);
begin
if FMyString <> Value then
FMyString := Value;
end;
// Optional initialization section. This is what your code is probably intending to use (if Prism
// supports it - don't have it on this machine to check).
initialization
// Any necessary loading initialization, etc. Called when the unit is being loaded into memory,
// so you have to be careful what you're doing here.
// Optional finalization section. This is where you do cleanup of anything allocated in the
// initialization section.
finalization
end.

Resources