Aldaviva / DarkNet

🌓 Enable native Windows dark mode for your WPF and Windows Forms title bars.
https://www.nuget.org/packages/DarkNet/
Apache License 2.0
66 stars 2 forks source link

Avoid crashing in non-Windows environments #9

Closed Aldaviva closed 1 year ago

Aldaviva commented 1 year ago

See report of crashes with Wine. It is not clear what the root cause is, as there is no stack trace to show which method call fails, the error message, or what exception class it throws. Typically, catching Win32Exception is good enough, but it may also be a native fault in unmanaged code.

It may also be smart to double-check that it doesn't crash in older Windows versions that, like Wine, don't contain the required DWM methods. I already tested this in Windows 7 before, but there may be a problem there now given the Wine crashes.

KeePass has good instructions of how to run .NET programs with Mono and Wine.

Aldaviva commented 1 year ago

Wine

Fedora 38 x64 Wine 8.12 from Fedora package repository

Setup

â›” DO NOT install "Wine Windows Program Launcher" from the Fedora Software GUI, because that will crash with the error 0110:err:module:load_wow64_ntdll failed to load L"\\??\\C:\\windows\\syswow64\\ntdll.dll" error c0000135.

sudo dnf install wine winetricks
env WINEPREFIX=$HOME/winedotnet wineboot --init
env WINEPREFIX=$HOME/winedotnet wine uninstaller --remove '{E45D8920-A758-4088-B6C6-31DBB276992E}' # Keep Mono and Wine separate
env WINEPREFIX=$HOME/winedotnet winetricks --force dotnet48 corefonts

Testing

env WINEPREFIX=$HOME/winedotnet wine ./darknet-demo-winforms.exe
Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named '#133' in DLL 'uxtheme.dll'.
   at Dark.Net.Win32.AllowDarkModeForWindow(IntPtr window, Boolean isDarkModeAllowed)
   at Dark.Net.DarkNet.SetModeForWindow(IntPtr windowHandle, Theme windowTheme, ThemeOptions options) in C:\Users\Ben\Documents\Projects\darknet\darknet\DarkNet.cs:line 195
   at Dark.Net.DarkNet.SetWindowThemeForms(Form window, Theme theme, ThemeOptions options) in C:\Users\Ben\Documents\Projects\darknet\darknet\DarkNet.cs:line 147
   at darknet_demo_winforms.Program.Main() in C:\Users\Ben\Documents\Projects\darknet\darknet-demo-winforms\Program.cs:line 23

So it looks like we just need to handle the managed exception EntryPointNotFoundException.

Mono

Fedora 38 x64 Mono 6.12.0 from Fedora package repository

Setup

sudo dnf install mono-complete # includes mono-winforms

Testing

mono ./darknet-demo-winforms.exe
Could not load signature of Dark.Net.DarkNet:SetWindowThemeWpf due to: Could not load file or assembly 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
Could not load signature of Dark.Net.IDarkNet:SetWindowThemeWpf due to: Could not load file or assembly 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.

Unhandled Exception:
System.TypeLoadException: Could not resolve the signature of a virtual method
  at darknet_demo_winforms.Program.Main () [0x0000e] in <600f536d9aef4f6994f5b7c786f46b5a>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Could not resolve the signature of a virtual method
  at darknet_demo_winforms.Program.Main () [0x0000e] in <600f536d9aef4f6994f5b7c786f46b5a>:0

The older DarkNet-Forms 1 avoided this by not having any methods that referred to any WPF types at all, using conditional compilation. With DarkNet 2, consumer Forms projects still don't have to refer to PresentationFramework, but some of the methods (even when not called) still refer to WPF types.

I don't think this can be fixed in one assembly without using reflection instead of strong typing.

Windows 7

Continues to work fine, does not throw EntryPointNotFoundException, even though the entry points in question did not exist in uxtheme.dll. Those P/Invoke calls are just no-ops.

Aldaviva commented 1 year ago

The Forms demo runs fine in Wine if we just catch EntryPointNotFoundException or Exception every place we call the secret UXTheme methods.