-Crash after cm_UnloadPlugins

The behaviour described in the bug report is either by design, or would be far too complex/time-consuming to be changed

Moderators: Hacker, petermad, Stefan2, white

Post Reply
User avatar
tbeu
Power Member
Power Member
Posts: 1354
Joined: 2003-07-04, 07:52 UTC
Location: Germany
Contact:

-Crash after cm_UnloadPlugins

Post by *tbeu »

I can reproduce this with TC8.5b5 on WinXPSP3.

Code: Select all

---------------------------
Total Commander 8.50b5
---------------------------
Access violation at address 03B9AFF0. Read of address 03B9AFF0.
Access violation at address 03B9AFF0. Read of address 03B9AFF0
Windows XP SP3 5.1 (Build 2600)

Please report this error to the Author, with a description
of what you were doing when this error occurred!

Windows exception: C0000005
Stack trace:
03B9AFF0
>46E133  46FCD0  46F134  42B137  425790  447917
425790  42AE4C  42AEE8  6FA2E3  
Raw:
46CBA8  46E133  50005A  6F0070  407713  46FCD0
423CF1  50005A  6F0070  630075  448385  403292
4030E4  46F1F9  6BDCD4  4076EE  46F8E5  46F8FB
402249  4033D0  4021C8  4023C7  4023EF  6BDCD4
46F9BE  46F134  42B137  4483A9  4A1B1C  50005A
6F0070  630075  630075  6F9A5D  4A2CED  6F737F
6F060E  6ACBB6  611E8C  611EAF  6F737F  6F060E
6B7B19  6BDE00  5DBBED  6B6A90  6B6A90  50005A
6F0070  630075  41E295  630075  6F7283  41E6F9
6F749B  6F0070  50005A  6F0070  630075  6F0070

Press Ctrl+C to copy this report!
Continue execution?
---------------------------
Ja   Nein   
---------------------------

---------------------------
Total Commander 8.50b5
---------------------------
Access violation at address 0321AFF0. Read of address 0321AFF0.
Access violation at address 0321AFF0. Read of address 0321AFF0
Windows XP SP3 5.1 (Build 2600)

Please report this error to the Author, with a description
of what you were doing when this error occurred!

Windows exception: C0000005
Stack trace:
0321AFF0
>46E133  46FCD0  46F134  42B137  425790  447917
425790  42AE4C  42AEE8  6FA2E3  
Raw:
46CBA8  46E133  407713  46FCD0  448385  403292
4030E4  46F1F9  446405  4076EE  46F8E5  46F8FB
4020A2  402226  4023C7  4023EF  446405  46F9BE
46F134  42B137  4483A9  428E49  428ED6  449F96
428F7E  445729  42922E  403296  446405  448631
436241  42746C  4479E2  446343  44825B  4482B7
44952D  446405  448631  42746C  4479E2  446343
445A8F  4271CF  4296B2  459B86  403296  482D0E
5D0573  46B195  449DE3  46B1A2  640061  447885
425790  4486DB  427FFD  448E3A  449F58  660518

Press Ctrl+C to copy this report!
Continue execution?
---------------------------
Ja   Nein   
---------------------------

---------------------------
Total Commander 8.50b5
---------------------------
Access violation at address 0331AFF0. Read of address 0331AFF0.
Access violation at address 0331AFF0. Read of address 0331AFF0
Windows XP SP3 5.1 (Build 2600)

Please report this error to the Author, with a description
of what you were doing when this error occurred!

Windows exception: C0000005
Stack trace:
0331AFF0
>46E133  46FCD0  46F134  42B137  446405  446343
44825B  4482B7  446343  445A8F  567990  65EF73
65B72A  64EABF  448E9F  446405  4479E2  448362
425790  447917  425790  42AE4C  42AEE8  6FA2E3

Raw:
46CBA8  46E133  407713  46FCD0  62B3DC  62CE3B
403292  4030E4  46F1F9  6F0070  4076EE  46F8E5
46F8FB  402249  4033D0  4021C8  4023C7  4023EF
6F0070  46F9BE  46F134  42B137  42906A  423841
423954  622E4E  4020A2  402226  402249  4023C7
4023EF  4023C7  4023EF  4026B8  4036C1  44D87C
402249  4033D0  4021C8  4023C7  4023EF  4026B8
4036C1  6F2F6A  4020A2  44DF31  44DF21  4020A2
402226  402249  44DF21  4026A0  402CEC  622A57
403296  6B9744  6232C2  623421  4202B0  62A857

