1) Open "Properties" window for Totalcmd.exe (Alt+Enter in Explorer).
2) Go to "Compatibility" tab.
3) Check "Run this program in compatibility mode for:" option and select "Windows 95" or "Windows 98 / "Windows ME".
4) Launch TC - it will start and immediately terminate silently.
Fix:
Totalcmd.exe: change $M Delphi directive from {$M $4000,$80000} to {$M $10000,$80000} - you may also increase the second parameter.
Totalcmd64.exe: change $M Delphi directive from {$M $1000 $1000000} to {$M $10000 $1000000}.
Explanation:
Each EXE file has (in its PE header) information about stack requirements: minimum and maximum stack size (for DLLs these values are ignored, because settings of EXE file that created the process are used). In Delphi, $M directive sets these values.
During the application startup, operating system allocates memory amount for the stack as requested by the "minimum size" value. Below this memory, OS places a memory page with PAGE_GUARD attribute set, and below this page OS creates a reserved address space (not allocated initially). Total size of these three areas is equal to "maximum size" value (rounded up to a 64kB boundary if needed). When application needs more and more stack, it touches the guard page eventually. This raises an EXCEPTION_GUARD_PAGE exception, but OS handles this exception internally and silently - it allocates an additional memory page in the previously reserved space and also recreates the guard page at the end of allocated memory space. This can be repeated until reserved address space is fully allocated - the stack can't grow anymore and the operating system raises a stack overflow exception.
For example, under Windows XP, when minimum stack size is 0x4000 and maximum stack size is 0x80000, the stack looks initially like this:
Code: Select all
BaseAddress: 000BC000 RegionSize: 00004000 State: COMMIT Protect: 00000004 <- allocated stack area
BaseAddress: 000BB000 RegionSize: 00001000 State: COMMIT Protect: 00000104 <- guard page
BaseAddress: 00040000 RegionSize: 0007B000 State: RESERVE <- reserved area
Code: Select all
BaseAddress: 0009A000 RegionSize: 00016000 State: COMMIT Protect: 00000004 <- allocated stack area
BaseAddress: 00099000 RegionSize: 00001000 State: COMMIT Protect: 00000104 <- guard page
BaseAddress: 00030000 RegionSize: 00069000 State: RESERVE <- reserved area
Code: Select all
BaseAddress: 00031000 RegionSize: 0007F000 State: COMMIT Protect: 00000004 <- allocated stack area
BaseAddress: 00030000 RegionSize: 00001000 State: RESERVE
To handle the stack properly, compilers don't allocate stack area in pieces larger than 0x1000 bytes at once - this guarantees that the guard page will be touched and not jumped over. When some function uses local variables larger than 0x1000 bytes, compiler allocates stack in 0x1000-byte pieces in a loop:
Code: Select all
SomeFunction:
push eax
stack_alloc_loop:
mov eax,$00000150 ; we are allocating local variables of total size 0x150 * 0x1000
add esp,$FFFFF004 ; same as: sub esp,$00000FFC
push eax ; touch the stack (potentially a guard page)
dec eax
jnz stack_alloc_loop
So, where the problem is? Well, Windows operating systems have bugs in stack handling - sometimes guard page is not created. This varies between Win9x and WinNT family, and in most cases happens only when the defined minimum stack size is almost equal or equal to the maximum stack size. In this case, when the stack is near overflow, a stack overflow exception can't be raised properly and application terminates immediately. To avoid all potential problems, always allocate stack in this way:
minimum size = x * 0x10000
maximum size = y * 0x10000
where: y > x
For example:
{$M $00050000,$00050000} - this will cause problems under Win9x
{$M $0003F000,$00040000} - this will cause problems under WinNT
{$M $00500000,$00500000} - this will cause problems under Win9x and WinNT
{$M $00010000,$00220000} - this will not cause any problems
In TC case, when compatibility mode with Win9x is on, the stack looks initially like this:
Code: Select all
BaseAddress: 000B0000 RegionSize: 00010000 State: COMMIT Protect: 00000004
BaseAddress: 00040000 RegionSize: 00070000 State: RESERVE
Summary:
First parameter of the $M directive (minimum stack size) should be set to $10000 in both x32 and x64 versions. This will not increase memory usage, because even now TC allocates more than $10000 bytes of stack before even shows any window. I don't know if there are problems with invalid stack structure in x64 applications, but it's better to be on the safe side.
Besides, the second $M parameter (maximum stack size) in x32 version could also be increased. It is currently only $80000 = 512kB of memory - it could be set for example to $200000 = 2MB. This memory won't be allocated without a real need - just 1.5MB of an additional address space will be reserved. This would allow TC plugins to use more stack without raising a stack overflow exception by TC. In x64 version, the maximum stack size is already set to $1000000 = 16MB - but there is a lot of address space in x64 mode, so such a large reserved address space isn't any problem. We should also remember, that stack usage in x64 mode is larger - each stack element (PUSH and POP instructions) occupies 8 bytes, not 4 bytes as in x32 mode.
Regards