WordpressIch schreibe meinen Blog hier schon immer zweisprachig, auf Deutsch und auf Englisch. Ganz früher war das noch mein eigener Webcode. Dann bin ich irgendwann auf WordPress umgestiegen. Und die Plugins für die mehrsprachigen Posts, etc., waren schon immer irgendwie … anstrengend.

Und jetzt hat mein bisheriges Plugin für den mehrsprachigen Blog vollends den Geist aufgegeben; vermutlich durch ein WP-Update oder PHP-Update. Wie auch immer. Hilft ja nix. Also, gibt’s jetzt mal wieder ein neues Plugin. Das verwaltet jetzt unterschiedliche Sprachen indem jeder Post genau eine Sprache hat, und dieselben Posts in unterschiedlichen Sprachen „nur“ verlinkt sind. Eigentlich ein ganz nettes Konzept.

Heißt aber auch das meine bisherigen Posts alle, wirklich alle, überarbeitet werden müssen, genau wie die Webseitenstruktur selbst und alle Extraseiten auch. Und das wird dauern.

Also, wird der Inhalt auf meiner Webseite bis auf weiteres immer so ein bisschen Kaputt aussehen. Tschuldigung.

Ich bin auf dieses Nuget gestoßen, weil ich WPF-UI-Anwendungen geschrieben habe und einen Folder-Picker brauchte. Irgendwer im Internet schlug vor, die WinForms-Dialoge zu verwenden. Ich hasse es irgendwie, zwei Frameworks zu verwenden. Und dann brachte jemand anderes das Nuget WindowsAPICodePack.Shell ins Spiel, mit seinen Klassen der Windows Common Dialogs, einschließlich der Möglichkeit zur Auswahl von Ordnern im File-Öffnen-Dialogfenster.

Also fing ich an, das Nuget zu benutzen. Irgendwann wies mich ein Freund darauf hin, dass das Nuget-Paket gar nicht wie ein offizielles Microsoft-Paket aussah, sondern wie ein Repackage, das irgendjemand gemacht hatte. Das brachte mich zum Nachdenken. Ich unterstelle niemandem böse Absichten, aber ich fand es sehr, sehr seltsam. Das offizielle Paket war . Und es gibt eine ganze Reihe von Paketen mit seltsamen Namen:

Ich mag das nicht. Selbst wenn keiner der Autoren böse Absichten hegt, gefällt mir das nicht, denn es riecht nach Betrug, Phishing und Schwachstellen. Tut mir leid, aber nein.

Also, wo ist das offizielle Paket? Es scheint verschwunden zu sein, deshalb gibt es die Repackages von Community-Mitgliedern. Warum ist es verschwunden? Keine Ahnung. Vielleicht wurde es bei einer halbautomatischen Bereinigung erwischt, da es verwaist war. Jemand behauptet es wäre Microsoft.Windows.SDK.Contracts ersetzt worden.

Letztendlich habe ich den Code in meinen Projekten ersetzt, indem ich entweder doch den WinForms-Dialog verwendet oder eine sehr kleine P/Invoke-Wrapper-Klasse geschrieben habe, die die Win32-API-Funktion direkt aufruft. Wen es interessiert, der kann sie sich gerne anschauen:

https://github.com/sgrottel/open-here/commit/9de68198e35f0f6dec9386372cc71bada54c2f5b

Die Moral von der Geschichte ist, dass ein Nuget-Paket nur so gut ist wie die Menschen, die es pflegen. Und ich meine Menschen, nicht Organisationen. Denn am Ende kommt es darauf an, ob eine einzelne Person dahinter steht und ihr Bestes geben will oder nicht.

GLM wurde in Version 1.0.0 veröffentlicht, und ich habe das Nuget-Paket entsprechend aktualisiert.

Dabei habe ich auch den Quellcode des Nuget-Pakets von Bitbucket nach Github umgezogen.

Ich bin mir nicht sicher, ob es sich lohnt, über die Aktualisierung von Nuget-Paketen zu schreiben. Ich würde eher sagen, das ist Business as usual. Aber dieser Fall hier ist etwas Besonderes, denn der GLM-Entwickler entschied sich für 1.0.0. Ich möchte zu diesem Anlass besonders gratulieren! Ich finde es peinlich, wie viele Projekte die „Schallmauer“ dieses Schrittes zu fürchten scheinen, um endlich von „Ich weiß nicht“ zu „Das ist irgendwie das, was ich machen wollte und es ist irgendwie in der ersten Version fertig.“ Zu gehen. Ich glaube, viele weitere Projekte sind reif und stabil genug, um diesen Schritt zu gehen. Aber sie tun es nicht. Ich weiß es nicht.

Dies ist mein erster Post im Jahr 2024, und erstaunlicherweise habe ich es geschafft, siebeneinhalb Monate lang nichts zu schreiben. Das sieht aus wie ein trauriger Rekord. Nun, es ist nicht so, dass ich in dieser Zeit nichts für die Community getan hätte. Es zeigt aber, dass meine Prioritäten woanders lagen als in diesem Blog. Aufgeben werde ich den hier aber nicht.