Press Ctrl+C to copy this report!
Continue execution?
---------------------------
Ja   Nein   
---------------------------
What I do is to view a MAT-File with MAT-File Viewer plugin (development debug version), close lister, call cm_UnloadPlugins and try to view MAT-file again.

I tried to debug but could not see the exception within plugin code.

TC 8.01 does not crash but fails to start the plugin after cm_UnloadPlugins, i.e. lister displays binary text (as if there is no plugin installed).
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50541
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

Unfortunately the crash address isn't in TC. I guess that it is in one of the plugins. If a plugin is busy in a background thread when you use this command, it may crash. Unfortunately there is nothing I can do to prevent that, so use this command only for plugin development.
Author of Total Commander
https://www.ghisler.com
User avatar
tbeu
Power Member
Power Member
Posts: 1354
Joined: 2003-07-04, 07:52 UTC
Location: Germany
Contact:

Post by *tbeu »

This is was I was already afraid of. Is there any chance that TC can tell if the exception occurs within TOTALCMD.EXE at time of the exception?
TC plugins: Autodesk 3ds Max / Inventor / Revit Preview, FileInDir, ImageMetaData (JPG Comment/EXIF/IPTC/XMP), MATLAB MAT-file Viewer, Mover, SetFolderDate, Solid Edge Preview, Zip2Zero and more
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50541
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

Addresses in TC usually start with 004 or 005 (4 or 5 when 6 digit only).
Author of Total Commander
https://www.ghisler.com
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

Showing file name of the module that caused an error would eliminate many crash reports (when the user sees that the crash is inside some plugin or shell extension). I mean something like this:

Code: Select all

function FindHInstance(Address : Pointer) : HINST;
var
  MemInfo : TMemoryBasicInformation;
begin
  Result:=0;
  if VirtualQuery(Address,MemInfo,SizeOf(MemInfo)) = SizeOf(MemInfo) then
  if MemInfo.State = MEM_COMMIT then
    Result:=HINST(MemInfo.AllocationBase);
end;

Code: Select all

var
  Buffer : packed array[0..MAX_COMPUTERNAME_LENGTH + MAX_PATH] of Char; {Or explicitly AnsiChar or WideChar}

Code: Select all

try
  ...
except
  Buffer[GetModuleFileName(FindHInstance(ExceptAddr),Buffer,SizeOf(Buffer) div SizeOf(Buffer[0])-1)]:=#0;
  ...
end;
Some notes:

- For zero-based arrays of chars, Delphi 2 treats Length(Buffer) differently than newer Delphi versions, so SizeOf(Buffer) div SizeOf(Buffer[0]) must be used instead of Length(Buffer).

- Assigning #0 by using Buffer[GetModuleFileName(...)]:=#0 is required for cases when GetModuleFileName fails (this is possible for dynamically generated code, that isn't located inside any EXE of DLL).

- Adding MAX_COMPUTERNAME_LENGTH guarantees space when TC has been launched with UNC prefix like this: "\\127.0.0.1\C$\Program Files\Total Commander\TOTALCMD.EXE".

- There must be -1 in the last parameter, so there is a guaranteed space for the #0 assignment; the range [0..MAX_COMPUTERNAME_LENGTH + MAX_PATH - 1] is still sufficient for the longest possible path.

Regards
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50541
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

Showing file name of the module that caused an error would eliminate many crash reports
That would indeed be very useful, but in most cases these happen inside of Windows DLLs called by plugins. TC tries to catch exceptions from plugins when calling the plugin, but this isn't possible when the plugin is a Lister plugin (own window procedure) or when it uses its own background thread.
Author of Total Commander
https://www.ghisler.com
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

Yeah, this can be problematic. So what about finding module names for all addresses from the stack trace? Instead of:

Code: Select all

Windows exception: C0000005 
Stack trace: 
0321AFF0 
>46E133  46FCD0  46F134  42B137  425790  447917 
425790  42AE4C  42AEE8  6FA2E3
something like this:

Code: Select all

Windows exception: C0000005 
Call stack:
ReturnAddr: 77F88203  Module: C:\WINDOWS\system32\ntdll.dll
ReturnAddr: 793DB3BC  Module: C:\WINNT\system32\kernel32.dll
ReturnAddr: 06416898  Module: C:\Program Files\Total Commander\plugins\wcx\xxx\yyy.wcx
ReturnAddr: 00403857  Module: C:\Program Files\Total Commander\TOTALCMD.EXE
Advanced user will then notice, that the cause of the problem may be inside plugin yyy.wcx. And even if not, somebody here will point at this immediately.
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50541
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

