Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2009-12

LCL and DLLs, initializing the widget set safely?

18 messages · 10 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Marco Alvarado

I'm trying to create a dll with an LCL form, and found that is not working. After looking around I found several posts that say LCL is not working in DLL, like:
http:/msg16490.html

I traced down the problem, and it seems the widget set is not being initialized in the DLL. While debbuging I got this:

---------------------------------------------
TWinControl.GetHandle Creating handle on the fly: :TForm1 WARNING:[TWinControl.HandleNeeded] creating handle for TForm1 while destroying handles!
[TWinControl.CreateWnd] START :TForm1
TWinControl.CreateWnd (V)TWin32WSCustomForm :TForm1 FinishCreateWindow :: CreateWindowEx pClassName=Window, WindowTitle= Failed to create win32 control, error: 1407 : No se ha encontrado la clase de ventana.
[FORMS.PP] ExceptionOccurred
Sender=Exception
Exception=Failed to create win32 control, error: 1407 : No se ha encontrado la clase de ventana.
Stack trace:
$1011936D FINISHCREATEWINDOW, line 226 of win32wscontrols.pp TApplication.HandleException Failed to create win32 control, error: 1407 : No se ha encontrado la clase de ventana.
Stack trace:
$1011936D FINISHCREATEWINDOW, line 226 of win32wscontrols.pp ---------------------------------------------

According to that, there is an ,
function FinishCreateWindow(), because CreateWindow() is being called without registering the window class. And that function is called when trying to resolve the window's handle.

I found that applications register the window class with calls to TApplication.Initialize() -> WidgetSet.AppInit() -> WinRegister()

Without knowledge of LCL's internals, I tried to call those methods and functions from the DLL's initialization section, but it didn't work. The exception was gone, but the DLL still failed and broke the debug information. Could someone with knowledge of the LCL tell if it's possible to initialize the widget set in the DLL, and how should that be done to be safely?

Regards!
-Marco

Reply by email

Christian Budde
to Marco Alvarado

Hi Marco and all others,

Show quoted text 7 lines
Without knowledge of LCL's internals, I tried to call those methods and functions from the DLL's initialization section, but it didn't work. The exception was gone, but the DLL still failed and broke the debug information. Could someone with knowledge of the LCL tell if it's possible to initialize the widget set in the DLL, and how should that be done to be safely?

I encountered the same problems. Going a bit further I even add a bounty for solving these DLL problems (in fact a bit more than just this). I even made several bug reports but everything in this regards is always marked as post 1.2, which is a reason why I probably won't touch lazarus until it finally reaches 1.0 or this feature has been integrated.

Or maybe we can join forces to solve this problem some day. I already had a look into the topic and I guess to solve it permanently (not by making some work around), several changes at LCL level are necessary. Maybe some others with more knowledge about the LCL can join the team as well.

Kind regards,

Christian

Reply by email

Marco van de Voort
to Christian Budde
Christian Budde wrote 12 lines
> Without knowledge of LCL's internals, I tried to call those methods > and functions from the DLL's initialization section, but it didn't > work. The exception was gone, but the DLL still failed and broke the > debug information. Could someone with knowledge of the LCL tell if > it's possible to initialize the widget set in the DLL, and how should > that be done to be safely? I encountered the same problems. Going a bit further I even add a bounty for solving these DLL problems (in fact a bit more than just this). I even made several bug reports but everything in this regards is always marked as post 1.2, which is a reason why I probably won't touch lazarus until it finally reaches 1.0 or this feature has been integrated.

It will more likely reach 1.0 before this is implemented.

Show quoted text 5 lines
Or maybe we can join forces to solve this problem some day. I already had a look into the topic and I guess to solve it permanently (not by making some work around), several changes at LCL level are necessary. Maybe some others with more knowledge about the LCL can join the team as well.

Start reading up on packages:

http://wiki.freepascal.org/packages