Jedenfalls möchte ich nicht einfach aufgebläht auflisten was ich gemacht habe, indem ich, z.B., mein Github-Journal abschreibe, denn das wäre einfach nur traurig. Wenn Ihr euch dafür interessieren, was in meinen Tiny Tools, Everything Search Client, Checkouts Overview, OpenHere, LittleStarter, MyToDoBoard, aktualisierten Nuget-Paketen, SimpleLog, etc. passiert ist, dann empfehle ich direkt einen Blick in mein Github-Profil zu werfen. Das ist dann wohl die unaufgeblähte Auflistung.

Also, für heute belasse ich es bei dieser kleinen, traurigen „I am still alive“-Nachricht.

Der EverythingSearchClient kann nun seine Suchergebnisse sortiert zurückgeben. Der entsprechende neue Release v0.4 ist auf Github und als NuGet-Packet verfügbar.

Die Funktion die Suchergebnisse sortiert zu liefern zu können war einfach einzubauen, da sie hauptsächlich im Everything-Service selbst implementiert ist. I musste nur die entsprechenden Flags für die Query-v2 Interprozess-Communications-Anfrage in meine CSharp-Client-Bibliothek übernehmen.

Damit habe ich nun keine aktuellen Pläne für weitere Funktionen in meinem EverythingSearchClient. Ich benutze die Library bereits in ein paar meiner eigenen Projekte. Wenn Ihr sie auch benutzt, dann würde ich mich freuen das zu hören. :-)

Rund um Lua und sein NuGet-Paket ist eine Menge passiert.

Lua 5.4.5 wurde veröffentlicht. Es war also Zeit für ein Update meines Lua NuGet Pakets. Und für eine Überraschung. Und für Schmerzen.

Am Ende wurde alles gelöst, und die neue Version des Lua NuGet-Pakets ist online.

Allerdings wurde der Lua-5.4.5-Release wieder entfernt, und hoffentlich kommt schnell Lua 5.4.6.

Update 20230514: Lua 5.4.6 is here!

Die Überraschung: Das Lua NuGet-Paket war nicht gelisted!?

Vor einigen Monaten haben meine Benachrichtigungen von Github aufgehört zu funktionieren. Keine Ahnung, warum. Aber da ich dort nicht viel Updates habe, war es für mich nie eine hohe Priorität, das zu fixen. Aus diesem Grund hätte ich dieses von Smurf-IV aufgeworfene Problem fast übersehen: „[Question] Why does nuget state that the packages have been witdrawn ?

Woot? Ich habe absolut keine Ahnung, was dort passiert ist. Aber ich habe eine Vermutung. Das NuGet-Paket wurde ursprünglich von der CoApp-Organisation erstellt. Irgendwann haben sie es jedoch nicht mehr aktualisiert. Als ich eine neuere Version von Lua benötigte, beschloss ich, selbst ein NuGet-Paket zu erstellen. Ich hatte bereits gute Erfahrungen mit der Erstellung nativer C++-NuGet-Pakete gemacht, also habe ich es ausprobiert, und es hat gut funktioniert. Zu diesem Zeitpunkt wandte ich mich an die CoApp-Organisation und fragte sie, ob ich das NuGet-Paket für Lua mitverfassen dürfte. Meine Idee war, dass ich dieses Paket unter Verwendung der schönen und präzisen NuGet-Paket-ID veröffentlichen könnte. Sie stimmten zu, gaben mir die Co-Autorenschaft, und seither veröffentliche ich NuGet-Pakete für jede Version von Lua.

Und nun wurde das Paket vom „Autor“ nicht mehr gelistet und als veraltet und mit kritischen Fehlern versehen gekennzeichnet. Was? Das kann ich Ihnen sagen, ich war es nicht!

Die Sache mit den „kritischen Fehlern“ hat mich ein wenig beunruhigt. In meinem früheren Job war ich auch damit beauftragt, unsere Codebasis regelmäßig auf CVEs zu überprüfen. Ich kenne also ein paar Dinge, auf die man achten muss. Ich habe einen schnellen Check für Lua gemacht, und ja, es gibt CVEs, aber nichts Ungewöhnliches. Nichts so Schwerwiegendes, dass das Paket so stark eingeschränkt werden müsste.

Und noch etwas wurde deutlich: Die CoApp-Org scheint überhaupt keine NuGet-Pakete mehr zu besitzen. Keines. Das ist ziemlich überraschend.

Also, meine Vermutung ist, dass die CoApp-Organisation auf NuGet vielleicht aufgelöst wurde, aber mit Sicherheit ihr Engagement für die Wartung der NuGet-Pakete eingestellt hat. Als Vorsichtsmaßnahme wurden alle NuGets abgeschaltet und vielleicht automatisch als veraltet und veraltet markiert. Bei der Sache mit den „kritischen Fehlern“ bin ich mir nicht sicher. Deshalb gehe ich von einer automatischen „Abschaltung“ aus, bei der wahllos alle Pakete markiert und aus der Liste entfernt wurden.

