valinet / WinCenterTitle

WinCenterTitle is a simple tool that allows you to center align the text in Windows 10 titlebars, the same way it was in Windows 8, 8.1, or even 3.1.
GNU General Public License v2.0
150 stars 15 forks source link

Is it possible to optionally disable the theme spoofing feature? #6

Closed Ingan121 closed 3 years ago

Ingan121 commented 3 years ago

I'm using a custom theme, and I think it looks better without the aero.msstyles behavior.

valinet commented 3 years ago

Hi

Sorry for the late reply, I was a bit busy last week and did not have time to look into this.

As far as I understand, you want the app to not patch dwm to use the 'aero code path', and instead of the white title bars, to get colored ones.

There is neither a config file option for this, nor a GUI (and probably will never be). The correct option is to recompile the application and patch out the line where it does this: *titlebar_color = 1; (in 1.1.0.0) or *titlebar_color = TITLEBARS_DESIRED_COLOR (in 1.2.0.0). This of course means downloading the entire Windows SDK, Visual Studio (or the Build Tools), familiarizing with an entirely new toolchain and environment just for a bloody one line patch.

When I am faced with situations like these, I either look for an alternative or resort to binary patching, which I highly recommend. Even for this, even though I wrote the app in the first place, I found it easier to look on the DLL for 2 minutes in Ghidra and determine the offset to patch, rather than to download the old source code, setup the project in Visual Studio etc. Plus, I don't want to distribute an additional set of binaries.

So, I recommend you patch the DLL that is injected into dwm and disable that line (which translates to a single instruction). You don't need any development tools or additional software for this. Copy WinCenterLibrary.dll in a new folder, and in there, create a new text file and call it "patch.ps1". In there, paste either of the following, depending on the version of WinCenterTitle you are using:

1.1.0.0:

param ($file)

$bytes = [System.IO.File]::ReadAllBytes($file);
$bytes[0x14B9] = 0x90;
$bytes[0x14BA] = 0x90;
$bytes[0x14BB] = 0x90;
[System.IO.File]::WriteAllBytes($file, $bytes);

1.2.0.0:

param ($file)

$bytes = [System.IO.File]::ReadAllBytes($file);
$bytes[0x1ED9] = 0x90;
$bytes[0x1EDA] = 0x90;
$bytes[0x1EDB] = 0x90;
[System.IO.File]::WriteAllBytes($file, $bytes);

Credit for the PowerShell idea: https://stackoverflow.com/questions/15834573/is-it-possible-to-edit-a-binary-file-using-windows-command-line.

Then, open a command window in that folder and type the following:

powershell -ExecutionPolicy Bypass -File patch.ps1 WinCenterTitleLibrary.dll

The file is patched in place. Copy the DLL back to the folder where you run WinCenterTitle from and hopefully it should still work. If not, please report here so I can figure it out what minor mistake I may have made.

As I said, binary patching is very advantageous for small edits. It would take you at least half an hour to set up a whole development environment for what equates to almost nothing. I could have uploaded the patched files myself here, but...

Either way, please report back in any case and again, I apologize for the slow reply and I hope I got it right.

Ingan121 commented 3 years ago

Unfortunately, the patch did not work and DWM continued to work as the default theme. Manually replacing two titlebar_color = TITLEBARS_DESIRED_COLORs to titlebar_color = 0 worked fine though. Thanks for the guide anyway. Screenshot w/ proper theming Screenshot w/o proper theming (ignore the leftmost square; I didn't realize there were two *titlebar_color = TITLEBARS_DESIRED_COLORs and thought your method doesn't work at all.)

valinet commented 3 years ago

Hi

Thanks for the feedback. I am glad you got it to work after all. It is pretty hard to think like a computer so to speak, my logic seemed solid, I wonder where my mistake was. I never tested though, physically I mean, in my head it seemed ok.

Anyway, I am happy that you sorted it out and I hope the software serves you well.

Also, I really like the idea of recompiling when it comes to tiny software packages. In the case of such projects, maintaining a configuration infrastructure that allows the settings to be changed at runtime would take too much effort that is better invested in actually working on the project. I guess I am a fan of the philosophy a project like suckless dwm proposes.

valinet commented 3 years ago

