View previous topic :: View next topic |
Author |
Message |
CyberShadow Site Admin
Joined: 29 Dec 2006 Posts: 21 Location: Moldova, Eastern Europe
|
Posted: Wed Sep 19, 2007 6:25 pm Post subject: Problem: DFL doesn't clean up after itself |
|
|
For a typical Windows application, this is not a problem at all. However, if I try to use DFL in a DLL, which is unloaded and then reloaded, DFL fails to initialize. It fails to register window classes (because these classes already exist and DFL never unregisters them). I tried to work around that by making DFL ignore class creation errors, however this causes a "abnormal program termination" error messagebox followed by the whole program terminating. I believe that that error is generated by libc, but I don't know what are the conditions for that happening.
I might investigate the exact cause of that error later. Meanwhile, please consider implementing some clean-up code - or, at least, some graceful handling of resources that have been already initialized |
|
Back to top |
|
|
CyberShadow Site Admin
Joined: 29 Dec 2006 Posts: 21 Location: Moldova, Eastern Europe
|
Posted: Thu Sep 20, 2007 3:04 pm Post subject: |
|
|
The "abnormal program termination" was due to CreateWindowEx failing, because the classes were registered by another module. Duh.
DFL really needs clean-up code. I may start writing some, but it'll probably be a hack to get it working for my project. |
|
Back to top |
|
|
CyberShadow Site Admin
Joined: 29 Dec 2006 Posts: 21 Location: Moldova, Eastern Europe
|
Posted: Fri Sep 21, 2007 9:14 am Post subject: |
|
|
Remembering registered classes and unregistering them on exit seems to have fixed it.
My changes:
Code: |
diff -u -r C:\Downloads\dfl-20070811\import\dfl/application.d C:\Soft\dmd\import\dfl/application.d
--- C:\Downloads\dfl-20070811\import\dfl/application.d 2007-06-15 13:23:08.000000000 +0300
+++ C:\Soft\dmd\import\dfl/application.d 2007-09-21 17:35:04.062500000 +0300
@@ -501,6 +501,8 @@
UnhookWindowsHookEx(msghook);
tctx.threadExit.removeHandler(&threadJustExited);
+
+ unregisterClasses(getInstance());
}
}
diff -u -r C:\Downloads\dfl-20070811\import\dfl/internal/utf.d C:\Soft\dmd\import\dfl/internal/utf.d
--- C:\Downloads\dfl-20070811\import\dfl/internal/utf.d 2007-05-13 19:35:16.000000000 +0300
+++ C:\Soft\dmd\import\dfl/internal/utf.d 2007-09-21 17:58:58.062500000 +0300
@@ -349,6 +349,7 @@
alias LONG function(HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcbValueName,
LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) RegEnumValueWProc;
alias ATOM function(WNDCLASSW* lpWndClass) RegisterClassWProc;
+ alias BOOL function(LPCWSTR lpClassName, HINSTANCE hInstance) UnregisterClassWProc;
alias BOOL function(HDC hdc, LPCWSTR lpString, int cbString, LPSIZE lpSize) GetTextExtentPoint32WProc;
alias HANDLE function(HINSTANCE hinst, LPCWSTR lpszName, UINT uType, int cxDesired, int cyDesired, UINT fuLoad)
LoadImageWProc;
@@ -1323,9 +1324,11 @@
char[] className;
}
+ATOM[] registeredClasses;
ATOM registerClass(inout WndClass wc)
{
+ ATOM result;
if(useUnicode)
{
version(STATIC_UNICODE)
@@ -1346,15 +1349,47 @@
}
wc.wcw.lpszClassName = toUnicodez(wc.className);
- return proc(&wc.wcw);
+
+ result = proc(&wc.wcw);
}
else
{
wc.wca.lpszClassName = unsafeAnsiz(wc.className);
- return RegisterClassA(&wc.wca);
+ result = RegisterClassA(&wc.wca);
}
+ registeredClasses ~= [result];
+ return result;
}
+void unregisterClasses(HINSTANCE hInstance)
+{
+ if(useUnicode)
+ {
+ version(STATIC_UNICODE)
+ {
+ alias UnregisterClassW proc;
+ }
+ else
+ {
+ const char[] NAME = "UnregisterClassW";
+ static UnregisterClassWProc proc = null;
+
+ if(!proc)
+ {
+ proc = cast(UnregisterClassWProc)GetProcAddress(user32, NAME.ptr);
+ if(!proc)
+ getProcErr(NAME);
+ }
+ }
+ foreach(atom;registeredClasses)
+ proc(cast(LPCWSTR)atom, hInstance);
+ }
+ else
+ {
+ foreach(atom;registeredClasses)
+ UnregisterClassA(cast(LPCSTR)atom, hInstance);
+ }
+}
BOOL getClassInfo(HINSTANCE hinst, char[] className, inout WndClass wc)
{
diff -u -r C:\Downloads\dfl-20070811\import\dfl/internal/winapi.d C:\Soft\dmd\import\dfl/internal/winapi.d
--- C:\Downloads\dfl-20070811\import\dfl/internal/winapi.d 2007-07-14 13:18:54.000000000 +0300
+++ C:\Soft\dmd\import\dfl/internal/winapi.d 2007-09-21 17:57:43.609375000 +0300
@@ -2538,6 +2538,8 @@
BOOL DestroyWindow(HWND hwnd);
ATOM RegisterClassExA(WNDCLASSEXA* lpwcx);
ATOM RegisterClassW(WNDCLASSW* lpWndClass);
+ BOOL UnregisterClassA(LPCSTR lpClassName, HINSTANCE hInstance);
+ BOOL UnregisterClassW(LPCWSTR lpClassName, HINSTANCE hInstance);
HWND GetActiveWindow();
LRESULT DefDlgProcA(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam);
LRESULT DefDlgProcW(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam);
|
It needs a setInstance call from user code to work properly. It's pretty hard to do it because HINSTANCE is a private type (HANDLE alias, which is a typedef and thus incompatible with base types) declared in dfl.internal._stdwindows - so I had to write Application.setInstance(cast(dfl.internal._stdcwindows.HINSTANCE)hInstance). |
|
Back to top |
|
|
Chris Miller
Joined: 27 Mar 2004 Posts: 514 Location: The Internet
|
Posted: Sat Sep 22, 2007 1:36 pm Post subject: |
|
|
Thanks, I will try to incorporate it soon.
- Chris |
|
Back to top |
|
|
CyberShadow Site Admin
Joined: 29 Dec 2006 Posts: 21 Location: Moldova, Eastern Europe
|
Posted: Sat Sep 22, 2007 1:39 pm Post subject: |
|
|
I'd like to add that this isn't a complete solution. If I understand Windows GUI correctly, each module must register its own window classes separately, and there may not be class name conflicts between modules. This means that, in order for DFL to be used by more than one module in the same application, DFL must create the classes with unique names (e.g. appending the module instance number to each class name). |
|
Back to top |
|
|
Chris Miller
Joined: 27 Mar 2004 Posts: 514 Location: The Internet
|
Posted: Fri Sep 28, 2007 10:30 pm Post subject: Re: Problem: DFL doesn't clean up after itself |
|
|
The current snapshot improves this situation. I also put up a new DLL example on the ExampleCode page.
It requires each EXE and DLL to have its own copy of DFL, I think you've been trying to share one. The window classes unregister when the EXE or DLL is unloaded.
Note that for having separate copies of DFL, it might be better for them to have their own threads so they can have their own Application.run(). The example doesn't do this. Sorry this use of DFL isn't well tested or supported, you'll probably have to hack your way through it, as you've been; I'll help along the way. |
|
Back to top |
|
|
CyberShadow Site Admin
Joined: 29 Dec 2006 Posts: 21 Location: Moldova, Eastern Europe
|
Posted: Fri Dec 07, 2007 11:38 am Post subject: |
|
|
Sorry, the changes in that snapshot did not solve my problem. The second time the DLL loads, it crashes with the following call stack:
__d_throw@4
_D3dfl11application13_initInstanceFT3dfl8internal12_stdcwindows6HANDLEZv
_D3dfl11application11Application11setInstanceFT3dfl8internal12_stdcwindows6HANDLEZv
...
To make it clear: I have a non-DFL EXE (which does have a message loop) and a DFL DLL. The EXE needs to load and unload the DFL DLL arbitrarily. DFL still does not unregister window classes, which causes it to fail initializing a second time.
Note: I re-applied my patch (specified above) and everything is working fine for me again. Either I'm not understanding something impotant about atoms/registered classes, or that's the only way to solve my problem. The patch makes note of which classes were registered, and unregisters them when DFL deinitializes.
Also, the DLL examples are missing calls to module destructors (_moduleDtor). The first DLL example on DigitalMars omits this as well, but it's a documentation bug on their side. |
|
Back to top |
|
|
Chris Miller
Joined: 27 Mar 2004 Posts: 514 Location: The Internet
|
Posted: Sat Dec 08, 2007 4:13 pm Post subject: |
|
|
It's not clear if you're calling Application.setInstance() in DllMain, but in this case it is required. See the example for when it needs to be called. If you don't, the window classes are registered in the exe and not the dll. If registered in the dll (by properly using Application.setInstance) they should be unloaded when the dll is unloaded, and they should be kept separate from other dlls, as the example works. |
|
Back to top |
|
|
CyberShadow Site Admin
Joined: 29 Dec 2006 Posts: 21 Location: Moldova, Eastern Europe
|
Posted: Sat Dec 08, 2007 4:16 pm Post subject: |
|
|
Like the DLL example, I do call Application.setInstance before calling the module static constructors. I don't know why would DFL register the classes with the EXE's instance. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|