Migrate theming from net461 to net50
NikolayXHD opened this issue ยท 25 comments
Beginning of discussion: #8522 (comment)
Have you seen https://github.com/Microsoft/Detours?
Thank you, will look at it the coming weekend
@NikolayXHD if you run with the Managed Debug Assistants turned on in VS, do they flag any kind of interop issues? It may be that a calling convention or argument marshalling is incorrectly specified, and that it worked under .NET Framework but fails under .NET Core.
Thank you, good to know it exists! Gave it a shot, unfortunately it no new information so far.
The program loads a bunch of DLLs, then ExecutionEngineException
, nothing intercepted or outputted in between
detours.net should be able to achieve what we need, not directly though.
.\DetoursNetRuntime myplugin.dll process_to_inject_hooks_into.exe
is not what we want. The goal would be to achieve hooking "from inside" a .net process by learning from detours.net codebase.
Plan
- Make sure the detours.net suggested approach is able to hook the theming api functions we need.
At this point, if successfull, we at least know that detours can do the job, with or without detours.net. - Achieve hooking from within the process.
point 1. Should be relatively straightforward
point 2. May prove difficult, however, the fact that detours.net codebase is not huge at all, suggests that using original detours from .NET is doable with moderate effort.
is there a list of the APIs that are currently hooked for theming?
8 methods, see Win32ThemeHooks.InstallHooks
Related WinForms issue dotnet/winforms#5166
I had success getting CoreHook to work with .net core as replacement for easyhook. https://github.com/unknownv2/CoreHook
CoreHook looks interesting but it does appear that WinForms theming is on the roadmap for .NET 8. dotnet/winforms#5166 (comment)
If they implement it, that will be November 2023. Unless someone volunteers to build in CoreHook and support it, we can probably count on 2024. I would just wait this one out, it's one year without dark theme in Git Extensions 4.0+.
Related WinForms issue dotnet/winforms#5166
I wouldn't count on this in the foreseeable future (if at all). dotnet/winforms#7641 is significantly more likely to occur, though I'd be surprised if it lands in .NET 8 timeframe.
I had success getting CoreHook to work with .net core as replacement for easyhook. https://github.com/unknownv2/CoreHook
@macsux Do you have some more information on that? Documentation ? An example repository doing it? Or is it a drop-in replacement for EasyHook?
It's pretty much a drop in replacement from what I remember. Here's me using it in one of the projects: https://github.com/macsux/cf-buildpack-windows-services/blob/master/src/WindowsServicesBuildpack/ScimControllerInjector.cs
It looked like a very simple replacement. Just replace LocalHook with CoreHook.LocalHook in Win32ThemeHooks. (after adding the nuget packages and the native dll's) Still, my attempt to replace EasyHook with CoreHook failed with this exception:
An unhandled exception of type 'System.ExecutionEngineException' occurred in Unknown Module.
Still, my attempt to replace EasyHook with CoreHook failed with this exception:
An unhandled exception of type 'System.ExecutionEngineException' occurred in Unknown Module.
Hello, I'm the author of another app that uses hooking as part of WinForms theming, and I ran into this same ExecutionEngineException issue when trying to move from Framework 4.7.2 to .NET 7. I determined the exception comes from the GetSysColor() hook only, so I created an issue here: dotnet/runtime#78086
As you can see from the reply, it's apparently because GetSysColor() is set up with a [SuppressGCTransition] attribute in the newer .NETs, and that breaks the hook and causes the ExecutionEngineException. Here's the code. It looks like just a normal p/invoke setup that the runtime can call explicitly, so I'm not sure why putting the attribute on just that one definition would affect all calls to the native GetSysColor() function even from native controls, but it seems to. I'm no expert on how this sort of thing works so I dunno. I'm at a loss for any way to get around it, but at least I thought I'd save others the trouble I went through of trying to figure it out.
It sounds like CoreHook isn't going to work after reading that thread (dotnet/runtime#78086). Seems like the best hope is dotnet/winforms#7641 for this issue, as RussKie originally suggested.
What if as a temporary solution before .net8 release you were to create a separate build with the dark theme? Something tells me that it shouldnt be too hard to implement
Closing as it is currently technically not possible, see #9191 (comment).
Would this work? (Disclaimer: not tested by me)
- https://github.com/ryanbester/dotnet-darkmode (https://www.nuget.org/packages/DarkMode)
- would take windows-system-settings from win10 - if this works it would be a oneliner (and perhaps an additional flag in the settings)
Crossposting some suggestions: #10312 (comment)
Any news regarding this discussion, with the comment @gerhardol mentioned?
No workaround for the hooking found.
No support for theming that I have seen also in the upcoming.net8
The theming support may be coming in .NET 9 it seems: dotnet/winforms#10985
@RussKie: If that's all right, I'd ping you once I think, you could "try" it.
I would be very much interested in the experience to enable dark mode for whatever custom controls you guys have. For all the normal stuff, it should be easy to migrate.
For now, you'd just use...
Later I guess we should have an additional project setting, so that also the Designer could pick up that color scheme, and then we generate and inject via ApplicationConfiguration.Initialize()
. If you pick Inherits
, then the System setting is applied wherever possible. (Problems are TabControl
- not natively themable at all, and neither are MonthCalendar
and therefore DateTimePicker
). Inherits
here is a "logical ambient" property, means, the idea is, that any control with that setting looks at its parent, a top-level control without a parent would look at Application.DefaultDarkMode
and the Application using Inherits
would look at the System.
So, for styling individual (custom) controls: If it's more than ForeColor
and BackColor
(and since I am working with Git Extensions, I know there is quite some coloring going on), the principal approach would be:
- Test, if dark mode is in the current context available by using
[Contropl.]IsDarkModeEnabled
. - Access the Dark Mode/Current palette for the entire application, by using
Application.SystemColors.[SystemColorName]
. - Get the color from there and style the control with it. This way, the color assignment should then work both for dark- and light mode, since that palette reflects the current setting (and we also have then a hook for future extensions).
And, as usual, Igor and I do our usual "Naming and Convention Dance", so, API names might not be cast in stone quite yet.
Looking very much forward for feedback, but also keep in mind: The MVP Summit is coming up, so I'll be going dark (pun intended) for next 2 weeks.