Now I looked on the screenshots. 2 mentions:

  1. I noticed you have a hackintosh install. Nice ;) always an interesting journey building a hackintosh.
  2. How does the glass version of WinCenterTitle look with the Windows 8.1 theme? I guess better than the Windows 10 version as the 8.1 theme has thicker borders around the windows, so the blur does not overflow the window as in the Windows 10 version. Thanks.
Ingan121 commented 3 years ago
  1. I installed it this week after a lot of failed attempts, and I got almost everything working except the battery percentage :)
  2. Besides bugs and limited customizability, the glass frame itself looks pretty good with it. I think it would be a good open-source alternative for glass8 which had not been updated since 20h1. Screenshot w/ Win8 theme, w/ Win7 theme (wallpaper blur is a bug)
  3. Btw, I can't get my builds of WinCenterTitle.exe working. I built it with the mentioned build instructions with VS2019 16.6.3, but it crashes DWM regardless of the WinCenterTitleLibrary.dll used (including the prebuilt ones from you.) I had to mix your build of WCT.exe with my WCTL.dll to use the modified dll. Crash logs? (the WCTL.dll used is the same thing as the one used for section 2 screenshots.)
valinet commented 3 years ago

Hi

Yes, I developed the glass frame more of an experiment, seeing how there was no news from glass8's developer about updating for 20H2, and how a lot of people are looking for an alternative (there are a few forum threads @MSFN).

The thing is, first off, it is pretty hard to update glass8 as it stands, because as far as I looked and compared dwm from 1909 and dwm from 2004/20h2, quite a few things have changed. A lot of the methods glass8 hooked have been removed, the back end architecture has changed substantially I may say.

And secondly, idk... it's the same reason I don't move on with the 'glass' branch of this project... I mean, I have a lot of stuff to work on, a lot of stuff I want to learn and worked quite a few time on this and have simply, you know, lost interest. I mean, I have tons of other good and more useful stuff to learn, so when this became repetitive and kind of boring, my brain's chemistry did not react anymore =)) and I have simply moved on. I'm doing this in my free time, so of course financially it has no value, and I would never make it shareware etc, but if it does not bring much on any other front, there's no incentive to keep working on it. For what is worth, I believe glass8's model of 'donations' was deeply wrong - you can't call it donation when you have to pay for it -, and also, by being closed source, since the developer now has abandoned the project, all the knowledge is lost, and that was the most important thing; now everyone has to start over, rewalk the same steps etc.

Also, I wrote a post on MSFN as well, my idea about was that this had a chance to come to fruition in a production-level project if I could gather a team to work on it over hear on GitHub. Because the thing is, I dare to say I am experienced enough in software development to be able to state that such a project is going to eat a lot of man hours, not necessarily on the actual code itself. Even the main WinCenterTitle app has only a few lines of code, but you stay a few good hours on a block of 10-20 lines until you figure out the knowledge to be able to write it down. So, as I said, developing this as a hobby is one thing, it sometimes makes sense to keep it in-house for small projects, but for things like this, I think it is very beneficial to share it with the world. But again, on such a project, this is an immense burden to take care of:

So, yeah, all of the above with a 1-man army? It is very hard and the thing is, you're not developing a cure for cancer either. So at least something that's worth the time. No, you're patching Microsoft's stupid window manager that they for some reason still haven't open sourced, or at least allowed for other window managers to take its place at least. But yeah, it is understandable when dwm is more than a window manager, as they shove in there a lot of stuff that does not make sense to be in a wm but it is; meanwhile, stuff that should be in there is in Explorer (like for example, the snap assist window overlay is part of Explorer, I have a separate project, WinOverview2, where I hacked it a bit). That manipulates windows, it should be in the window manager if you ask me, but whatever... dwm is a newish software, I think they could open source it if they wanted to take the time to polish for a public release, but due to the same reasons I posted above, they probably don't want to.

