Delphi ignores CS_OWNDC flag when creating TFrame component, wglMakeCurrent subsequently fails - delphi

I'm trying to put together some components for using OpenGL in RAD Studio 2009. I want to have multiple Rendering Contexts.
My idea is to have something like a "main OpenGL component", called GLMaster. It's a TFrame descendant so it should provide me with a DC. Also there's a component for a GLMonitor, acting as camera.
I create an OpenGL RC in the overriden GLMaster.Loaded inside an "if not (csDesigning in ComponentState) then"-clause. The DC for this I do not keep, it's stated in the RAD Studio help that you shouldn't:
"TWinControl.Handle
Do not refer to the Handle property during component creation or streaming. The underlying window does not exist until the first time the Handle property is referenced. When this occurs, the HandleNeeded method is called automatically."
I handle this by function pointers in components using GLMaster pointing to GLMaster.GetCurrentDC (returns a HDC).
During destruction GLMonitor wants to clean up some render-to textures and other OpenGL resources. When retrieving the DC for a call to wglMakeActive the function pointer is followed and execution jumps to GLMaster.GetCurrentDC. Pointer(Self) tells me that it's the same GLMaster that we created the "master RC" in during the streaming of the components. The property [GLMaster.]Handle is now invalid! If I however free the GLMonitor in the app form's OnClose (using simply GLMonitor_1.Free;) the Handle inside GLMaster.GetCurrentDC is valid end everything works.
I managed to see that the handle used in GLMaster.Loaded is not the same (!?!?) as after the initialization is done and the app form is showed to the user. When I found this I googled my problem and added an overriden CreateParams to add CS_OWNDC to the GLMaster component. A breakpoint there tells me that it's being executed.
Why does the VCL/Delphi silently provides the newly created components with other Handles, and thereby indirectly other DCs, even if I provide the OWNDC flag? Is there someway to get an "ALL components are now loaded and their properties are read from the .dfm file and"-message so I can run GLMaster.InitEverything inside the component?
Right now I'm thinking the fastest way onward is to put this InitEverything in the main form's OnShow handler. And then a call to GLMatser.CleanAll in the main form's OnClose. But I don't want any code in the form, that's the reason I created the components in the first place!
P.S.
Not that I think it matters for this particular problem but I use OpenGL 3.2 Core Profile.

I'm answering the "Why does the VCL/Delphi silently provides the newly created components with other Handles".
Why an control's Window Handle might change at run-time
Your TFrame (an TWinControl descendant) is placed on an other TWinControl, let's say a TForm. The parent container provides property wrappers around many settings, allowing us to easily make changes; Some of those changes can't be implemented without re-creating the windhow handle. You can get a long list of properties that cause this to happen by doing a search for RecreateWnd in Forms.pas, Controls.pas etc.
Examples of properties that call RecreateWnd to implement a change at run-time:
TScollBox.BorderStyle
TForm.BorderIcons (for MDI childs)
When your TFrame's parent needs to change Window Handle, your TFrame is going to have to change Window Handle as well. Your Delphi Control, no matter the descendense, needs to be handle this, and other controls have it worst: Controls that are implemented as wrappers around Windows controls need to save state and reload state (TEdit, TComboBox, ...)
What I think is going on
While streaming (loading your form) some control does something that needs a Window Handle before loading finishes. It's very likely your own code! After everything finishes loading the Form might need to re-create it's Handle, and this in turn causes your Frame's handle to be changed.
What you might want to override
Given the way the VCL works, you need to be prepared for your Window Handle to change. You should do a search in Controls.pas for the words Handle, HDC. If your control is so intimately linked to it's Window Handle and HDC, it's in your best interest to read up on this stuff.
Take a look at those routintes. Maybe you can sopot a better place to hook:
CreateHandle
CreateWnd
DestroyHandle
DestroyWnd
GetDeviceContext

