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. :-)

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.

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

Ich stelle vor, ein neues kleines Tool für einen sehr spezifischen Zweck: OpenHere

Es findet laufende Instanzen des Windows Datei-Explorers. Von der obersten Instanz holt es sich den geöffneten Pfad und alle ausgewählten Dateien. Per Befehlszeilenanwendung können diese Informationen abgerufen werden. Oder die GUI-Anwendung erlaubt in einem Fenster eines von bis zu zwölf konfigurierten Programmen auszuwählen und zu öffnen.

Meine Tastatur hat mehrere frei belegbare Makrotasten, die ich jahrelang nicht benutzt habe. Ganz einfach, weil ich keine Ahnung hatte, was ich mit ihnen anfangen sollte. Dann kam Windows 11. Eine der vielleicht am meisten kritisierten Funktionen ist das neue Kontextmenü im Dateiexplorer, in dem die meisten Funktionen versteckt sind, die man für Dateien und Ordner aufrufen kann oder eben auch nicht. Da dachte ich mir, es wäre doch schön, über die Makrotasten etwas für die ausgewählte Datei auszulösen, z. B. das Öffnen in Notepad++ oder das Öffnen des gesamten Ordners in Visual Studio Code oder in Fork oder so etwas. Und genau dafür habe ich OpenHere geschrieben.

Ich habe einiges über Low-Level-Funktionen zu Icons gelernt, z.B. das konsequente und korrekte Laden dieser in großen Auflösungen, und ich habe mehr Erfahrung im Umgang mit WPF sammeln können.

Heute habe ich eine neue Version meines Checkouts-Overview-Tools veröffentlicht.

Version 1.1 bringt neue Funktionen, Verbesserung des Scannens der Festplatten zum Finden neuer Repository-Checkouts und die Möglichkeit beim Update von Entry-Status-Informationen auch ein git fetch durchzuführen.

Ein paar kleinere Verbesserungen der Benutzeroberfläche erzeugen ein konsistenteres Aussehen.

Ladet den neue Release von Github herunter: Release Feature Release v1.1 – Better Disk Scanning and Git Fetch · sgrottel/checkouts-overview (github.com)

Ich habe ein neues Tool in meinem Software-Werkzeugkasten: KeePass HotKey ist ein Wrapper-Programm zum Öffnen einer KeePass-DB oder zum Auslösen der Auto-Type-Funktion.

Dieses Tool ist sehr spezifisch für meinen Anwendungsfall:

  • Ich möchte eine Aktion über eine spezielle Hardware-Taste auf meiner Tastatur auslösen
  • Diese Aktion soll entweder eine bestimmte, vom Benutzer konfigurierte KeePass-Datenbankdatei öffnen oder
  • die „auto-type selected“-Funktion von KeePass auslösen, wenn eine KeePass-Instanz geöffnet ist, läuft und einen ausgewählten Eintrag hat.

Quellcode und releaste Binaries sind auf Github verfügbar.

Heute stelle ich euch das Tool Checkouts Overview vor.

https://github.com/sgrottel/checkouts-overview
https://go.grottel.net/checkouts-overview

Was? Warum? Weil mir dieses kleine Tool hilft.

In meinem privaten Setup habe ich eine Menge kleinerer Repos ausgecheckt, mit denen ich nur hin und wieder arbeite. Außerdem habe ich mehrere Repos, die einige Textdokumente und deren Historie verwalten. Manche dieser Repos werden mit Servern synchronisiert, die nur gelegentlich online sind, teils um Strom zu sparen, teils aufgrund von VPN- und Netzwerkverbindungsgeschichten. Und so verliere ich oft den Überblick über den Status der Synchronisierung dieser verschiedenen Repos.

Ist alles eingecheckt? — Meistens, ja. Wenn meine Änderungen schon vollständig war.

Ist alles gepusht? — vielleicht.

Sind Branches aktiv? — keine Ahnung.

Ihr braucht diese Anwendung vielleicht nicht, wenn Ihr einen besser strukturierten Arbeitsablauf habt als ich. Meiner ist chaotisch, also brauche ich die Hilfe eines Tools, dieses Tools.

Wenn Ihr interessiert seid, findet Ihr weitere Informationen im Github-Repository der App.

Hinweis: Und was das Icon der App angeht, es geht um (Repository-)Klone, richtig.

Redate ist ein weiteres Werkzeug in meinem wachsenden Werkzeugkasten. Die Idee ist einfach: Viele Programme erzeugen Dateien, schreiben Dateien, aktualisieren Dateien, mit genau demselben Inhalt wie zuvor. Das Datum des letzten Schreibzugriffs der Datei wird natürlich verändert. Der Inhalt bleibt derselbe. Andere Tools wiederum verwenden das Schreibdatum als Hinweis darauf, ob die Dateien geändert wurden. Was ja auch Sinn macht, oder?

Dieses kleine Werkzeug, „Redate“, speichert die MD5-Hashes der Dateien ab, zusammen mit ihrem ursprünglichen Schreibdaten. Wenn das Werkzeug dann erneut auf dieser Liste von Dateien ausgeführt wird, stellt es das ursprüngliche Schreibdatum für alle Dateien mit unveränderten MD5-Werten wieder her. Und das ist es.

