Tygur Posted October 22, 2003 Posted October 22, 2003 In certain cases, my form won't close. Clicking the X in the upper right corner does nothing, and the Close method is just as helpless. The Closing event doesn't even fire. Overriding WndProc shows that the form does receive the WM_CLOSE message, but it just doesn't seem to do anything about it. Does anyone have any idea what could cause such behavior? My app does do some rather unusual things with the win32 api, so you can get creative with your ideas.. The form shows in its own thread, because the app in question is actually a console app. If I override WndProc and abort the thread whenever I receive WM_CLOSE, the form will actually close. So far, this is the only way I've managed to close it. Is this a safe way of doing it, though, or am I supposed to get problems later on? Quote
*Experts* Volte Posted October 22, 2003 *Experts* Posted October 22, 2003 How are you showing the form in the first place, and how are you sustaining the console app while it is shown? Quote
AlexCode Posted October 22, 2003 Posted October 22, 2003 My app does do some rather unusual things with the win32 api, so you can get creative with your ideas.. How creative can we get?? :) What exactly are you doing with the Win32 API? It's possible that the unmanaged call must be finished before closing the form it self... Quote Software bugs are impossible to detect by anybody except the end user.
Tygur Posted October 22, 2003 Author Posted October 22, 2003 In reply to VolteFace: Well, the app generally just runs as a console app. When the form has to show, it starts a new thread, which shows the form and keeps it going using Application.Run(). The only purpose of the new thread is to run the form, and it ends when the form ends (and vice versa as well). While ending the thread does take away the form, is this a good way of doing it, since the Close method isn't working? Of course, the first part of my question is why would the form not be closing in the first place, but if ending the thread works well enough, then I'll just do that. I just put together some sample code to show what I mean by showing a form in its own thread. Create a new VB Console Application and replace the code in Module1 with this. You will also need to add a reference to System.Windows.Forms. Imports System.Threading Imports System.Windows.Forms Module Module1 Sub Main() Do Console.Write("Gimme a command: ") Dim InString As String = Console.ReadLine If InString.ToLower.StartsWith("echo ") Then Console.WriteLine(InString.Substring(5)) ElseIf InString.ToLower.Equals("show the form") Then Console.WriteLine("I will show you the form.") Dim T As New Threading.Thread(AddressOf ShowForm) ShowEvent = New ManualResetEvent(False) T.Start() ShowEvent.WaitOne() 'It may not really be safe to call Activate() from 'this thread, but i'm just doing it here for the 'sake of this demonstration. Otherwise the form 'will stay in the background when it shows. FormToFocus.Activate() ElseIf InString.ToLower.Equals("exit") Then Console.WriteLine("Bye.") Exit Do End If Loop End Sub Dim ShowEvent As Threading.ManualResetEvent Dim FormToFocus As Form Sub ShowForm() Dim F As New Form() F.Show() FormToFocus = F ShowEvent.Set() Application.Run(F) End Sub End Module Quote
*Experts* Volte Posted October 22, 2003 *Experts* Posted October 22, 2003 That code works fine for me. The form closes normally. Quote
Tygur Posted October 22, 2003 Author Posted October 22, 2003 In reply to AlexCode: Well, the entire app is making all sorts of api calls. So much so that I've wondered if I would've been better off using C++ with managed extensions. That would've saved me a lot of trouble, because I wouldn't have had to declare each constant and function. There are two things I'm doing that involve the form in question that might be part of the problem. One thing I'm doing (the most unusual) is taking a dialog from another process and using it in my form as if it's a control. The other thing is the code I got from the Knowledge Base that shows the BrowseForFolder dialog. I grabbed it and modified it for my own use. By themselves, neither is causing any problems. But if I use the BrowseForFolder dialog and then use my rather nonstandard control, the form will never close after that. If I just use one or the other, or if I show the BrowseForFolder dialog after using the usurped dialog, everything is fine. By the way, when I refer to "using" the usurped dialog, i mean showing it, controlling it, and then destroying it. Also, it looks and acts just like a control on the form. Quote
Tygur Posted October 22, 2003 Author Posted October 22, 2003 That code works fine for me. The form closes normally. It's supposed to work fine. I'm just showing how you could show a form in its own thread. Quote
*Experts* Volte Posted October 22, 2003 *Experts* Posted October 22, 2003 Well, perhaps you could post *your* code? Quote
AlexCode Posted October 22, 2003 Posted October 22, 2003 I like when people come here and post this "unthinkable" ideas... :D One thing I'm doing (the most unusual) is taking a dialog from another process and using it in my form as if it's a control. But for now, and to try to understand your problem, could you explain to me what's the use for that? As you said, you're building a VB.net app that should have been done with C++ or C# that directly support unmanaged code... Quote Software bugs are impossible to detect by anybody except the end user.
aewarnick Posted October 22, 2003 Posted October 22, 2003 As far as I know, C# is just like VB.net except syntax. Maybe you mean C++.net. Quote C#
AlexCode Posted October 22, 2003 Posted October 22, 2003 As far as I know, C# is just like VB.net except syntax. Maybe you mean C++.net. Me, as far as I know (I'm a VB.net developer) but was told to me that could be used a Unmanaged tag to call unmanaged code. Don't know much more about it but it was told to me by one MCT while I was taking the MS Certifications... Quote Software bugs are impossible to detect by anybody except the end user.
aewarnick Posted October 22, 2003 Posted October 22, 2003 That sounds like C++.net, but I hope you are right for my sake! Quote C#
*Experts* Volte Posted October 22, 2003 *Experts* Posted October 22, 2003 You might be thinking of the unsafe keyword which allows the usage of pointers and such in C#. Quote
*Experts* jfackler Posted October 22, 2003 *Experts* Posted October 22, 2003 Is this not a threading problem? Quote
Tygur Posted October 22, 2003 Author Posted October 22, 2003 It is my understanding that there is really no difference between C# and VB.NET other than syntax, the unsafe keyword, and VB.NET's stubborn refusal to deal with unsigned types. As for my code, I would rather not post it because there is a lot of it, and it's not that simple. The way I'm taking over the dialog is by using Get/SetWindowLong to change its styles, and I'm also making it a child of my form. Technically, it's being made a child of a control on a control on the form, because all the code to capture it is encapsulated in a class that derives from Control, and that control sits on a Panel. Anyhow, I'm also calling AttachThreadInput to make things look/work more natural. Among other things, it prevents the form from fading out like it lost focus when the focus moves to my dialog/control. I'm also sending messages around and using various focus api functions to make tabbing work right. When I'm done with it, I terminate the other process. As for BrowseForFolder, before anyone asks, here's the knowledge base article with the code I used: http://support.microsoft.com/default.aspx?scid=kb;EN-US;811004 The only real change I made was I added the capability to choose what was selected when the dialog first showed. The point at which the form refuses to close is while the taken-over dialog is still showing. Once again, all I have to do is show the BrowseForFolder Dialog, and then show the dialog/control. If I don't show BrowseForFolder, everything runs smoothly. Also, the problem did show up rarely before I added the BrowseForFolder code. But now that it's in, the bug is easily reproduced. I don't have a clue what used to trigger it before. The form itself consists of a TreeView on the side and a Panel that that is used to contain controls, depending on what is selected in the TreeView. If one item is selected, my usurped dialog will show. Another item will show a control that has a button that will show the Browse For Folder Dialog. There are other items that show other things, but none of them make any API calls. By the way, the app itself does make a large amount of API calls elsewhere, but they are irrelevant to this problem. This is a very strange problem, and I don't really expect to figure it out, but it would be nice. At the very least, I would like to be able to find out for sure that terminating the thread is a "safe" and generally-accepted way of closing the form. Quote
*Experts* jfackler Posted October 23, 2003 *Experts* Posted October 23, 2003 When the form has to show, it starts a new thread, which shows the form and keeps it going using Application.Run(). The only purpose of the new thread is to run the form, and it ends when the form ends (and vice versa as well). How do you start the thread, is it interrupted to perform some console code? Instead of terminating the thread, have you tried to .resume it before raising the close event? How about an .isalive check on the thread. Are you joining the thread? Since terminating the thread closes the window, how are you killing the thread? Might I suggest application.exitthread method which exits the message loop on the current thread and closes all windows on the thread. Jon Quote
*Experts* jfackler Posted October 23, 2003 *Experts* Posted October 23, 2003 Posts crossed. Have you considered a thread log? Jon Quote
Tygur Posted October 23, 2003 Author Posted October 23, 2003 How do you start the thread, is it interrupted to perform some console code? Instead of terminating the thread, have you tried to .resume it before raising the close event? How about an .isalive check on the thread. Are you joining the thread? Since terminating the thread closes the window, how are you killing the thread? Might I suggest application.exitthread method which exits the message loop on the current thread and closes all windows on the thread. The whole purpose of the thread is so the console code can continue running. The console code actually has no interaction with what goes on in this thread. The thread is never stopped. In fact, it should never be stopped, because it's running the message loop for the form. If the form is closed normally, Application.Run() returns, and the thread ends. If the thread is aborted, ThreadAbortException is presumably raised in the message loop for the form, and I'm hoping .NET handles everything the way it is supposed to. The solution I have in place now is actually calling Thread.Abort from within the thread itself. It's actually getting called from within my override of WndProc when I detect that WM_CLOSE is getting sent. Quote
*Experts* jfackler Posted October 23, 2003 *Experts* Posted October 23, 2003 What is the .ThreadState of the thread when the form stops responding? Jon Quote
*Experts* jfackler Posted October 23, 2003 *Experts* Posted October 23, 2003 Does the BrowseForFolder Dialog, or any of the APIs suspend the running thread? Oh the questions, the questions..... Jon Quote
Tygur Posted October 23, 2003 Author Posted October 23, 2003 The thread the form is in never stops. It shouldn't. Otherwise, the form would stop responding. Also, the problem is not that the form isn't responding. It just isn't closing. I can still click on it, move it around, and otherwise interact with it. I just can't close it. Quote
*Experts* jfackler Posted October 23, 2003 *Experts* Posted October 23, 2003 OK, my last suggestion. Does the opportunity for an item that should be consumed or produced, be consumed or produced, out of sync with its consumer? With lots of api calls and background running code, sync problems can create a subtle bug that can cause the program to fail. Now I'm done. I'll watch for the report of your investigation. Jon Quote
Tygur Posted October 23, 2003 Author Posted October 23, 2003 OK, my last suggestion. Does the opportunity for an item that should be consumed or produced, be consumed or produced, out of sync with its consumer? With lots of api calls and background running code, sync problems can create a subtle bug that can cause the program to fail. Now I'm done. I'll watch for the report of your investigation. Jon Well, I really don't think this is a threading problem. At least not within my app. The thread the form is running in does not interact with the rest of the app at all. The main thread just spawns a new thread, and then goes about its business. The new thread shows the form and does its thing. The new thread doesn't start any new threads of its own, and the two don't interact at all. However, the dialog I'm messing with does reside in a different process, and I suppose that simple fact could be causing issues, but I'm not sure where. All the communication between the two processes is done via SendMessage (and possibly PostMessage, but u get the idea). Well, I haven't heard any objections so far to my closing the form by aborting the thread, so I guess that's the route I'll be taking. It's just weird that the form suddenly stops closing like that. When looking around in the Platform SDK to get some clue, i did stumble across something, however... The documentation for WM_CLOSE says, under Remarks: By default, the DefWindowProc function calls the DestroyWindow function to destroy the window. ..and this can be found in the DestroyWindow documentation: If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child or owned windows when it destroys the parent or owner window. The function first destroys child or owned windows, and then it destroys the parent or owner window. ..and this is found under Remarks for DestroyWindow: A thread cannot use DestroyWindow to destroy a window created by a different thread. The dialog window does (indirectly) become a child of the form, and, since it's in a whole other process, it's certainly in a different thread, so this information is interesting.. ..but it doesn't seem to apply, because if I never show the Browse For Folder Dialog, and still use the dialog/control, everything will work fine, anyway. Also, if the form does start refusing to close, it will always refuse to close, even if I get rid of the dialog and its process. Quote
Tygur Posted October 23, 2003 Author Posted October 23, 2003 ok, i just decided to try calling DestroyWindow myself with I receive WM_CLOSE, and that works, too. So now I got two options. Override WndProc and either Abort the thread, or call the DestroyWindow api function on the form when I receive the WM_CLOSE message... Quote
*Experts* jfackler Posted October 24, 2003 *Experts* Posted October 24, 2003 I would think DestroyWindow would be cleaner than thread.abort. Any resources left when DestroyWindow is called? Jon Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.