How hide an Form of a dll file? - forms

I have a dll file that contains several Forms.
I have a reference to main function in console application that loads this dll.
I want hide main Form of dll file, but all attempts until now was without sucess.
I tried several ways for this, but nothing that I tried worked, except for hide from taskbar.
This is my code:
Main form on dll
unit Conectar;
interface
uses
Windows, Messages, SysUtils, Classes {.......};
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
params.exstyle := params.exstyle and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
ShowWindow(Form1.Handle,SW_HIDE);
end;
Dll
library DLL;
uses
System.SysUtils,
Conectar,
vcl.Forms,
Classes;
{$R *.res}
procedure ShowDllForm; stdcall;
begin
Form1 := TForm1.Create(nil);
try
Form1.ShowModal;
finally
FreeAndNil(Form1);
end;
end;
exports
ShowDllForm;
begin
end.
Console application
{$APPTYPE GUI}
var
Msg: TMsg;
procedure ShowDllForm;stdcall;
external 'DLL.dll' name 'ShowDllForm';
begin
CoInitialize(nil);
ShowDllForm;
CoUninitialize;
end.

procedure TForm1.FormCreate(Sender: TObject);
begin
SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
end;

SOLUTION:
Dll
procedure ShowDllForm; stdcall;
begin
Application.CreateForm(TForm1, Form1);
ShowWindow(Application.Handle, SW_HIDE);
end;
Console application
var
Msg: TMsg;
begin
ShowDllForm;
while GetMessage(Msg, 0, 0, 0) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.
OR
on dll file in FormActivate event:
Form1.Hide;

Related

Delphi Access Violation on form show

Sorry for having to open new question but I can't find an answer anywhere.
My app is still in progress, but basically I'm trying to call another Form from my MainForm when initializing players, however I get an Access Violation error. Would you please explain to me why this could be happening?
My MainForm code:
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls, jpeg, pngimage, getPlayer_u;...
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Randomize;
InitGameSetup();
end;...
procedure TfrmMain.InitGameSetup();
begin
SetWindowProperties();
InitBackGround();
InitMainMenu();
InitGameBoard();
InitScrabbleTileRack();
InitPlayers();
// GameLoop();
end; ...
procedure TfrmMain.InitPlayers();
var
I : Integer;
sName, sSurname : string;
begin
setLength(Players, NUMBER_OF_PLAYERS);
for I := 1 to High(Players) do
begin
GetPlayer(); ---------------- problem is here
with Players[I] do
begin
Name := sName;
Surname := sSurname;
end;
end;
end;...
procedure TfrmMain.GetPlayer();
begin
frmGetPlayer.Show;
end;
My frmGetPlayer:
unit getPlayer_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TfrmGetPlayer = class(TForm)
btnSubmit: TButton;
edtName: TEdit;
edtSurname: TEdit;
procedure FormCreate(Sender: TObject);
procedure btnSubmitClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
sPlayerName, sPlayerSurname : string;
end;
var
frmGetPlayer: TfrmGetPlayer;
implementation
{$R *.dfm}
procedure TfrmGetPlayer.btnSubmitClick(Sender: TObject);
begin
sPlayerName := edtName.Text;
sPlayerSurname := edtSurname.Text;
if not ((Length(sPlayerName) >= 1) and (Length(sPlayerSurname) >= 1)) then
MessageDlg('Please enter a name and surname.', mtInformation, [mbOK], 0)
else
Self.Free;
end;
procedure TfrmGetPlayer.FormCreate(Sender: TObject);
begin
with Self do
begin
Position := poScreenCenter;
BorderStyle := bsDialog;
end;
end;
end.
My dpr:
program main_p;
uses
Forms,
main_u in 'main_u.pas' {frmMain},
getPlayer_u in 'getPlayer_u.pas' {frmGetPlayer};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TfrmMain, frmMain);
Application.Run;
end.
The error:
Only your MainForm object is created automatically at program startup. Inside its OnCreate event, your Player Form object hasn't been created yet, so the frmGetPlayer variable is not pointing at a valid object.
frmGetPlayer is a global variable, so it is initially nil. The error message is telling you that you are accessing invalid memory near address 0, which is almost always an indication of accessing a member of a class via a nil pointer.
So, you can't call frmGetPlayer.Show() until after you have created the Player Form object and assigned frmGetPlayer to point at it. Which the code you showed is not doing.

PostgreSQL: Capture RAISE NOTICE from a client connection via ZeosLib/Lazarus

