#unhide (This post can be viewed by guests also)
Hallo !
Ich hab mir das Problem jetzt doch mal näher angesehen und einen Patch dafür gemacht, nachdem M$ leider noch immer säumig ist.
Nachdem er nicht nur deutschsprachige OA-Benutzer interessieren könnte, habe ich meine Erklärungen dafür auf Englisch geschrieben...
Analysis and fix for the fullscreen switching bug in Windows 7
================================================== ============
You may already know that ALT+Enter is a commonly used hotkey to switch
application between window mode and full-screen mode. For the cmd prompt,
the hotkey should work as expected on prior windows OS up to windows XP
and for example enters real Text mode for DOS applications.
But for Windows Vista and Windows 7, cmd can't be switched to
full-screen mode, this is by design.
For further information see
http://support.microsoft.com/kb/926657
There is a very interesting article explaining the rasons behind this,
which I recommend you to read:
http://colinxu.wordpress.com/2011/02...ssue-analysis/
Now it's also known and explained in the KB-Article, that you can get
back fullscreen with either a WDM-driver for the Windows XP display driver
model or to switch back to the default VGA video driver, which still
supports the old driver model.
This works, but there are some issues that users experienced, most notably
a bug in the Full screen switching code that causes a NTVDM lockup which I
will cover in this article.
How to enable Fullscreen for your DOS apps
==========================================
But first, how to conveniently use DOS Fullscreen applications without the
need to be stuck with the VGA driver all the time?
The preferred method is to install the XPDM display driver for your graphics
board, but if this doesn't work, I also have a very convenient solution.
So each approach is described here:
Installing the XPDM driver for your video card
-------------------------------------------
First, check if there is a Windows XP videp driver available for your graphics
board. You need to have the driver installation (.inf) files available, so
it won't help to just have an .exe Installer, because most likely this
installer checks for the OS version and refuses to run on Windows 7.
So in case you just get an .exe, you first need to extract the .inf
and .sys files from it, i.e. via 7-zip. Here is a thread describing how to
extract Intel video drivers for instance:
https://communities.intel.com/thread/12591
After you have the .inf files, you need to check if they contain a section
for Windows 7 or if the refuse to run there.
Intel Video drivers i.e. have this section:
Code:
[IntelGfx.NTx86.6.0]
; no install on Vista/Win7
[IntelGfx.NTx86.6.2]
; no install on Win8
[IntelGfx.NTx86.5.1]
%iSNBGM1% = iSNBM0, PCI\VEN_8086&DEV_0106
%iSNBGD1% = iSNBD0, PCI\VEN_8086&DEV_0102
%iSNBGM2% = iSNBM0, PCI\VEN_8086&DEV_0116
%iSNBGD2% = iSNBD0, PCI\VEN_8086&DEV_0112
%iSNBGM2P% = iSNBM0, PCI\VEN_8086&DEV_0126
%iSNBGD2P% = iSNBD0, PCI\VEN_8086&DEV_0122
%iSNBGD3% = iSNBD0, PCI\VEN_8086&DEV_010A
%iIVBGD0% = iIVBD0, PCI\VEN_8086&DEV_0162
%iIVBGM0% = iIVBM0, PCI\VEN_8086&DEV_0166
%iIVBGD0SRV% = iIVBD0, PCI\VEN_8086&DEV_016A
%iIVBGD0GT1% = iIVBD0, PCI\VEN_8086&DEV_0152
%iIVBGM0GT1% = iIVBM0, PCI\VEN_8086&DEV_0156
%iIVBGD0SRVGT1% = iIVBD0, PCI\VEN_8086&DEV_015A
As you can see, there is a section for Win7 (IntelGfx.NTx86.6.0), but
it doesn't contain any entries. So simply copy all the PCI strings
from Windows XP section (IntelGfx.NTx86.5.1) to this section
(just place the entries right under [IntelGfx.NTx86.6.0] line.
After fixing the .inf file, go to the Devie manager, uninstall the
WDDM driver and update the driver to the XPDM driver by pointing
the installation source to the diectory you have your .inf file in.
If everything works, it will detect and install the XP driver and if
you have luck, you are then running the XP driver which supports
fullscreen.
If you fail or are unabe to do this, please consider the following
option instead:
Using Fullsreen by dynamically switching between VGA and WDDM
---------------------------------------------------------------
Note: This is just meant for users that don't have a Windows XP Display
driver available for their Video card, using the XP Video driver is still the
preferred method as it saves you don't need to switch between the
video modes as described here. However the XPDM display driver may
have performance issues, so you can decide which works better for
you.
In Windows 7, you can dynamically enable and disable the video driver
without reboot, which is great because this way, you can also script this
and therefore make a batchfile, which disables your current WDM video
driver so that you have the standard VGA XPDM video driver, then
switch to fullscreen, use your DOS app and after you are done
restore the WDM video driver with devcon.
I made a patch that automates this for you which can be found
here
Otherwise here is how to do it:
* Download devcon [
http://support.microsoft.com/kb/311272] and place
it in your application directory, i.e. C:\OA4
* Put my Fullscreen switch utility (fullscr) attached below into your application directory.
* Now create a batchfile to start your application, i.e. STARTOA4.bat with
the following content:
------------------------------------------------------------------------------
Code:
@echo off
c:
cd \oa4
setlocal enableDelayedExpansion
for /f "tokens=1 skip=1 delims=:" %%I in ('devcon listclass display') do set J=%%I && devcon disable "!J:~0,44!"
fullscr
oa4.exe
fullscr 2
for /f "tokens=1 skip=1 delims=:" %%I in ('devcon listclass display') do set J=%%I && devcon enable "!J:~0,44!"
endlocal ------------------------------------------------------------------------------
You obviously have to adapt OA4.EXE to the application file for your application
to start (i.e. you may have a batch file to start, then use "call mybat.bat"
so that the startoa.bat isn't quit) and the "cd" at the beginning to go to your
application's directory. This is
necessary, as all Batch files that are run as admin via a link start in
%SystemRoot%, so you have to go to your dir first.
* Now as devcon is messing around with the system hardware, it needs
Administration rights to do its task.
So you now should create a shortcut to STARTOA4.BAT, i.e. on your desktop
and in its properties under "Advanced..." select [x] Run as administrator.
Done, now everytime you click your shortcut, video mode is switched to standard
VGA, then your DOS APP is run in fullscreen and
after exit, VGA card driver is restored back by enabling it. The fullscr 2 command does a switch back to windowed mode [fullscr.exe was updated on 2016/10/13 to support this], as some WDDM video drivers crash when switching back from textmode directly when reenabling.
I hope it works for you.
Don't forget to change the video resolution when in Standard VGA mode to
your normal resolution, as it's usually impractical to be stuck at 640x480
and even the Standard VGA driver normally supports higher resolutions,
but default is 640x480 when you disable your WDDM Display driver for
the first time.
As you now have the possibility to run your application in full screen text
mode, now let's go for the bugs that you are facing.
The Fullscreen NTVDM Bug
========================
Just try the following to reproduce it:
Just run your favourite DOS program in full screen mode, either by using
the trick mentioned above or by generally running in Standard VGA mode
and editing the settings of your favourite DOS program to switch it to Full screen
(Properties of .EXE -> Screen -> change from Windowed to Full screen) so that
it starts in full screen mode. Also ensure that you enabled the possibility to
capture ALT+Tab for switch (default). When the DOS App starts in Full-screen,
try to switch away with ALT+TAB and boom, your machine keeps hangig for almost
a Minute, afterwars you are back in the GUI mode and have a message box telling
you "NTVDM has encountered a system error. The parameter is incorrect".
This is especially naughty combined with the bug that the guy in the blog
mentioned above found. Once you switched to VGA and back to the driver,
Fullscreen is not blocked and therefore you also get into this lockup trouble,
once you notice that the mode switch went wrong and you want to go back
(i.e. by pressing ALT+RETURN again).
So that's pretty annoying, of course, so time to debug this issue.
It's not easy to find, as you can't have your debugger open while in full
screen, so I attached the WinDbg kernel debugger to the COM port and tried
to debug a usermode application with it.
Let's see where it hangs in ntvdm:
Code:
1: kd> !process 0 0 ntvdm.exe
PROCESS 84171460 SessionId: 1 Cid: 0f04 Peb: 7ffd6000 ParentCid: 0968
DirBase: 3ed32500 ObjectTable: 8633df00 HandleCount: 60.
Image: ntvdm.exe
1: kd> .process /r /p 84171460
Implicit process is now 84171460
.cache forcedecodeuser done
Loading User Symbols
..........................
1: kd> !process 84171460
PROCESS 84171460 SessionId: 1 Cid: 0f04 Peb: 7ffd6000 ParentCid: 0968
DirBase: 3ed32500 ObjectTable: 8633df00 HandleCount: 60.
Image: ntvdm.exe
VadRoot 85d8ee60 Vads 66 Clone 0 Private 285. Modified 53. Locked 0.
DeviceMap 8a275650
Token 9f245ac8
ElapsedTime 00:00:07.140
UserTime 00:00:00.000
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 0
Working Set Sizes (now,min,max) (1010, 50, 345) (4040KB, 200KB, 1380KB)
PeakWorkingSetSize 1010
VirtualSize 56 Mb
PeakVirtualSize 56 Mb
PageFaultCount 1153
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 644
THREAD 84dd25e0 Cid 0f04.07ac Teb: 7ffdf000 Win32Thread: fe50add8 WAIT: (UserRequest) UserMode Non-Alertable
841180e8 NotificationEvent
Not impersonating
DeviceMap 8a275650
Owning Process 0 Image: <Unknown>
Attached Process 84171460 Image: ntvdm.exe
Wait Start TickCount 3571787 Ticks: 257 (0:00:00:04.015)
Context Switch Count 225
UserTime 00:00:00.187
KernelTime 00:00:00.437
Win32 Start Address ntvdm!EntryPoint (0x0e2e5095)
Stack Init 8a5c5fd0 Current 8a5c5bc8 Base 8a5c6000 Limit 8a5c3000 Call 0
Priority 10 BasePriority 8 PriorityDecrement 2 IoPriority 2 PagePriority 5
ChildEBP RetAddr
8a5c5be0 82687b15 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
8a5c5c18 82686403 nt!KiSwapThread+0x266
8a5c5c40 826802cf nt!KiCommitThreadWait+0x1df
8a5c5cb8 8284a515 nt!KeWaitForSingleObject+0x393
8a5c5d20 8265c42a nt!NtWaitForSingleObject+0xc6
8a5c5d20 76e564f4 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8a5c5d34)
0122f9d8 76e55e6c ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0122f9dc 7502179c ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
0122fa48 752ef003 KERNELBASE!WaitForSingleObjectEx+0x98 (FPO: [SEH])
0122fa60 752eefb2 kernel32!WaitForSingleObjectExImplementation+0x75 (FPO: [3,0,4])
0122fa74 0e323125 kernel32!WaitForSingleObject+0x12 (FPO: [2,0,0])
0122fa8c 0e32321c ntvdm!CheckScreenSwitchRequest+0x2f (FPO: [1,0,0])
0122faa8 0e2e53e4 ntvdm!cpu_simulate+0xd4 (FPO: [0,1,4])
0122fab4 0e2e535f ntvdm!host_main+0x5f (FPO: [2,0,4])
0122faf0 0e2e559c ntvdm!main+0x3a (FPO: [SEH])
0122fbd0 752f1174 ntvdm!host_main+0x217 (FPO: [SEH])
0122fbdc 76e6b3f5 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
0122fc1c 76e6b3c8 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
0122fc34 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
THREAD 840f78f0 Cid 0f04.0268 Teb: 7ffde000 Win32Thread: fe4e1008 WAIT: (UserRequest) UserMode Alertable
841180e8 NotificationEvent
Not impersonating
DeviceMap 8a275650
Owning Process 0 Image: <Unknown>
Attached Process 84171460 Image: ntvdm.exe
Wait Start TickCount 3571786 Ticks: 258 (0:00:00:04.031)
Context Switch Count 16
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address ntvdm!ConsoleEventThread (0x0e317abb)
Stack Init 8a5e1fd0 Current 8a5e1bc8 Base 8a5e2000 Limit 8a5df000 Call 0
Priority 13 BasePriority 10 PriorityDecrement 2 IoPriority 2 PagePriority 5
ChildEBP RetAddr
8a5e1be0 82687b15 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
8a5e1c18 82686403 nt!KiSwapThread+0x266
8a5e1c40 826802cf nt!KiCommitThreadWait+0x1df
8a5e1cb8 8284a515 nt!KeWaitForSingleObject+0x393
8a5e1d20 8265c42a nt!NtWaitForSingleObject+0xc6
8a5e1d20 76e564f4 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8a5e1d34)
0238fca0 76e55e6c ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0238fca4 0e3164eb ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
0238fcb4 0e317a38 ntvdm!nt_process_suspend_event+0x1b (FPO: [0,0,0])
0238fd44 0e317b01 ntvdm!nt_event_loop+0x1a1 (FPO: [0,31,4])
0238fd78 752f1174 ntvdm!ConsoleEventThread+0x46 (FPO: [SEH])
0238fd84 76e6b3f5 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
0238fdc4 76e6b3c8 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
0238fddc 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
THREAD 83fb9d48 Cid 0f04.0ff4 Teb: 7ffdd000 Win32Thread: 00000000 WAIT: (WrLpcReply) UserMode Non-Alertable
83fb9f7c Semaphore Limit 0x1
Waiting for reply to ALPC Message 9ef6f548 : queued at port 840941d8 : owned by process 84101200
Not impersonating
DeviceMap 8a275650
Owning Process 0 Image: <Unknown>
Attached Process 84171460 Image: ntvdm.exe
Wait Start TickCount 3571791 Ticks: 253 (0:00:00:03.953)
Context Switch Count 52
UserTime 00:00:00.015
KernelTime 00:00:00.000
Win32 Start Address ntvdm!HeartBeatThread (0x0e309a56)
Stack Init 8a633fd0 Current 8a633ad0 Base 8a634000 Limit 8a631000 Call 0
Priority 15 BasePriority 15 PriorityDecrement 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr
8a633ae8 82687b15 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
8a633b20 82686403 nt!KiSwapThread+0x266
8a633b48 826802cf nt!KiCommitThreadWait+0x1df
8a633bc0 826cfb66 nt!KeWaitForSingleObject+0x393
8a633be8 8288ebee nt!AlpcpSignalAndWait+0x7b
8a633c0c 82884c6f nt!AlpcpReceiveSynchronousReply+0x27
8a633c9c 828782ca nt!AlpcpProcessSynchronousRequest+0x276
8a633cf8 8288fb17 nt!LpcpRequestWaitReplyPort+0x6a
8a633d20 8265c42a nt!NtRequestWaitReplyPort+0x4c
8a633d20 76e564f4 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8a633d34)
024dfa5c 76e558ac ntdll!KiFastSystemCallRet (FPO: [0,0,0])
024dfa60 752f1331 ntdll!NtRequestWaitReplyPort+0xc (FPO: [3,0,0])
024dfa80 752ecbcb kernel32!ConsoleClientCallServer+0x88 (FPO: [4,0,4])
024dfb40 752ecb55 kernel32!GetConsoleScreenBufferInfoEx+0x3d (FPO: [2,41,4])
024dfbb0 0e311cb5 kernel32!GetConsoleScreenBufferInfo+0x1b (FPO: [2,24,0])
024dfbf0 0e31210d ntvdm!resizeWindow+0x28 (FPO: [2,9,4])
024dfc04 0e3123ce ntvdm!textResize+0x5b (FPO: [0,0,0])
024dfc10 0e312611 ntvdm!nt_init_screen+0x1ce (FPO: [0,0,0])
024dfc24 0e2fc60f ntvdm!nt_set_paint_routine+0x1b2 (FPO: [2,0,4])
024dfc3c 0e319249 ntvdm!choose_vga_display_mode+0x16d (FPO: [0,0,4])
024dfc64 0e3195f4 ntvdm!syncVGAEmulationToHardware+0x399 (FPO: [0,5,4])
024dfc68 0e319795 ntvdm!fullScreenToWindowed+0xf (FPO: [0,0,0])
024dfca4 0e3095ca ntvdm!DoHandShake+0x13a (FPO: [SEH])
024dfcc0 0e309916 ntvdm!DelayHeartBeat+0x53 (FPO: [1,2,4])
024dfcdc 0e309ad0 ntvdm!Win32_host_timer+0x19 (FPO: [0,2,0])
024dfd10 752f1174 ntvdm!HeartBeatThread+0x7a (FPO: [SEH])
024dfd1c 76e6b3f5 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
024dfd5c 76e6b3c8 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
024dfd74 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0]) The last thread is interesting to us, it hangs at GetConsoleScreenBufferInfo
when waiting for a LPC message. We have to consider the new console
architecture of Windows 7 which is very well explained here:
http://blogs.technet.com/b/askperf/a...sole-host.aspx
There now is a seperated Usermode process called ConHost.exe that hosts
a console. It is talked to by a LPC call. So NTVDM is asking ConHost about
GetConsoleScreenBufferInfo, but ConHost.exe is not replying causing the hang.
That means we have to debug ConHost and check where it locks up:
Code:
1: kd> !process 0 0 conhost.exe
PROCESS 84101200 SessionId: 1 Cid: 0548 Peb: 7ffd8000 ParentCid: 018c
DirBase: 3ed323a0 ObjectTable: 9e88c418 HandleCount: 46.
Image: conhost.exe
1: kd> .process /r /p 84101200
Implicit process is now 84101200
.cache forcedecodeuser done
Loading User Symbols
.................
1: kd> !process 84101200
PROCESS 84101200 SessionId: 1 Cid: 0548 Peb: 7ffd8000 ParentCid: 018c
DirBase: 3ed323a0 ObjectTable: 9e88c418 HandleCount: 46.
Image: conhost.exe
VadRoot 8590b548 Vads 44 Clone 0 Private 166. Modified 0. Locked 0.
DeviceMap 8a275650
Token 863c3a98
ElapsedTime 00:00:07.046
UserTime 00:00:00.000
KernelTime 00:00:00.015
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 0
Working Set Sizes (now,min,max) (904, 50, 345) (3616KB, 200KB, 1380KB)
PeakWorkingSetSize 904
VirtualSize 39 Mb
PeakVirtualSize 39 Mb
PageFaultCount 926
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 204
THREAD 840ef030 Cid 0548.0230 Teb: 7ffde000 Win32Thread: fe9d9dd8 WAIT: (UserRequest) UserMode Non-Alertable
83ef73b8 SynchronizationEvent
Not impersonating
DeviceMap 8a275650
Owning Process 0 Image: <Unknown>
Attached Process 84101200 Image: conhost.exe
Wait Start TickCount 3571791 Ticks: 253 (0:00:00:03.953)
Context Switch Count 98
UserTime 00:00:00.000
KernelTime 00:00:00.031
Win32 Start Address conhost!ConsoleLpcThread (0x00dc159c)
Stack Init 8a5d9fd0 Current 8a5d9bc8 Base 8a5da000 Limit 8a5d7000 Call 0
Priority 11 BasePriority 8 PriorityDecrement 2 IoPriority 2 PagePriority 5
ChildEBP RetAddr
8a5d9be0 82687b15 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
8a5d9c18 82686403 nt!KiSwapThread+0x266
8a5d9c40 826802cf nt!KiCommitThreadWait+0x1df
8a5d9cb8 8284a515 nt!KeWaitForSingleObject+0x393
8a5d9d20 8265c42a nt!NtWaitForSingleObject+0xc6
8a5d9d20 76e564f4 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8a5d9d34)
004af6ac 76e55e6c ntdll!KiFastSystemCallRet (FPO: [0,0,0])
004af6b0 76e3fc72 ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
004af714 76e3fb56 ntdll!RtlpWaitOnCriticalSection+0x13e (FPO: [2,17,4])
004af73c 00dc1584 ntdll!RtlEnterCriticalSection+0x150 (FPO: [1,3,4])
004af744 00dc17df conhost!LockConsole+0xb (FPO: [0,0,0])
004af74c 00dc4577 conhost!RevalidateConsole+0xa (FPO: [1,0,0])
004af774 00dc1661 conhost!SrvGetConsoleScreenBufferInfo+0x15 (FPO: [2,6,4])
004af840 752f1174 conhost!ConsoleLpcThread+0x13e (FPO: [1,45,0])
004af84c 76e6b3f5 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
004af88c 76e6b3c8 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
004af8a4 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
THREAD 84064a28 Cid 0548.06b8 Teb: 7ffdf000 Win32Thread: fe9f6b80 WAIT: (UserRequest) UserMode Non-Alertable
84103500 SynchronizationEvent
83e5a858 NotificationEvent
Not impersonating
DeviceMap 8a275650
Owning Process 0 Image: <Unknown>
Attached Process 84101200 Image: conhost.exe
Wait Start TickCount 3571791 Ticks: 253 (0:00:00:03.953)
Context Switch Count 90
UserTime 00:00:00.000
KernelTime 00:00:00.140
Win32 Start Address conhost!ConsoleInputThread (0x00dc300a)
Stack Init 8a5f5fd0 Current 8a5f5748 Base 8a5f6000 Limit 8a5f3000 Call 544
Priority 8 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr
8a5f5760 82687b15 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
8a5f5798 82686403 nt!KiSwapThread+0x266
8a5f57c0 826826ef nt!KiCommitThreadWait+0x1df
8a5f593c 8284b625 nt!KeWaitForMultipleObjects+0x535
8a5f5bc8 8284b392 nt!ObpWaitForMultipleObjects+0x262
8a5f5d18 8265c42a nt!NtWaitForMultipleObjects+0xcd
8a5f5d18 76e564f4 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8a5f5d34)
0097f508 76e55e4c ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0097f50c 00de2f04 ntdll!NtWaitForMultipleObjects+0xc (FPO: [5,0,0])
0097f650 00dcee01 conhost!DisplayModeTransition+0x6fe (FPO: [2,71,4])
0097f6e8 767986ef conhost!ConsoleWindowProc+0x419 (FPO: [4,31,4])
0097f714 76798876 USER32!InternalCallWinProc+0x23
0097f78c 767970f4 USER32!UserCallWinProcCheckWow+0x14b (FPO: [SEH])
0097f7e8 7679738f USER32!DispatchClientMessage+0xda (FPO: [SEH])
0097f810 76e5642e USER32!__fnDWORD+0x24 (FPO: [1,3,0])
0097f83c 76798f8f ntdll!KiUserCallbackDispatcher+0x2e (FPO: [0,0,0])
0097f840 76798fc2 USER32!NtUserGetMessage+0xc (FPO: [4,0,0])
0097f85c 00dc30bb USER32!GetMessageW+0x33 (FPO: [4,0,4])
0097f8a0 752f1174 conhost!ConsoleInputThread+0xed (FPO: [1,8,4])
0097f8ac 76e6b3f5 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
0097f8ec 76e6b3c8 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
0097f904 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0]) Seems like both threads have a deadlock condition, so time to take a closer
look on it. First interesting up in the stack is LockConsole:
Code:
1: kd> u conhost!LockConsole
conhost!LockConsole:
00dc1579 68c081df00 push offset conhost!gConsoleInformation (00df81c0)
00dc157e ff155c13dc00 call dword ptr [conhost!_imp__RtlEnterCriticalSection (00dc135c)]
00dc1584 c3 ret
So there is a global Critical section object for the lock.
RevalidateConsole just calls LockConsole and always returns 0:
Code:
1: kd> u conhost!RevalidateConsole
conhost!RevalidateConsole:
00dc17d5 8bff mov edi,edi
00dc17d7 55 push ebp
00dc17d8 8bec mov ebp,esp
00dc17da e89afdffff call conhost!LockConsole (00dc1579)
00dc17df 8b4508 mov eax,dword ptr [ebp+8]
00dc17e2 c700c081df00 mov dword ptr [eax],offset conhost!gConsoleInformation (00df81c0)
00dc17e8 33c0 xor eax,eax
00dc17ea 5d pop ebp
00dc17eb c20400 ret 4
Now let's have a look at ConsoleWindowProc, which is calling DisplayModeTransition:
Code:
...
conhost!ConsoleWindowProc+0x28:
00dc2f2b c745ac01000000 mov dword ptr [ebp-54h],1
00dc2f32 e842e6ffff call conhost!LockConsole (00dc1579)
00dc2f37 8b0d6082df00 mov ecx,dword ptr [conhost!gConsoleInformation+0xa0 (00df8260)]
00dc2f3d 8b355882df00 mov esi,dword ptr [conhost!gConsoleInformation+0x98 (00df8258)]
...
00dc2fca 7409 je conhost!ConsoleWindowProc+0x306 (00dc2fd5)
00dc2fcc e834e5ffff call conhost!UnlockConsole (00dc1505)
00dc2fd1 8365ac00 and dword ptr [ebp-54h],0
00dc2fd5 ff75b4 push dword ptr [ebp-4Ch]
So the global lock is being held by ConsoleWindowProc when calling
DisplayModeTransition. Now let's check DisplayModeTransition: where it
hangs:
Code:
conhost!DisplayModeTransition+0xb5:
00de28bb 7512 jne conhost!DisplayModeTransition+0xc9 (00de28cf)
00de28bd 8b357013dc00 mov esi,dword ptr [conhost!_imp__DbgPrintEx (00dc1370)]
00de28c3 68c030de00 push offset conhost!`string' (00de30c0)
00de28c8 51 push ecx
00de28c9 6a70 push 70h
00de28cb ffd6 call esi
00de28cd ebcd jmp conhost!DisplayModeTransition+0x96 (00de289c)
00de28cf f705cc82df0000020000 test dword ptr [conhost!gConsoleInformation+0x10c (00df82cc)],200h
00de28d9 8b357013dc00 mov esi,dword ptr [conhost!_imp__DbgPrintEx (00dc1370)]
00de28df 8b3d2413dc00 mov edi,dword ptr [conhost!_imp__NtSetEvent (00dc1324)]
00de28e5 0f84e7000000 je conhost!DisplayModeTransition+0x1cc (00de29d2)
00de28eb 838d10ffffffff or dword ptr [ebp-0F0h],0FFFFFFFFh
00de28f2 53 push ebx
00de28f3 ff35fc82df00 push dword ptr [conhost!gConsoleInformation+0x13c (00df82fc)]
00de28f9 c7850cffffff00ba3cdc mov dword ptr [ebp-0F4h],0DC3CBA00h
00de2903 ffd7 call edi
00de2905 3bc3 cmp eax,ebx
00de2907 89851cffffff mov dword ptr [ebp-0E4h],eax
00de290d 0f8c86000000 jl conhost!DisplayModeTransition+0x193 (00de2999)
00de2913 a1cc81df00 mov eax,dword ptr [conhost!gConsoleInformation+0xc (00df81cc)]
00de2918 a38084df00 mov dword ptr [conhost!gConsoleInformation+0x2c0 (00df8480)],eax
00de291d e81edcffff call conhost!UnlockConsoleForCurrentThread (00de0540)
00de2922 898504ffffff mov dword ptr [ebp-0FCh],eax
00de2928 8d850cffffff lea eax,[ebp-0F4h]
00de292e 50 push eax
00de292f 53 push ebx
00de2930 6a01 push 1
00de2932 8d85ecfeffff lea eax,[ebp-114h]
00de2938 50 push eax
00de2939 ffb508ffffff push dword ptr [ebp-0F8h]
00de293f ff15f412dc00 call dword ptr [conhost!_imp__NtWaitForMultipleObjects (00dc12f4)]
00de2945 89851cffffff mov dword ptr [ebp-0E4h],eax
00de294b 8d8500ffffff lea eax,[ebp-100h]
00de2951 50 push eax
00de2952 e87eeefdff call conhost!RevalidateConsole (00dc17d5)
00de2957 3bc3 cmp eax,ebx
00de2959 898514ffffff mov dword ptr [ebp-0ECh],eax
00de295f 7d27 jge conhost!DisplayModeTransition+0x182 (00de2988)
00de2961 399d00ffffff cmp dword ptr [ebp-100h],ebx
00de2967 7414 je conhost!DisplayModeTransition+0x177 (00de297d)
00de2969 8b8504ffffff mov eax,dword ptr [ebp-0FCh]
00de296f 53 push ebx
00de2970 ff350483df00 push dword ptr [conhost!gConsoleInformation+0x144 (00df8304)]
00de2976 a38484df00 mov dword ptr [conhost!gConsoleInformation+0x2c4 (00df8484)],eax
00de297b ffd7 call edi
00de297d 8b8514ffffff mov eax,dword ptr [ebp-0ECh]
00de2983 e91a060000 jmp conhost!DisplayModeTransition+0x79c (00de2fa2)
00de2988 ffb504ffffff push dword ptr [ebp-0FCh]
00de298e e88bdbffff call conhost!LockConsoleForCurrentThread (00de051e)
00de2993 891d8084df00 mov dword ptr [conhost!gConsoleInformation+0x2c0 (00df8480)],ebx
00de2999 399d1cffffff cmp dword ptr [ebp-0E4h],ebx
00de299f 7431 je conhost!DisplayModeTransition+0x1cc (00de29d2)
00de29a1 8125cc82df00ffbfffff and dword ptr [conhost!gConsoleInformation+0x10c (00df82cc)],0FFFFBFFFh
00de29ab 53 push ebx
00de29ac ff350483df00 push dword ptr [conhost!gConsoleInformation+0x144 (00df8304)]
00de29b2 ffd7 call edi
...
As the function is quite long and a bit complicated, let me wrap this up for you as
stripped pseudocode to better understand what is going on here:
Code:
#define CONSOLE_VDM_REGISTERED 0x200
g_uOwningThread = gConsoleInformation.OwningThread;
nRecursions = UnlockConsoleForCurrentThread();
dwWaitRet = NtWaitForMultipleObjects(HandleCount, &Handles, WaitAny, 0, &Timeout);
dwRevalConsRet = RevalidateConsole(&pstgConsoleInformation);
if ( dwRevalConsRet >= 0 )
{
LockConsoleForCurrentThread(nRecursions);
g_uOwningThread = 0;
if (dwWaitRet) {
// ERROR: VDM not responding to initial request
...
}
if ( arg_1 == FALSE )
{
// Display Mode transition to windowed
...
// Flag 0x200 seems to indicate, that we are currently operating in
// real textmode Fullscreen
if ( gConsoleInformation_Flags & CONSOLE_VDM_REGISTERED )
{
NtSetEvent(hObject, 0);
fsctrlstr.StateHeader = g_BaseAddress;
fsctrlstr.StateLength = g_Length;
if (NtWaitForMultipleObjects(HandleCount, &Handles, WaitAny, 0, &Timeout) < 0 ||
GdiFullscreenControl(FullscreenControlSaveHardwareState, &State, StateSize, &State, &StateSize) < 0)
{
// ERROR: VDM not responding: Save Video States Failed
}
else
{
ConsoleControl(ConsoleSetVDMCursorBounds, NULL, 0);
ConnectToEmulator(FALSE);
}
if ( gConsoleInformation_Flags & CONSOLE_VDM_REGISTERED )
{
Timeout = (LARGE_INTEGER)-600000000i64;
dwWaitRet = NtSetEvent(hObject, 0);
if ( dwWaitRet >= 0 )
dwWaitRet = NtWaitForMultipleObjects(HandleCount, &Handles, WaitAny, 0, &Timeout);
...
}
}
}
}
if ( pstgConsoleInformation )
{
g_nRecursions = nRecursions;
NtSetEvent(g_hDoneEvent, 0);
}
return dwRevalConsRet; First thing to notice: As seen above, RevalidateConsole always returns
0, that means that the first IF path is always taken and the rest after that
path will never be reached. That dead code is a good place that can be used for
a patch lateron.
What is interesting are the functions UnlockConsoleForCurrentThread and
LockConsoleForCurrentThread:
Code:
1: kd> u conhost!UnlockConsoleForCurrentThread
conhost!UnlockConsoleForCurrentThread:
00de0540 8bff mov edi,edi
00de0542 56 push esi
00de0543 8b35c881df00 mov esi,dword ptr [conhost!gConsoleInformation+0x8 (00df81c8)]
00de0549 85f6 test esi,esi
00de054b 760c jbe conhost!UnlockConsoleForCurrentThread+0x19 (00de0559)
00de054d 57 push edi
00de054e 8bfe mov edi,esi
00de0550 e8b00ffeff call conhost!UnlockConsole (00dc1505)
00de0555 4f dec edi
00de0556 75f8 jne conhost!UnlockConsoleForCurrentThread+0x10 (00de0550)
00de0558 5f pop edi
00de0559 8bc6 mov eax,esi
00de055b 5e pop esi
00de055c c3 ret
Code:
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION; So gConsoleInformation.RecursionCount gets returned and if there are
Recursions, the console is Unlocked the number of recursions given.
Now the counterpart LockConsoleForCurrentThread:
Code:
1: kd> u conhost!LockConsoleForCurrentThread
conhost!LockConsoleForCurrentThread:
00de051e 8bff mov edi,edi
00de0520 55 push ebp
00de0521 8bec mov ebp,esp
00de0523 56 push esi
00de0524 8b7508 mov esi,dword ptr [ebp+8]
00de0527 eb05 jmp conhost!LockConsoleForCurrentThread+0x10 (00de052e)
00de0529 e84b10feff call conhost!LockConsole (00dc1579)
00de052e 3935c881df00 cmp dword ptr [conhost!gConsoleInformation+0x8 (00df81c8)],esi
00de0534 75f3 jne conhost!LockConsoleForCurrentThread+0xb (00de0529)
00de0536 5e pop esi
00de0537 5d pop ebp
00de0538 c20400 ret 4
So it calls LockConsole for the given number of recursions, unless it's
already locked. In the above code, it's adding back the number of
recursive Locks that were taken and released prior to
NtWaitForMultipleObjects() call.
Fine so far, but there also is a call to RevalidateConsole() which does
a LockConsole internally as seen above. This doesn't hurt, as LockConsole
only adds locks, if there are too few, but as RevalidateConsole()
already adds the missing lock, it doesn't do anything. As there is
already a lock from the ConsoleWindowProc in place, the call to
Revalidateconsole() is unnecessary, as LockConsoleForCurrentThread() will
also be able to add the missing lock itself.
Its return values are of no use either, as the code branch is never taken.
So this call is another thing that can be eliminated. -> Even more space
Now where does the deadlock finally happen?
It's the NtWaitForMultipleObjects call after ConnectToEmulator(), so my
assumption about what is happening here is the following:
Normally there is always one event queued to the VDM, then the event
hObject is signalled to actually run the task in the VDM and then it's
waiting for the VDM to complete the task by waiting on the wait-handles
via NtWaitForMultipleObjects. Now one of these tasks calls
GetConsoleScreenBufferInfo (As seen in the stack above), which in turn
is waiting for the lock. As the lock is actually being held by the
ConsoleWindowProc --> DEADLOCK!!
Now it seems that this may be expected so there are the
UnlockConsoleForCurrentThread and LockConsoleForCurrentThread functions
that temporarily remove the lock to prevent such situations while
waiting for the console to execute the requested task and afterwards
immediately place a lock on it again. But this is only done on the first
Wait-call and the deadlock-Condition actually occurs on the third
NtWaitForMultipleObjects call. These code blocks are only used for NTVDM
processing anway (as they are guarded by CONSOLE_VDM_REGISTERED flags),
so I guess this was just a programming error.
So this leads us to a strategy for fixing:
How to patch this deadlock
==========================
The simplest solution would be to just eliminate all the locking calls
and just leave the UnlockConsoleForCurrentThread call so that there are
no locks to wait for. This indeed works, but doesn't look like a proper
solution. When having a look at the way the NtWaitForMultipleObjects
function is called in all 3 places, it can be seen that it's always the
same call and it's always using the same variables. This gives us the
opportunity to reuse the existing calls to it and just add a wrapper
with UnlockConsoleForCurrentThread before and LockConsoleForCurrentThread
afterwards so that it's "guarded" by the Mutex releasing functions.
In pseudocode, we would replace all NtWaitForMultipleObjects calls with:
Code:
nRecursions = UnlockConsoleForCurrentThread();
dwWaitRet = NtWaitForMultipleObjects(HandleCount, &Handles, WaitAny, 0, &Timeout);
LockConsoleForCurrentThread(nRecursions);
To see how this can be accomplished, have a look at how this Sequence
is implemented in the ASM-code:
Code:
007A2905 |. 3BC3 CMP EAX,EBX
007A2907 |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX
007A290D |. 0F8C 86000000 JL conhost.007A2999
007A2913 |. A1 CC817B00 MOV EAX,DWORD PTR DS:[7B81CC]
007A2918 |. A3 80847B00 MOV DWORD PTR DS:[7B8480],EAX
007A291D |. E8 1EDCFFFF CALL conhost.007A0540 ; UnlockConsoleForCurrentThread
007A2922 |. 8985 04FFFFFF MOV DWORD PTR SS:[EBP-FC],EAX
007A2928 |. 8D85 0CFFFFFF LEA EAX,DWORD PTR SS:[EBP-F4]
007A292E |. 50 PUSH EAX
007A292F |. 53 PUSH EBX
007A2930 |. 6A 01 PUSH 1
007A2932 |. 8D85 ECFEFFFF LEA EAX,DWORD PTR SS:[EBP-114]
007A2938 |. 50 PUSH EAX
007A2939 |. FFB5 08FFFFFF PUSH DWORD PTR SS:[EBP-F8]
007A293F |. FF15 F4127800 CALL DWORD PTR DS:[<&ntdll.NtWaitForMult>; ntdll.ZwWaitForMultipleObjects
007A2945 |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX
007A294B |. 8D85 00FFFFFF LEA EAX,DWORD PTR SS:[EBP-100]
007A2951 |. 50 PUSH EAX ; /Arg1
007A2952 |. E8 7EEEFDFF CALL conhost.007817D5 ; \RevalidateConsole
007A2957 |. 3BC3 CMP EAX,EBX
007A2959 |. 8985 14FFFFFF MOV DWORD PTR SS:[EBP-EC],EAX
007A295F |. 7D 27 JGE SHORT conhost.007A2988
007A2961 |. 399D 00FFFFFF CMP DWORD PTR SS:[EBP-100],EBX
007A2967 |. 74 14 JE SHORT conhost.007A297D
007A2969 |. 8B85 04FFFFFF MOV EAX,DWORD PTR SS:[EBP-FC]
007A296F |. 53 PUSH EBX
007A2970 |. FF35 04837B00 PUSH DWORD PTR DS:[7B8304]
007A2976 |. A3 84847B00 MOV DWORD PTR DS:[7B8484],EAX
007A297B |. FFD7 CALL EDI
007A297D |> 8B85 14FFFFFF MOV EAX,DWORD PTR SS:[EBP-EC]
007A2983 |. E9 1A060000 JMP conhost.007A2FA2
007A2988 |> FFB5 04FFFFFF PUSH DWORD PTR SS:[EBP-FC] ; /Arg1
007A298E |. E8 8BDBFFFF CALL conhost.007A051E ; \LockConsoleForCurrentThread
007A2993 |. 891D 80847B00 MOV DWORD PTR DS:[7B8480],EBX
007A2999 |> 399D 1CFFFFFF CMP DWORD PTR SS:[EBP-E4],EBX
Now let's also have a look at the other places in the code, that call
the NtWaitForMultipleObjects function (only those who are used that have
gConsoleInformation_Flags set to CONSOLE_VDM_REGISTERED (0x200), as
only they are relevant for Fullscreen handling):
Code:
007A2DD8 |. 0F8C 95000000 JL conhost.007A2E73
007A2DDE |. 8D85 0CFFFFFF LEA EAX,DWORD PTR SS:[EBP-F4]
007A2DE4 |. 50 PUSH EAX
007A2DE5 |. 53 PUSH EBX
007A2DE6 |. 6A 01 PUSH 1
007A2DE8 |. 8D85 ECFEFFFF LEA EAX,DWORD PTR SS:[EBP-114]
007A2DEE |. 50 PUSH EAX
007A2DEF |. FFB5 08FFFFFF PUSH DWORD PTR SS:[EBP-F8]
007A2DF5 |. FF15 F4127800 CALL DWORD PTR DS:[<&ntdll.NtWaitForMult>; ntdll.ZwWaitForMultipleObjects
007A2DFB |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX
007A2E01 |. 3D 02010000 CMP EAX,102
007A2EE5 |. 7C 23 JL SHORT conhost.007A2F0A
007A2EE7 |. 8D85 0CFFFFFF LEA EAX,DWORD PTR SS:[EBP-F4]
007A2EED |. 50 PUSH EAX
007A2EEE |. 53 PUSH EBX
007A2EEF |. 6A 01 PUSH 1
007A2EF1 |. 8D85 ECFEFFFF LEA EAX,DWORD PTR SS:[EBP-114]
007A2EF7 |. 50 PUSH EAX
007A2EF8 |. FFB5 08FFFFFF PUSH DWORD PTR SS:[EBP-F8]
007A2EFE |. FF15 F4127800 CALL DWORD PTR DS:[<&ntdll.NtWaitForMult>; ntdll.ZwWaitForMultipleObjects
007A2F04 |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX
007A2F0A |> 399D 1CFFFFFF CMP DWORD PTR SS:[EBP-E4],EBX
It can be seen that the code for calling it is always the same.
So the first function that does the locked calls can be rewritten to
immediately lock again after the wait and reused in all functions.
Be aware that the code is a minefield full of relocations because of the
many globals that get used and these places cannot be overwritten with
our code without removing them from the relocation table. On the other hand,
code that is necessary and contains relocations shouldn't be removed as this
would also require touching the relocations. Therefore a solution is needed
that just fits in place.
So here is my solution to this (see comments for details)
Code:
0046290D EB 48 JMP SHORT conhost.00462957 ; Jump to regained space that does the actual
0046290F 90 NOP ; JL that should be at that place and then
00462910 90 NOP ; issue the CALL to the original code before
00462911 90 NOP ; continuing
00462912 90 NOP
00462913 |. A1 CC814700 MOV EAX,DWORD PTR DS:[4781CC] ; ATTN: Relocation fixups!
00462918 |. A3 80844700 MOV DWORD PTR DS:[478480],EAX ; ATTN: Relocation fixups!
0046291D |. E8 1EDCFFFF CALL conhost.00460540 ; UnlockConsoleForCurrentThread
00462922 |. 8985 04FFFFFF MOV DWORD PTR SS:[EBP-FC],EAX
00462928 |. 8D85 0CFFFFFF LEA EAX,DWORD PTR SS:[EBP-F4]
0046292E |. 50 PUSH EAX
0046292F |. 53 PUSH EBX
00462930 |. 6A 01 PUSH 1
00462932 |. 8D85 ECFEFFFF LEA EAX,DWORD PTR SS:[EBP-114]
00462938 |. 50 PUSH EAX
00462939 |. FFB5 08FFFFFF PUSH DWORD PTR SS:[EBP-F8]
0046293F |. FF15 F4124400 CALL DWORD PTR DS:[<&ntdll.NtWaitForMult>; ntdll.ZwWaitForMultipleObjects
00462945 |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX
0046294B FFB5 04FFFFFF PUSH DWORD PTR SS:[EBP-FC] ; Lock was placed here instead of RevalidateConsole
00462951 E8 C8DBFFFF CALL conhost.0046051E ; LockConsoleForCurrentThread
00462956 C3 RETN ; RETURN from CALL
00462957 7C 40 JL SHORT conhost.00462999 ; Here is the JL that originally was above
00462959 E8 B5FFFFFF CALL conhost.00462913 ; Now call the original code
0046295E EB 33 JMP SHORT conhost.00462993 ; And continue at the code after the original Lock
00462960 90 NOP ; Align
00462961 |. 399D 00FFFFFF CMP DWORD PTR SS:[EBP-100],EBX ; -- Start of dead code
00462967 |. 74 14 JE SHORT conhost.0046297D
00462969 |. 8B85 04FFFFFF MOV EAX,DWORD PTR SS:[EBP-FC]
0046296F |. 53 PUSH EBX
00462970 |. FF35 04834700 PUSH DWORD PTR DS:[478304]
00462976 |. A3 84844700 MOV DWORD PTR DS:[478484],EAX
0046297B |. FFD7 CALL EDI
0046297D |> 8B85 14FFFFFF MOV EAX,DWORD PTR SS:[EBP-EC]
00462983 |. E9 1A060000 JMP conhost.00462FA2
00462988 |> FFB5 04FFFFFF PUSH DWORD PTR SS:[EBP-FC] ; /Arg1
0046298E |. E8 8BDBFFFF CALL conhost.0046051E ; \LockConsoleForCurrentThread
00462993 |. 891D 80844700 MOV DWORD PTR DS:[478480],EBX ; -- End of dead code, execution continues here
00462999 |> 399D 1CFFFFFF CMP DWORD PTR SS:[EBP-E4],EBX
Now the CALLs to Wait function get substituted like this:
Code:
00462DD8 |. 0F8C 95000000 JL conhost.00462E73
00462DDE E8 30FBFFFF CALL conhost.00462913 ; Call our new wait function from above
00462DE3 EB 1C JMP SHORT conhost.00462E01 ; And continue at original location after wait
00462DE5 |. 53 PUSH EBX ; Start of dead code
00462DE6 |. 6A 01 PUSH 1
00462DE8 |. 8D85 ECFEFFFF LEA EAX,DWORD PTR SS:[EBP-114]
00462DEE |. 50 PUSH EAX
00462DEF |. FFB5 08FFFFFF PUSH DWORD PTR SS:[EBP-F8]
00462DF5 |. FF15 F4124400 CALL DWORD PTR DS:[<&ntdll.NtWaitForMult>; ntdll.ZwWaitForMultipleObjects
00462DFB |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX ; End of dead code
00462E01 |. 3D 02010000 CMP EAX,102 ; First instruction after Wait
Same here:
Code:
00462EE5 |. 7C 23 JL SHORT conhost.00462F0A
00462EE7 E8 27FAFFFF CALL conhost.00462913
00462EEC EB 1C JMP SHORT conhost.00462F0A
00462EEE |. 53 PUSH EBX
00462EEF |. 6A 01 PUSH 1
00462EF1 |. 8D85 ECFEFFFF LEA EAX,DWORD PTR SS:[EBP-114]
00462EF7 |. 50 PUSH EAX
00462EF8 |. FFB5 08FFFFFF PUSH DWORD PTR SS:[EBP-F8]
00462EFE |. FF15 F4124400 CALL DWORD PTR DS:[<&ntdll.NtWaitForMult>; ntdll.ZwWaitForMultipleObjects
00462F04 |. 8985 1CFFFFFF MOV DWORD PTR SS:[EBP-E4],EAX
00462F0A |> 399D 1CFFFFFF CMP DWORD PTR SS:[EBP-E4],EBX
So finally this is now fixed, as M$ doesn't seem to care.
Btw.: CONHOST issues some debug messages, if you attached a kernel debugger,
you can use the command
Code:
ed Kd_USERGDI_MASK ff
to enable tracing these messages.
Why is this not happening when starting the console in windowed mode?
================================================== ===================
As we have seen in the stack trace at the time of deadlock above, there are
some functions on the stack that finally cause a call to
GetConsoleScreenBufferInfo API which causes the deadlock. So let's trace
down the function calls on the stack and compare the execution flow to
see where it differs between start in full screen and start in windowed
mode (and then sithcing to fulscreen).
The function in nt_init_screen+0x1ce seems to be good candidate, so let's
trace it. Let's have a look at the function s a whole first:
Code:
0: kd> u ntvdm!nt_init_screen+0x11c
ntvdm!nt_init_screen+0x11c:
0e5e231c 8bff mov edi,edi
0e5e231e 53 push ebx
0e5e231f 33db xor ebx,ebx
0e5e2321 391d4483620e cmp dword ptr [ntvdm!soft_reset (0e628344)],ebx
0e5e2327 0f842b010000 je ntvdm!nt_init_screen+0x258 (0e5e2458)
0e5e232d 56 push esi
0e5e232e be49040000 mov esi,449h
0e5e2333 56 push esi
0e5e2334 e8451f0100 call ntvdm!sas_hw_at (0e5f427e)
0e5e2339 3c10 cmp al,10h
0e5e233b 7643 jbe ntvdm!nt_init_screen+0x180 (0e5e2380)
0e5e233d 56 push esi
0e5e233e e8dc210100 call ntvdm!sas_hw_at_no_check (0e5f451f)
0e5e2343 3c04 cmp al,4
0e5e2345 7228 jb ntvdm!nt_init_screen+0x16f (0e5e236f)
0e5e2347 56 push esi
0e5e2348 e8d2210100 call ntvdm!sas_hw_at_no_check (0e5f451f)
0e5e234d 3c07 cmp al,7
0e5e234f 741e je ntvdm!nt_init_screen+0x16f (0e5e236f)
0e5e2351 33c0 xor eax,eax
0e5e2353 391dacc3630e cmp dword ptr [ntvdm!EGA_GRAPH+0xc (0e63c3ac)],ebx
0e5e2359 0f95c0 setne al
0e5e235c 40 inc eax
0e5e235d 0faf05ecc4630e imul eax,dword ptr [ntvdm!PCDisplay+0xc (0e63c4ec)]
0e5e2364 0faf05e8c4630e imul eax,dword ptr [ntvdm!PCDisplay+0x8 (0e63c4e8)]
0e5e236b 8bf0 mov esi,eax
0e5e236d eb0d jmp ntvdm!nt_init_screen+0x17c (0e5e237c)
0e5e236f 8b3534c5630e mov esi,dword ptr [ntvdm!PCDisplay+0x54 (0e63c534)]
0e5e2375 0faf35e8c4630e imul esi,dword ptr [ntvdm!PCDisplay+0x8 (0e63c4e8)]
0e5e237c 3bf3 cmp esi,ebx
0e5e237e 7505 jne ntvdm!nt_init_screen+0x185 (0e5e2385)
0e5e2380 be80020000 mov esi,280h
0e5e2385 391d54d9650e cmp dword ptr [ntvdm!sc+0x34 (0e65d954)],ebx
0e5e238b 754d jne ntvdm!nt_init_screen+0x1da (0e5e23da)
0e5e238d 391d7c2b630e cmp dword ptr [ntvdm!BeepLastDuration+0x38 (0e632b7c)],ebx
0e5e2393 7521 jne ntvdm!nt_init_screen+0x1b6 (0e5e23b6)
0e5e2395 a1f0c4630e mov eax,dword ptr [ntvdm!PCDisplay+0x10 (0e63c4f0)]
0e5e239a 3b05782b630e cmp eax,dword ptr [ntvdm!BeepLastDuration+0x34 (0e632b78)]
0e5e23a0 7514 jne ntvdm!nt_init_screen+0x1b6 (0e5e23b6)
0e5e23a2 393d602b630e cmp dword ptr [ntvdm!BeepLastDuration+0x1c (0e632b60)],edi
0e5e23a8 750c jne ntvdm!nt_init_screen+0x1b6 (0e5e23b6)
0e5e23aa 3935702b630e cmp dword ptr [ntvdm!BeepLastDuration+0x2c (0e632b70)],esi
0e5e23b0 0f8497000000 je ntvdm!nt_init_screen+0x24d (0e5e244d)
0e5e23b6 a13cc5630e mov eax,dword ptr [ntvdm!PCDisplay+0x5c (0e63c53c)]
0e5e23bb 0fafc7 imul eax,edi
0e5e23be 8935b0d9650e mov dword ptr [ntvdm!sc+0x90 (0e65d9b0)],esi
0e5e23c4 a3acd9650e mov dword ptr [ntvdm!sc+0x8c (0e65d9ac)],eax
0e5e23c9 e8e4fcffff call ntvdm!textResize (0e5e20b2)
0e5e23ce a1f0c4630e mov eax,dword ptr [ntvdm!PCDisplay+0x10 (0e63c4f0)]
0e5e23d3 a3782b630e mov dword ptr [ntvdm!BeepLastDuration+0x34 (0e632b78)],eax
0e5e23d8 eb61 jmp ntvdm!nt_init_screen+0x23b (0e5e243b)
0e5e23da 33db xor ebx,ebx
0e5e23dc 43 inc ebx
0e5e23dd 391d7c2b630e cmp dword ptr [ntvdm!BeepLastDuration+0x38 (0e632b7c)],ebx
0e5e23e3 752a jne ntvdm!nt_init_screen+0x20f (0e5e240f)
0e5e23e5 393d602b630e cmp dword ptr [ntvdm!BeepLastDuration+0x1c (0e632b60)],edi
0e5e23eb 7522 jne ntvdm!nt_init_screen+0x20f (0e5e240f)
0e5e23ed 3935702b630e cmp dword ptr [ntvdm!BeepLastDuration+0x2c (0e632b70)],esi
0e5e23f3 751a jne ntvdm!nt_init_screen+0x20f (0e5e240f)
0e5e23f5 a1682b630e mov eax,dword ptr [ntvdm!BeepLastDuration+0x24 (0e632b68)]
0e5e23fa 3b054cd9650e cmp eax,dword ptr [ntvdm!sc+0x2c (0e65d94c)]
0e5e2400 750d jne ntvdm!nt_init_screen+0x20f (0e5e240f)
0e5e2402 a1582b630e mov eax,dword ptr [ntvdm!BeepLastDuration+0x14 (0e632b58)]
0e5e2407 3b0504d9650e cmp eax,dword ptr [ntvdm!host_screen_scale (0e65d904)]
0e5e240d 743e je ntvdm!nt_init_screen+0x24d (0e5e244d)
0e5e240f a13cc5630e mov eax,dword ptr [ntvdm!PCDisplay+0x5c (0e63c53c)]
0e5e2414 0fafc7 imul eax,edi
0e5e2417 8935b0d9650e mov dword ptr [ntvdm!sc+0x90 (0e65d9b0)],esi
0e5e241d a3acd9650e mov dword ptr [ntvdm!sc+0x8c (0e65d9ac)],eax
0e5e2422 e8eefcffff call ntvdm!graphicsResize (0e5e2115)
0e5e2427 a14cd9650e mov eax,dword ptr [ntvdm!sc+0x2c (0e65d94c)]
0e5e242c a3682b630e mov dword ptr [ntvdm!BeepLastDuration+0x24 (0e632b68)],eax
0e5e2431 a104d9650e mov eax,dword ptr [ntvdm!host_screen_scale (0e65d904)]
0e5e2436 a3582b630e mov dword ptr [ntvdm!BeepLastDuration+0x14 (0e632b58)],eax
0e5e243b 891d7c2b630e mov dword ptr [ntvdm!BeepLastDuration+0x38 (0e632b7c)],ebx
0e5e2441 8935702b630e mov dword ptr [ntvdm!BeepLastDuration+0x2c (0e632b70)],esi
0e5e2447 893d602b630e mov dword ptr [ntvdm!BeepLastDuration+0x1c (0e632b60)],edi
0e5e244d a1782b630e mov eax,dword ptr [ntvdm!BeepLastDuration+0x34 (0e632b78)]
0e5e2452 a39cd9650e mov dword ptr [ntvdm!sc+0x7c (0e65d99c)],eax
0e5e2457 5e pop esi
0e5e2458 5b pop ebx
0e5e2459 c3 ret
A lot of compares in there! We know from the stack trace above that it hits
0e5e23c9 e8e4fcffff call ntvdm!textResize (0e5e20b2)
when starting in full screen mode. So now trace the function in windowed mode:
Code:
0: kd> ! process 0 0 ntvdm.exe
PROCESS 840d8030 SessionId: 1 Cid: 08b8 Peb: 7ffda000 ParentCid: 0968
DirBase: 3ed323a0 ObjectTable: 9ea89170 HandleCount: 59.
Image: ntvdm.exe
0: kd> .process /r /p 840d8030
Implicit process is now 840d8030
.cache forcedecodeuser done
Loading User Symbols
..........................
0: kd> ba e1 ntvdm!nt_set_paint_routine+0x1ad
0: kd> g
Display Mode transition to windowed
Breakpoint 1 hit
ntvdm!nt_set_paint_routine+0x1ad:
001b:0e6d260c e80bfdffff call ntvdm!nt_init_screen+0x11c (0e6d231c)
1: kd> t
ntvdm!nt_init_screen+0x11c:
001b:0e6d231c 8bff mov edi,edi
001b:0e6d231f 33db xor ebx,ebx
001b:0e6d2321 391d4483710e cmp dword ptr [ntvdm!soft_reset (0e718344)],ebx
001b:0e6d2327 0f842b010000 je ntvdm!nt_init_screen+0x258 (0e6d2458)
001b:0e6d232d 56 push esi
001b:0e6d232e be49040000 mov esi,449h
001b:0e6d2333 56 push esi
001b:0e6d2334 e8451f0100 call ntvdm!sas_hw_at (0e6e427e)
001b:0e6d2339 3c10 cmp al,10h
001b:0e6d233b 7643 jbe ntvdm!nt_init_screen+0x180 (0e6d2380)
001b:0e6d2380 be80020000 mov esi,280h
001b:0e6d2385 391d54d9740e cmp dword ptr [ntvdm!sc+0x34 (0e74d954)],ebx
001b:0e6d238b 754d jne ntvdm!nt_init_screen+0x1da (0e6d23da)
001b:0e6d238d 391d7c2b720e cmp dword ptr [ntvdm!BeepLastDuration+0x38 (0e722b7c)],ebx
001b:0e6d2393 7521 jne ntvdm!nt_init_screen+0x1b6 (0e6d23b6)
001b:0e6d2395 a1f0c4720e mov eax,dword ptr [ntvdm!PCDisplay+0x10 (0e72c4f0)]
001b:0e6d239a 3b05782b720e cmp eax,dword ptr [ntvdm!BeepLastDuration+0x34 (0e722b78)]
001b:0e6d23a0 7514 jne ntvdm!nt_init_screen+0x1b6 (0e6d23b6)
001b:0e6d23a2 393d602b720e cmp dword ptr [ntvdm!BeepLastDuration+0x1c (0e722b60)],edi
001b:0e6d23a8 750c jne ntvdm!nt_init_screen+0x1b6 (0e6d23b6)
001b:0e6d23aa 3935702b720e cmp dword ptr [ntvdm!BeepLastDuration+0x2c (0e722b70)],esi
001b:0e6d23b0 0f8497000000 je ntvdm!nt_init_screen+0x24d (0e6d244d)
001b:0e6d244d a1782b720e mov eax,dword ptr [ntvdm!BeepLastDuration+0x34 (0e722b78)]
001b:0e6d2452 a39cd9740e mov dword ptr [ntvdm!sc+0x7c (0e74d99c)],eax
001b:0e6d2457 5e pop esi
001b:0e6d2458 5b pop ebx The function should hit 0e5e23b6, but it never does.
Let's have a look at some pseudocode again:
Code:
if (dw0e722b7c || dw0e72c4f0 != dw0e722b78 || dw0e722b60 != edi || dw0e722b70 != esi)
{
...
textResize();
} So it seems, that everything is equal already (text size already calcualted?)
when starting in windowed mode, but when starting in full scren mode, there
seems to be a difference:
Code:
0: kd> dd ntvdm!PCDisplay+0x10
0e5dc4f0 00000010
0: kd> dd ntvdm!BeepLastDuration+0x34
0e5d2b78 00000008
So that's the reason for this. From my point of view, it's still the fault of
conhost not to permit calls from NTVDM while it's processing some requests.
The patcher
===========
I wrote a little patcher that applies the patches and hopefully works for every
released version of conhost.exe on Windows 7.
Just download the patch and run it as administrator and then check if the bug
is fixed. I'd love to hear from you if it worked for you too.
The file can be downloaded as unregistered member from here:
http://www.waldbauer.com/tmp/reference.php?