Nun, mein Commitment, das Lua-Nuget-Paket zu pflegen, bleibt bestehen!

Ich werde weiterhin Versions-Updates und ähnliches veröffentlichen. Deshalb habe ich die 5.4.x Versionen des Lua-Pakets (wieder) aufgelistet und die Warnhinweise, dass sie veraltet sind, veraltet sind oder kritische Fehler haben, entfernt. Über die Beweggründe der CoApp-Gruppe kann ich nur spekulieren. Aber das werde ich nicht tun.

Der Schmerz: Umzug von AppVeyor zu Github Actions

All dies geschah im Zuge der Veröffentlichung der Version 5.4.5 von Lua. Eine Aufgabe, die seit langem auf meiner ToDo-Liste steht, war die Migration und Konsolidierung der verschiedenen Code- und CI-Plattformen. Bis jetzt habe ich die verschiedenen Varianten von Lua für das NuGet-Paket auf AppVeyor gebaut. Und ich plante schon lange, diese Aufgabe auf Github Actions zu verlagern. Also habe ich beschlossen, dass jetzt endlich die Zeit ist, das zu tun. Ein Grund dafür ist ein bevorstehender Feiertag in Deutschland, der mir die zusätzliche Zeit verschafft hat, mich um diese Aufgabe zu kümmern.

Während Github-Aktionen für einfache Projekte ganz nett sind, und obwohl sie ziemlich leistungsfähig sind, stoße ich sofort auf ein Problem: Mein NuGet-Paket enthält auch Build-Flavors von älteren Visual C++-Tools, derzeit bis hinunter zu v120. Und auf der Runner-VM der GitHub-Aktionen sind nur das neueste Visual C++ und die neuesten Toolsets installiert.

Und so begann für mich eine lange Reihe von Versuchen und Fehlern. Eine Menge Schmerzen, Kopfschmerzen und „warum, warum, warum…“-Momente. Am Ende habe ich die folgenden Lösungen gefunden:

HowTo: Anpassen der Visual Studio 2022-Installation auf Github Actions Runner

Fangen wir einfach an. Der Github-Runner wird mit Visual Studio Enterprise 2022 installiert. Daher sollte die neueste Toolchain sofort funktionieren, nämlich Visual C++ v143, v142 und v141, und das hat sie auch. Nun zu der Toolchain v140, die über den Visual Studio Installer installiert werden kann. Das sollte also einfach sein. Nun, theoretisch ja. Der Visual Studio Installer bietet Befehlszeilenargumente zum Ändern einer Installation und zum Hinzufügen von Komponenten, und das ist es, was ich tun muss. Das einzige Problem ist, dass der Installer keine Konsolenanwendung ist. Wenn er gestartet wird, kehrt die aufrufende Konsole sofort zurück, so dass ein Job im Workflow Yaml nicht warten kann, zumindest nicht automatisch. Ich muss den Installer mit `Start-Process` aufrufen und dem Befehl explizit sagen, dass er auf das Ende des erzeugten Prozesses warten soll.

Wenn Sie den Installer lokal in einer Eingabeaufforderung, z. B. einer Powershell, ausführen, werden Sie Ausgabemeldungen sehen. Wenn Sie ihn auf Github CI ausführen, ist das nicht der Fall. Wie ich schon sagte, ist der Installer keine Konsolenanwendung. Als solche führt er wahrscheinlich eine manuelle Konsolen-Host-Verbindung durch, um seine Ausgabemeldungen einzuschieben. Und auf Github CI existiert der Konsolen-Host wahrscheinlich nicht, da die Prozessausgabe vom CI-Agentenprozess in eine temporäre Datei geleitet wird. Infolgedessen findet der Installer keinen Konsolenhost und gibt daher auch keine Ausgabemeldungen aus. Ich befinde mich im Blindflug. Um zu wissen, ob der Befehl erfolgreich war oder nicht, verwende ich „vs_installer.exe export“, um die Liste der installierten Komponenten auszugeben. Das ist nicht schön, aber gut genug für die manuelle Fehlersuche.

So, das hat funktioniert:

- name: Modify Visual Studio 2022
  if: matrix.toolConfig.vs2022addMod != ''
  shell: pwsh
  run: |
    $component = "${{matrix.toolConfig.vs2022addMod}}"
    Write-Host "Adding:" $component

    Start-Process -FilePath "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" -ArgumentList "modify","--add",$component,"--installPath","`"C:\Program Files\Microsoft Visual Studio\2022\Enterprise`"","--passive","--includeRecommended","--norestart","--noUpdateInstaller" -Wait

    Start-Process -FilePath "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" -ArgumentList "export","--config","info-post.txt","--installPath","`"C:\Program Files\Microsoft Visual Studio\2022\Enterprise`"","--passive","--noUpdateInstaller" -Wait
    Write-Host "VS2022 Components after modification:"
    Write-Host (gc info-post.txt)

    Write-Host "Added:"
    Write-Host (gc info-post.txt | Select-String $component)

HowTo: Visual Studio 2013 auf Github Actions Runner

