Datei / Produktversion einer exe ermitteln



  • Hallo,

    ich muss in meinem Programm die Version einer exe (oder auch dll) ermitteln. Im .NET Framework 4.8 funktioniert das wunderbar mit

    var info = System.Diagnostics.FileVersionInfo.GetVersionInfo(Pfad);
    

    Ich nutze aber .NET 7.0. Die Quelltextzeile bleibt identisch, allerdings fliegt eine Exception

    Unable to find an entry point named 'GetFileVersionInfoSizeExW' in DLL 'version.dll'
    

    Ich habe im Netz gefunden, dass ich einen DLL-Import machen muss. Aber wie der in meinem Fall exakt aussehen muss, habe ich leider nicht herausgefunden. Folgendes habe ich:

    [DllImport("version.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetFileVersionInfoSizeExW(int dwFlags, string fileName, int handle);
    

    Die Exception fliegt aber noch immer. Was mache ich hier falsch? Passt die Import-Deklaration nicht?

    Vielen Dank im Voraus

    VG Torsten



  • Das es früher funktioniert hat war wohl "zufall" weil die .Net Framework Runtime eine version.dll im startup schon geladen hat. Aber in .Net ist das nicht mehr der fall:
    https://github.com/dotnet/runtime/issues/79878



  • Hallo nochmal,

    okay, aus welcher Assembly kommt <Libraries.Version>? Der Compiler behauptet, er kennt <Libraries> nicht. Auf die partielly Klasse Interop.Libraries kann ich irgendwie nicht zugreifen, die kennt der Compiler nicht 😞

    VG Torsten



  • Die Klasse Interop.Libraries ist ja als internal (innerhalb der .NET Sourcen) deklariert, daher kein Zugriff von außen.
    Das Problem ist aber ein anderes, nämlich, daß für LoadLibrary (bzw. LoadLibraryEx) (welches intern von [DllImport] verwendet wird) der Pfad angegeben werden sollte, s. LoadLibraryEx: Parameters:

    Wenn die Zeichenfolge einen Modulnamen ohne Pfad angibt und mehrere geladene Module denselben Basisnamen und dieselbe Erweiterung aufweisen, gibt die Funktion ein Handle an das Modul zurück, das zuerst geladen wurde.

    Leider ist bei [DllImport] ja nur die Angabe eines String-Literals möglich, denn eigentlich müßtest du dort als Pfad ja @"%windir%\System32\version.dll" angeben (und die Umgebungsvariable entsprechend aufgelöst werden).

    Du kannst zwar mal direkt @"C:\Windows\System32\version.dll" (bzw. wo auch immer dein Windows-Verzeichnis liegt) angeben, aber dies wäre nicht für alle Windows-Systeme gültig!

    Du müßtest also dann selber LoadLibrary(Ex) sowie GetProcAddress mit dem für das System richtigen Pfad aufrufen, wie z.B. in Simplify LoadLibrary/GetProcAddress helper for dynamic parallel DLL instantiation.

    Hast du denn selber (in deinem Projekt bzw. im Windows-PATH) eine "version.dll"? Überprüfe mal mit "where version.dll" (in der Eingabeaufforderung) den Pfad.



  • @firefly: Sehe ich das richtig, wenn du selbst keine version.dll in deiner Solution hättest wäre das Problem nicht da?
    Weil dokumentiert ist sie schon für .NET 7 auch: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.fileversioninfo.getversioninfo?view=net-7.0

    Würde mich wundern wenn das per se nicht geht. Ich verwende das zumindest in Unit Tests in .NET 7.0 Projekten ohne Probleme.

    Wenn es wirklich notwendig sein sollte etwas via Native-Code zu laden, dann würde ich es mit CsWin32 machen: https://github.com/microsoft/CsWin32/ das erzeugt die ganzen DllImport+rundherum-Code für dich.

    MfG SideWinder



  • @SideWinder sagte in Datei / Produktversion einer exe ermitteln:

    @firefly: Sehe ich das richtig, wenn du selbst keine version.dll in deiner Solution hättest wäre das Problem nicht da?
    Weil dokumentiert ist sie schon für .NET 7 auch: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.fileversioninfo.getversioninfo?view=net-7.0

    Das habe ich nicht behauptet. Laut dem verlinkten bugreport wurde mit .Net 7 das verhalten geändert wann die runtime selbst versucht eine version.dll zu laden.
    Vorher passierte das direkt beim start der runtime selbst wodurch wohl die variante der version.dll geladen wurde, welche GetFileVersionInfoSizeExW bereitstellt.

    Mit .Net 7 wird das laden delayed wodurch es passieren kann dass eine andere version.dll geladen wird vermutlich weil sich dadurch der suchpfad für dlls, welche von LoadLibraryExW verwendet wird, verändert hat oder ein anderer code eine andere version.dll via LoadLibraryExW geladen hat und da anscheinend LoadLibraryExW matching anhand des datei namens macht führt der 2. aufruf dazu das nichts geladen wird sondern der handle zu der bereits geladenen version zurückgegeben wird.

    Da es bei dir funktioniert liegt wohl daran, dass bei deinen durchläufen immer die korrekte version.dll geladen wird.

    @Th69 Wenn du zeit und lust für experimente hast könnte man folgendes versuchen um zu schauen welche version.dll bei dir geladen wird.

    Da ich mit C# nicht so vertraut bin hier mal der ablauf beschrieben ohne c# code.

    • Erstelle ein Test projekt, welche diesen Fehler zeigt
    • Importiere LoadLibraryExW und GetModuleFileName via dllimport oder sonst wie.
    • Rufe in der "Main" methode foglendes auf (Ist der C code musst du dann passend ändern dass es mit der C# dllimport variante funktioniert:
    HMODULE mod2 = LoadLibraryExW(L"version.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
    <char array> path[1024]
    GetModuleFileNameW(mod2, path, 1024);
    

    In Path sollte dann der pfad zu der version.dll stehen, welche von der runtime geladen wurde



  • Ich habe gerade mit VS 2022 eine .NET 7 Konsolenapplikation erstellt und kann den Fehler (Exception) nicht nachstellen (weder bei AnyCPU/x64 noch x86):

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace LoadLibraryTest
    {
    	internal class Program
    	{
    		[DllImport("kernel32.dll", SetLastError = true)]
    		static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
    
    		[System.Flags]
    		enum LoadLibraryFlags : uint
    		{
    			None = 0,
    			DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
    			LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
    			LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
    			LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
    			LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
    			LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
    			LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
    			LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
    			LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
    			LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
    			LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008,
    			LOAD_LIBRARY_REQUIRE_SIGNED_TARGET = 0x00000080,
    			LOAD_LIBRARY_SAFE_CURRENT_DIRS = 0x00002000,
    		}
    
    		[DllImport("kernel32.dll", SetLastError = true)]
    		public static extern uint GetModuleFileName([In] IntPtr hModule, [Out] StringBuilder lpFilename, [In][MarshalAs(UnmanagedType.U4)]int nSize);
    
    		static void Main(string[] args)
    		{
    			Console.WriteLine("LoadLibraryTest:");
    
    			Console.WriteLine(Environment.Is64BitProcess ? 64 : 32);
    
    			var info = FileVersionInfo.GetVersionInfo(Environment.ProcessPath);
    			Console.WriteLine(info.FileName);
    
    			IntPtr hModule = LoadLibraryEx("version.dll", IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_SEARCH_SYSTEM32);
    
    			var path = new StringBuilder(1024);
    			GetModuleFileName(hModule, path, path.Capacity);
    
    			Console.WriteLine(path);
    		}
    	}
    }
    
    

    Die Ausgabe ist bei mir:

    LoadLibraryTest:
    64
    E:\Projects\C#\LoadLibraryTest\bin\Debug\net7.0\LoadLibraryTest.exe
    C:\WINDOWS\SYSTEM32\version.dll
    

    (bzw. 32)

    Bei @TorDev muß also noch eine andere "Version.dll" eingebunden sein.

    Edit: Aber selbst, wenn ich eine Dummy-"Version.dll" in den Ausgabeordner packe, ändert sich nichts an der Abarbeitung und Ausgabe.



  • @TorDev: Feedback?



  • Hallo,

    ich bin noch dran, Feedback kommt noch 😉

    VG Torsten


Anmelden zum Antworten