To me, with projects like this, the main challenge is minimizing the amount of 'hooking', in-memory patching etc that you do, in order to minimize the stuff you need to adjust when a new release of the binary you mess with comes out. The first version of WinCenterTitle, the one where I hooked DrawTextW is as future proof as it can get: they will always need to draw the text and that's how you draw text. Like, i had the courage to deploy that on some mission-critical systems at work, and it works just fine, because being simple means it has fewer places where it can brake, especially since it does something shady. The current version still does a moderate amount of hooking, and it already is prone to breaking on a newer Windows release, but at least the amount of stuff I hook is pretty low so manual fixing should not be too laborious. With the glass branch, even in its current state, it is a nightmare. I hook a ton of stuff, depend on a lot of things, and it is not even me that does the effects on the window, plus, as far as I remember, it is just the glass on the borders that I got to implementing, text is still drawn with a solid color and as far as I remember, it took me a lot of time to figure how to draw it properly and to this day I haven't figured it out yet.

Speaking about your problem getting it to compile, again, it emphasizes my points above; don't get me wrong, I am not blaming you, let me explain: on a proper OS (like, idk, GNU/Linux let's say), that is pro its users, not against them, one can easily hook into any executable with mechanisms like LD_PRELOAD for example; Windows obsoleted AppInitDLLs and similar stuff, so all that daemon thing is required which is pretty complex for a thing that only has to inject a remote process. As far as I remember, for 1.2.0.0, I indeed changed how injection is done:

Thing is, I made the shell code injection method default on x64, even though the old one works as well. So, I have now pushed an update to the repo, on master, with a new version of the header that does the injection, where you can configure a define to choose the hooking method that you want: shell code, or classic. Please, test with both, classic works on both x86 and x64, shell code only on x64 for the moment (it principle it works on x86 as well provided a suitable shell code, but there you don't really need to do this because the easiest method works just fine: just check the return of GetExitCodeThread which is the address of the loaded module - the problem this does not work on x64 is because GetExitCodeThread returns a DWORD which is 32-bits on both x86, and x64, while memory addresses on x64 are obviously 64-bit). So, back on track, to test this, open the file libs\libvalinet\valinet\hooking\exeinject.h, and find the define: #define HOOK_METHOD ... and ... will be either HOOK_CLASSIC_LOOKUP, either HOOK_BY_SHELLCODE. Test with both and report back, please, maybe this will help you reproduce a working executable with at least one of those. If one succeeds but the other fails, then I can investigate the other, or I mean, yeah, maybe just leave one as the default, the shell code thing is mostly a gimmick anyway, i did it for learning more than anything; I have to admit, the shell code method is newer, so yeah, it may be rougher, but it worked on my system, that's why it made it to upstream.

Also, thank you very much for your feedback, and sorry for my long replies.

Ingan121 commented 3 years ago

Thank you for your response, I'll do what you mentioned and tell you the results when I have enough time (maybe Thursday.) I really appreciate your work :)

Ingan121 commented 3 years ago

I tried both methods, and although I failed to get them working, they showed some different behaviors: HOOK_BY_SHELLCODE does nothing while HOOK_CLASSIC_LOOKUP crashes DWM. Logs I'm sorry that I have no x86 Win10 systems available to test. Also trying to build x86 on my x64 system fails because of failing to open valinet/hooking/exeinject.h' (which opens without any problem when building for x64, and funchook builds fine with x86 native tools cmd.)

I think I have to do a clean start of VS (by reinstalling?) to get it working, but I'm OK with the current method (mixing with your binary) and I think I'm just too lazy to endure a long time to reinstall it.

valinet commented 3 years ago

Yeah, forget about 32-bit, I mean, I don't know about it, I never test it. Maybe there are some mistakes in the solution file regarding to paths, idk...

So, from what I understand, the main issue here is that you cannot reproduce a working build of the exe file. Do my most recent pre-compiled binaries work for you while the builds you do locally crash?

Ingan121 commented 3 years ago

Yes, your recent pre-compiled binaries work fine, while when I try to run my own builds, they don't work. Neither the source zip from the [https://github.com/valinet/WinCenterTitle/releases/tag/1.2.0.0](1.2.0.0 release) nor the latest code doesn't work. Screenshot

Ingan121 commented 3 years ago

Oh, it was my fault. I found out I missed the VERY IMPORTANT part in the readme. Followed that and my builds started working properly :)

I think I forgot that because my utilities folder already had the inherited Users permission and the symbols folder was included in the release zip.

valinet commented 3 years ago

I am glad that you figured it out. I will close this issue now.