I have developed a client application that use PostgreSQL 8.4 RDBMS.
My Application is written in Lazarus and ZeosLib 7.2 for database access.
I use a lot of stored procedures and in specific point I use raise notice to get info of the procedure status, Es:
RAISE NOTICE 'Step 1: Import Items from CSV file';
....
....
RAISE NOTICE 'Step 2: Check Items data';
When I execute procedures in PgAdmin3 it show notice in "Messages" Tab.
There is a way to capture raised notices in my client application?
Ok, while I interesting in this topic too, here is some working example crated after the quick investigation:
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ZConnection, ZDbcPostgreSql;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
pgConn: TZConnection;
procedure Button1Click(Sender: TObject);
procedure pgConnAfterConnect(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
procedure PGNotifyProcessor(arg: Pointer; message: PAnsiChar); cdecl;
begin
Form1.Memo1.Lines.Add(message);
end;
{ TForm1 }
procedure TForm1.pgConnAfterConnect(Sender: TObject);
var
pg: IZPostgreSQLConnection;
args: Pointer;
begin
pg := pgConn.DbcConnection as IZPostgreSQLConnection;
pg.GetPlainDriver.SetNoticeProcessor(pg.GetConnectionHandle, #PGNotifyProcessor, args);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
pgConn.ExecuteDirect('select foo(''bar'')');
end;
end.
It works for me.
I guess this example is not accurate and contains some issues. For example usage LCL calls in the procedure called from external source. But I hope it is enough to start.
Test environment is: FPC 2.6.4, Lazarus 1.5, Postgres 9.3, Linux Mint

Is there an Indy server property equivalent to TCustomWinSocket.Data?

When I work with TServerSocket I can use the property Data to store a pointer to a class for example for each client.
Now I will use Indy TIdCmdTCPServer and I'd like to know if there is an equivalent property.
Yes, there is - the TIdContext.Data property. In TIdCmdTCPServer events that give you a TIdCommand parameter instead of a TIdContext parameter, you can access the TIdContext object from the TIdCommand.Context property. For example:
type
TMyClass = class
// add whatever you need...
end;
procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
MyCls: TMyClass;
begin
MyCls := TMyClass.Create;
// initialize MyCls as needed...
AContext.Data := MyCls;
end;
procedure TForm1.IdCmdTCPServer1Disconnect(AContext: TIdContext);
begin
AContext.Data.Free;
AContext.Data := nil;
end;
procedure TForm1.IdCmdTCPServer1CommandHandlerCommand(ACommand: TIdCommand);
var
MyCls: TMyClass;
begin
MyCls := TMyClass(ACommand.Context.Data);
// use MyCls as needed...
end;
Indy also has another useful feature. You can derive a custom class from TIdServerContext, add whatever you want to it, and then assign it to the server's ContextClass property before activating the server. That way, you can simply typecast any TIdContext pointer to your class type when you need to access your custom members. For example:
type
TMyContext = class(TIdServerContext)
public
// add whatever you need...
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited;
//...
end;
destructor TMyContext.Destroy;
begin
//...
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdCmdTCPServer1.ContextsClass := TMyContext;
end;
procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
MyCtx: TMyContext;
begin
MyCtx := TMyClass(AContext);
// initialize MyCtx as needed...
end;
procedure TForm1.IdCmdTCPServer1CommandHandlerCommand(ACommand: TIdCommand);
var
MyCtx: TMyContext;
begin
MyCtx := TMyClass(ACommand.Context);
// use MyCtx as needed...
end;
This way, you don't need to waste time and memory allocating a separate class per client, when you can use the one that the server already creates for you.

Changing information in all Objects the same time

I'm a bit new with objects and I have really have been stuck on this, I hope you guys can help me.
I create a simple application to exemplify the problem I am having with Delphi.
I have an object that inherited from TButton, but in creation time I can assign a "Group" to this button, and after I want to be able to change the caption of all button created and to enable and disable it depending on the group that it belongs.
There are 5 button in the form:
Left G1 - Create a button to group one in the left side the form
Right G2 - Create a button to group two in the right side the form
Add caption - Add caption to all button created independent of the group (same caption to all)
Enable G1 - Enable all button that belongs to group one
Disable G1 - Disable all button that belongs to group one
What I want to do is to be able to create as many buttons I want for each different group and then change all the captions at once and enable and disable separate groups at once, this is a sample project from a much bigger application that have lots of objects created so go trough all the objects in the form would be very consuming it would be nice if the change could be made by the object not by the main form.
Please guys I don't want to anyone to make the work for me, I want someone to point me in the right direction, should I use a class even knowing that I can't change the individual properties of the objects is there a way around? Can I somehow implement it on the object or do I need to implement it on the unit calling those objects. Any pointer?
Many thanks in advance.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TSample = class(TButton)
private
class var count: integer;
protected
public
procedure increseCount;
constructor Create(AOwner: TComponent; Ypos, Group:Integer); overload;
class procedure rename(name: string);
class procedure enableGroup(Group: Integer; value: Boolean);
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
constructor TSample.Create(AOwner: TComponent; Ypos, Group:Integer);
begin
self.Create(AOwner);
self.Top := count *50;
self.Left := Ypos;
self.Tag := Group;
increseCount;
self.Parent := AOwner as TWinControl;
end;
procedure TSample.increseCount;
begin
count := count + 1;
end;
class procedure TSample.enableGroup(Group: Integer; value: Boolean);
begin
//???
end;
class procedure TSample.rename(name: string);
begin
//self.Caption := name; ???
end;
procedure TForm2.Button1Click(Sender: TObject);
var
left: TSample;
begin
left := TSample.Create(self, 24, 1);
end;
procedure TForm2.Button2Click(Sender: TObject);
var
right: TSample;
begin
right := TSample.Create(self, 200, 2);
end;
procedure TForm2.Button3Click(Sender: TObject);
begin
TSample.rename('Oi!');
end;
procedure TForm2.Button4Click(Sender: TObject);
begin
TSample.enableGroup(1, True);
end;
procedure TForm2.Button5Click(Sender: TObject);
begin
TSample.enableGroup(1, False);
end;
end.
Ok, after the hint from David I find the solution:
First I create an array the same kind of the object
Then I create a class function that create this object and save into this array
Now every time I need to access any of those objects I can just browse this list and change what need to be change.
Why I did this way? Before I was browsing all objects in my main form what was taking a lot of processing, doing this way I just have to browse objects of the kind I want to change.
Thanks I hope it can help others in the future.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TSample = class(TButton)
private
class var count: integer;
class var List: Array of TSample;
protected
public
procedure increseCount;
constructor Create(AOwner: TComponent; Ypos, Group:Integer); overload;
class function CreateInstance(AOwner: TComponent; Ypos, Group:Integer): TSample; overload;
class procedure rename(name: string);
class procedure enableGroup(Group: Integer; value: Boolean);
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
class function TSample.CreateInstance(AOwner: TComponent; Ypos, Group:Integer): TSample;
var
i: Integer;
begin
i := Length(List)+1;
SetLength(List, i);
List[i-1] := self.Create(AOwner, Ypos, Group);
end;
constructor TSample.Create(AOwner: TComponent; Ypos, Group:Integer);
begin
self.Create(AOwner);
self.Top := count *50;
self.Left := Ypos;
self.Tag := Group;
increseCount;
self.Parent := AOwner as TWinControl;
end;
procedure TSample.increseCount;
begin
count := count + 1;
end;
class procedure TSample.enableGroup(Group: Integer; value: Boolean);
var
i: Integer;
begin
for i := 0 to Length(List)-1 do
if List[i].Tag = Group then
List[i].Enabled := value;
end;
class procedure TSample.rename(name: string);
var
i: Integer;
begin
for i := 0 to Length(List)-1 do
List[i].Caption := name;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
left: TSample;
begin
left := TSample.CreateInstance(self, 24, 1);
end;
procedure TForm2.Button2Click(Sender: TObject);
var
right: TSample;
begin
right := TSample.CreateInstance(self, 200, 2);
end;
procedure TForm2.Button3Click(Sender: TObject);
begin
TSample.rename('Oi!');
end;
procedure TForm2.Button4Click(Sender: TObject);
begin
TSample.enableGroup(1, True);
end;
procedure TForm2.Button5Click(Sender: TObject);
begin
TSample.enableGroup(1, False);
end;
end.

How to access TPanel child form controls?

I have a main form with TPanel. I have also a Form2 with a TButton which I show in TPanel as a child. I mean TPanel of main form is parent of Form2. I use these steps to create the form2 in MainForm OnCreate method
MainFormOnCreate()
Form2 := TForm2.create(nil)
Form2.Parent := Panel1;
Form2.show;
But the problem is that when I access the button on Form2 it does nothing. For example, when I want to disable the button on Form2 I use this method
A button2 on main form with on click event
btn2OnClick();
Form2.btn.enabled := false;
But it does nothing. Some friends says it's because of child to TPanel it will get no message.
So give me a solution.
Thanks in advance
The main problem is, that you create 2 instances of TForm2.
Your .dpr file look like this
begin
Application.Initialize;
Application.CreateForm( TForm1, Form1 );
Application.CreateForm( TForm2, Form2 );
Application.Run;
end.
After you create an instance of TForm2 in TForm1.OnCreate and save this instance into global variable Form2, another instance of TForm2 is created and stored into Form2.
In the TForm1.btn5.OnClick event you address the second created, non visible TForm2.
Solution
go to Project / Options -> Formula and remove TForm2 from AutoCreate List
store the instance of TForm2 created inside of TForm1 in a private class field of TForm1
Your code should look like this
.dpr file:
begin
Application.Initialize;
Application.CreateForm( TForm1, Form1 );
Application.Run;
end.
Unit1.pas
TForm1 = class( TForm )
...
procedure FormCreate( Sender : TObject );
procedure btn2Click( Sender : TObject );
private
FForm2 : TForm2;
...
end;
procedure TForm1.FormCreate( Sender : TObject );
begin
FForm2 := TForm2.Create( Self );
FForm2.Parent := Panel1;
FForm2.Show;
end;
procedure TForm1.btn2Click( Sender : TObject );
begin
FForm2.btn.Enabled := True;
end;
Try this one
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Unit2;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
lForm: TForm;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(lForm) then
TForm2(lForm).Button1.Enabled:= False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
lForm := TForm2.Create(self);
lForm.Parent := Panel1;
lForm.Align:= alClient;
lForm.show;
end;