Dies lässt mit dem Build mit der ältesten noch unterstützten Toolkette: v120 aka Visual Studio 2013, und das ist ein echter Schmerz! Ich kann es den meisten Leuten nicht verübeln, da dies eine über 10 Jahre alte Software ist. Ich würde für so etwas keinen Support geben wollen. Aber mit diesem NuGet-Paket tue ich es irgendwie doch.

Das aktuelle Visual Studio unterstützt diese Toolkette nicht. Ich brauche also eigentlich Visual Studio 2013. Fragen Sie das Internet, was zu tun ist, und es wird Ihnen sagen, Visual Studio 2013 zu installieren, z. B. die kostenlose „Expression“ Edition, über Chocolatey. Ich habe es versucht, und es schlug mit der kryptischsten Fehlermeldung aller Zeiten fehl:

„Exit code indicates the following: Generischer MSI-Fehler. Dies ist ein lokaler Umgebungsfehler, kein Problem mit einem Paket oder dem MSI selbst […]“ bla bla bla.

Es gibt eine Menge falscher Hinweise, was zu tun ist. Der eigentliche Grund ist ein ganz anderer: Das Paket lädt eigentlich nur den Bootstrap-Installer von Visual Studio herunter, etwa 1,2 MB. Der Rest des Pakets wird bei der Installation auf Anforderung heruntergeladen. Und genau da liegt das Problem. Microsoft hat diese alten Download-Quellen entfernt, da sie anfällig waren, weil sie mit SHA-1-Hashes signiert waren. Und damit sind jetzt alle Chocolatey-Pakete kaputt. … Ich könnte mich irren, aber die Auswirkungen und die spärlichen Informationen, die ich aus den Logdateien herausgeschnitten habe, sehen wirklich so aus.

Das heißt, ich brauche ein vollständiges Visual Studio 2013. Zu diesem Zeitpunkt ist es offiziell über Microsofts Download-Website my.visualstudio.com verfügbar. Nachdem ich mich angemeldet und durch eine dynamische Website geklickt habe, versteht sich. Für meine Automatisierungszwecke nicht akzeptabel. Also habe ich die DVD-ISO auf meinem OneDrive neu gehostet. Ich würde sagen, das ist ok, aber nicht ideal. Und jetzt muss ich auch noch das komplette Visual Studio 2013 installieren. Klingt teuer. Klingt mühsam. Klingt langsam. Klingt wie ein Job für Docker. … Nun, ja, aber leider würde ich ein Windows-Docker-Image benötigen, das einen Windows-Docker-Host benötigt, und das scheint auf Github noch nicht unterstützt zu werden. Also, ja, ich muss Visual Studio 2013 auf dem Runner installieren.

