Wygeneruj pliki manifestu dla modelu COM bez rejestracji

87

Mam kilka aplikacji (niektóre natywne, niektóre .NET), które używają plików manifestu, dzięki czemu można je wdrażać w całkowitej izolacji , bez konieczności jakiejkolwiek globalnej rejestracji COM. Na przykład zależność od serwera dbgrid32.ocx com jest zadeklarowana w następujący sposób w pliku myapp.exe.manifest, który znajduje się w tym samym folderze co myapp.exe:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="myapp.exe" version="1.2.3.4" />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="dbgrid32.ocx" version="5.1.81.4" />
    </dependentAssembly>
  </dependency>
</assembly>

Plik dbgrid32.ocx jest wdrażany w tym samym folderze wraz z własnym plikiem dbgrid32.ocx.manifest:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="dbgrid32.ocx" version="5.1.81.4" />
  <file name="dbgrid32.ocx">
     <typelib
        tlbid="{00028C01-0000-0000-0000-000000000046}"
        version="1.0"
        helpdir=""/>
    <comClass progid="MSDBGrid.DBGrid"
       clsid="{00028C00-0000-0000-0000-000000000046}"
       description="DBGrid  Control" />
  </file>
</assembly>

To wszystko działa dobrze, ale ręczne utrzymywanie tych plików manifestu jest trochę uciążliwe. Czy istnieje sposób na automatyczne generowanie tych plików? Idealnie chciałbym po prostu zadeklarować zależność aplikacji od listy serwerów COM (zarówno natywnych, jak i .NET), a następnie pozwolić, aby pozostałe zostały wygenerowane automatycznie. Czy to możliwe?

Wim Coenen
źródło
+1 również: ponownie oznaczono regfreecom, ponieważ ten tag jest bardziej powszechny w przypadku wolnego od rejestru COM
MarkJ
Czy mogę użyć wyższej wersji mstscax.dll w moim własnym folderze instalacyjnym za pomocą pliku manifestu?
Acewind
@acewind yes. (Możesz zadać nowe pytanie ze szczegółami).
UuDdLrLrSs
@UuDdLrLrSs Dobra wiadomość! W tym miejscu zamieszczam nowe pytanie: stackoverflow.com/questions/63575746/ ...
Acewind

Odpowiedzi:

63

Wygląda na to, że idealne rozwiązanie jeszcze nie istnieje. Podsumowując niektóre badania:

Make My Manifest ( link )

To narzędzie skanuje projekt VB6 w poszukiwaniu zależności COM, ale obsługuje również ręczne deklarowanie późno wiązanych zależności COM (tj. Tych używanych przez CreateObject).

Co ciekawe, to narzędzie umieszcza wszystkie informacje o zależnościach w manifeście aplikacji. Aplikacja exe i jej zależności są opisane jako pojedynczy zestaw składający się z wielu plików. Wcześniej nie zdawałem sobie sprawy, że to jest możliwe.

Wygląda na bardzo dobre narzędzie, ale od wersji 0.6.6 ma następujące ograniczenia:

  • tylko dla aplikacji VB6, zaczyna się od pliku projektu VB6. Szkoda, ponieważ wiele z tego, co robi, tak naprawdę nie ma nic wspólnego z VB6.
  • aplikacja w stylu kreatora, nieodpowiednia do integracji w procesie kompilacji. Nie jest to duży problem, jeśli twoje zależności nie zmieniają się zbytnio.
  • freeware bez źródła, poleganie na nim ryzykownie, ponieważ w każdej chwili może stać się porzucone.

Nie testowałem, czy obsługuje biblioteki .NET com.

regsvr42 ( link do projektu code )

To narzędzie wiersza polecenia generuje pliki manifestu dla natywnych bibliotek COM. Wywołuje DllRegisterServer, a następnie szpieguje samo-rejestrację, ponieważ dodaje informacje do rejestru. Może również generować manifest klienta dla aplikacji.

To narzędzie nie obsługuje bibliotek .NET COM, ponieważ nie ujawniają one procedury DllRegisterServer.

Narzędzie jest napisane w C ++. Kod źródłowy jest dostępny.

mt.exe

Część zestawu Windows SDK (można go pobrać z witryny MSDN ), który już masz, jeśli masz zainstalowane oprogramowanie Visual Studio. Jest to udokumentowane tutaj . Za jego pomocą można generować pliki manifestu dla natywnych bibliotek COM w następujący sposób:

mt.exe -tlb:mycomlib.ocx -dll:mycomlib.ocx -out:mycomlib.ocx.manifest

Możesz wygenerować pliki manifestu dla bibliotek .NET COM w następujący sposób:

mt.exe -managedassemblyname:netlib.dll -nodependency -out:netlib.dll.manifest

