This post is intended for Jens, or people who have expirience with the dockign code in N++.
I used Delphi to code the DBGp plugin. Then I "released" the framework for creating plugins for N++ in Delphi. Fidvo's SimpleScript is created on it for example, I'm sure it traveled arround. I think I didn't yet send it to Don, because it has one problem that I'm trying to solve for the last two weeks.
Here we go.
A window is created, registered with N++ (NPPM_MODELESSDIALOG). Then the window is shown (to ensure everything is properly initialized - Delphi issues) and then it's registered as a docking window (NPPM_DMMREGASDCKDLG).
As long as the window is docked everything works normal. But when it is floated, in certian cases the whole application hangs. One example beeing: A button (standard windows object) on the floating window is pressed, and this causes to show a MessageBox. The application hangs before the message box is shown. But you do hear the sound. So this must be some sort of redraw problem.
I have been debugging this from all points of view and what it seems is that the application (N++) stops somewhere in ntdll.dll after it passes thru some toolbar message procedure and is waiting for a message. Or perhaps a deadlock. I'm not sure.
(I did download the Symbols of windows.dlls but they didn't work.. Foobar windows. Does anyone have expirience with this?)
(I even created a script that translates VisualStudio stack trace to include Delphi symbols...)
So my question is:
What is the difference between a docked form and a floating form? Is it a problem, that the form is created by CreateWindow and not CreateDialog? Are there any special requirements for the tbdata structure?
Is there some message that a docked form must answer or return 0/1 in its message loop to work correctly?
I'll work some more on this when I have time, but any pointers help.
Thanks and best regards
-Zobo
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
When using the docking feature, I noticed two non-obvious things:
1) the tTbData structure (the argument of NPPM_DMMREGASDCKDLG message) must be a global or static variable;
2) the pszName member of the tTbData structure must also be a global or static variable.
Maybe it's the cause of your problem.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi DV.
Thanks for the suggestion. Altho I already did alloc all those variables (so that they say in memory, making them like static), I took this moment and cleaned up this routine. So this should not cause any more problems.
But sadly, did not help... This is the stack trace from VC++
kernel32.dll!7c809773()
user32.dll!7e418625()
user32.dll!7e4187bb()
user32.dll!7e41c63f()
user32.dll!7e41f65d()
notepad++Debug.exe!DockingCont::runProcCaption(HWND__ * hwnd=0x000f663c, unsigned int Message=0x004798c6, unsigned int wParam=0x008c082e, long lParam=0x00000087) Line 398 C++
notepad++Debug.exe!DockingCont::wndCaptionProc(HWND__ * hwnd=, unsigned int Message=, unsigned int wParam=, long lParam=) Line 144 + 0x1c C++
I got different stack traces at different times (some going down 10 functions other about 100), but the top is usualy the same. Something gets passed to the Caption or Tab wnd procedure then this one calls the run proc and then they run the default procedure:
return ::CallWindowProc(_hDefaultCaptionProc, hwnd, Message, wParam, lParam);
Subclassing this beeing called I think.
Than thet gets stuck either in ntdll.dll or kernel32.dll...
Can anyone figure out what that message is?
I'll get those bugs and if it's the last thing I do!
-Zobo
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Just another point of data (that probably won't make sense to anyone but zobo):
I've done some testing in Simple Script, and even played around with some variations to see if I could narrow down the exact symptoms of the problem. If I inherit a form class from TNppdockingform, then regardless of whether I even call RegisterDockingForm, the program hangs as soon as both of the following requirements are met:
1) It is floating
2) I press a button, even if that button does nothing.
It doesn't matter in what order you perform these steps (e.g. press a button while it's docked, and everything's fine. Then undock it, and the immediately the program hangs)
If, however, I make it a modal window, or I leave it docked, everything works just fine.
This is the main reason why I haven't put out an update for SimpleScript since discovering the problem, because I don't know where to proceed from here. I can either take out the docking code and make it a modal window (which makes it more difficult to work with) or I can make it a docking form and leave in the bug with a note in the readme warning users not to undock it. Neither solution appeals to me, and the way I proceed will determine several things about the plugin in the future, such as whether to put some of the options on the menu or on the form itself.
I know it's a little selfish, but I have to admit that I'm glad it's not a problem just in Simple Script. That means that there are other (and probably smarter) people working on the issue. Hopefully we can figure out a solution.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I read something about Notepad++ being or always wanting to be on top, topmost window.
If making the plug-in modal seems to work... Perhaps Notepad++ itself is performing some kind of action, when it gets out of focus, because of the undocked plug-in dialog. I don't know, maybe this N++ action can't be executed when there is a modal window. I could imagine that when N++ interferes in the showing of the dialog and then the dialog continues, something is messed up.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Also, _if_ N++ interferes in the showing of the dialog, perhaps the problem is in the undocked form itself because of some kind of event happening, while the form was not yet created completely.
Could this have something to do with focussing (problems) in N++, like after switching to and returning from another application?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
When I create the docked dialog, I make the following steps:
1) call WinAPI's CreateDialog() function, i.e.
hDlg = CreateDialog(...);
where hDlg is the dialog's window handle;
2) after the dialog is initialized - i.e. when its hDlg is already valid - two messages are sent:
SendMessage(hNppWnd, NPPM_DMMREGASDCKDLG, 0, (LPARAM) &data);
SendMessage(hNppWnd, NPPM_MODELESSDIALOG, (WPARAM) MODELESSDIALOGADD, (LPARAM) hDlg);
where data is the tTbData structure;
3) to show the dialog, call
SendMessage(hNppWnd, NPPM_DMMSHOW, 0, (LPARAM) hDlg);
4) finally, when your dialog receives WM_NOTIFY in its DialogProc, you may process this notification in similar way:
procedure MyDialog.OnNotify(hDlg: HWND, lParam: LPARAM);
var pnmh: LPNMHDR;
begin
pnmh := (LPNMHDR) lParam;
// sorry, I don't remember how LPARAM becomes LPNMHDR in Delphi
if (pnmh->hwndFrom == hNppWnd) then
begin
case LOWORD(pnmh->code) of
DMN_CLOSE: begin end;
DMN_FLOAT: begin end;
DMN_DOCK: begin end;
end;
end;
end;
You can add your own processing for the DMN_* notifications if it's needed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Also, after you close your dialog (hDlg), do not destroy it, and there is no need to create it again.
When you want to see the dialog again (after it was closed), just call
As you can see, the value of hDlg MUST be valid when you send NPPM_DMMSHOW.
Otherwise, if you have destroyed this dialog, you must unregister it previous data, then register the new created dialog.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi DV.
All of the above is understandable and I'm already doing all of that (including not destroying the form when it closes), however I think that the biggest problem here is that Delphi does not use CreateDialog, but uses CreateWindow and then creates each component on it one after another. Maybe not even right away, but when the window is first shown...
This creates a messy hierarchy were I'm not even sure how the WMs are traveling.
Recently I had a situation, where I somehow created a loop inside the form. The only thing happening was that all components on the form have been getting WM_GETDGLCODE over and over again.
I had various lockups depending on weather the form has the right parent hwnd (Application.Handle), is it registered with notepad or not, and even what things the Notepad dicking manager does to it (I tried changing some of N++'s code) (the docking manager changes a windows parent, style etc...).
I could really use:
1. some hardocre lowlever windows hacker
2. someone really experienced with low level Delphi
3. about 12 hours more per day
I'll try to get my hands on a newer Delphi IDE. I use 6 and debugging isn't its best features. Plus I'd like it to spit out .dbg files that I could load in VC++.
I'm only scared that in the end it just wont be possible to use Delphi's framework for N++ plugins. Creating Dialogs "by hand" kind of kill the plus of delphi.
Any ideas if it would help if I introduce additional threads for forms?
Re,
-Zobo
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
As to obtaining a newer Delphi IDE, Turbo Delphi is free and is syntactically equivalent to BDS 2006. I used Turbo Delphi to develop the color picker plugin. One drawback to Turbo Delphi is the restriction of adding third-party components which is probably a non-issue for developing plugin dlls for npp. However, there is a known hack for adding components to Turbo Delphi.
I am clueless to a best practice for writing a plugin for npp in Delphi. I know to use Free as opposed to Destroy etc. But I still don't know what is expected with Register and Unregister as well as other npp / scintilla functions that the plugin is supposed to communicate with.
I admit that I know next to nothing about debugging and profiling. Sometimes I just drop a memo onto a form and while the program runs I dump various values into memo to see what is happening. I know this is crude but I don't know how to use Dunit or other more elegant methods.
This might be a tall order but I think a special build of Notepad++ that functions as a plugin tester could demystify some of our problems.
Don
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi Don R. Thanks for the suggestion. I'll google about it.
Currently I actually spend most time "debugging" Notepad++, not the plugin, trying to figure out how the messages flow from one part of the code to another...
Codign is so much like Kugn fu!
-Zobo
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I spend the last (...it feels like a month) week or two debugging this problem whenever I had some free time.
The worst part was, that none of the IDEs, that I had installed at that time, gave me enough info from the stack to even remotely understand what was going on.
Anyway finally after installing a newer Delphi IDE that miraculously used the exported symbols of windows .dlls to show some usable stack I had the ability to see where things were going. A lot of assembly code and refreshing on ASM knowledge later I figured out that there was an infinite loop inside DefDlgProc that went on and on sending WM_GETDLGCODE to 3 elements of the NPP docking form (button, static and tabcontrol). Later I found this: http://support.microsoft.com/kb/149501
And it's true. The minute I create a form in Delphi that has a Paged component on it and it looses focus, the whole thing goes into a hang. I haven't had the time to solve it yet but at least I know what's the damn problem now.
Problem two. Fidvo reported that his Edit condtol behaved strange. I analyzed that and figured out that it behaves strange in two ways. If it's registered as a modeless dialog (that means that IsDialogMessage is used to process it's messages) then the Enter key doesn't work. If it's not registered (that means that the message goes to TranslateMessage and DispatchMessage) the Home and End keys don't work (and probably some more). Anyway. I did a small test and figured that VCL main message loop must do some more magic. I introduced another message loop in the plugin as a proof of this. It seems that the VCL need to process certain messages in a different way and re send them with an offset.
Again, I know what the problem is, just haven't come up with a solution yet (lack of time).
I hope I can fix the issues soon and release this framework for people to use. We see an explosion of N++ plugins lately making this a really unique editor and I think by giving out a Delphi framework even more programmers will be able to contribute their ideas as plugins.
Thank you very much.
-Z
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I managed to do a workarround. As described ealyer, WS_EX_CONTROLPARENT ExStyle causes the default dialog handler to loop indefinetly when it does a previous component search. It's strange to hell and back, that the search pointer ends up looping on the DocingWindows components.
Anyway, one way arround is, to add this pesky style to all the relevant components (that is hard, because the code can't just figure out what you're up to), the other is to remove it from all components (much easier). No looping occures this way. Probably some other strange stuff is going to be happening but this is better than hanging the whole application.
I also added some code, so you can assign an icon to your form and it will be used when docks are tabbed.
Since I'm still not going to release all of this (it's a mess to tell you), I suggest you tell me how to get ahold of your latest code and I'll throw arround a few functions for you. Tell me if this works for you.
Best regards,
-Zobo
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
You can just download the source for the latest published version (1.11) from the Notepad++ Plugins Project page. All the work I've done since then is in a separate module so you don't have to worry about getting the modules out of sync.
To help you wade through the various pieces of the module, ScriptWriter.pas is the only module containing a docking form (ScriptWriterForm) in the plugin. And of course, simplescriptplugin.pas is the module that sets up the menu and creates the form.
Thanks so much for your hard work.
-Fidvo
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This post is intended for Jens, or people who have expirience with the dockign code in N++.
I used Delphi to code the DBGp plugin. Then I "released" the framework for creating plugins for N++ in Delphi. Fidvo's SimpleScript is created on it for example, I'm sure it traveled arround. I think I didn't yet send it to Don, because it has one problem that I'm trying to solve for the last two weeks.
Here we go.
A window is created, registered with N++ (NPPM_MODELESSDIALOG). Then the window is shown (to ensure everything is properly initialized - Delphi issues) and then it's registered as a docking window (NPPM_DMMREGASDCKDLG).
As long as the window is docked everything works normal. But when it is floated, in certian cases the whole application hangs. One example beeing: A button (standard windows object) on the floating window is pressed, and this causes to show a MessageBox. The application hangs before the message box is shown. But you do hear the sound. So this must be some sort of redraw problem.
I have been debugging this from all points of view and what it seems is that the application (N++) stops somewhere in ntdll.dll after it passes thru some toolbar message procedure and is waiting for a message. Or perhaps a deadlock. I'm not sure.
(I did download the Symbols of windows.dlls but they didn't work.. Foobar windows. Does anyone have expirience with this?)
(I even created a script that translates VisualStudio stack trace to include Delphi symbols...)
So my question is:
What is the difference between a docked form and a floating form? Is it a problem, that the form is created by CreateWindow and not CreateDialog? Are there any special requirements for the tbdata structure?
Is there some message that a docked form must answer or return 0/1 in its message loop to work correctly?
I'll work some more on this when I have time, but any pointers help.
Thanks and best regards
-Zobo
When using the docking feature, I noticed two non-obvious things:
1) the tTbData structure (the argument of NPPM_DMMREGASDCKDLG message) must be a global or static variable;
2) the pszName member of the tTbData structure must also be a global or static variable.
Maybe it's the cause of your problem.
Hi DV.
Thanks for the suggestion. Altho I already did alloc all those variables (so that they say in memory, making them like static), I took this moment and cleaned up this routine. So this should not cause any more problems.
But sadly, did not help... This is the stack trace from VC++
kernel32.dll!7c809773()
user32.dll!7e418625()
user32.dll!7e4187bb()
user32.dll!7e41c63f()
user32.dll!7e41f65d()
notepad++Debug.exe!DockingCont::runProcCaption(HWND__ * hwnd=0x000f663c, unsigned int Message=0x004798c6, unsigned int wParam=0x008c082e, long lParam=0x00000087) Line 398 C++
notepad++Debug.exe!DockingCont::wndCaptionProc(HWND__ * hwnd=, unsigned int Message=, unsigned int wParam=, long lParam=) Line 144 + 0x1c C++
I got different stack traces at different times (some going down 10 functions other about 100), but the top is usualy the same. Something gets passed to the Caption or Tab wnd procedure then this one calls the run proc and then they run the default procedure:
return ::CallWindowProc(_hDefaultCaptionProc, hwnd, Message, wParam, lParam);
Subclassing this beeing called I think.
Than thet gets stuck either in ntdll.dll or kernel32.dll...
Can anyone figure out what that message is?
I'll get those bugs and if it's the last thing I do!
-Zobo
Just another point of data (that probably won't make sense to anyone but zobo):
I've done some testing in Simple Script, and even played around with some variations to see if I could narrow down the exact symptoms of the problem. If I inherit a form class from TNppdockingform, then regardless of whether I even call RegisterDockingForm, the program hangs as soon as both of the following requirements are met:
1) It is floating
2) I press a button, even if that button does nothing.
It doesn't matter in what order you perform these steps (e.g. press a button while it's docked, and everything's fine. Then undock it, and the immediately the program hangs)
If, however, I make it a modal window, or I leave it docked, everything works just fine.
This is the main reason why I haven't put out an update for SimpleScript since discovering the problem, because I don't know where to proceed from here. I can either take out the docking code and make it a modal window (which makes it more difficult to work with) or I can make it a docking form and leave in the bug with a note in the readme warning users not to undock it. Neither solution appeals to me, and the way I proceed will determine several things about the plugin in the future, such as whether to put some of the options on the menu or on the form itself.
I know it's a little selfish, but I have to admit that I'm glad it's not a problem just in Simple Script. That means that there are other (and probably smarter) people working on the issue. Hopefully we can figure out a solution.
I read something about Notepad++ being or always wanting to be on top, topmost window.
If making the plug-in modal seems to work... Perhaps Notepad++ itself is performing some kind of action, when it gets out of focus, because of the undocked plug-in dialog. I don't know, maybe this N++ action can't be executed when there is a modal window. I could imagine that when N++ interferes in the showing of the dialog and then the dialog continues, something is messed up.
Also, _if_ N++ interferes in the showing of the dialog, perhaps the problem is in the undocked form itself because of some kind of event happening, while the form was not yet created completely.
Could this have something to do with focussing (problems) in N++, like after switching to and returning from another application?
When I create the docked dialog, I make the following steps:
1) call WinAPI's CreateDialog() function, i.e.
hDlg = CreateDialog(...);
where hDlg is the dialog's window handle;
2) after the dialog is initialized - i.e. when its hDlg is already valid - two messages are sent:
SendMessage(hNppWnd, NPPM_DMMREGASDCKDLG, 0, (LPARAM) &data);
SendMessage(hNppWnd, NPPM_MODELESSDIALOG, (WPARAM) MODELESSDIALOGADD, (LPARAM) hDlg);
where data is the tTbData structure;
3) to show the dialog, call
SendMessage(hNppWnd, NPPM_DMMSHOW, 0, (LPARAM) hDlg);
4) finally, when your dialog receives WM_NOTIFY in its DialogProc, you may process this notification in similar way:
procedure MyDialog.OnNotify(hDlg: HWND, lParam: LPARAM);
var pnmh: LPNMHDR;
begin
pnmh := (LPNMHDR) lParam;
// sorry, I don't remember how LPARAM becomes LPNMHDR in Delphi
if (pnmh->hwndFrom == hNppWnd) then
begin
case LOWORD(pnmh->code) of
DMN_CLOSE: begin end;
DMN_FLOAT: begin end;
DMN_DOCK: begin end;
end;
end;
end;
You can add your own processing for the DMN_* notifications if it's needed.
Also, after you close your dialog (hDlg), do not destroy it, and there is no need to create it again.
When you want to see the dialog again (after it was closed), just call
SendMessage(hNppWnd, NPPM_DMMSHOW, 0, (LPARAM) hDlg);
As you can see, the value of hDlg MUST be valid when you send NPPM_DMMSHOW.
Otherwise, if you have destroyed this dialog, you must unregister it previous data, then register the new created dialog.
Hi DV.
All of the above is understandable and I'm already doing all of that (including not destroying the form when it closes), however I think that the biggest problem here is that Delphi does not use CreateDialog, but uses CreateWindow and then creates each component on it one after another. Maybe not even right away, but when the window is first shown...
This creates a messy hierarchy were I'm not even sure how the WMs are traveling.
Recently I had a situation, where I somehow created a loop inside the form. The only thing happening was that all components on the form have been getting WM_GETDGLCODE over and over again.
I had various lockups depending on weather the form has the right parent hwnd (Application.Handle), is it registered with notepad or not, and even what things the Notepad dicking manager does to it (I tried changing some of N++'s code) (the docking manager changes a windows parent, style etc...).
I could really use:
1. some hardocre lowlever windows hacker
2. someone really experienced with low level Delphi
3. about 12 hours more per day
I'll try to get my hands on a newer Delphi IDE. I use 6 and debugging isn't its best features. Plus I'd like it to spit out .dbg files that I could load in VC++.
I'm only scared that in the end it just wont be possible to use Delphi's framework for N++ plugins. Creating Dialogs "by hand" kind of kill the plus of delphi.
Any ideas if it would help if I introduce additional threads for forms?
Re,
-Zobo
Zobo,
As to obtaining a newer Delphi IDE, Turbo Delphi is free and is syntactically equivalent to BDS 2006. I used Turbo Delphi to develop the color picker plugin. One drawback to Turbo Delphi is the restriction of adding third-party components which is probably a non-issue for developing plugin dlls for npp. However, there is a known hack for adding components to Turbo Delphi.
I am clueless to a best practice for writing a plugin for npp in Delphi. I know to use Free as opposed to Destroy etc. But I still don't know what is expected with Register and Unregister as well as other npp / scintilla functions that the plugin is supposed to communicate with.
I admit that I know next to nothing about debugging and profiling. Sometimes I just drop a memo onto a form and while the program runs I dump various values into memo to see what is happening. I know this is crude but I don't know how to use Dunit or other more elegant methods.
This might be a tall order but I think a special build of Notepad++ that functions as a plugin tester could demystify some of our problems.
Don
Hi Don R. Thanks for the suggestion. I'll google about it.
Currently I actually spend most time "debugging" Notepad++, not the plugin, trying to figure out how the messages flow from one part of the code to another...
Codign is so much like Kugn fu!
-Zobo
Good news everyone!
I spend the last (...it feels like a month) week or two debugging this problem whenever I had some free time.
The worst part was, that none of the IDEs, that I had installed at that time, gave me enough info from the stack to even remotely understand what was going on.
Anyway finally after installing a newer Delphi IDE that miraculously used the exported symbols of windows .dlls to show some usable stack I had the ability to see where things were going. A lot of assembly code and refreshing on ASM knowledge later I figured out that there was an infinite loop inside DefDlgProc that went on and on sending WM_GETDLGCODE to 3 elements of the NPP docking form (button, static and tabcontrol). Later I found this: http://support.microsoft.com/kb/149501
And it's true. The minute I create a form in Delphi that has a Paged component on it and it looses focus, the whole thing goes into a hang. I haven't had the time to solve it yet but at least I know what's the damn problem now.
Problem two. Fidvo reported that his Edit condtol behaved strange. I analyzed that and figured out that it behaves strange in two ways. If it's registered as a modeless dialog (that means that IsDialogMessage is used to process it's messages) then the Enter key doesn't work. If it's not registered (that means that the message goes to TranslateMessage and DispatchMessage) the Home and End keys don't work (and probably some more). Anyway. I did a small test and figured that VCL main message loop must do some more magic. I introduced another message loop in the plugin as a proof of this. It seems that the VCL need to process certain messages in a different way and re send them with an offset.
Again, I know what the problem is, just haven't come up with a solution yet (lack of time).
I hope I can fix the issues soon and release this framework for people to use. We see an explosion of N++ plugins lately making this a really unique editor and I think by giving out a Delphi framework even more programmers will be able to contribute their ideas as plugins.
Thank you very much.
-Z
Zobo:
Thank you so much for your efforts. I certainly appreciate the work you've done, as I wouldn't have had the slightest clue where to start.
I look forward to seeing a new framework. I have some other ideas for plugins that I would like to develop, but this issue has been holding me back.
You're the greatest.
-Fidvo
Hello Fidvo
I managed to do a workarround. As described ealyer, WS_EX_CONTROLPARENT ExStyle causes the default dialog handler to loop indefinetly when it does a previous component search. It's strange to hell and back, that the search pointer ends up looping on the DocingWindows components.
Anyway, one way arround is, to add this pesky style to all the relevant components (that is hard, because the code can't just figure out what you're up to), the other is to remove it from all components (much easier). No looping occures this way. Probably some other strange stuff is going to be happening but this is better than hanging the whole application.
I also added some code, so you can assign an icon to your form and it will be used when docks are tabbed.
Since I'm still not going to release all of this (it's a mess to tell you), I suggest you tell me how to get ahold of your latest code and I'll throw arround a few functions for you. Tell me if this works for you.
Best regards,
-Zobo
Zobo:
You can just download the source for the latest published version (1.11) from the Notepad++ Plugins Project page. All the work I've done since then is in a separate module so you don't have to worry about getting the modules out of sync.
To help you wade through the various pieces of the module, ScriptWriter.pas is the only module containing a docking form (ScriptWriterForm) in the plugin. And of course, simplescriptplugin.pas is the module that sets up the menu and creates the form.
Thanks so much for your hard work.
-Fidvo