Ich verwende es zum Beispiel für Vue.js-Projekte, um die Schreibdaten der Dateien in den dist-Ordnern beizubehalten. Dann kann ein einfacher FTP-Sync nur die geänderten Dateien für das endgültige Deployment übertragen. Dies hilft bei Projekten mit vielen Assets-Dateien die sich nicht verändern.

Ihr könnt Quellcode und Binär-Releases von Github herunterladen.

Dieser Artikel präsentiert die Details zu Plugin-Plugin-Abhängigkeiten in MegaMol™. Als Hauptszenario nutzt ein Plugin (oder mehrere) exportierte Call-Klassen eine anderen Plugins (und nicht des MegaMol™-Cores).

Tatsächlich wurde am MegaMol™-Core nichts geändert. Die Plugin-Plugin-Abhängigkeiten funktionierten bereits größtenteils. Die Einschränkungen und im Besonderen die Probleme im Fehlerfall sind unten beschrieben.

Testproject: interplugin_test

Dieses Projekt hat zwei Plugins:

https://bitbucket.org/MegaMolDev/megamol_interplugin_test

Plugin A exportiert 2 Module und 1 Call.

Plugin B exportiert ein Modul, welches aber den Call aus Plugin A nutzt.

Export des Call-Header

Normalerweise werden Module und Calls nur über ihrer Metadaten-Description exportiert, als Teil der Implementierung in „plugin_instance“. Diese Metadaten reichen dem MegaMol™-Core um die Module und Calls zur Laufzeit zu instanziieren. Die Klassen selbst werden üblicherweise nicht direkt exportiert.

Wenn wir nun allerdings einen Klasse, einen Call, in einem anderen Plugin zur Compile-Zeit nutzen wollen, dann muss diese Klasse exportiert sein. Zunächst muss der Header der Klasse im Ordner der öffentlichen Header des exportierenden Plugins liegen. Typischerweise sieht das so aus wie im Demoprojekt: „./a/include/interplugin_test_a/IplgDemoCall.h“ Ich empfehle auch die Headerdatei im Visual Studio Solution Explorer in den Filter “Public Header Files” zu legen um hier konsistent zu sein.

Zusätzlich muss die Klasse noch exportiert werden. Das geschieht mit dem API-Macro des Plugins: z. B. „INTERPLUGIN_TEST_A_API”, welches in Haupt-Header des Plugins definiert ist, z. B. „interplugin_test_a/interplugin_test_a.h“

Natürlich müssen die Metadaten-Descriptions des Calls trotzdem nach wie vor in der „plugin_instance“ exportiert werden (vglf. „interplugin_test_a.cpp“ Zeile 51).

Calls in einem anderen Plugin einsetzen

Um den Call nun in einem anderen Plugin zu nutzen muss dieses prinzipiell nur gegen das exportierende Plugin linken. Dem Demoprojekt folgend nennen wir das exportierende Plugin A und das nutzende Plugin B.

Zunächst sind beides einfach nur Bibliotheken, und A ist eine Abhängigkeit für B. Für Visual Studio empfehle ich das Skript „configure.win.pl“ und die Datei „ExtLibs.props.input“ anzupassen, als würde man irgendeine andere Drittherstellerbibliothek hinzufügen. Im Demoprojekt ist der Pfad von Plugin A in „ExtLibs.props.input“ hardgecoded. Anschließend kann das neue User-Macro in den Projekteinstellungen genutzt werden:

  • C/C++ > General > Additional Include Directories
    • Z. B. $(PluginAPath)include\
  • Linker > General > Additional Library Directories
    • Z. B. $(PluginAPath)lib\$(PlatformName)\$(Configuration)\
  • Linker > Input > Additional Dependencies
    • Z. B. interplugin_test_a.lib

Der Linker nutzt nur die Import-Bibliothek von Plugin A, nicht das kompilierte Plugin selbst.

Nun können auch die öffentlichen Header von Plugin A gefunden und Includet werden, vgl. „./b/src/IplgValueInvertB.cpp“ Zeile 3. Plugin B kann nun die exportierte Klasse ganz normal nutzen, wie jede andere Klasse jeder anderen Bibliothek auch. Keine Sonderbehandlung ist notwendig.

Unter Linux könnte man im „CMakeLists.txt“ auch entsprechende Einstellungen einbauen. Ich habe das nicht gemacht.

Ich installiere alles von MegaMol™ beim Bauen in ein lokales Unterverzeichnis in meinem Home (angegeben durch den Install-Prefix bei den Build-Skripten). Dadurch sind alle Teile MegaMols™ an derselben Stelle. Das betrifft natürlich auch den Core, welcher bereits durch das „CMakeLists.txt“ gefunden wird, und auch Plugin A, welches daher versehentlich gefunden wurde. Daher ist der Include-Pfad für die öffentlichen Header des „installierten“ Plugins bereits bekannt. Außerdem ist es unter Linux nicht notwendig Shared Objects gegen ihre Abhängigkeiten zu linken. Das ist Aufgabe des Runtime-Loaders sämtliche Referenzen aufzulösen. Daher sind weitere Einstellungen zum Bauen des Plugins schlicht nicht notwendig.

