Can anybody explain how this should work (Delphi XE2 running on Windows10)?
In a minimalist application - a form with two buttons - with the following code:
procedure TForm2.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
Printer.Canvas.TextOut(10,10,'Hello World');
Printer.EndDoc;
end;
procedure TForm2.Button2Click(Sender: TObject);
var
MyPrinter : TPrinter;
begin
MyPrinter := Printer;
MyPrinter.BeginDoc;
MyPrinter.Canvas.TextOut(10,10,'Hello World');
MyPrinter.EndDoc;
MyPrinter.Free;
end;
If I click Button1, the program prints a 'Hello World' at my default printer, and closes normally when I close it (with the close button). If instead, I click Button2, the program prints an identical page but now when I close with the close button I get an Error 217 message.
I can't find clarification in the Delphi documentation regarding exactly how to use the Printer function and TPrinter variables. I am quite happy using a derivative of the Button1 technique to print - if it is confirmed that this is the 'bullet-proof' way to go, but would like to understand why the Button2 method doesn't work. Should I just assume that it is not my responsibility to free any TPrinter object I instantiate, or is there a clearer explanation?
When you are running this code:
MyPrinter := Printer;
MyPrinter.BeginDoc;
MyPrinter.Canvas.TextOut(10,10,'Hello World');
MyPrinter.EndDoc;
MyPrinter.Free;
Printer is deleted on function exit. So when appication is terminating, it tries to delete it again, and you get an error. More than that, if you try to run this code second time, it will raise as well. MyPrinter is just a pointer to the global TPrinter object, returned by the Printer function. You should not delete things, that you have not created.
Documentation of the Printer function:
Returns a global instance of TPrinter to manage interaction with the
printer.
Printer creates an instance of a global TPrinter object the first time
it is called. Use the Printer function to print using the TPrinter
object.
Note: The global TPrinter object is freed automatically when the
application shuts down. After a call to SetPrinter, the printer that
is returned is not automatically freed. It is the caller's
responsibility to either free the return value, or replace it using
another call to SetPrinter and to free the substitute printer that the
second SetPrinter call returns.
Related
Well, here I am again, trying to resolve an old problem.
Briefly, I get an AV when I try to free a modal form which does not have any owner, and didnt have been freed before.
frmItensVenda := TfrmItensVenda.Create(nil);
frmItensVenda.vtipo := vtipo;
frmItensVenda.vcontrole := strtoint(edit1.Text);
frmItensVenda.Ednota.Text := Edit5.Text;
frmitensvenda.lbvend.Caption := combobox3.Text;
frmitensvenda.lbnome.Caption := combobox1x.Text;
frmItensVenda.limite := limite;
if label10.caption <> '' then
frmItensVenda.vcli := strtoint(label10.caption);
frmItensVenda.ShowModal;
Frmitensvenda.Close;
frmItensVenda.Free;
If I just activate it and then close(without doing a thing), no AV happens. Putting a break-point before the 'free' command, it shows me the variable inside the form if I put the mouse cursor on it.
But if I insert one item in the grid, using the breakpoint at the same place, when I move the cursor to the same line, it doesnt show the variables anymore, but says 'inacessible value'.
And if I proceed running the code, as the next line has the 'free' command I get an AV.
What makes me believe there is some piece of code on that procedure that is doing something unexpected to the code, but I can tell you that there is no 'free' or similar command to the form in question there.
My solution(temporary) was to just comment the '.free' command, but if I run MadException I got a memory leak when I close the application (hey, anything is better than this EAccessViolation thing for me right now..)
Any sugestions?
Ok, found the answer, finally.
The problem was a global array.
It was declared
vm1 : array[1..100] of currency;
but it was assigned a value at position 0.
To my despair, there was no error when the variable was assigned, just when I tried to free the form.
So simple when you find it.. (!!!)
Well, at least I figured it out. Thanks everyone for the support!
OP : frmItensVenda is a global variable automatic created(but not initialized).
I see you do frmItensVenda := TfrmItensVenda.Create(nil);
Look for Application.CreateForm(TfrmItensVenda, frmItensVenda); in your .dpr file.
If it is there you creating a new instance !
{$R *.RES}
begin
Application.Initialize;
Application.Title := 'AServer';
...
Application.CreateForm(TfrmItensVenda, frmItensVenda);
...
Application.Run;
end.
And yes, dynamic form management is a must really (expecially in large applications.).
At trouble with large Forms, part of my solution was to, as much as possible to create dynamic.
Only when they are needed and then free them straight after.
frmItensVenda := TfrmItensVenda.Create(nil);
frmItensVenda.ShowModal;
OP : My solution(temporary) was to just comment the '.free' command,
don't do that : use instead
frmItensVenda.Release;
The "Method Release" removes the form and frees its associated memory.
Release procedure;
description
With release, you can remove the form from memory.
Release is the form to be taken until after the execution of event handlers of the form and its child components is completed.
In all event handlers release should be used instead free to avoid access violations.
The cases where you need to use Release are times when you're in the middle of an event handler (eg, OnClick), where further processing after the event will have to access the form.
In that case calling Release instead posts a WM_RELEASE message which doesn't free the event until the event handler is done and control has returned to the message pump (ProcessMessages/Application.Run).
Reading the delphi help though, it is recommended that you use the release command.
As for the Release v.s Free method. My understanding is that "Release" is specific to forms, and allows form-related even-handlers to finish before freeing resources.
Whereas "Free" is a generic method of freeing an object from memory (so should also work with forms.)
Today one of my friend ask me about below code :
var
a: Integer;
begin
ShowMessage(IntToStr(a));
end;
This is local variable and not been initialized , ok ?
Put code in OnClick event of a button component and then run code in three diffrent ways below :
Click on the button and see result , result = 1635841
Press Enter key and see result , result = 1
Press Space key and see result , reuslt = 1636097
I test code in two diffrent computer & see same result , any idea about this ?
Since the variable is not initialized, its value can be anything. Since your result is 'something', there is nothing unusual at all going on here.
procedure TForm1.Button1Click(Sender: TObject);
var
a: Integer;
begin
ShowMessage(IntToStr(Integer(a)));
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(Integer(Pointer(TButtonControl(Button1)))));
end;
on my machine this code produces same message as compiler uses ebx register for a variable, while TButtonControl.WndProc uses ebx to store pointer to Self(as EAX will be overwritten after WinAPI function calls from TbuttonControl.WndProc) which is button1 before calling the actual handler Button1Click. So alas, on Delphi 2007 message text is too predictable.
[edited]
You can see what's happening inside VCL while debugging if you turn on Use debug DCUs option in your project compiler options Compiler->Debugging->Use debug DCUs.
See this similar Stackoverflow question.
In Delphi local variables are not initialised by default. The programmer is responsible for that and should always set a value before reading it. The value of an unitialised variable depends on the content of the actual allocated memory cells used for that variable. So any value is possible here.
I am using the Skype4COM control. My program is trying to delete around 3K contacts from my contact list in Skype using a For loop, however
1) It takes a lot of time
2) it may crash, with a "MyApp has stopped working"
My guess is that somehow I need to "slow down" what I am doing.
Would I do that with Sleep();? Because I am not sure if that is also gonna "pause" the connection between Skype and my program.
To summarize: I am doing an action with a huge ammount of entries, and because of that big ammount, my program is hanging for a long time, and eventually crashes (sometimes). Is there a way to prevent that?
Skype4COM is STA by the way.
Thanks!
Move the processing into a separate thread. Your problem appears to be that Windows thinks the app has stopped responding because it's not processing it's message loop.
Calling Application.ProcessMessages is the wrong solution, because it does a lot more than you might think. You can end up with problems with reentrancy, or things happening that you don't expect.
Make sure that the thread calls CoInitialize before it creates the COM object, and calls CoUnitialize when it's done. You can find examples of using COM in a thread here; the article refers to ADO, but demonstrates the use of CoInitialize/CoUninitialize.
EDIT: After the comments, I'm adding an example of receiving a custom message in a Delphi app. The thread will need access to the UM_IDDELETED constant; you can do this by (preferably) adding it to a separate unit and using that unit in both your main form's unit and the thread's unit, or simply by defining it in both units.
// uCustomMsg.pas
const
UM_IDDELETED = WM_APP + 100;
// Form's unit
interface
uses ..., uCustomMsg;
type
TForm1=class(TForm)
// ...
private
procedure UMIDDeleted(var Msg: TMessage); message UM_IDDELETED;
//...
end;
implementation
procedure TForm1.UMIDDeleted(var Msg: TMessage);
var
DeletedID: Integer;
begin
DeletedID := Msg.WParam;
// Remove this item from the tree
end;
// Thread unit
implementation
uses
uCustomMsg;
// IDListIdx is an integer index into the list or array
// of IDs you're deleting.
//
// TheFormHandle is the main form's handle you passed in
// to the thread's constructor, along with the IDList
// array or list.
procedure TYourThread.Execute;
var
IDToDelete: Integer; // Your ID to delete
begin
while not Terminated and (IDListIdx < IdList.Count) do
begin
IDToDelete := IDList[IDListIdx];
// ... Do whatever to delete ID
PostMessage(TheFormHandle, UM_IDDELETED, IDToDelete, 0);
end;
end;
if you are using a loop to delete each contact you can place a call to Application.ProcessMessages this should fix the issue
[edit]
the call should be in the loop
I'm was trying to write a dll library in Delphi wih a function that creates an instance of a TFrame descendant and returns it. But when I imported this function in an application, every time I called it I would get an exception like "the 'xxx' control has no parent window". I'm not 100% sure, but the exception appeared in the constructor of that class when any of GUI controls was accessed.
Could you please tell me what the reason of that behaviour is? Should I just use TForm descendants instead or is there a better solution?
Thank you!
About the error
That error message is raised from the Controls.pas unit, from the TWinControl.CreateWnd method. Essentially that code is used to create the Window handle for your TWinControl descendant (TFrame, TButton, TEdit... if it can have keyboard focus it's an TWinControl descendant), and it's actually an very sensible error message: You can't have a Window without an WindowParent, and since we're talking about the VCL here, it makes a lot of sense to try and get the parent window handle from TWinControl.Parent; And that's not assigned.
That's not WHY the error message is popping up. You get to see that error message because some of the code you're using to set up the frame requires an Window handle for some operation. It could be anything, like setting the Caption of some component (that internally requires an window handle do to some calculation). I personally really hate it when that happens. When I create GUI's from code I try to delay the assignment of Parent as much as possible, in an attempt to delay the creation of the window, so I got bitten by this many times.
Specific to your DLL usage, possible fix
I'm going to put my psycho mind reader hat on. Since you need to return a FRAME from your DLL, and you can't return the actual Frame because that's an Delphi-specific object and you're not allowed to return Delphi-specific objects over DLL boundaries, my guess is you're returning an Window Handle, as all the nice API's do, using a function definition like this:
function GiveMeTheNiceFrame:HWND;
The trouble is, that routine requires the creation of the actual Window Handle, by a call to TWinControl.CreateWnd, and in turn that call requires an parent window handle to set up the call to Windows.CreateWindowEx, and the routine can't get an parent window handle, so it errors out.
Try replacing your function with something allong the lines of:
function GiveMeTheNiceFrame(OwnerWindow:HWND):HWND;
begin
Result := TMyNiceFrame.CreateParanted(OwnerWindow).Handle;
end;
... ie: use the CreateParented(AParentWindow:HWND) constructor, not the usual Create(AOwner:TComponent) and pass an owner HWND to your DLL.
There are a few important things to remember:
When using DLLs, both your DLL and your EXE each have an Application instance that are struggling for control. The Controls in your DLL will see the Application instance that belongs to the DLL; the Controls in your EXE will see the Application instance that belongs to the EXE. That struggle is not there when using packages, as then there will only be one Application instance.
Frames are Controls, but they are not Forms.
When using Controls in an application, they cannot visually exist without a parent Control (usually a Form or a container that has a parent hierarchy towards a Form).
Some Controls cannot expose their full functionality unless they exist visually and have a valid parent.
Try to reproduce your problem inside the EXE; if you cannot reproduce, it is probably the first thing in the above list.
--jeroen
Sounds like you simply need to assign the component (a form or part of a form, like a panel) that holds the frame to theframe.parent.
You cannot do GUI work before it is assigned. Frames are parts of forms for reuse, and normally need to assign some parent to them.
Move the GUI code to onshow or a procedure you call explicitely, so that the calling code can assign parent.
Or make the parent a parameter in the function.
I found this (CreateParams is called as part of CreateWnd):
procedure TCustomFrame.CreateParams(var Params: TCreateParams);
begin
inherited;
if Parent = nil then
Params.WndParent := Application.Handle;
end;
And Application.Handle = 0 so it always throws the error later in CreateWnd.
After reading this
Delphi: How to call inherited inherited ancestor on a virtual method?
I have solved it by overriding CreateParams in my frame to miss out the tCustomFrame version:
type
tCreateParamsMethod = procedure(var Params: TCreateParams) of object;
type
tMyScrollingWinControl = class(TScrollingWinControl);
procedure TDelphiFrame.CreateParams(var Params: TCreateParams);
var
Proc: tCreateParamsMethod;
begin
TMethod(Proc).Code := #TMyScrollingWinControl.CreateParams;
TMethod(Proc).Data := Self;
Proc(Params);
end;
Now it's just throwing errors when trying to set the focus on subcontrols, which I think I will fix by intercepting WM_FOCUS but we'll how it goes from here.
function CreateFrame(hwndParent: HWnd): HWnd; stdcall;
var
frame: tFrame;
begin
Result := 0;
try
frame := TDelphiFrame.CreateParented(hwndParent);
Result := frame.Handle;
except on e: Exception do
ShowMessage(e.Message);
end;
end;
You can avoid this message by assigning nil to the parent OnClose event, sometimes it works:
SomeControl.Parent := nil;//Before free your TControl
SomeControl.Free;
I think this is very cool solution. I think it is not tried before :)
I'm using a Dummy Parent (which is a Form).
function MyFrame_Create(hApplication, hwndParent:THandle; X, Y, W, H:Integer):Pointer; stdcall;
var Fr: TMyFrame;
F: TForm;
CurAppHandle: THandle;
begin
CurAppHandle:=Application.Handle;
Application.Handle:=hApplication;
//---
F:=TForm. Create(Application);//Create a dummy form
F.Position:=poDesigned;
F.Width:=0; F.Top:=0; F.Left:=-400; F.Top:=-400;//Hide Form
F.Visible:=True;
//---
Fr:=TMyFrame.Create(Application);
Fr.Parent:=F;//Set Frame's parent
//Fr.ParentWindow:=hwndParent;
Windows.SetParent(Fr.Handle, hwndParent);//Set Frame's parent window
if CurAppHandle>0 then Application.Handle:=CurAppHandle;
//---
Fr.Left:=X;
Fr.Top:=Y;
Fr.Width:=W;
Fr.Height:=H;
Result:=Fr;
end;//MyFrame_Create
procedure MyFrame_Destroy(_Fr:Pointer); stdcall;
var Fr: TMyFrame;
F: TObject;
begin
Fr:=_Fr;
F:=Fr.Parent;
Fr.Parent:=Nil;
if (F is TForm) then F.Free;
//SetParent(Fr.Handle, 0);
//Fr.ParentWindow:=0;
Fr.Free;
end;//MyFrame_Destroy
I am trying to extend a 3rd party application so that it can be invoked via command line in addition to using the windows form GUI (mixed mode is desired). It's a fairly simple program which basically loads a file and then you click a button it starts sending UDP network packets.
I need to invoke the application from another and would like to pass in an argument and need to be able to return the ExitCode to the calling app. From what i've read, in order to do so you need to add the compiler directive {APPTYPE CONSOLE}.
I did this and my application worked as I wanted it to except sending the network packets slowed down to a crawl. I found that whenever I moved my mouse around on the form. That the network transfer rate increased significantly. I suspect there is some type of Windows Message queue problem and moving mouse is causing interrupts which in turn is causing the message queue to be processed?
I have googled around and tried calling Application.ProcessMessages and PeekMessages in a Timer with a 1ms interval and that didn't help at all. I found in this user manual for some other application it says that Indy 10 is supported in both APPTYPE CONSOLE and GUI types. Quite frankly this just confuses me as I would have assumed that all network library would work in both modes... but like I said I'm not familiar with Delphi.
I am positive that the issue is isolated to a single line in my application and that is whether or not {APPTYPE CONSOLE} is included or not.
Anyone have any ideas?
Version Info:
Delphi 7 Personal (Build 4.453)
Indy 9.0.4
If you add {APPTYPE CONSOLE} to your application even though you desire mixed mode execution, then you will have to live with a console even when the application is in GUI mode. You can of course close the console, but this will cause some flicker and feels a bit hackish to me.
You should be able to do what you want without a console program. A small test program proves that the exit code can be read from a GUI program:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ExitCode := 42;
Timer1.Interval := 1000;
Timer1.Enabled := TRUE;
end;
If this is executed with the following cmd file:
#echo off
start /WAIT project1.exe
echo %ERRORLEVEL%
the program shows its main form for 1 second, closes, and the script prints 42 to the console window.
Now for capturing the output - doing this from a GUI program is actually easier than doing it from a console program, if you allow for the use of a temporary file. You need to start the program with a command line parameter anyway, so why not give it the name of a temporary file, wait for the application to finish, read in the file and delete it afterwards?
If you want an application to return an "error" code there is no need to make it a console application. You only need to set the ExitCode, e.g.
ExitCode := 10;
in a batch file
#Echo off
project1
echo %errorlevel%
Will display the application, then display 10 when.
Note: It is also possible to create a console window dynamically from the windows API using AllocConsole or to attach using AttachConsole.
I created an object wrapper for this once, but no longer have the code available. From memory it didn't support redirection (because I didn't need it).
If I understand you correctly, then you want your app to have two modes:
If no argument is passed, run in GUI mode
Run in non-GUI mode otherwise
The easiest is if you can centralize your logic so it can be called from one method (CoreLogic in my example).
The below app then should work fine.
Two tricks:
Application.ShowMainForm := False; that will not make the MainForm show at all.
ExitCode := 327; which will set your return code (like mghie and Gerry already mentioned).
A few notes:
because the CoreLogic does not process any windows messages, anything in your application that depends on Windows messages being processed will stall.
if you need windows message processing, then just all Application.ProcessMessages() inside your CoreLogic
if you need your form to be visible, then you change the logic inside your MainForm to test for the commandline parameters, and exit when it's work as been done (by calling Application.Terminate()). The best place to put that logic in is the event method for the MainForm.OnShow event.
Hope this helps :-)
program VCLAppThatDoesNotShowMainForm;
uses
Forms,
MainFormUnit in 'MainFormUnit.pas' {MainForm},
Windows;
{$R *.res}
procedure CoreLogic;
begin
Sleep(1000);
ExitCode := 327;
end;
procedure TestParams;
begin
if ParamCount > 0 then
begin
MessageBox(0, CmdLine, PChar(Application.Title), MB_ICONINFORMATION or MB_OK);
CoreLogic();
Application.ShowMainForm := False;
end;
end;
begin
Application.Initialize();
Application.MainFormOnTaskbar := True;
TestParams();
Application.CreateForm(TMainForm, MainForm);
Application.Run();
end.
A timer with 1ms will only fire about every 40 ms (due to Windows limitations), so it won't help. I have seen effects like you describe with mixed console and GUI apps, another is that they don't minimize properly.
Instead of enabling the console in the project, you could probably use the CreateConsole API call (Not sure whether the name is correct) to create one after the programm was started. I have seen no adverse effects in the one (!) program I have done this.
But this is only necessary if you want to write to the console. If you only want to process command line parameters and return an exit code, you do not need a console. Just evaluate the ParamCount/ParamStr functions for the parameters and set ExitCode for the return value.
If some threads in your console application call Synchronize (and I guess the Indy stuff is actually doing that), you have to make some preparations:
Assign a method to the WakeMainThread variable. This method must have the signature of TNotifyEvent.
Inside this method call CheckSynchronize.
For additional information see the Delphi help for these two items.