If the title is the question, then no, Delphi does not ignore CS_OWNDC when creating a frame. Just tested with the below procedure with a frame on a form. When the flag is not specified, the line is drawn on itself again and again, resulting with a line from (0,0) to (10,10). When the DC is owned, the line extends from (0,0) to (50,50), no need to tell but the retrieved DC is same at all times.
procedure TFrame2.WmPaint(var Msg: TWMPaint);
var
i: Integer;
DC: HDC;
Pt: TPoint;
begin
inherited;
for i := 1 to 5 do begin
DC := GetDC(Handle);
GetCurrentPositionEx(DC, #Pt);
LineTo(DC, Pt.X + 10, Pt.Y + 10);
ReleaseDC(Handle, DC);
end;
end;

if your question is this:
ALL components are now loaded and their properties are read from the .dfm file and
The answer is:
Yes, just override the Loaded method of your component. It is called when all the references are valid (after dfm reading is finished).

Related

Can ShortCut-Key, that has been processed by Delphi TAction, be allowed to propagate further to other components?

Core problem: I don't like the way how TcxGrid delete (that is initiated by the default Ctrl+Del shortcut key for the TcxGrid or by the Delete button of the cx-navigator) is working: it is simple and direct call to the delete of the underlying dataset, there is no TcxGrid(/DataController).OnDelete(Sender: TObject, var AHandled: Boolean) event for the cxGrid or cxGridDataView. And from https://www.devexpress.com/Support/Center/Question/Details/Q308755/tcxgrid-deleting-record I understand that I can only configure the confirmation message, but I have no control over the Delete itself, i.e., I can not implement custom Delete and therefore I don't even try to ask for the straight solution of this problem. My question is about workaround.
That is why I am refusing to use cx-navigator and I opt to introduce TAction that handles Ctrl+Del shortcut key for the entire form. I have multiple grids on the form each with its own problematic Delete procedure that should be handled in non-standard way by the custom procedure.
That is why my DeleteAction determines the ActiveControl (TcxGridSite, TcxGrid) initially and then calls relevant Delete procedure afterwards. All that is fine. But some other components (e. g. TcxDBTextEdit) have default Ctrl+Del handling as well and that handling is very, very good. But if shortcut key Ctrl+Del is handled by action (and it is marked as handled even in the case when no active grid can not be found and therefor even in the case when ActionExecute has not done anything useful) then further propagation is stopped. That can be observed empirically and that can be seen theoretically from http://edn.embarcadero.com/article/38447 .
But maybe still there is some workaround how the shortcut key that has been processed by the ActionExecute can still be propagated further to the default components if they are Active controls.
I know that TAction is for (form-wide global) menu functionality but TcxGrid is not extensible enough and that is why should try to stretch the Delphi TAction design as well.
Probably there would be various ways to achieve the processing you require, there are quite a few places you can intervene during key processing, as you've already read from Peter Below's article linked in your question.
A place to intervene that I can think of which would contain all the pieces together is the form's IsShortCut method. One probable disadvantage of such a method could be that you'd have to implement it on all the forms that you want their key handling behavior changed.
Normally key processing continues only if it's not handled by shortcuts. Below solution reports the shortcut to be not processed for that reason, but then has to cause the execution of the action manually. I tested the code with a regular edit as can be seen, but I guess it shouldn't make any difference.
function TForm1.IsShortCut(var Message: TWMKey): Boolean;
begin
if (ActiveControl is TEdit) and (Message.CharCode = VK_DELETE) and
(KeyDataToShiftState(Message.KeyData) = [ssCtrl]) then begin
Result := False; // if you don't require the action to be executed, just exist here
ActionList1.IsShortCut(Message); // executes the action, ignore result
// returning false will result in main form's shortcut handler to be called
// below is only required if this is the main form
if Application.MainForm = Self then
Message.CharCode := 0;
end else
Result := inherited IsShortCut(Message);
end;

Drag&Drop using Winapi for Third-Party Control

I need to integrate drag&drop of our existing application with a new component.
Our application uses Winapi.ActiveX do implement this. The new component comes with its own drag&drop events which aren't Winapi-compatible, though.
The main problem for now is dragging things into the new control. Dragging out of it should be achievable, if all else fails by caching the object under the cursor on mouse down.
As I understand it you can't override drag&drop registration, that is if I call RegisterDragDrop on the component before they do it somewhere in their code, I'll be the one holding the registration.
The observed behavior, however, is that calling RegisterDragDrop reports success, but dragging something over the control doesn't lead to a DragEnter event on the IDropTarget. Their drag&drop event fires just fine, even after RegisterDragDrop, but as I said, the returned data object only holds valid data for a select few types of draggable objects.
I also tried registering the panel that contains this component for drag&drop, but that didn't work either.
What would you suggest?
Update:
I realized, I was trying to register the wrong handle. When I try to register the right handle, I actually get a drag&drop already registered error.
I guess I didn't formulate it too clearly, but the question is, how do you implement drag&drop for a closed-source component that already has it's own implementation, which doesn't provide enough flexibility for your taste?
What I can think of:
Bug the developer of the component until they change the component.
Do some API hooking to go in between the component's implementation and the OS.
Hopefully something else that is simple.
Update:
Thanks so far, now I can drag things into the control the way I want it.
The next step is dragging things out. Here the problem is that other drop targets in the application don't understand the control's IDataObject. For a different control we already have code that uses IDataObject::SetData to append data in a format that all the application's drop targets can understand.
The difference with the new control, however is that when it initiates a drag&drop operation, it doesn't use a IDataObject that has IDataObject::SetData implemented (returns E_NOTIMPL); Is there a way to replace the IDataObject with a different one, while the d&d operation is ongoing?
Try to reset already registered IDropTarget of your ActiveX control after control creation with the following code:
const
sOleDropTargetInterface = 'OleDropTargetInterface';
procedure ResetDropTarget(AWnd: HWND);
var
Unknown: IUnknown;
begin
Unknown := IUnknown(GetProp(AWnd, PChar(GlobalFindAtom(sOleDropTargetInterface))));
if Assigned(Unknown) then
try
if not SetProp(AWnd, PChar(GlobalFindAtom(sOleDropTargetInterface)), 0) then
RaiseLastOSError;
Unknown._Release;
finally
Unknown := nil;
end;
end;
Update
kobik points the right solution: using of RevokeDragDrop function.

Why TObject.Free is called before the first line of code of my application is executed?

We are trying to figure out if we have memory leaks in our software. So, I have been using various tools and programs to help me find possible memory leaks. One of the software I used was AQTime. As it came with Delphi XE, it was only a demo. So, I was not really able to get any useful information from it. Then, I decided to use free software, MemProof. So far, it has shown me many issues with our software that requires attention. One of which is an error.
As soon as I start my program through MemProof, it lists 2 errors, which is attempting to destroy non-existent object from the unit file, system.pas. So, when I actually put a break point within TObject.Free procedure, it breaks even before my program started all the way. Stepping through the procedure Free in system.pas, I found out that TIconimage is trying to destroy or free itself. In other word, free procedure is not invoked from within my program prior to actually starting up.
Here is the actual Free procedure:
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
After that observation, I removed the breakpoint and let the program run all the way. My programs main window popped up ready for user input. However, I also found out that TObject.Free procedure is invoked non-stop if any part of my program's WINDOW is displayed on the screen. I don't understand that at all. Why is that? Can anyone explain? How is TForm is related to TObject.Free in any shape or form as the procedure is constantly invoked when the TForm is displayed on the screen?
Thanks in advance.
Regarding why TObject.Free executes a lot, every single time an object is destroyed, any object, that method will be called. All classes derive from TObject, it's the common ancestor, so almost any action in a Delphi program involves large numbers of object create/destroy pairs and consequently will hit TObject.Free.
Regarding detection of memory leaks, you have all you need built in to Delphi to solve this. The FastMM memory manager can be run in "report memory leaks" mode and it will give you loads of diagnostics of any memory that you leak.
Consider the following trivial program:
program Leaker;
begin
ReportMemoryLeaksOnShutdown := True;
TObject.Create;
end.
This results in the following output:
You just need to set ReportMemoryLeaksOnShutdown to True somewhere in your app (the start of the .dpr file is as good a place as any).
If you wish to receive more information in the report then you can download the full version of FastMM and configure it to your heart's content.
Then you get output like this:
A memory block has been leaked. The size is: 84
This block was allocated by thread 0x1304, and the stack trace (return addresses) at the time was:
40455E [System][System.#GetMem]
405A2F [System][System.TObject.NewInstance]
40602E [System][System.#ClassCreate]
4474C2 [Classes][Classes.TStringList.Create]
C275A3 [Main.pas][Main][Main.TMainForm.CreateAuxiliaryForms][997]
C84C8A [OrcaFlex.dpr][OrcaFlex][OrcaFlex.OrcaFlex][351]
75E633CA [BaseThreadInitThunk]
77519ED2 [Unknown function at RtlInitializeExceptionChain]
77519EA5 [Unknown function at RtlInitializeExceptionChain]
The block is currently used for an object of class: TStringList
It's truly wonderful. It tells me that the leaking memory was allocated in Main.pas line 997, and that's precisely where I put my intentional leak!
As you know, TApplication has an Icon property which you can f.i. assign in the application settings in project options. This property is reflected with an FIcon field of TApplication which is created in the constructor of the Application object. TIcon has a TIconImage field representing the actual image which gets created in its constructor. When the Application object loads and assigns the icon from the project resource file, this initial 'TIconImage' has to be freed in order to prevent a leak. All this happens even before Application.Initialize is called in the project source, because the Application object is constructed from the initialization section of 'controls.pas'.
Lots of things are happening when an application is launching or running. When launching, the streaming mechanism creates objects (resource streams, readers, class finders, component lists ..) and then frees them. Even a blank VCL form (with no controls on it) when running, creates a list each time it gets activated to find a control to put the focus on, and then frees this list. With complex GUI applications, a variety of graphics objects can be created and freed even if you hover the mouse on something. Or the alignment/arrangement code can create/free objects even if you press your mouse on to something.
To debug leaks you can take the course outlined by David's answer, or when using a 3rd party product concentrate on what it says leaked, not on every object which gets created/freed. :)
TObject.Free will be called whenever ANY instance of a class in Delphi is Free'd.
This includes a whole host of objects that are created and destroyed simply as part of the normal execution of a Delphi program, including in response to events processed automatically by a TForm object in response to the messages generated by the system simply to maintain the window object itself in existence in the Windows own Window Manager.
For example, consider this snipped fragment of code from the TCustomForm WndProc:
WM_MEASUREITEM:
begin
:
Canvas := TControlCanvas.Create;
with Canvas do
try
:
finally
Canvas.Free;
end;
:
end;
The key here being that in response to a WM_MEASUREITEM message, a custom form (and therefore a standard TForm derived class, since this ultimately derives from TCustomForm) creates a temporary TControlCanvas, which it then Free's when it is finished with it.
This may not necessarily be the source of the TObject.Free calls that you are seeing in your particular form's case, it is just an example, but shows how a TForm merely existing can result in other objects being brought into existence and destroyed in response to automatic, system generated messages.

Creating a compound control (parent of other controls) at runtime

I has a piece of code where I override TStringGrid's inplace editor and the hint window. For this, I created my own string grid based on TStringGrid and use a TEdit for inplace editor and a TPanel for tool tips. In TMyStringGrid.Create constructor I initialize them like this:
Constructor TMyStringGrid.Create();
Begin
inherited Create(AOwner);
MyEditor:= TEdit.Create(Self);
MyEditor.Parent := Self;
End;
In this case the owner (the main form) is freeing the controls. I used this for years and it worked.
The thing is that other people argue that the programmer should use NIL instead the Self when instantiation the child controls and later to manually free them in the Destroy destructor. It seems that the second alternative has gigantic advantage over the first one, especially when you dynamically create lots of child controls (not my case). Other problem with my code, they say, is that the child controls may be freed after an Application.ProcessMessages call while the application may still want to use them.
So, I should let my code unchanged or should I manually create and free the child controls?
There is a any Borland example of compound controls?
Delphi 7, Win XP
Reference: http://delphi.about.com/od/kbcurt/ss/dynamiccreateno.htm
Yes you can use your code without changing it.
There is a any Borland example of compound controls?
Best example is to check the implementation of TLabeledEdit.
It was creating the label in constructor
if Assigned(FEditLabel) then exit;
FEditLabel := TBoundLabel.Create(Self);
FEditLabel.FreeNotification(Self);
FEditLabel.FocusControl := Self;
There's no good reason to pass nil instead of Self in this situation. That AOwner parameter is there specifically for that reason. Take advantage of it.
There's a reason to pass nil when creating a control and manually destroy it, but for a completely different situation: if you're creating a control (typically a form) inside a function. This is a pretty common pattern, for example:
MyDialog := TMyDialog.Create(nil);
try
result := MyDialog.ShowModal;
finally
MyDialog.Free;
end;
There, you want to free it immediately instead of waiting around until the current form gets destroyed, which could be much later. But when it comes to sub-components, you usually want them to be destroyed at the same time as the parent component, not later, so pass Self to AOwner and let the VCL take care of it for you.
Considering that constructor, the grid instance is owned by Aowner (which is tipically a TForm or a TFrame). The inplace editor is owned and parented by the grid instance. I don't see how ProcessMessages would cause destruction of the child objects, since they will be destroyed in the destroying loop of TMyStringGrid. That's nothing I can see of wrong on that implementation - and I use that same design for the components I create. Ownership is there on VCL to easy our lifes when managing objects' lifetime. And is not the case where nil is recomended as owner, which is shown in Mason' answer.
In the pattern shown by Mason , the reason for the NIL is that, without a owner, the object destruction will not enter in a Notification loop. If you create a lot of components which destruction you handle manually, you want to make sure the owner is NIL, otherwise a lot of code get executed (without need) in each component construction/destruction.
Many moons ago, there was an excelent white paper on the web archive of (now defunct) eagle-software.com

Retrieving Delphi Window Handles

I am trying to get the window handles to a Delphi application from an external application. I can see that there are a few windows created (TApplication, TFrmMain and a few others), and I know that TApplication is the "controller", but never visible. However, can I read what the value for the real window is? I know that it is TFrmMain (for this specific application), but is it possible to actually figure this out somehow? Is the information stored in the window properties,or somewhere else? Thanks!
No, there is no documented way to discover which of the windows represents Application.MainForm from outside the application. In newer versions of Delphi, the main form's window handle isn't necessarily Application.MainForm.Handle anyway; applications can handle the OnGetMainFormHandle event to return whatever they want — that's used for choosing the parent window for modal dialogs.
You can guess by looking for windows with "main" in their class names, but even if you find one, there's no guarantee that there's only one instance of it. Applications can have multiple top-level windows, in which case it doesn't make much sense to designate any one of them as the "main" one.
The class name of any Delphi form is also the registered window classname of the underlying "Windows window". So you should be able to use the FindWindow() Windows API call to get the window handle of TFrmMain a little something like:
hWnd := FindWindow('TFrmMain', NIL);
If there are (potentially) multiple instances of a given form class name then you may be able to distinguish between them by using the 2nd parameter (Window Name, i.e. "caption" or title). If that still isn't enough then you may need to get a little bit more sophisticated and look at using the EnumWindows() function and checking the properties of the windows to find the one of interest.
To test the classname of an arbirary window handle (e.g. in your callback function that you use with EnumWindows()), use GetClassName(), e.g:
function GetWindowClassName(const aHWND: HWND): String;
var
buf: array[0..255] of Char; // Tip: Use a more appropriately sized array
begin
GetClassName(SomeHWND, #buf, Length(buf));
result := buf;
end;
...
if SameText(GetWindowClassName(hwnd), 'TFrmMain') then
...
etc
Without specific details of your particular implementation challenge it's difficult to say which is most likely to work best for you, but hopefully that should be enough pointers to get you heading down the right track.
I usually use WinDowse to help me get started, but then you have to use the API functions as described by Deltics.

Resources