This would work with 3-4 stack items, but not with 20 or 30 as I often see.
Author of Total Commander
https://www.ghisler.com
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

I agree that for 20 addresses this would make exception dialog much higher. However:

- this dialog may be not very pretty - in this special case, when the user sees it, it should be rather more informative than pretty,

- some popular system DLLs (ntdll.dll, kernel32.dll, user32.dll, gdi32.dll, shell32.dll) could be simply filtered,

- module names may be just an addition to the current information and may be limited to some maximum number of lines.

Regards
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50541
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

OK, I will think about a good solution. Maybe I could show only modules with the extension of a plugin.
Author of Total Commander
https://www.ghisler.com
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

Update to my post about GetModuleFileName above.

Let's talk about a buffer size, when calling this API function.



Let's assume that we have an application (EXE) or library (DLL) in such a local path, that meets the requirement for a standard Windows path - is shorter than MAX_PATH = 260 chars, and after appending a terminating null character it is still not greater than MAX_PATH.

For example, let's consider such paths (259 chars -> MAX_PATH = 260 chars after appending #0):
C:\iiiiiiii...244 "i" chars in total...iiiiiiii\Program.exe
C:\iiiiiiii...244 "i" chars in total...iiiiiiii\Library.dll

We may think, that a buffer defined as "packed array [0..MAX_PATH-1] of Char" is enough to receive the longest possible path with GetModuleFileName. But it's not true.

Case 1:
The EXE may be launched with CreateProcess, or the DLL may be loaded with LoadLibrary, by appending '\\?\' prefix to the path - something like this:

Code: Select all

CreateProcess('\\?\C:\iiiiiiii...244 "i" chars in total...iiiiiiii\Program.exe',...)
Now the required buffer length will be:

Code: Select all

4 (\\?\) +
3 (C:\) +
244 ("i" chars) +
1 (\) +
11 (Project.exe) +
1 (terminating null char)
= 264, which is greater than MAX_PATH, and is equal to MAX_PATH+4.
So the buffer [0..MAX_PATH+4-1] is required to successfully get the full path (and for example extract the file name part) in this case.

Case 2:
Let's create a network share named 'ssssssss...80 "s" chars in total...ssssssss' for 'C:' drive on the local machine. Now we can use something like:

Code: Select all

CreateProcess('\\?\UNC\127.000.000.001\ssssssss...80 "s" chars in total...ssssssss\iiiiiiii...244 "i" chars in total...iiiiiiii\Program.exe',...)
Now the required buffer length will be:

Code: Select all

8 (\\?\UNC\) +
15 (127.000.000.001) +
1 (\) +
80 ("s" chars) +
1 (\) +
244 ("i" chars) +
1 (\) +
11 (Project.exe) +
1 (terminating null char)

Note: maximum server name length is MAX_COMPUTERNAME_LENGTH = 15, maximum share name length is 80.
= 362, which is greater than MAX_PATH, and is equal to MAX_PATH+102.
So the buffer [0..MAX_PATH+102-1] is required to successfully get the full path (and for example extract the file name part) in this case.



The examples above are for local paths, that meet the MAX_PATH limitation. But under Windows NT family, when using '\\?\' prefix, maximum possible path length is 32767 chars (+ terminating null char) - see MSDN "Naming Files, Paths, and Namespaces". To handle such paths, the buffer [0..$8000-1] is required.



Summary:
It is out of control of TC, how its EXE will be launched. It's also out of control, what DLLs and how will be loaded into TC's process (for example message hooks). So, to be on the safe side, TC should use buffer of size $8000 chars:

Code: Select all

var
  Buffer : packed array[0..$8000-1] of Char;

  Buffer[GetModuleFileName(...,Buffer,SizeOf(Buffer) div SizeOf(Buffer[0])-1)]:=#0;
But there is a danger of stack exhaustion, when declaring a local variable of length $8000, in particular in some recursively called functions. So the alternative is:

Code: Select all

var
  Buffer : string;

  SetLength(Buffer,$8000);
  SetLength(Buffer,GetModuleFileName(...,PChar(Buffer),Length(Buffer)));
We don't have to append "-1" at the end of buffer length in this case, and we don't have to worry about the proper null-termination - Delphi inserts a null character at the end of the dynamic string automatically.
Post Reply