(If you don't want to go that way, start specifying what should work, and how you would do it, and how you solve the problems the package article above names in an userfriendly way)

Reply by email

Marco Alvarado
to Marco van de Voort

I'm really happy to inform that I solved the bug, and now I have a working version of LCL 0.9.28.2 in DLL. It seems the fix might not be difficult for someone who understands the LCL's internals. I'll explain what I did hoping someone could work on it.

Somehow an instance of TApplication is being created in DLL, but it's not being initialized like in .EXE. One just needs to initialize the widgetset by calling Application.Initialize(). That solves the exception when creating forms.

The other bug I got was related to TWinControl.CreateParented(). Replaced it by TForm.Create(nil) and Windows.SetParent(), and now everything is fine.

Also I'm creating the components dinamically after initializing the Application. I'll test designing forms later.

Best regards!
-Marco

Reply by email

Phil Hess
to Marco Alvarado

What widgetset are you testing? Calling Application.Initialize never worked before on Windows.

On Carbon, an LCL form in a .dylib was working as long as a year ago, but Windows would freeze when the DLL form is displayed.

Thanks.

-Phil

----- "Marco Alvarado" wrote 25 lines
I'm really happy to inform that I solved the bug, and now I have a working version of LCL 0.9.28.2 in DLL. It seems the fix might not be difficult for someone who understands the LCL's internals. I'll explain what I did hoping someone could work on it. Somehow an instance of TApplication is being created in DLL, but it's not being initialized like in .EXE. One just needs to initialize the widgetset by calling Application.Initialize(). That solves the exception when creating forms. The other bug I got was related to TWinControl.CreateParented(). Replaced it by TForm.Create(nil) and Windows.SetParent(), and now everything is fine. Also I'm creating the components dinamically after initializing the Application. I'll test designing forms later. Best regards! -Marco -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Massimo Fazzolari
to Phil Hess

It sound great! I really need to use LCL with DLL!

2009/12/9 Phil Hess

Show quoted text 44 lines
What widgetset are you testing? Calling Application.Initialize never worked before on Windows. On Carbon, an LCL form in a .dylib was working as long as a year ago, but Windows would freeze when the DLL form is displayed. Thanks. -Phil ----- "Marco Alvarado" wrote: > I'm really happy to inform that I solved the bug, and now I have a > working version of LCL 0.9.28.2 in DLL. It seems the fix might not be > difficult for someone who understands the LCL's internals. I'll > explain what I did hoping someone could work on it. > > Somehow an instance of TApplication is being created in DLL, but it's > not being initialized like in .EXE. One just needs to initialize the > widgetset by calling Application.Initialize(). That solves the > exception when creating forms. > > The other bug I got was related to TWinControl.CreateParented(). > Replaced it by TForm.Create(nil) and Windows.SetParent(), and now > everything is fine. > > Also I'm creating the components dinamically after initializing the > Application. I'll test designing forms later. > > Best regards! > -Marco > > -- > _______________________________________________ > Lazarus mailing list > > http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20091209/1ecb2d01/attachment.html>

Reply by email

Gerard N/A
to Marco Alvarado: I'm really happy to inform that I solved the bug, and now I have a working version of LCL…
Marco Alvarado wrote 5 lines
I'm really happy to inform that I solved the bug, and now I have a working version of LCL 0.9.28.2 in DLL. It seems the fix might not be difficult for someone who understands the LCL's internals. I'll explain what I did hoping someone could work on it.

Am I wrong or this cannot be really fixed unless a Delphi-like package system is implemented?

Regards,

Gerard.

Reply by email

Marco van de Voort
to Gerard N/A
Gerard N/A wrote 6 lines
> working version of LCL 0.9.28.2 in DLL. It seems the fix might not be > difficult for someone who understands the LCL's internals. I'll > explain what I did hoping someone could work on it. Am I wrong or this cannot be really fixed unless a Delphi-like package system is implemented?

It depends on the comfort level desired. Both in working language constructs, as in building.

Still, a detailed way how they got as far as they did (and what is working and what is not) would help.

Specially, the link.res files when linking both the program as the dll would be great.

One can use -s to stop at the linking stage and backup the link.res on systems using the GNU Linker.

Reply by email

Mattias Gaertner
to Gerard N/A: Am I wrong or this cannot be really fixed unless a Delphi-like package system is…
Show quoted text 9 lines
On Tue, Dec 8, 2009 at 11:39 PM, Marco Alvarado wrote: > I'm really happy to inform that I solved the bug, and now I have a > working version of LCL 0.9.28.2 in DLL. It seems the fix might not be > difficult for someone who understands the LCL's internals. I'll > explain what I did hoping someone could work on it. > Am I wrong or this cannot be really fixed unless a Delphi-like package system is implemented?

Delphi like packages makes many things easier like calling the initialization/finalization sections in the right order and only once. You can emulate this.
The most important thing is sharing the VMTs and global variables. This can not be emulated.
The shared VMTs and global variables are needed, when you want to use a LCL form of a DLL in a LCL application.

I didn't try, but it should be possible to put an LCL application into a dynamic lib on all platforms and accessing it only via pointers. And with various hacks it is possible to share more.

The main problem is, that the guys knowing the compiler and linker issues, thinks that the disadvantages of packages outweighs the advantages, so it is not worth implementing.

Mattias

Reply by email

Marco Alvarado
to Mattias Gaertner

I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs, one was solved by placing
Application.Initialize() right before dynamically creating the form (I'm not using lfm), and another related to CreateParented(). With those bugs solved, now I have a perfectly running Netscape plugin created with Lazarus and LCL 0.9.28. I tested the DLL on Firefox 3.5. The only files I kept from Delphi are the resource files, since those bring the MIME-type and other info the browser requires in the DLL, and Lazarus doesn't support custom version info fields at the moment (hope in the future). The DLL's size is 1483KB.

I think that LCL just misses the application initialization in DLL... at which moment it must be called is something I don't know. Maybe placing an initialization flag and calling it once before creating components might be a workaround.

Lazarus rules!!

-Marco

2009/12/9 Mattias Gaertner :

Show quoted text 28 lines
On Wed, 9 Dec 2009 09:33:32 +0100 "Gerard N/A" wrote: > On Tue, Dec 8, 2009 at 11:39 PM, Marco Alvarado wrote: > > I'm really happy to inform that I solved the bug, and now I have a > > working version of LCL 0.9.28.2 in DLL. It seems the fix might not be > > difficult for someone who understands the LCL's internals. I'll > > explain what I did hoping someone could work on it. > > > > Am I wrong or this cannot be really fixed unless a Delphi-like package > system is implemented? Delphi like packages makes many things easier like calling the initialization/finalization sections in the right order and only once. You can emulate this. The most important thing is sharing the VMTs and global variables. This can not be emulated. The shared VMTs and global variables are needed, when you want to use a LCL form of a DLL in a LCL application. I didn't try, but it should be possible to put an LCL application into a dynamic lib on all platforms and accessing it only via pointers. And with various hacks it is possible to share more. The main problem is, that the guys knowing the compiler and linker issues, thinks that the disadvantages of packages outweighs the advantages, so it is not worth implementing.

Reply by email

zeljko
to Marco Alvarado
Marco Alvarado wrote 10 lines
I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs, one was solved by placing Application.Initialize() right before dynamically creating the form (I'm not using lfm), and another related to CreateParented(). With those bugs solved, now I have a perfectly running Netscape plugin created with Lazarus and LCL 0.9.28. I tested the DLL on Firefox 3.5. The only files I kept from Delphi are the resource files, since those bring the MIME-type and other info the browser requires in the DLL, and Lazarus doesn't support custom version info fields at the moment (hope in the future). The DLL's size is 1483KB.

Is it possible to see that part of code ?

Reply by email

Marco Alvarado
to Marco Alvarado: I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs,…

Ok, I made one last test to load designed forms and works too. Here is the whole experiment for you guys to check (660KB):

http://gamelix.com/demos/lazarus/nplugin/nplugin.7z

The original Delphi 7 component by Andrei N. Kashin is there, my fixes are in directories prueba/ and prueba_lfm/

To test in Firefox, compile on Laz 0.9.28 or 0.9.29, and copy the .DLL into the browser's plugin folder (i.e. C:\Program Files\Mozilla Firefox\plugins\ ), then open the file DEMOFIG.HTM contained in the folders. You can also check if the plugin is installed in Firefox 3.5

Tools > Add-ons > Plugins > "My Generic Sample Plugin".

I tested on two PCs without problems. Now I tested on a virtualized Windows and got a crash, but the DLL loading is working right, might be related to something else.

Hope this helps. Regards!
-Marco

Reply by email

Mattias Gärtner
to Marco Alvarado: I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs,…

Zitat von Marco Alvarado :

Show quoted text 10 lines
I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs, one was solved by placing Application.Initialize() right before dynamically creating the form (I'm not using lfm), and another related to CreateParented(). With those bugs solved, now I have a perfectly running Netscape plugin created with Lazarus and LCL 0.9.28. I tested the DLL on Firefox 3.5. The only files I kept from Delphi are the resource files, since those bring the MIME-type and other info the browser requires in the DLL, and Lazarus doesn't support custom version info fields at the moment (hope in the future). The DLL's size is 1483KB.

What about Application.Run?

Show quoted text 6 lines
I think that LCL just misses the application initialization in DLL... at which moment it must be called is something I don't know. Maybe placing an initialization flag and calling it once before creating components might be a workaround. Lazarus rules!!

:)

Maybe this can be added to one of those pages:
http://wiki.lazarus.freepascal.org/packages
http://wiki.lazarus.freepascal.org/shared_library
http://wiki.lazarus.freepascal.org/Lazarus/FPC_Libraries

Mattias

Reply by email

Marco van de Voort
to Marco Alvarado: I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs,…
Marco Alvarado wrote 6 lines
I converted a demo of a Netscape plug-in from Delphi to Lazarus. I just found two bugs, one was solved by placing Application.Initialize() right before dynamically creating the form (I'm not using lfm), and another related to CreateParented(). With those bugs solved, now I have a perfectly running Netscape plugin created with Lazarus and LCL 0.9.28.

Netscape is not created in lazarus, and doesn't communicate over plugin interface using pascal types.

Which is exactly the problem packages try to solve. Please do not confuse the situation more than necessary.

Reply by email

Christian Budde
to Marco van de Voort

Hi Marco,

Show quoted text 5 lines
Netscape is not created in lazarus, and doesn't communicate over plugin interface using pascal types. Which is exactly the problem packages try to solve. Please do not confuse the situation more than necessary.

I don't think he is confusing the situation more than necessary, it was the original topic to use the LCL in DLLs and not to change the package system in Lazarus. Personally I'm fine with the way it is right now (compiling packages rather than dynamically linking). My only problem was that I could not build plugins (not Netscape plugins, but the same flavour I guess) with a GUI that uses the LCL and this didn't work so far.

I'll now have a closer look at what he did to make it working maybe it will solve my problem as well.

One question to the OP though, does the debugger work again? I was also able - with some modifications (probably the same as you did) - to get it working, but the debugger was not working correctly. Also I had some issues with the mouse and keyboard input, which is why I thought to solve this from a low level point of view.

Kind regards,

Christian

Reply by email

Marco van de Voort
to Mattias Gaertner: On Wed, 9 Dec 2009 09:33:32 +0100 Delphi like packages makes many things easier like…
Mattias Gaertner wrote 13 lines
> On Tue, Dec 8, 2009 at 11:39 PM, Marco Alvarado wrote: > > I'm really happy to inform that I solved the bug, and now I have a > > working version of LCL 0.9.28.2 in DLL. It seems the fix might not be > > difficult for someone who understands the LCL's internals. I'll > > explain what I did hoping someone could work on it. > > > > Am I wrong or this cannot be really fixed unless a Delphi-like package > system is implemented? Delphi like packages makes many things easier like calling the initialization/finalization sections in the right order and only once. You can emulate this.

(one can emulate pascal in asm :-)

The most important thing is sharing the VMTs and global variables. This can not be emulated. The shared VMTs and global variables are needed, when you want to use a LCL form of a DLL in a LCL application.

Well, all kinds of mix forms are possible and parts can be emulated. They either require a lot of handcoding, manualy project management, flattening etc.

If you take the basic premise that any symbol only exists once in all binaries (.exe+dll), then a VMT is just an external symbol, just like vars, procedures and functions.

So you need all symbols that should be externally callable, and need to generate export sections for every binary format for them. Also possibly deal with schemes that make sets of symbols somewhat version independant (all kinds of late binding)

However you are likely to get stuck if you really try to scale up the minor experiments one platform that people echo from time to time. You need a bit of luck here, not too complicated usage there, and only one platform since that other platform needs an indirection for a certain symbol kind to make it exportable etc.

The experiments are interesting though (specially the recent very long shared library bugreport with lots of packages) since they make a clearer picture of what _details_ need to be done for packages (even though the big picture is clear).

Show quoted text 7 lines
I didn't try, but it should be possible to put an LCL application into a dynamic lib on all platforms and accessing it only via pointers. And with various hacks it is possible to share more. The main problem is, that the guys knowing the compiler and linker issues, thinks that the disadvantages of packages outweighs the advantages, so it is not worth implementing.

Packages are something awfully magical to many people. But the base principles of (non dynamical loadable) packages are not that dramantic:

- A symbol only exists once. Takes care of duplicate RTL state and VMT problems.
- one clear initialization sequence, possibly as close as possible to the undivided binary. Additional tables need to be generated for this.
- enough meta info and project files to make the whole process compiler controlled instead of hand crafted. The compiler oversees the entire process of .bpl (.fpl?) and .exe, and thus has .ppu's for the .fpl's.
- (FPC extra requirement) solution must be portable

Cutting corners by working around explicit compiler support will only result in an unsupportable mess.

Reply by email

Marco van de Voort
to Christian Budde: I don't think he is confusing the situation more than necessary, it was the original…
Christian Budde wrote 10 lines
> Netscape is not created in lazarus, and doesn't communicate over plugin > interface using pascal types. > > Which is exactly the problem packages try to solve. Please do not confuse > the situation more than necessary. I don't think he is confusing the situation more than necessary, it was the original topic to use the LCL in DLLs and not to change the package system in Lazarus.

packages and package system in Lazarus are unrelated, because delphi uses the first mechanism to implement the second it is not the same

please read: http://wiki.freepascal.org/packages first.

However there might be some confusion here, if you mean having a totally contained Lazarus program in a DLL together with RTL and DLL, then that is somewhat comparable with the netscape example.

But that is not what afaik the thread was about, and what I meant, that is to have a libfpc.so (RTL+FCL and other packages without dependancies) and liblcl.so and reuse them for all my lazarus applications.

The problem then is that it is not just a few plugin functions to register, but the complex system of objects, methods, variables, memory management etc over DLL borders, IOW the full content of the FPC language and libraries.

Reply by email

Florian Klaempfl
to Marco van de Voort
Show quoted text 23 lines
On Wed, Dec 09, 2009 at 03:59:46PM +0100, Christian Budde wrote: >> Netscape is not created in lazarus, and doesn't communicate over plugin >> interface using pascal types. >> >> Which is exactly the problem packages try to solve. Please do not confuse >> the situation more than necessary. > > I don't think he is confusing the situation more than necessary, it was > the original topic to use the LCL in DLLs and not to change the package > system in Lazarus. packages and package system in Lazarus are unrelated, because delphi uses the first mechanism to implement the second it is not the same please read: http://wiki.freepascal.org/packages first. However there might be some confusion here, if you mean having a totally contained Lazarus program in a DLL together with RTL and DLL, then that is somewhat comparable with the netscape example. But that is not what afaik the thread was about, and what I meant, that is to have a libfpc.so (RTL+FCL and other packages without dependancies) and liblcl.so and reuse them for all my lazarus applications.

Having librtl.so etc. is not hard, I did this once with lazarus, instead of linking against an .a you link against a .so (well, against 50 MB of .so in case of lazarus). The problem are windows dlls: they work completly different than .so

Reply by email