How does one adhere in VB6 to Restart Manager messages. Here is some text
that Microsoft provides in the WinQual tests for Win7. Would that be the
Form_QueryUnload? If so, am I to just return False there or what is
Microsoft expecting?
Thanks,
Sarah
--------------------------------------------------------------------------------------------------------------------------------------------------
Some applications as part of their first launch display EULAs or auto update
prompts, registration forms, etc. If your application performs in this way,
it could be falsely reported as non-compliant with the restart requirement.
If any of the above files are reported for this reason, you should restart
testing from a clean state (clean OS). This time around, before running the
/postinstall phase, execute any run-once functionality in the application
PRIOR to running the /postinstall phase of the toolkit.
IMPACT IF NOT FIXED: By failing to shutdown gracefully, customers could lose
data when the OS finally forces a shutdown. It also slows the
shutdown/reboot process, impacting performance perceptions.
HOW TO FIX: In a critical shutdown, applications that return FALSE to
WM_QUERYENDSESSION will be sent WM_ENDSESSION and closed, while those that
time out in response to WM_QUERYENDSESSION, will be terminated. By following
these guidelines, you can ensure that your application will handle critical
shutdowns gracefully:
WM_QUERYENDSESSION with LPARAM = ENDSESSION_CLOSEAPP(0x1): GUI 19
applications must respond (TRUE) immediately in preparation for a restart
WM_ENDSESSION with LPARAM = ENDSESSION_CLOSEAPP(0x1): Applications must
return a 0 value within 30 seconds and shut down. At a minimum, applications
should prepare by saving any user data and state information that is needed
after a restart
Console applications that receive CTRL_C_EVENT notification should shut down
immediately. Drivers must not veto a system shutdown event
More information and guidance on requesting and responding to system
shutdowns and the RestartManager is available HERE
--------------------------------------------------------------------------------------------------------------------------------------------------
Yes.
Normally you prompt and save in queryunload, cancelling if the user
requests it, and then shutdown in the unload.
If the user cancels, and it's an important shutdown, it will be terminated.
--
Dee Earley (dee.e...@icode.co.uk)
i-Catcher Development Team
iCode Systems
(Replies direct to my email address will be ignored.
Please reply to the group.)
0 The user chose the Close command from the Control menu on the form.
1 The Unload statement is invoked from code.
2 The current Microsoft Windows operating environment session is ending.
3 The Microsoft Windows Task Manager is closing the application.
4 An MDI child form is closing because the MDI form is closing.
5 A form is closing because its owner is closing.
# 4 and 5 might need a little extra attention,
making sure that you don't put porgram-closing
code in sub-windows. But in general it goes
something like this:
If cancel = 0 then
' ask about saving open files before closing and provide a cancel
option.
Else
' quit. Either the system or your own code is telling you to close.
End If
The UnloadMode parameter...
> parameter in QueryUnload?
>
> 0 The user chose the Close command from the Control menu on the form.
> 1 The Unload statement is invoked from code.
> 2 The current Microsoft Windows operating environment session is ending.
> 3 The Microsoft Windows Task Manager is closing the application.
> 4 An MDI child form is closing because the MDI form is closing.
> 5 A form is closing because its owner is closing.
>
> # 4 and 5 might need a little extra attention,
> making sure that you don't put porgram-closing
> code in sub-windows. But in general it goes
> something like this:
>
> If cancel = 0 then
if unloadmode = 0 then
Woops. Thanks.
Sorry it took so long to try out the code, but sadly "no go". I
implemented the code fragment in an app and then ran the Windows 7 Windows
Qualification test. Here is the code fragment and what I received as the
result. :-(
----------------------------------------------------------------------------------------------------------------------------------------------------
Code Fragment
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
'Handle the request.
Cancel = False
'Process the request conforming to Microsoft Windows 7 compatibilty
requirements.
If UnloadMode = 0 Then
'Ask about saving open files before closing and provide a cancel
option.
'There is nothing to ask about, as there are no data files to save.
Else
'Quit. Either the system or your own code is telling you to close.
'Fall through to end the program.
Unload Me
End If
End Sub
----------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------
Windows 7 Qualification Report
----------------------------------------------------------------------------------------------------------------------------------------------------
7. Adhere to Restart Manager messages
Test case: Don't block reboot: PASS WITH WARNINGS
WARNING: The following files did not respond to system restart requests:
Executable c:\program files (x86)\butterflyvista\metricwiz\metricwiz.exe
failed to shutdown.
Some applications as part of their first launch display EULAs or auto update
prompts, registration forms, etc. If your application performs in this way,
it could be falsely reported as non-compliant with the restart requirement.
If any of the above files are reported for this reason, you should restart
testing from a clean state (clean OS). This time around, before running the
/postinstall phase, execute any run-once functionality in the application
PRIOR to running the /postinstall phase of the toolkit.
IMPACT IF NOT FIXED: By failing to shutdown gracefully, customers could lose
data when the OS finally forces a shutdown. It also slows the
shutdown/reboot process, impacting performance perceptions.
HOW TO FIX: In a critical shutdown, applications that return FALSE to
WM_QUERYENDSESSION will be sent WM_ENDSESSION and closed, while those that
time out in response to WM_QUERYENDSESSION, will be terminated. By following
these guidelines, you can ensure that your application will handle critical
shutdowns gracefully:
WM_QUERYENDSESSION with LPARAM = ENDSESSION_CLOSEAPP(0x1): GUI 19
applications must respond (TRUE) immediately in preparation for a restart
WM_ENDSESSION with LPARAM = ENDSESSION_CLOSEAPP(0x1): Applications must
return a 0 value within 30 seconds and shut down. At a minimum, applications
should prepare by saving any user data and state information that is needed
after a restart
Console applications that receive CTRL_C_EVENT notification should shut down
immediately. Drivers must not veto a system shutdown event
More information and guidance on requesting and responding to system
shutdowns and the RestartManager is available HERE.
----------------------------------------------------------------------------------------------------------------------------------------------------
As you can see, the test reported a failure even though I am implementing
the Form_QueryUnload.
Thoughts?
Thanks,
Sarah
Remove the above line. Not sure if this fixes the problem, but this is not
the proper place for it.
So if the system is restarting you'll get an event
with unloadmode of 2 or 3. You should always let
those through and quit. When you call Unload Me there
you cause another QueryUnload event where unloadmode
is 1.
If you don't need to check for files to save, etc.,
then you don't need to do anything. Just let the
form get unloaded. If you do need to check you use something
like the following (but don't put it in dialogue forms
that might fire an unloadmode of 5)
Private Sub Form_QueryUnload(cancel As Integer, unloadmode As Integer)
Dim LRet as long
' quit unless the X close button was clicked. You
' might also check here for File ->Exit. That would
' result in unloadmode of 1 but to the person using
' the program it's the same as the Close button.
If unloadmode <> 0 then Exit sub
'still going? Then Close button was clicked.
LRet = MsgBox("Do you want to save the current file before quitting?", 35)
Select Case LRet
Case 7
Exit Sub '-- let it unload. The system will get the
'-- return message it's waiting for.
Case 6
'-- save work and then deal with quitting
Case 2
cancel = 1 '-- They decided not to close, so block any further
unload action.
End Select
Actually, it doesn't start a loop; "Unload Me" is not needed because the
form is already unloading but it is apparently ignored
> Private Sub Form_QueryUnload(cancel As Integer, unloadmode As Integer)
> Dim LRet as long
> ' quit unless the X close button was clicked. You
> ' might also check here for File ->Exit. That would
> ' result in unloadmode of 1 but to the person using
> ' the program it's the same as the Close button.
>
> If unloadmode <> 0 then Exit sub
I'd suggest using the constants for better readability
if unloadmodeM<vbFormControlMenu Then Exit sub
of course, that means it won't ask to save if the form is closing for any
other reason so that may not be what is wanted.
> 'still going? Then Close button was clicked.
>
> LRet = MsgBox("Do you want to save the current file before quitting?",
> 35)
> Select Case LRet
> Case 7
> Exit Sub '-- let it unload. The system will get the
> '-- return message it's waiting for.
> Case 6
> '-- save work and then deal with quitting
> Case 2
> cancel = 1 '-- They decided not to close, so block any further
> unload action.
> End Select
and I'd definitely use the constants there rather than the numeric values
LRet = MsgBox("Do you want to save the current file before quitting?", _
vbYesNoCancel,"Save changes?")
Select Case LRet
Case vbNo:
' no action needed
Case vbYes:
'save and clear any 'dirty' flags
Case vbCancel:
cancel = True ' try to abort the unload
End Select
I had the code WITHOUT the added "Unload Me" until last night. I added that
hoping that that would get rid of the warning, but sadly does not. The
warning persists.
Questions:
1. Is there an easier way to test the shutdown without having to go through
the lengthly windows 7 qualification test?
2. Do all VB6 based applications suffer from this warning?
3. Thoughts on getting rid of the warning?
Yes, I will take the Unload Me statement out again, but will not run the
test, as that was already done.
Sarah
Interesting. You're right. I figured it would
result in at least one more QueryUnload event,
and that that might be why she was getting
an error report. I don't see why the
"Qualification Report" would be complaining
then. She even explicitly set cancel to False/0.
Maybe there's more to the code than what was
posted.
| > If unloadmode <> 0 then Exit sub
|
| of course, that means it won't ask to save if the form is closing for any
| other reason so that may not be what is wanted.
|
I suppose one could ask to save if Windows is
quitting.
| and I'd definitely use the constants there rather than the numeric values
|
Suit yourself. :)
After reading the document above, you need to subclass the form and
intercept WM_QUERYENDSESSION, and check LPARAM value for ENDSESSION_CLOSEAPP
or ENDSESSION_CRITICAL, and if so return +1(TRUE constant in the Windows
API). You should not prompt to save when you see this flag, just do what you
think is best(To save or not to save).
WM_QUERYENDSESSION Message:
http://msdn.microsoft.com/en-us/library/aa376890(VS.85).aspx
I thought someone might have given you a
definitive answer by now, but that hasn't happened.
Maybe you should try posting your actual code.
If your EXE is not properly responding to a system
message to close then you must be doing something to
keep it open. Returning 1 in QueryUnload or Unload?
Adding some kind of reference that keeps the program
running?
I found out that the problem is not just "my problem", but rather one that
anyone who creates VB6 based applications (possibly C# and VB.Net too from
what I am read on one post) suffer.
I found a tool, RMTool.exe, which Microsoft distributes as part of the
Windows Vista Qualification Test Tools. I got lucky to even find that.
That is what Microsoft evidently uses for both Windows Vista and Windows 7.
The tool allows a user to test the restart functionality. You simply use a
command line similar to the following just substituting the PID for the
application being tested. You can get the PID by going to the Windows Task
Manager and then "View | Select Columns" and then checking the box to view
PID (first entry).
"C:\Program Files\Microsoft\Logo Testing Tools for Windows\Restart
Manager\x86\rmtool.exe" -p 7256 -R
I created a simple VB6 project. When I say simple. I am not kidding. I
did not even change names. My first attempt was a plain Project1.exe
applet. I added nothing to the form, no background code. I saved and
clicked on make. I then got the process ID and ran the line above.
Guess what? VB6 failed. I ran the test against NotePad and that worked
like a charm. I suspect that all Microsoft applications work. VB6 based
ones do not.
STEP 2: I added subclassing.
I deciced to try some subclassing, as that was recommended. I took my plain
vanila application and added the following. By the way, I only added the two
message box statements later to see if I am even getting the messages
called. I am not, but I am getting ahead of myself.
--------------------------
New Subclassing Module:
--------------------------
Option Explicit
Public glPrevWndProc As Long
Public Const GWL_WNDPROC = (-4)
Public Const WM_QUERYENDSESSION = &H11
Public Const WM_ENDSESSION = &H16
Public Const ENDSESSION_LOGOFF = &H80000000
Public Const ENDSESSION_CRITICAL = &H40000000
Public Const ENDSESSION_CLOSEAPP = &H1
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal
hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA"
(ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
Public Function pMyWindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
'Process the WM_QUERYENDSESSION message. We need to process this message
for Windows 7 compatibility issues.
Select Case uMsg
Case WM_ENDSESSION
MsgBox "Inside WM_ENDSESSION", vbOKOnly, App.ProductName
pMyWindowProc = 0
Exit Function
Case WM_QUERYENDSESSION
'Assume a standard response to start.
pMyWindowProc = 1 'TRUE, Allow the system to end gracefully.
MsgBox "Inside WM_QUERYENDSESSION", vbOKOnly, App.ProductName
'Handle the specific sub-message.
Select Case lParam
Case ENDSESSION_LOGOFF
'Fall through and take the default action.
Case ENDSESSION_CRITICAL
'Fall through and take the default action.
Case ENDSESSION_CLOSEAPP
'Fall through and take the default action.
Case Else
'Fall through and take the default action.
End Select
Exit Function
Case WM_ENDSESSION
End Select
'Pass back all unprocessed messages to the original procedure associated
with the form.
pMyWindowProc = CallWindowProc(glPrevWndProc, hWnd, uMsg, wParam, lParam)
End Function
--------------------------
Form1 Code
--------------------------
Option Explicit
Private Sub Form_Load()
'Subclass Main Form: We want to process windows messages.
glPrevWndProc = SetWindowLong(Me.hWnd, GWL_WNDPROC, AddressOf
pMyWindowProc)
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
'Stops the form from intercepting Window's messages by resetting the
default procedures associated with the form.
Call SetWindowLong(Me.hWnd, GWL_WNDPROC, glPrevWndProc)
End Sub
--------------------------
Here is a run from NotePad: A working scenario:
-----------------------------------------------------------------------------------------------------------------------
Note: PID 6564 was of a running NotePad.exe. All tests done on Microsoft
Windows Vista.
C:\>"C:\Program Files\Microsoft\Logo Testing Tools for Windows\Restart
Manager\x86\rmtool.exe" -p 6564 -R
Starting Session
StartSession() returned 0
SUCCESS: StartSession()
Session Key: ebcedc00899770428fa4f29c7338f7f0??ª
Registering file
RegisterResources() returned 0
SUCCESS: RegisterResources()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 6640, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:6564, type 1, stat 1) - Notepad ()
Shuting down applications
SUCCESS: RmShutdown()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 6640, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:6564, type 1, stat 2) - Notepad ()
Restarting Applications
SUCCESS: RmRestart()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 6640, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:6564, type 1, stat 2) - Notepad ()
Ending Session
EndSession() returned 0
SUCCESS: EndSession()
-----------------------------------------------------------------------------------------------------------------------
Here is from a failed VB6 application (with or without subclassing and with
or without a Form_QueryUnload):
-----------------------------------------------------------------------------------------------------------------------
C:\>"C:\Program Files\Microsoft\Logo Testing Tools for Windows\Restart
Manager\x86\rmtool.exe" -p 1516 -R
Starting Session
StartSession() returned 0
SUCCESS: StartSession()
Session Key: bc96075ed55ce64caab5da4fd8c4f67f!!ª
Registering file
RegisterResources() returned 0
SUCCESS: RegisterResources()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 2124, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:1516, type 1, stat 1) - Project1.exe ()
Shuting down applications
*** FAILURE ***: RmShutdown()
Ending Session
EndSession() returned 0
SUCCESS: EndSession()
-----------------------------------------------------------------------------------------------------------------------
Usually I hit Ctrl+C to break the RMTool execution after the "Shuting down
applications" line, as the timeout is hugely long. The application usually
winds up hanging and needs to be forcefully shutdown, but that depends on
whether or not RMTool is broken and the like.
I found out by adding in the MsgBox statements that the message handler,
when using RMTool.exe never gets the WM_QUERYENDSESSION or the
WM_ENDSESSION. VB6 never gets the Form_QueryUnload or Form_Unload. Further
testing without RMTool reveals that Form_QueryUnload comes after the
WM_QUERYENDSESSION message.
Summary:
VB6, which is a form based programming language, has some sort of issue
complying with Microsoft Restart Manager requiremment for Windows Vista and
Windows 7.
Out of sheer curiosity I decided to try a Visual Studio 2010 VB.Net
executable with no code or form items added, WindowsApplication1.exe. I did
not even change the names. The test was a success, so Visual Studio 2010
VB.Net passes the Restart Manager test with flying colors. Sadly, VB6 does
not. Here is the result of my test.
-----------------------------------------------------------------------------------------------------------------------
C:\>"C:\Program Files\Microsoft\Logo Testing Tools for Windows\Restart
Manager\x86\rmtool.exe" -p 4108 -R
Starting Session
StartSession() returned 0
SUCCESS: StartSession()
Session Key: f09a4c7bb7adbf488a9b9ec883f93527??g
Registering file
RegisterResources() returned 0
SUCCESS: RegisterResources()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 8156, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:4108, type 1, stat 1) - WindowsApplication1 ()
Shuting down applications
SUCCESS: RmShutdown()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 8156, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:4108, type 1, stat 2) - WindowsApplication1 ()
Restarting Applications
SUCCESS: RmRestart()
Getting affected apps.
RmGetList() needs 1 structs, reboot reasons 0, returned 0xea
SUCCESS: Allocating RM_PROCESS_INFO array
SUCCESS: GetAffectedApps()
My PID: 8156, Affected Apps: 1, needed 1, reboot reasons 0
PID(1:4108, type 1, stat 2) - WindowsApplication1 ()
Ending Session
EndSession() returned 0
SUCCESS: EndSession()
-----------------------------------------------------------------------------------------------------------------------
I need to get VB6 to work with Microsoft's Restart Manager.
Any thoughts? Like I said, anyone developing a VB6 based application will
suffer this problem as me.
Sorry for being long winded, but there was a lot of information to impart.
Thanks in advance,
Sarah
Thanks to a certain someone named "Gary"
http://www.codenewsgroups.net/group/microsoft.public.vb.general.discussion/topic16868.aspx
when doing a Google search (Google, if you are reading this message, bring
back the old Google News!) solved the problem. I tried Gary's suggestion in
my Project1 and it worked. Here is what Gary said on the topic:
-------------------------------------------------------------------------------------
Paul,
I finally discovered the problem. An OCX had a form that didn't have Unload
Me in the QueryUnload event. Apparently every QueryUnload in every form of
both the EXE and any OCXs or DLLs called by the EXE must have:
If UnloadMode =2 then Unload Me
Gary
-------------------------------------------------------------------------------------
I did a Google search on UnloadMode constants, since I agree that "2" is
annoying to look at and came up with:
QueryUnload:
vbFormControlMenu = 0
vbFormCode = 1
vbAppWindows = 2
vbAppTaskManager = 3
vbFormMDIForm = 4
vbFormOwner = 5
That makes Gary's added line:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If UnloadMode = vbAppWindows Then
Unload Me
End If
End Sub
There is no subclassing necessary, but every form and every OCX apparently
must process this request.
Thanks,
Sarah
>(Google, if you are reading this message, bring
>back the old Google News!)
Google >> More >> Groups >> Advanced Groups Search (upper right hand
side) >> Group (down a ways on the page) >> key in
microsoft.public.vb.* or other newsgroup name as appropriate.
(You used to be able to put multiple newsgroup names to include
comp.???.* as well in there but Google dropped that capability years
ago.)
Tony
--
Tony Toews, Microsoft Access MVP
Tony's Main MS Access pages - http://www.granite.ab.ca/accsmstr.htm
Tony's Microsoft Access Blog - http://msmvps.com/blogs/access/
For a convenient utility to keep your users FEs and other files
updated see http://www.autofeupdater.com/
Pardon me for saying this, but that just sounds incredibly
superstitious.
Yes, if Windows is shutting down, logo requirements aside, it makes
sense you'd want your application to end gracefully.
That means what it means, and is entirely independent of your
application architecture.
Implication? The code above may indeed help you pass some test or
another, but it doesn't address the goal.
Programming is *not* a mindless pursuit...
--
.NET: It's About Trust!
http://vfred.mvps.org
I agree with this assessment. And closing forms may only be a partial
solution, depending on the overall architecture.
OP: See http://vb.mvps.org/samples/SysInfo for a working sample
designed (in part) to provide you the notification to do as Nobody
suggests.