Wenn Sie trotzdem das exportierende Plugin explizit finden möchten, beispielsweise, weil es nicht installiert sein sollte, dann müssten Sie am sinnvollsten ein entsprechendes CMake-Find-Skript schreiben und in der „CMakeLists.txt“ des abhängigen Plugins „find_package“ nutzen. Zumindest augenblicklich ist mir das aber egal, da das exportierende Plugin sowieso installiert sein muss, damit alles zur Laufzeit funktionieren kann.

MegaMol™ mit beiden Plugins konfigurieren

In der MegaMol™-Konfigurationsdatei müssen beide Plugins ganz normal angegeben werden, entweder explizit mit ihren Namen (empfohlen) oder per File-Globbing. Sie sollten aber die Anhängigkeiten beachten und die Plugins in der richtigen Reihenfolge laden, z. B. Plugin A sollte vor Plugin B geladen werden. Technisch ist es aber egal.

Beide Plugins, alle Plugins, sind im Prinzip nur Dlls. Wird Plugin B zuerst geladen, also die Dll, so werden auch alle abhängigen Dlls geladen. Dabei ist auch die Dll Plugin A dabei welche daher in den Speicher geladen wird. Allerdings sind die Metadaten von Plugin A noch nicht in den Core geladen und seine Module und Calls daher noch nicht zur Laufzeit verfügbar. Wird nun das Kommando ausgeführt um Plugin A zu laden, und das Betriebssystem aufgefordert diese Dll zu laden, so befindet diese sich schon im Hauptspeicher. Anschließend werden die Metadaten in den Core geladen und stehen den Factories zur Verfügung.

Warnung vor zyklischen Abhängigkeiten

Zyklische Abhängigkeiten sollten, wie immer, vermieden werden. In der Theorie sollte es möglich sein zyklische Abhängigkeiten aufzulösen. Allerdings, sollten Sie ihren Code in diese Richtung entwickeln, dann empfehle ich dringend ein weiteres Plugin zu starten in dem Sie alle gemeinsam genutzten Klassen auslagern, z. B. Calls.

Laufzeitverhalten wenn abhängige Plugins nicht gefunden wurden

Die Fehlermeldungen des Betriebssystems wenn eine Dll (Plugin) nicht geladen werden kann sind ziemlich nutzlos. (Ich arbeite hier an Verbesserungen, aber das ist weit nicht so einfach wie man denken würde.) Normalerweise sagt die Fehlermeldung nur, dass die Dll nicht geladen werden konnte. Denken Sie also daran, dass diese Fehlermeldung nun auch kommen kann wenn ein Plugin die Plugins nicht laden kann von denen es selbst abhängt. Die Plugin-Abhängigkeiten müssen also sowohl zur Compile-Zeit als auch zur Laufzeit auflösbar sein. Beides löst sich selbst wenn einfach alle Plugins in den gleichen lokalen Ordner „installiert“ werden.

Versionsnummertests

Alle Plugins prüfen zum Zeitpunkt wenn sie geladen werden die Versionsnummern von Core und Vislib. Damit werden Inkonsistenzen zur Laufzeit verhindert. Stellen Sie sich vor, sie bauen den Core und arbeiten dann an ihrem Plugin. Dabei fällt eine Bugt in der Vislib auf und wird von ihnen gefixt. Core und Plugin würden nun unterschiedliche Vislibs benutzt und nur diese Versionsnummerprüfungen teilen ihnen mit, dass das eine wirklich schlechte Idee ist.

Nun ersetzen Sie in diesem Szenario die Vislib mit Plugin A. Willkommen in der Hölle. Augenblicklich gibt es keinen Mechanismus um Versionsnummer zwischen Plugins zu überprüfen. (Wahrscheinlich kommt auch keiner vor der Implementierung des berüchtigten „Call-Interface-Redesigns“.) Sie müssen einfach aufpassen!

 

Einfache Computergraphik-Demos entwickeln wir gerne als Konsolenanwendungen. Das Konsolenfenster ist einfach praktisch für Debug-Ausgaben. Wenn wir die Programme dann aber auf unserer Stereo-Powerwall zeigen, dann stört das kurz aufblitzende Konsolenfenster massiv. Darum habe ich mal kurz ein kleines Tool zusammengesteckt, dass Konsolenanwendungen startet, die Konsole nicht anzeigt, ihren Inhalt aber abgreift, damit man im Notfall nachsehen kann warum es mal wieder nicht funktioniert.

Ich präsentiere die HiddenConsole:

HiddenConsole.zipHiddenConsole.zip Application starter hiding the console window
[55.3 KB; MD5: 848cbd8aa901fe38be8179d65b6d2162; Mehr Info]

Und weil ich es kann, der Quelltext ist frei verfügbar:

https://bitbucket.org/sgrottel-uni/hiddenconsole