Istnieją jednak pewne problemy z tym narzędziem:

  • Pierwszy fragment nie wygeneruje atrybutów progid, co zepsuje klientów używających CreateObject z progidami.
  • Drugi fragment kodu wygeneruje <runtime>i <mvid>elementy, które należy usunąć, zanim manifesty zaczną działać.
  • Generowanie manifestów klienta dla aplikacji nie jest obsługiwane.

Może przyszłe wydania SDK poprawią to narzędzie, testowałem to w Windows SDK 6.0a (vista).

Wim Coenen
źródło
1
Myślę, że przegapiłeś jedną opcję: mazecomputer.com, ale nie wiem nic o tym, czego nie opisuje strona internetowa.
Bob
MMM przekieruje również biblioteki DLL inne niż COM (standardowe). Nie jestem pewien, czy inne narzędzia to robią.
Bob
Tylko uwaga dla nerwowych: udostępniono źródło MMM. Z drugiej strony wydaje się, że jest to spowodowane tym, że autor postanowił przestać nad tym pracować. Wciąż pozytywny znak.
Gavin,
2
Witryna MMM nie jest już dostępna, ale miejsce, w którym został umieszczony kod źródłowy, jest nadal dostępne dla wersji 0.9 i 0.12 .
Scott Chamberlain,
1
Właśnie wypróbowałem mt.exe dla bibliotek .NET COM, jak opisano powyżej, i działało bez modyfikacji manifestu przy użyciu wersji 7.1A. Ponadto linki do MMM nie działały, ale Unattented Make My Manifest wydaje się wykonywać przyzwoitą pracę.
bzuillsmith
28

Za pomocą zadania GenerateApplicationManifest programu MSBuild wygenerowałem manifest w wierszu polecenia identyczny z manifestem generowanym przez program Visual Studio. Podejrzewam, że Visual Studio używa GenerateApplicationManifest podczas kompilacji. Poniżej znajduje się mój skrypt kompilacji, który można uruchomić z wiersza poleceń za pomocą programu msbuild "msbuild build.xml"

Dziękuję Dave Templinowi i jego postowi, który wskazał mi zadanie GenerateApplicationManifest oraz dalszą dokumentację tego zadania przez MSDN .

build.xml

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Build">
        <ItemGroup>
            <File Include='MyNativeApp.exe'/>
            <ComComponent Include='Com1.ocx;Com2.ocx'/>
        </ItemGroup>
        <GenerateApplicationManifest
            AssemblyName="MyNativeApp.exe"
            AssemblyVersion="1.0.0.0"
            IsolatedComReferences="@(ComComponent)"
            Platform="x86"
            ManifestType="Native">
            <Output
                ItemName="ApplicationManifest"
                TaskParameter="OutputManifest"/>
        </GenerateApplicationManifest>
    </Target>   
</Project>
mcdon
źródło
Myślę, że to naprawdę powinno być oznaczone jako odpowiedź na to pytanie. Teraz używam tego do zautomatyzowania całego generowania manifestów. Dziękuję @mcdon, zaoszczędziłeś mi dużo pracy.
Pete Magsig,
Zgadzam się, że to najlepsze rozwiązanie w przypadku budowania w programie Visual Studio. Prawdopodobnie nie jest oceniany wyżej tylko dlatego, że został opublikowany znacznie później niż inne odpowiedzi
dschaeffer
jak dodać to do csproj?
jle
@jle Myślę, że możesz dodać go do swojego csproj w celu AfterBuild. Oto link z msdn i kolejny post na temat wydarzeń przed kompilacją i po kompilacji. Uwaga: Nie testowałem włączania tego do csproj, ale podejrzewam, że to zadziała.
mcdon
Czy manifest można wygenerować dla biblioteki DLL C # COM, aby każdy plik exe mógł go używać (zamiast generować manifest na każdym pliku exe i
włączać
9

Make My Manifest (MMM) jest dobrym narzędziem do tego. Możliwe jest również napisanie skryptu przetwarzającego wszystkie pliki DLL / OCX za pomocą mt.exe w celu wygenerowania manifestu dla każdego z nich, a następnie scalenie ich wszystkich razem. MMM jest zwykle lepsze / łatwiejsze, ponieważ obsługuje również wiele specjalnych / dziwnych przypadków.

jdve
źródło
3
Trochę się denerwuję tą sprawą MMM; to tylko blog, darmowy, ale bez kodu źródłowego, tylko link do "samorozpakowującego się exe" i widzę komentarze o narzędziu powodującym awarię XP. mmm ...
Wim Coenen
Te „awarie” oznaczały śmierć samego narzędzia MMM. Zostało to naprawione w wersji 0.6.5, ale i tak będziesz potrzebować 0.6.6, ponieważ chociaż nadal jest wersją beta, nie wygasa. Jak już sugerowano, zawsze możesz użyć MT.EXE.
Bob
mt.exe nie generuje progidów, gdy używam go na natywnych serwerach com, takich jak dbgrid32.ocx
Wim Coenen
Użycie bez
rejestracji
8