- name: Install VS 2013 tools
  if: matrix.toolConfig.toolset == 'v120'
  shell: pwsh
  working-directory: ${{env.GITHUB_WORKSPACE}}
  run: |
    Write-Host "Downloading VS 2013 Express for Windows Desktop (w Update 5) DVD ISO..."
    Invoke-WebRequest -Uri "${{ secrets.VS2013ISO_URL }}" -OutFile dvd.iso
    gci dvd.iso
    $dvdiso = (Resolve-Path .\dvd.iso).Path

    Write-Host "Mounting Disk..."
    $DiskImage = Mount-DiskImage -ImagePath $dvdiso -StorageType ISO -PassThru

    $volumeInfo = (Get-Volume -DiskImage $DiskImage)
    $volumeInfo

    cd ($volumeInfo.DriveLetter + ":\")
    gci

    Write-Host "Installing Visual Studio 2013 Express..."
    Start-Process -FilePath ($volumeInfo.DriveLetter + ":\wdexpress_full.exe") -ArgumentList "/Q","/NoRestart","/NoRefresh","/NoWeb","/Full" -Wait

    Write-Host "Unmounting Disk..."
    Dismount-DiskImage -DevicePath $DiskImage.DevicePath

Und das funktioniert. Und ist, wie erwartet, schmerzhaft langsam. Um den Overhead dieses Installationsschritts zu begrenzen, ist es sinnvoll, die Breite der Job-Matrix im Workflow zu verringern: nur einen Job für die Installation und danach alle Build-Schritte nacheinander ausführen. Im Endeffekt ist dies fast um den Faktor 4 schneller, als man aufgrund des massiven Installations-Overheads erwarten würde. Zum Glück für mich ist dieser Workflow kein Pull-Request-Blocker während der aktiven Entwicklung, sondern nur eine Release-Automatisierung.

Gedanken zum Abschluss

Letztendlich funktioniert die Migration zu Github-Aktionen. War es das wert? Ich meine, Lua wird etwa alle zwei Jahre veröffentlicht. Brauche ich dafür überhaupt CI? Und überhaupt, ist das native NuGet nicht tot, ersetzt durch andere Mittel, wie vcpkg, oder so?

Nun, all dies ist wahr. Ich habe dieses Unterfangen nicht weiterverfolgt, weil ich ein CI für mein Lua-NuGet-Paket haben wollte. Mein Hauptgrund ist „weil ich es kann“, oder eigentlich „weil ich es noch nicht kann“. Es war eine gute Gelegenheit, mein Github-Aktions-Fu zu verbessern. Meine Empfehlung an Sie: Wenn Sie eine solche Gelegenheit sehen, um zu lernen und Ihre Fähigkeiten zu verbessern, mit dem Vorteil, etwas Brauchbares daraus zu machen, tun Sie es.

Und, jetzt warte ich auf Lua 5.4.6 und vielleicht neue Überraschungen…

[:de]Ich habe ein weiteres neues Tool zu meiner Tiny Tools Collection hinzugefügt: ToggleDisplay

Code: https://github.com/sgrottel/tiny-tools-collection/tree/main/ToggleDisplay
Ausführbare Datei: https://github.com/sgrottel/tiny-tools-collection/releases/tag/ToggleDisplay-v1.0

Damit könnt Ihr Displays aktivieren, deaktivieren und umschalten.

Warum das? Mein Computer ist an 2-3 Bildschirme angeschlossen. Zwei Computermonitore auf meinem Schreibtisch für die Arbeit. Und ein Fernseher auf der anderen Seite des Raumes, z.B. um Spiele von meinem Computer aus zu spielen oder um Videodateien in Ruhe anzusehen.

Oft genug fahre ich den Computer hoch, und dann verschwindet meine Maus vom Desktop, weil ich vergessen habe, dass der Fernseher vorher „an“ war, und die Maus hat sich über die Desktop-Monitore hinaus bewegt. Das ist ärgerlich. Die eingebaute Funktion „Windows-Taste + P“ ist verständlicherweise auf zwei Monitore beschränkt. Also musste ich immer „Windows + P“ drücken, dann „Weitere Einstellungen“, warten, bis der Dialog erscheint, herumfummeln, auf „Übernehmen“ drücken, … Ihr versteht, was ich meine.

Also habe ich im Internet recherchiert, wie man eine Anzeige programmgesteuert aktivieren oder deaktivieren kann. Und es gibt mehrere kostenlose Tools, mit denen das möglich ist. Ich habe zwei ausprobiert, und beide haben nicht funktioniert. Dann gibt es einen Hack, bei dem eine ausführbare Datei von Windows 10 unter Windows 11 verwendet wird. Ja, nein. Also gut. Suchen Sie weiter!

Es stellt sich heraus, dass es eine einfache API dafür gibt: ChangeDisplaySettingsEx. Nach einigen Experimenten konnte ich die Anzeige deaktivieren, aber nicht (wieder) aktivieren. Nicht gut genug. Weiter suchen!

Kurz später stellt sich heraus, dass es eine zweite API gibt, die nicht so einfach ist und für die es so gut wie keine brauchbare Dokumentation gibt: SetDisplayConfig. Dies scheint die API zu sein, die der in Windows eingebaute Dialog zur Anzeigekonfiguration verwendet. Aber … wie. Ich fand Code von „PuFF1k“ auf StackOverflow (https://stackoverflow.com/a/62038912/552373), der die API-Aufrufe des Windows-Dialogs zurückentwickelt hat. Ich habe seinen Code ausprobiert, und es funktioniert. Toll! Vielen Dank, PuFF1k!

Im Kern ist der Tricks, keine modeInfo-Daten an SetDisplayConfig zu übergeben, und alle sourceInfo.modeInfoIdx und targetInfo.modeInfoIdx aller Paths auf DISPLAYCONFIG_PATH_MODE_IDX_INVALID zu setzen.

Ein paar Überarbeitungen und Aufräumarbeiten später habe ich ToggleDisplay, bereit, mit der Welt geteilt zu werden.

Übrigens habe ich jetzt auch den Quellcode einiger meiner älteren Tools in dieses Tiny Tools Collection Repository aufgenommen:

Bei dieser Gelegenheit habe ich auch diese Projekte auf die neuesten DotNet-Laufzeiten aktualisiert. Eine automatisierte Build-Pipeline oder Releases habe ich nicht aufgesetzt. Vielleicht ein anderes Mal.[:en]I added another new tool to my Tiny Tools Collection: ToggleDisplay

Code: https://github.com/sgrottel/tiny-tools-collection/tree/main/ToggleDisplay
Released Binary: https://github.com/sgrottel/tiny-tools-collection/releases/tag/ToggleDisplay-v1.0

It allows you to enable, disable, and toggle a display.

Why? My computer is connected to 2-3 displays. Two computer monitors on my desk for work. And a TV on the other side of the room, e.g. to play games from my computer or to watch video files in style.

Often enough I boot the computer, and then my mouse disappears from the desktop, because I forgot the TV was configured “on” before, and the mouse moved beyond the desktop monitors. Annoying. The built-in feature “Windows-Key + P” is understandably limited to two monitors. So, I always had to press “Windows + P”, then “Further Settings”, wait for the dialog to appear, fiddle around, press apply, … you get my point.

So, I researched the net a bit on how to programmatically enable or disable a display. And there are several free tools to do that. I tried two, and both did not work. Then there is a hack with using a Windows 10 executable on Windows 11. Yeah, no. Ok. Search on!

It turns out, there is an easy API for that: ChangeDisplaySettingsEx. Some experimental code later I was able to deactivate the display, but not to (re-)activate it. Not good enough. Search on!

Some search later, turns out there is a second API, not as simple and with next to no useful documentation: SetDisplayConfig. This one seems to be the API the windows built in display configuration dialog uses. But … how. I found code by “PuFF1k” on StackOverflow (https://stackoverflow.com/a/62038912/552373) who reverse engineered the API calls of the windows dialog. I tried his code, and it works. Nice! Thank you, PuFF1k!

The core of the trick is to not provide any modeInfo data to SetDisplayConfig, and to set all sourceInfo.modeInfoIdx and targetInfo.modeInfoIdx of all paths to DISPLAYCONFIG_PATH_MODE_IDX_INVALID.

Some refactoring and some cleanup later, I have ToggleDisplay, ready to be shared with the world.

By the way, I now also included source code of some of my older tools in this Tiny Tools Collection repository:

I uses that opportunity to also update these projects to recent DotNet runtimes. I did not set up any automated build pipeline or releases. Maybe some other time.[:]

[:de]Dies ist mein dritter und letzter Artikel in meiner Serie über Anwendungs-Icons und -Logos. Diesmal schreibe ich über die Größe der Icons und warum wir darauf achten sollten. Zugegeben, hier geht es ziemlich um Perfektion, aber ich zeige eine leicht erreichbare und gute Optimierung die es wert ist. Seht Euch diese beiden Bilder an:

Sie zeigen das gleiche Icon. Genau das gleich Icon. Wirklich. Und beide zeigen das Bild mit 32×32 Pixeln.

Icon-Größen

Nochmal zur Wiederholung, wozu dienen Icons: Es handelt sich um eine ikonische, grafische Darstellung eines Logos, die besonders für kleine Größen optimiert ist, wie Favicons, kleine Logos oder Softwareanwendungs-Icons. Sie sind für sehr kleine Größen gedacht, traditionell bis runter zu 16×16 Pixel. Bei höher aufgelösten Bildschirmen ist diese superkleine Größe nicht mehr ganz so relevant. Deshalb habe ich für mein obiges Beispiel 32×32 gewählt. Diese Icons sollen also für solch kleinen Größen funktionieren. Ja, wir erstellen ausdrücklich Icons für diese kleinen Größen. Und genau das ist mein Argument, dass wir uns die Mühe machen sollten, die Grafiken auch für genau diese Größen zu optimieren, die wir anstreben, wenn wie das schon machen: 16×16, 24×24, 32×32, 48×48 und 64×64, traditionell.

Worin besteht nun der Unterschied zwischen den beiden obigen Bildern? Lassen Sie mich ohne zusätzliche Interpolation hineinzoomen, um den Unterschied deutlicher zu machen:

Das linke Bild ist das Referenzbild, das ich aus dem Clipart übernommen habe. Es zeigt genau das, was ich zeigen möchte, und stammt beispielsweise aus einer externen Designquelle. Aber die Linien stimmen nicht mit dem Pixelraster überein. Daher führt die Anti-Aliasing-Interpolation zu dem unscharfen Bild. Das rechte Bild ist die gleiche Grafik, aber alle Linienscheitelpunkte wurden leicht verschoben, um sie genau am Pixelraster auszurichten. Das Ergebnis ist ein viel schärferes Aussehen. Und der Aufwand ist minimal. Es genügt, das Symbol zu duplizieren und ein paar Eckpunkte in Ihrem Lieblingsgrafikprogramm zu verschieben und auf dem Pixelraster einschnappen zu lassen. Das ist es auf jeden Fall wert.

Ok, können wir nicht einfach für 16×16 optimieren und gut ist?

Nein. Zum einen ist 16×16 sehr, sehr klein, und wie oben geschrieben, verliert es im Zeitalter der hochauflösenden Displays an Bedeutung. Ähnlich wie bei der Abstraktion von einem Logo zu einem Icon, wie in meinem ersten Artikel dieser Serie beschrieben, vereinfachen viele Icons und entfernen Details, wenn sie von 32×32 Pixel großen Versionen auf die 16×16 Pixel großen Versionen heruntergehen:

Und der zweite Grund sind die Zwischengrößen, die berüchtigten 24×24 Pixel. Das ist ein Skalierungsfaktor von 1,5 gegenüber der 16×16-Version. Jede Linie könnte wieder in den Zwischenpixeln landen und verschwimmen, wenn man einfach hochskaliert.

Es ist also sinnvoll, mehrere Größen des Symbols zu erstellen, wobei jede Größe eine optimierte Platzierung der Eckpunkte der Grafik aufweist. Je nach Komplexität der Symbolgrafik ist eine weitere Hochskalierung irgendwann irrelevant und kann automatisch erfolgen. Die Größe von 64×64 Pixeln ist ein traditioneller Punkt dafür.

Ich persönlich versuche normalerweise, Icons mit 32×32 Pixeln zu entwerfen. Die 64×64-Pixel- und 256×256-Pixel-Versionen sind dann automatische Upscales, werden aber immer ausdrücklich in die Icon-Dateien aufgenommen. Die drei traditionellen Größen, die noch fehlen, 16×16, 24×24 und 48×48 Pixel, werden manuell optimiert, damit sie schärfer aussehen. Natürlich ist dieser Ansatz nur ein Ausgangspunkt, und manchmal hat die Referenz eine andere Größe.

Die geraden Linien

Es geht also nur um gerade horizontale und vertikale Linien, denn nur diese können perfekt mit dem Pixelraster übereinstimmen? Nein. Jede Form verliert an Details und wird bei kleineren Größen zunehmend unschärfer. Ich habe oben geschrieben, dass die Verringerung der grafischen Details bei einer Verkleinerung notwendig sein könnte. Das gilt für alle Formen. Und es kann nicht nur eine _Reduzierung_ sein. Manchmal kann eine Änderung oder sogar ein vollständiger Ersatz einer Form sinnvoll sein, wie im obigen Beispiel. Insbesondere bei der Verkleinerung auf 16×16 Pixel sind die Konzepte der Pixelkunst mit ihrer Reduzierung der meisten Details und der besonderen Betonung anderer Details eine Überlegung wert:

Das rechte Bild zeigt das Clipart-Original. Das mittlere Bild zeigt die Vektorgrafik des 16×16 Pixel großen Bildes auf der linken Seite. Schauen Sie sich den Riemen des Helms an. Dieser ist überhaupt nicht mehr gebogen. Stattdessen werden ein paar volle Pixel für die Gesamtform und ein paar teilweise gefüllte Pixel für ein kontrolliertes Anti-Aliasing verwendet.

Zusammenfassung

Icons sind als sehr kleine Darstellungen eines Logos und für Ihre Anwendung, Webseite oder ähnliches gedacht. Da dies ihr Zweck ist, sollten wir darauf achten, sie auch für diese Größen zu optimieren!

  • Formen, insbesondere, aber nicht ausschließlich, horizontale und vertikale Linien, sollten genau an den Grenzen des Pixelrasters platziert werden, um Unschärfen aufgrund von Interpolation zu vermeiden.
  • Die 16×16-, 24×24-, 32×32- und 48×48-Pixel-Versionen eines Symbols profitieren am meisten von einer manuellen Optimierung, vielleicht sogar von einer Reduzierung der Grafikdetails oder einer Änderung der Form.
  • Was auch immer wir tun, wir sollten immer die Qualität im Auge behalten.

So könnte ein SVG, das nur ein Bild in einer bestimmten Größe darstellt, als Datenquelle für Ikonenbilder verwendet werden. Aber wenn es für alle Größen verwendet wird, wird die visuelle Qualität bei einigen Größen immer schlechter sein als bei expliziten pixelbasierten Grafiken, die für diese spezifische Größe optimiert sind.

Artikelserie

[:en]This is my third and final article in my series about application icons and logos. This time I am going to write about icon sizes, and why you should care for it. Granted, it’s a little bit about perfection, but it is about an easily achievable optimization. Look at those two images:

These show the same icon. The very same icon. Really. And both show the image at 32×32 pixels.

Icon Sizes

Let’s reiterate what icons are for: it’s an iconic, graphical representation of a logo, especially optimized for small sizes, like favicons, small logos or software application icons. They are meant to work at very small sizes, traditionally down to 16×16 pixels. With higher resolution displays, this super-small size might no longer be that relevant. That is why I chose 32×32 for my example above. So, we want icons to work for those small sizes. Yes, we explicitly create icons for those small sizes. And this is my argument, that we should go the extra mile also optimizing the graphics for exactly those sizes we aim for: 16×16, 24×24, 32×32, 48×48, and 64×64, traditionally.

So, what is the difference between the two images above? Let me zoom in without additional interpolation to make the difference more clearly visible:

The left image is the reference image I got from the clipart. It does show what I want to show, and might come from an external design source. But the lines do not align with the pixel grid. As a result, anti-aliasing interpolation results in the blurry visual. The right image, is the same graphics, but all the line vertices have been slightly move to align exactly with the pixel grid. The result is a much crisper appearance. And the effort it minimal. Just a duplication of the icon and touching and snapping of a couple of vertices in your favorite graphics editor. Totally worth it.

Ok, so, can’t we just optimize for 16×16 and we are good?

No. For one, 16×16 is very very small, and as written above looses it’s importance in the age of high resolution displays. Similar to the abstraction from a logo to and icon, as written in my first article of this series, many icons simplify and remove details when they go down from 32×32 pixel sized versions to the 16×16 pixel sized versions:

And the second reason are the in between sizes, infamously the 24×24 pixels. It’s a scaling factor of 1.5x from the 16×16 version. Any line might end up again in between pixels and blurry if you just scale up.

So, it does make sense to create multiple sizes of the icon, each with optimized placement of the graphics’ vertices. At some point, depending on the complexity of the icon’s graphics, further upscaling is irrelevant, and can be done automatically. The 64×64 pixel size is a traditional point for this.

Personally, I usually try to design icons at 32×32 pixels. The 64×64 pixel and 256×256 pixel versions are then automatic upscales, but are always explicitly included in the icon files. The three traditional sizes still missing, 16×16, 24×24, and 48×48 pixels, are the manually optimized for crisper appearance. Of course, this approach is just a starting point, and sometimes the reference is at a different size.

The Straight Lines

So, all this is only about straight horizontal and vertical lines, as only those could perfectly align with the pixel grid? No. Any shape loses detail and gets increasingly blurry at smaller sizes. I wrote above that the reduction of graphical detail might be needed when going down in size. That is true for all shapes. And it might not only be a _reduction_. Sometimes an alteration or even complete replacement of a shape might make sense, as in the example above. Especially, when going to 16×16 pixels in size, the concepts of pixel art, with their reduction of most detail an especial emphasize on other detail is worth a thought:

The right image show the clipart original. The center image shows the vector graphics of the 16×16 pixel image on the left. Look at the strap of the helmet. That is no longer curved at all. Instead it emphasizes on a couple of full pixels for the overall shape, and a couple of partially filled pixels for explicit an controlled anti-aliasing.

Summary

Icons are meant as very small sized representations of a logo and for your application, web page, or similar. As it is their purpose, I argue we should care for optimizing for those sizes as well!

  • Shapes, especially, but not limited to, horizontal and vertical lines, should be placed precisely at pixel grid boundaries to avoid blurriness due to interpolation.
  • 16×16, 24×24, 32×32, and 48×48 pixel sized versions of an icon benefit most from manual optimization, maybe even graphics detail reduction or shape alteration
  • Whatever we do, let’s keep quality always in mind.

So, an SVG, which is only one image at one size could be used as an icon image data source. But if used for all sizes, it will always fall short in the visual quality on some sizes, compared to explicit pixel-based graphics, optimized for that specific size.

Series

[:]

[:de]Vor einiger Zeit habe ich hier auf meiner Website einen Abschnitt über Tools eingerichtet, die ich benutze und mag. Ich hab diese Serie begonnen, indem ich über das Suchtool Everything von Voidtools geschrieben hatte, das superschnell und großartig ist.

Seitdem habe ich Everything in verschiedene interne Tools von mir eingesetzt, meist über das Everything-CLI und parsen dessen Ausgabe. Allerdings hatte ich einige Probleme mit Unicode-Dateinamen. Dann habe ich mir Dotnet-Libraries angesehen, vor allem Everything .Net Client und EverythingNet. Beide sind im Grunde P/Invoke-Wrapper um das Everything SDK, das selbst ein Wrapper um Interprocess Calls (IPC) zum Everything-Dienst ist. Und da ich mich mit Low-Level-Techniken wie Windows-Message-basierender IPC auskenne und ich keine Wrapper von Wrapper von Funktionen mag, habe ich beschlossen, eine eigene Library zu schreiben: Everything Search Client

Es ist eine .Net 6.0 Bibliothek, komplett in CSharp geschrieben, mit einigen P/Invoke-Aufrufen zu nativen Windows-Funktionen des Betriebssystems und direkter Kommunikation mit dem Everything-Dienst.

Der Code ist auf Github verfügbar und das fertige Nuget-Paket ist auf Nuget.org.

Wenn Sie es nützlich finden und es in einem eigenen Tool verwenden, würde ich mich freuen, davon zu hören: Used By, How to Contribute[:en]Some time ago I started a section on my website here about tools I use and like. I started that series writing about the Everything search tool by Voidtools, which is lightning fast and awesome.

Since then I integrate Everything into several internal tools of mine. Most of the time, I used the Everything command line client and parsed its output. However, I had some trouble with Unicode file names. Then I looked at Dotnet library solutions, namely Everything .Net Client and EverythingNet. Both are basically only P/Invoke wrappers around the Everything SDK, which itself is a wrapper around Interprocess Calls (IPC) to the Everything service. And so, since I know my stuff around low level techniques like Windows Message based IPC, and since I don’t like wrappers of wrappers of functions, I decided to write a library of my own: Everything Search Client

It is a .Net 6.0 library, completely written in CSharp, with some P/Invoke calls to native Windows functions of the Operating System, and directly talking to the Everything service.

The code is available on Github and the ready-to-use nuget package is on Nuget.org.

If you find it useful and use it in a tool of your own, I would love to hear about it: Used By, How to Contribute[:]

[This article is only available in English. Sorry.]

Git has this cursed function to fuck up your files by doing unspeakable things to your line endings.

For example, from Githubs Documentation on Line Endings:

On Windows, you simply pass true to the configuration. For example:

$ git config –global core.autocrlf true

Please, never never never never never never never never never never never never do this!

THERE IS NO REASON TO DO IT!

Git is here to keep track of our files, NOT TO CHANGE OUR FILES IN ANY WAY.

So, please, just, never never never never never never never never never never never never do this! Leave my file endings alone!