Możesz użyć spin-offu Unattended Make My Manifest do generowania manifestów bezpośrednio w zautomatyzowanych kompilacjach. Używa pliku skryptu do dodawania zależnych składników COM. To jest fragment przykładowego ini z dostępnymi poleceniami:

# Unattended MMM script
#
# Command names are case-insensitive. Reference of supported commands:
#
# Command: Identity
#
#   Appends assemblyIdentity and description tags.
#
#   Parameters       <exe_file> [name] [description]
#      exe_file      file name can be quoted if containing spaces. The containing folder 
#                    of the executable sets base path for relative file names
#      name          (optional) assembly name. Defaults to MyAssembly
#      description   (optional) description of assembly
#
# Command: Dependency
#
#   Appends dependency tag for referencing dependent assemblies like Common Controls 6.0, 
#     VC run-time or MFC
#
#   Parameters       {<lib_name>|<assembly_file>} [version] [/update]
#     lib_name       one of { comctl, vc90crt, vc90mfc }
#     assembly_file  file name of .NET DLL exporting COM classes
#     version        (optional) required assembly version. Multiple version of vc90crt can
#                    be required by a single manifest
#     /update        (optional) updates assembly_file assembly manifest. Spawns mt.exe
#
# Command: File
#
#   Appends file tag and collects information about coclasses and interfaces exposed by 
#     the referenced COM component typelib.
#
#   Parameters       <file_name> [interfaces]
#     file_name      file containing typelib. Can be relative to base path
#     interfaces     (optional) pipe (|) separated interfaces with or w/o leading 
#                    underscore
#
# Command: Interface
#
#   Appends comInterfaceExternalProxyStub tag for inter-thread marshaling of interfaces
#
#   Parameters       <file_name> <interfaces>
#     file_name      file containing typelib. Can be relative to base path
#     interfaces     pipe (|) separated interfaces with or w/o leading underscore
#
# Command: TrustInfo
#
#   Appends trustInfo tag for UAC user-rights elevation on Vista and above
#
#   Parameters       [level] [uiaccess]
#     level          (optional) one of { 1, 2, 3 } corresponding to { asInvoker, 
#                    highestAvailable, requireAdministrator }. Default is 1
#     uiaccess       (optional) true/false or 0/1. Allows application to gain access to 
#                    the protected system UI. Default is 0
#
# Command: DpiAware
#
#   Appends dpiAware tag for custom DPI aware applications
#
#   Parameters       [on_off]
#     on_off         (optional) true/false or 0/1. Default is 0
#
# Command: SupportedOS
#
#   Appends supportedOS tag
#
#   Parameters       <os_type>
#     os_type        one of { vista, win7 }. Multiple OSes can be supported by a single 
#                    manifest
#

Będzie działać na 32- lub 64-bitowym systemie Windows.

wqw
źródło
+1 Ciekawe, zwłaszcza że kod źródłowy jest dostępny. Jestem trochę zdezorientowany podobieństwem nazw, najwyraźniej „zamanifestuj się” i „zamanifestuj bez nadzoru” to różne narzędzia różnych autorów.
Wim Coenen
2
Uwaga - od 2017 r. (8 lat później ...) ten projekt jest nadal aktywny z okazjonalnymi aktualizacjami konserwacyjnymi. github.com/wqweto/UMMM/commits/master . Działa dobrze i używam go rutynowo.
UuDdLrLrSs
0

Aby wypełnić identyfikatory ProgID, których nie zawiera mt.exe, możesz wywołać ProgIDFromCLSIDje w celu wyszukania ich w rejestrze. Wymaga to tradycyjnej rejestracji COM przed wypełnieniem pliku manifestu, ale później plik manifestu będzie samowystarczalny.

Ten kod C # dodaje identyfikatory ProgID do wszystkich klas COM w manifeście:

var manifest = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("s", "urn:schemas-microsoft-com:asm.v1");
foreach (var classElement in manifest.XPathSelectElements("s:assembly/s:file/s:comClass", namespaceManager)) {
    var clsid = Guid.Parse(classElement.Attribute("clsid").Value);
    int result = ProgIDFromCLSID(ref clsid, out string progId); if (result != S_OK) throw new COMException($"ProgID lookup failed for {clsid}.", result);
    classElement.SetAttributeValue("progid", progId);
}
manifest.Save(fileName);

Kod opiera się na tych definicjach międzyoperacyjnych:

[DllImport("ole32.dll")] static extern int ProgIDFromCLSID([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID);
const int S_OK = 0;
Edward Brey
źródło