Vermittelte Komponenten für Windows-Runtime für eine quergeladene UWP-App

In diesem Artikel wird ein für Unternehmen bestimmtes Feature beschrieben, das von Windows 10 höher unterstützt wird und .NET-Apps, die Toucheingaben unterstützen, die Verwendung vorhandenen Codes ermöglichen, der für wichtige unternehmenskritische Vorgänge verantwortlich ist.

Einführung

Hinweis Der Beispielcode, der zu diesem Dokument gehört, kann für Visual Studio 2015 & 2017 heruntergeladen werden. Die Microsoft Visual Studio-Vorlage zum Erstellen von vermittelten Windows-Runtime Komponenten kann hier heruntergeladen werden: Visual Studio 2015-Vorlage für universelle Windows-Apps für Windows 10

Windows enthält ein neues Feature namens vermittelte Windows-Runtime Components für quergeladene Anwendungen. Wir verwenden den Begriff IPC (Inter-Process Communication), um die Möglichkeit zu beschreiben, vorhandene Desktopsoftwareressourcen in einem Prozess (Desktopkomponente) auszuführen, während sie mit diesem Code in einer UWP-App interagieren. Dies ist ein vertrautes Modell für Unternehmensentwickler, da Datenbankanwendungen und Anwendungen, die NT-Dienste in Windows verwenden, eine ähnliche Mehrprozessarchitektur nutzen.

Das Querladen der App ist eine wichtige Komponente dieses Features. Unternehmensspezifische Anwendungen haben keinen Platz im allgemeinen Microsoft Store für Verbraucher und Unternehmen haben sehr spezifische Anforderungen hinsichtlich Sicherheit, Datenschutz, Verteilung, Einrichtung und Wartung. Daher ist das Querlademodell sowohl ein wichtiges Merkmal für diejenigen, die dieses Feature verwenden wollen, als auch ein kritisches Implementierungsdetail.

Datenorientierte Anwendungen sind ein wichtiges Ziel für diese Anwendungsarchitektur. Es ist vorgesehen, dass vorhandene Geschäftsregeln, z. B. in SQL Server, ein gemeinsamer Bestandteil der Desktopkomponente sein sollen. Dies ist sicherlich nicht die einzige Art von Funktionalität, die von der Desktopkomponente unterstützt werden kann; ein großer Teil der Nachfrage nach diesem Feature hängt mit Datenbeständen und existierender Geschäftslogik zusammen.

Schließlich wurde dieses Feature aufgrund der hohen Popularität der .NET-Runtime und der C#-Sprache in der Unternehmensentwicklung entwickelt, wobei die Verwendung von .NET sowohl für die UWP-App als auch für die Desktopkomponentenseite hervorgehoben wurde. Während für die UWP-App andere Sprachen und Runtimes möglich sind, wird im zugehörigen Beispiel nur C# veranschaulicht und ausschließlich auf die .NET-Runtime beschränkt.

Anwendungskomponenten

Hinweis Dieses Feature ist ausschließlich für die Verwendung von .NET vorgesehen. Sowohl die Client-App als auch die Desktopkomponente müssen mit .NET erstellt worden sein.

Anwendungsmodell

Dieses Feature basiert auf der allgemeinen Anwendungsarchitektur, die als MVVM (Model View View-Model) bezeichnet wird. Daher wird davon ausgegangen, dass das "Modell" vollständig in der Desktopkomponente untergebracht ist. Es sollte daher sofort offensichtlich sein, dass die Desktopkomponente "kopflos" ist (d. h. keine Benutzeroberfläche enthält). Die Ansicht ist vollständig in der quergeladenen Unternehmensanwendung enthalten. Obwohl es keine Notwendigkeit gibt, dass diese Anwendung mit dem "Ansichtsmodell"-Konstrukt erstellt wird, gehen wir davon aus, dass die Verwendung dieses Musters üblich ist.

Desktopkomponente

Die Desktopkomponente in diesem Feature ist ein neuer Anwendungstyp, der als Teil dieses Features eingeführt wird. Diese Desktopkomponente kann nur in C# geschrieben werden und muss für .NET 4.6 oder höher für Windows 10 gedacht sein. Der Projekttyp ist eine Hybridlösung aus dem CLR für UWP, da das prozessübergreifende Kommunikationsformat UWP-Typen und -Klassen umfasst, während die Desktopkomponente alle Teile der .NET-Runtime-Klassenbibliothek aufrufen darf. Die Auswirkungen auf das Visual Studio-Projekt werden weiter unten ausführlich beschrieben. Diese Hybridkonfiguration ermöglicht das Marshalling von UWP-Typen zwischen der Anwendung, die auf den Desktopkomponenten basiert, während Desktop-CLR-Code innerhalb der Desktopkomponentenimplementierung aufgerufen werden kann.

Vertrag

Der Vertrag zwischen der quergeladenen Anwendung und der Desktopkomponente wird im Hinblick auf das UWP-Typsystem beschrieben. Dies umfasst das Deklarieren einer oder mehrerer C#-Klassen, die eine UWP darstellen können. Weitere Informationen zum Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic finden Sie im MSDN-Thema Erstellen Windows-Runtime Klasse mit C#.

Hinweis Enumerationen werden in der Windows-Runtime-Komponentenvertrag zwischen Desktopkomponente und quergeladener Anwendung aktuell nicht unterstützt.

Quergeladene Anwendung

Die quergeladene Anwendung ist eine normale UWP-App in jeder Hinsicht außer einer: Sie wird quergeladen und nicht über den Microsoft Store installiert. Die meisten Installationsmechanismen sind identisch: Das Manifest und das Anwendungspaket sind ähnlich (eine Ergänzung zum Manifest wird später ausführlich beschrieben). Sobald das Querladen aktiviert ist, kann ein einfaches PowerShell-Skript die erforderlichen Zertifikate und die Anwendung selbst installieren. Es ist die normale bewährte Methode, dass die quergeladene Anwendung den WACK-Zertifizierungstest bestehen, der im Menü "Projekt/ Store" in Visual Studio enthalten ist.

Hinweis: Querladen kann in Einstellungen -> Update und Sicherheit –> Für Entwickler aktiviert werden.

Wichtig: Es ist zu beachten, dass der im Rahmen von Windows 10 ausgelieferte App-Broker nur eine 32-Bit-Version ist. Die Desktopkomponente muss 32-Bit sein. Quergeladene Anwendungen können 64-Bit sein (vorausgesetzt, es sind sowohl 64-Bit- als auch 32-Bit-Proxys registriert), dies ist jedoch atypisch. Das Erstellen der quergeladenen Anwendung in C# mithilfe der normalen "neutralen" Konfiguration und der Standardeinstellung "32-Bit bevorzugen" erstellt natürlich quergeladene Anwendungen in einer 32-Bit-Version.

Serverinstancing und AppDomains

Jede quergeladene Anwendung erhält eine eigene Instanz eines App Broker-Servers (so genannte "Multiinstanzerstellung"). Der Servercode wird innerhalb einer einzelnen AppDomain ausgeführt. Auf diese Weise können mehrere Versionen von Bibliotheken in separaten Instanzen ausgeführt werden. Beispiel: Anwendung A benötigt V1.1 einer Komponente und Anwendung B V2. Diese werden sauber getrennt, indem V1.1- und V2-Komponenten in separaten Serververzeichnissen vorhanden sind und die Anwendung auf den jeweiligen Server verweist, der die gewünschte Version unterstützt.

Die Servercodeimplementierung kann von mehreren App Broker-Serverinstanzen gemeinsam genutzt werden, indem sie mehrere Anwendungen auf dasselbe Serververzeichnis verweisen. Es gibt weiterhin mehrere Instanzen des App Broker-Servers, aber sie führen identischen Code aus. Alle Implementierungskomponenten, die in einer einzigen Anwendung verwendet werden, sollten im selben Pfad vorhanden sein.

Definieren des Vertrags

Der erste Schritt beim Erstellen einer Anwendung mit diesem Feature besteht darin, den Vertrag zwischen der quergeladenen Anwendung und der Desktopkomponente zu erstellen. Dies darf ausschließlich mit Windows-Runtime-Typen erfolgen. Glücklicherweise sind diese leicht mit C#-Klassen zu deklarieren. Beim Definieren dieser Konversationen, die in einem späteren Abschnitt behandelt werden, gibt es jedoch wichtige Leistungsaspekte.

Die Abfolge zum Definieren des Vertrags ist wie folgt:

Schritt 1: Erstellen Sie in Visual Studio eine neue Klassenbibliothek. Stellen Sie sicher, dass Sie das Projekt mithilfe der Klassenbibliotheksvorlage und nicht mit der Windows-Runtime Component-Vorlage erstellen.

Natürlich folgt eine Implementierung, aber dieser Abschnitt behandelt nur die Definition des Prozessübergreifenden Vertrags. Das begleitende Beispiel enthält die folgende Klasse (EnterpriseServer.cs), deren Anfangsform wie folgt aussieht:

namespace Fabrikam
{
    public sealed class EnterpriseServer
    {

        public ILis<String> TestMethod(String input)
        {
            throw new NotImplementedException();
        }
        
        public IAsyncOperation<int> FindElementAsync(int input)
        {
            throw new NotImplementedException();
        }
        
        public string[] RetrieveData()
        {
            throw new NotImplementedException();
        }
        
        public event EventHandler<string> PeriodicEvent;
    }
}

Dadurch wird eine Klasse "EnterpriseServer" definiert, die von der quergeladenen Anwendung instanziiert werden kann. Diese Klasse stellt die in der RuntimeClass versprochene Funktionalität bereit. Die RuntimeClass kann verwendet werden, um den Winmd-Verweis zu generieren, der in der quergeladenen Anwendung enthalten ist.

Schritt 2: Bearbeiten Sie die Projektdatei manuell, um den Ausgabetyp des Projekts in Windows-Runtime Komponente zu ändern.

Klicken Sie dazu in Visual Studio mit der rechten Maustaste auf das neu erstellte Projekt, und wählen Sie "Projekt entladen" aus, klicken Sie dann erneut, und wählen Sie "EnterpriseServer.csproj bearbeiten" aus, um die Projektdatei, eine XML-Datei, zur Bearbeitung zu öffnen.

Suchen Sie in der geöffneten Datei nach dem <OutputType>-Tag, und ändern Sie den Wert in "winmdobj".

Schritt 3: Erstellen Sie eine Buildregel, die eine Windows-Metadatendatei „Referenz“ (WINMD-Datei) erstellt. d.h. keine Implementierung hat.

Schritt 4: Erstellen Sie eine Buildregel, die eine Windows-Metadatendatei "Implementierung" erstellt, d. h. die die gleichen Metadateninformationen enthält, aber auch die Implementierung.

Dies erfolgt durch die folgenden Skripte. Fügen Sie die Skripte zur Befehlszeile des Postbuildereignisses in Projekt Eigenschaften>Buildereignisse hinzu.

Beachten Sie, dass das Skript sich abhängig von der Version von Windows, auf die Sie abzielen (Windows 10) und der verwendeten Version von Visual Studio unterscheidet.

Visual Studio 2015

    call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86 10.0.14393.0

    md "$(TargetDir)"\impl    md "$(TargetDir)"\reference

    erase "$(TargetDir)\impl\*.winmd"
    erase "$(TargetDir)\impl\*.pdb"
    rem erase "$(TargetDir)\reference\*.winmd"

    xcopy /y "$(TargetPath)" "$(TargetDir)impl"
    xcopy /y "$(TargetDir)*.pdb" "$(TargetDir)impl"

    winmdidl /nosystemdeclares /metadata_dir:C:\Windows\System32\Winmetadata "$(TargetPath)"

    midl /metadata_dir "%WindowsSdkDir%UnionMetadata" /iid "$(SolutionDir)BrokeredProxyStub\$(TargetName)_i.c" /env win32 /x86 /h   "$(SolutionDir)BrokeredProxyStub\$(TargetName).h" /winmd "$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(SolutionDir)BrokeredProxyStub\dlldata.c" /proxy "$(SolutionDir)BrokeredProxyStub\$(TargetName)_p.c"  "$(TargetName).idl"
    mdmerge -n 1 -i "$(ProjectDir)bin\$(ConfigurationName)" -o "$(TargetDir)reference" -metadata_dir "%WindowsSdkDir%UnionMetadata" -partial

    rem erase "$(TargetPath)"

Visual Studio 2017

    call "$(DevEnvDir)..\..\vc\auxiliary\build\vcvarsall.bat" x86 10.0.16299.0

    md "$(TargetDir)"\impl
    md "$(TargetDir)"\reference

    erase "$(TargetDir)\impl\*.winmd"
    erase "$(TargetDir)\impl\*.pdb"
    rem erase "$(TargetDir)\reference\*.winmd"

    xcopy /y "$(TargetPath)" "$(TargetDir)impl"
    xcopy /y "$(TargetDir)*.pdb" "$(TargetDir)impl"

    winmdidl /nosystemdeclares /metadata_dir:C:\Windows\System32\Winmetadata "$(TargetPath)"

    midl /metadata_dir "%WindowsSdkDir%UnionMetadata" /iid "$(SolutionDir)BrokeredProxyStub\$(TargetName)_i.c" /env win32 /x86 /h "$(SolutionDir)BrokeredProxyStub\$(TargetName).h" /winmd "$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(SolutionDir)BrokeredProxyStub\dlldata.c" /proxy "$(SolutionDir)BrokeredProxyStub\$(TargetName)_p.c"  "$(TargetName).idl"
    mdmerge -n 1 -i "$(ProjectDir)bin\$(ConfigurationName)" -o "$(TargetDir)reference" -metadata_dir "%WindowsSdkDir%UnionMetadata" -partial

    rem erase "$(TargetPath)"

Sobald die Referenz-winmd erstellt wurde (im Ordner "Referenz" im Zielordner des Projekts), wird sie manuell in jedes nutzende quergeladene Anwendungsprojekt übertragen (kopiert) und referenziert. Dies wird im nächsten Abschnitt näher erläutert. Die in den obigen Buildregeln verkörperte Projektstruktur stellt sicher, dass sich die Implementierung und die Referenz-Winmd in eindeutig getrennten Verzeichnissen in der Buildhierarchie befinden, um Verwirrung zu vermeiden.

Quergeladene Anwendungen im Detail

Wie bereits erwähnt, wird die quergeladene Anwendung wie jede andere UWP-App erstellt, aber es gibt ein weiteres Detail: Deklarieren der Verfügbarkeit der RuntimeClass (es) im quergeladenen Anwendungsmanifest. Dadurch kann die Anwendung einfach neu schreiben, um auf die Funktionalität in der Desktopkomponente zuzugreifen. Ein neuer Manifesteintrag im Abschnitt <Erweiterung> beschreibt die in der Desktopkomponente implementierte RuntimeClass und enthält Informationen dazu, wo sie sich befindet. Diese Deklarationsinhalte im Manifest der Anwendung sind für Apps für Windows 10 identisch. Beispiel:

<Extension Category="windows.activatableClass.inProcessServer">
    <InProcessServer>
        <Path>clrhost.dll</Path>
        <ActivatableClass ActivatableClassId="Fabrikam.EnterpriseServer" ThreadingModel="both">
            <ActivatableClassAttribute Name="DesktopApplicationPath" Type="string" Value="c:\test" />
        </ActivatableClass>
    </InProcessServer>
</Extension>

Die Kategorie ist inProcessServer, da es mehrere Einträge in der OutOfProcessServer-Kategorie gibt, die für diese Anwendungskonfiguration nicht verfügbar sind. Beachten Sie, dass die <Path-Komponente> immer clrhost.dll enthalten muss (dies wird jedoch nicht erzwungen, und die Angabe eines anderen Werts schlägt auf nicht definierte Weise fehl).

Der Abschnitt <ActivatableClass> ist identisch mit einer echten In-Process-RuntimeClass, die von einer Windows-Runtime Komponente im App-Paket bevorzugt wird. <ActivatableClassAttribute> ist ein neues Element, und der Attribut-Name="DesktopApplicationPath" und Type="string" sind obligatorisch und immer gleich. Das Wert „Value“ verweist auf den Speicherort, an dem sich die Winmd-Implementierung der Desktopkomponente befindet (weitere Details hierzu finden Sie im nächsten Abschnitt). Jede von der Desktopkomponente bevorzugte RuntimeClass sollte über eine eigene <AktivableClass>-Elementstruktur verfügen. Die ActivatableClassId muss mit dem vollqualifizierten Namespace-Namen der RuntimeClass übereinstimmen.

Wie im Abschnitt "Definieren des Vertrags" erwähnt, muss ein Projektverweis auf die Rereferenz-Winmd der Desktopkomponente erfolgen. Das Visual Studio-Projektsystem erstellt normalerweise eine Verzeichnisstruktur mit zwei Ebenen mit demselben Namen. Im Beispiel ist das enterpriseIPCApplication\EnterpriseIPCApplication. Die Referenz-Winmd wird manuell in dieses Verzeichnis der zweiten Ebene kopiert, und dann wird das Dialogfeld "Projektverweise" verwendet (klicken Sie auf die Schaltfläche Durchsuchen…), um diese Winmd zu suchen und darauf zu verweisen. Danach sollte der Namespace der obersten Ebene der Desktopkomponente (z. B. Fabrikam) als Knoten der obersten Ebene im Teil "Verweise" des Projekts angezeigt werden.

Hinweis: Es ist sehr wichtig, die Referenz-Winmd in der quergeladenen Anwendung zu verwenden. Wenn Sie versehentlich die Winmd-Implementierung in das quergeladene App-Verzeichnis übertragen und darauf verweisen, wird wahrscheinlich ein Fehler im Zusammenhang mit "IStringable kann nicht gefunden werden" angezeigt. Dies ist ein sicheres Zeichen dafür, dass auf das falsche Winmd verwiesen wurde. Die Postbuildregeln in der IPC-Server-App (im nächsten Abschnitt ausführlich beschrieben) trennen diese beiden Winmd sorgfältig in separate Verzeichnisse.

Umgebungsvariablen (insbesondere %ProgramFiles%) können in <ActivatableClassAttribute Value="path"> verwendet werden. Wie bereits erwähnt, unterstützt der App-Broker nur 32-Bit, sodass %ProgramFiles% in C:\Programme (x86) aufgelöst wird, wenn die Anwendung auf einem 64-Bit-Betriebssystem ausgeführt wird.

Desktop-IPC-Serverdetails

In den beiden vorherigen Abschnitten werden die Deklaration der Klasse und der Ablauf des Transports der Referenz-Winmd in das quergeladene Anwendungsprojekt beschrieben. Der Großteil der verbleibenden Arbeit in der Desktopkomponente umfasst die Implementierung. Da der Zweck der Desktopkomponente darin besteht, Desktopcode aufrufen zu können (in der Regel um vorhandene Coderessourcen wiederzuverwenden), muss das Projekt auf besondere Weise konfiguriert werden. Normalerweise verwendet ein Visual Studio-Projekt mit .NET eines von zwei "Profilen". Eine ist für Desktop (".NetFramework") und eines für den UWP-App-Teil der CLR (".NetCore"). Eine Desktopkomponente in diesem Feature ist eine Hybridlösung aus diesen beiden. Daher wird der Referenzabschnitt sehr sorgfältig konstruiert, um diese beiden Profile zu mischen.

Ein normales UWP-App-Projekt enthält keine expliziten Projektverweise, da die gesamte Windows-Runtime-API-Oberfläche implizit enthalten ist. Normalerweise werden nur andere Projektübergreifende Verweise vorgenommen. Ein Desktopkomponentenprojekt verfügt jedoch über einen sehr speziellen Satz von Verweisen. Es beginnt als "Desktop\Klassenbibliothek"-Projekt und ist daher ein Desktopprojekt. Daher müssen explizite Verweise auf die Windows-Runtime-API (über Verweise auf winmd-Dateien) erfolgen. Fügen Sie wie unten dargestellt die richtigen Verweise hinzu.

<ItemGroup>
    <!-- These reference are added by VS automatically when you create a Class Library project-->
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
    <!-- These reference should be added manually by editing .csproj file-->

    <Reference Include="System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
      <HintPath>$(MSBuildProgramFiles32)\Microsoft SDKs\NETCoreSDK\System.Runtime.WindowsRuntime\4.0.10\lib\netcore50\System.Runtime.WindowsRuntime.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows">
      <HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Foundation.FoundationContract">
      <HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\Windows.Foundation.FoundationContract\1.0.0.0\Windows.Foundation.FoundationContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Foundation.UniversalApiContract">
      <HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\Windows.Foundation.UniversalApiContract\1.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Networking.Connectivity.WwanContract">
      <HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\Windows.Networking.Connectivity.WwanContract\1.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Activation.ActivatedEventsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Activation.ActivatedEventsContract\1.0.0.0\Windows.ApplicationModel.Activation.ActivatedEventsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Activation.ActivationCameraSettingsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Activation.ActivationCameraSettingsContract\1.0.0.0\Windows.ApplicationModel.Activation.ActivationCameraSettingsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Activation.ContactActivatedEventsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Activation.ContactActivatedEventsContract\1.0.0.0\Windows.ApplicationModel.Activation.ContactActivatedEventsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Activation.WebUISearchActivatedEventsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Activation.WebUISearchActivatedEventsContract\1.0.0.0\Windows.ApplicationModel.Activation.WebUISearchActivatedEventsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Background.BackgroundAlarmApplicationContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Background.BackgroundAlarmApplicationContract\1.0.0.0\Windows.ApplicationModel.Background.BackgroundAlarmApplicationContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Calls.LockScreenCallContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Calls.LockScreenCallContract\1.0.0.0\Windows.ApplicationModel.Calls.LockScreenCallContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Resources.Management.ResourceIndexerContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Resources.Management.ResourceIndexerContract\1.0.0.0\Windows.ApplicationModel.Resources.Management.ResourceIndexerContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Search.Core.SearchCoreContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Search.Core.SearchCoreContract\1.0.0.0\Windows.ApplicationModel.Search.Core.SearchCoreContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Search.SearchContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Search.SearchContract\1.0.0.0\Windows.ApplicationModel.Search.SearchContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.ApplicationModel.Wallet.WalletContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.ApplicationModel.Wallet.WalletContract\1.0.0.0\Windows.ApplicationModel.Wallet.WalletContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Devices.Custom.CustomDeviceContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Devices.Custom.CustomDeviceContract\1.0.0.0\Windows.Devices.Custom.CustomDeviceContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Devices.Portable.PortableDeviceContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Devices.Portable.PortableDeviceContract\1.0.0.0\Windows.Devices.Portable.PortableDeviceContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Devices.Printers.Extensions.ExtensionsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Devices.Printers.Extensions.ExtensionsContract\1.0.0.0\Windows.Devices.Printers.Extensions.ExtensionsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Devices.Printers.PrintersContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Devices.Printers.PrintersContract\1.0.0.0\Windows.Devices.Printers.PrintersContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Devices.Scanners.ScannerDeviceContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Devices.Scanners.ScannerDeviceContract\1.0.0.0\Windows.Devices.Scanners.ScannerDeviceContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Devices.Sms.LegacySmsApiContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Devices.Sms.LegacySmsApiContract\1.0.0.0\Windows.Devices.Sms.LegacySmsApiContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Gaming.Preview.GamesEnumerationContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Gaming.Preview.GamesEnumerationContract\1.0.0.0\Windows.Gaming.Preview.GamesEnumerationContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Globalization.GlobalizationJapanesePhoneticAnalyzerContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Globalization.GlobalizationJapanesePhoneticAnalyzerContract\1.0.0.0\Windows.Globalization.GlobalizationJapanesePhoneticAnalyzerContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Graphics.Printing3D.Printing3DContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Graphics.Printing3D.Printing3DContract\1.0.0.0\Windows.Graphics.Printing3D.Printing3DContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Management.Deployment.Preview.DeploymentPreviewContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Management.Deployment.Preview.DeploymentPreviewContract\1.0.0.0\Windows.Management.Deployment.Preview.DeploymentPreviewContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Management.Workplace.WorkplaceSettingsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Management.Workplace.WorkplaceSettingsContract\1.0.0.0\Windows.Management.Workplace.WorkplaceSettingsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Media.Capture.AppCaptureContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Media.Capture.AppCaptureContract\1.0.0.0\Windows.Media.Capture.AppCaptureContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Media.Capture.CameraCaptureUIContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Media.Capture.CameraCaptureUIContract\1.0.0.0\Windows.Media.Capture.CameraCaptureUIContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Media.Devices.CallControlContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Media.Devices.CallControlContract\1.0.0.0\Windows.Media.Devices.CallControlContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Media.MediaControlContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Media.MediaControlContract\1.0.0.0\Windows.Media.MediaControlContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Media.Playlists.PlaylistsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Media.Playlists.PlaylistsContract\1.0.0.0\Windows.Media.Playlists.PlaylistsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Media.Protection.ProtectionRenewalContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Media.Protection.ProtectionRenewalContract\1.0.0.0\Windows.Media.Protection.ProtectionRenewalContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Networking.NetworkOperators.LegacyNetworkOperatorsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Networking.NetworkOperators.LegacyNetworkOperatorsContract\1.0.0.0\Windows.Networking.NetworkOperators.LegacyNetworkOperatorsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Networking.Sockets.ControlChannelTriggerContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Networking.Sockets.ControlChannelTriggerContract\1.0.0.0\Windows.Networking.Sockets.ControlChannelTriggerContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Security.EnterpriseData.EnterpriseDataContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Security.EnterpriseData.EnterpriseDataContract\1.0.0.0\Windows.Security.EnterpriseData.EnterpriseDataContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Security.ExchangeActiveSyncProvisioning.EasContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Security.ExchangeActiveSyncProvisioning.EasContract\1.0.0.0\Windows.Security.ExchangeActiveSyncProvisioning.EasContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Services.Maps.GuidanceContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Services.Maps.GuidanceContract\1.0.0.0\Windows.Services.Maps.GuidanceContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Services.Maps.LocalSearchContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Services.Maps.LocalSearchContract\1.0.0.0\Windows.Services.Maps.LocalSearchContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.System.Profile.SystemManufacturers.SystemManufacturersContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.System.Profile.SystemManufacturers.SystemManufacturersContract\1.0.0.0\Windows.System.Profile.SystemManufacturers.SystemManufacturersContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.System.Profile.ProfileHardwareTokenContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.System.Profile.ProfileHardwareTokenContract\1.0.0.0\Windows.System.Profile.ProfileHardwareTokenContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.System.Profile.ProfileRetailInfoContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.System.Profile.ProfileRetailInfoContract\1.0.0.0\Windows.System.Profile.ProfileRetailInfoContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.System.UserProfile.UserProfileContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.System.UserProfile.UserProfileContract\1.0.0.0\Windows.System.UserProfile.UserProfileContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.System.UserProfile.UserProfileLockScreenContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.System.UserProfile.UserProfileLockScreenContract\1.0.0.0\Windows.System.UserProfile.UserProfileLockScreenContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.UI.ApplicationSettings.ApplicationsSettingsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.UI.ApplicationSettings.ApplicationsSettingsContract\1.0.0.0\Windows.UI.ApplicationSettings.ApplicationsSettingsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.UI.Core.AnimationMetrics.AnimationMetricsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.UI.Core.AnimationMetrics.AnimationMetricsContract\1.0.0.0\Windows.UI.Core.AnimationMetrics.AnimationMetricsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.UI.Core.CoreWindowDialogsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.UI.Core.CoreWindowDialogsContract\1.0.0.0\Windows.UI.Core.CoreWindowDialogsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.UI.Xaml.Hosting.HostingContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.UI.Xaml.Hosting.HostingContract\1.0.0.0\Windows.UI.Xaml.Hosting.HostingContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Windows.Web.Http.Diagnostics.HttpDiagnosticsContract">
      <HintPath>$(MsBuildProgramFiles32)\Windows Kits\10\References\Windows.Web.Http.Diagnostics.HttpDiagnosticsContract\1.0.0.0\Windows.Web.Http.Diagnostics.HttpDiagnosticsContract.winmd</HintPath>
      <Private>False</Private>
    </Reference>
</ItemGroup>

Die oben genannten Verweise sind eine sorgfältige Mischung aus Verweisen, die für den ordnungsgemäßen Betrieb dieses Hybridservers von entscheidender Bedeutung sind. Das Protokoll besteht darin, die CSPROJ-Datei zu öffnen (wie in der Vorgehensweise zum Bearbeiten des Projekts OutputType beschrieben), und fügen Sie diese Verweise nach Bedarf hinzu.

Sobald die Verweise ordnungsgemäß konfiguriert sind, besteht die nächste Aufgabe darin, die Funktionalität des Servers zu implementieren. Weitere Informationen finden Sie unter den Bewährten Methoden für die Interoperabilität mit Windows-Runtime-Komponenten (UWP-Apps mit C#/VB/C++ und XAML). Die Aufgabe besteht darin, eine Windows-Runtime-Komponenten-DLL zu erstellen, die Desktopcode als Teil ihrer Implementierung aufrufen kann. Das begleitende Beispiel enthält die wichtigsten Muster, die in Windows-Runtime verwendet werden:

  • Methodenaufrufe

  • Windows-Runtime Ereignisquellen der Desktopkomponente

  • Windows-Runtime Async-Operationen

  • Zurückgeben von Arrays von Basistypen

Installieren

Um die App zu installieren, kopieren Sie die Implementierungs-Winmd in das richtige Verzeichnis, das im Manifest der zugeordneten quergeladenen Anwendung angegeben ist: <ActivableClassAttributes> Value="path". Kopieren Sie auch alle zugehörigen Supportdateien und die Proxy-/Stub-DLL (die Details der letzteren werden unten behandelt). Wenn Sie die Implementierungs-Winmd nicht in den Serververzeichnisspeicherort kopieren, werden alle Aufrufe der quergeladenen Anwendung auf der RuntimeClass einen Fehler "Klasse nicht registriert" auslösen. Fehler beim Installieren des Proxys/Stubs (oder Fehler beim Registrieren) führt dazu, dass alle Aufrufe ohne Rückgabewerte fehlschlagen. Dieser letztere Fehler ist häufig nicht mit sichtbaren Ausnahmen verknüpft. Wenn Ausnahmen aufgrund dieses Konfigurationsfehlers beobachtet werden, verweisen sie möglicherweise auf "ungültige Umwandlung".

Überlegungen zur Server-Implementierung

Der Desktop-Windows-Runtime Server kann als "Worker" oder "Task" basiert betrachtet werden. Jeder Aufruf am Server wird in einem Nicht-UI-Thread ausgeführt, und der gesamte Code muss multithreadfähig und sicher sein. Welcher Teil der quergeladenen Anwendung die Funktionalität des Servers aufruft, ist ebenfalls wichtig. Es ist wichtig, das Aufrufen von zeitintensivem Code aus einem UI-Thread in der quergeladenen Anwendung immer zu vermeiden. Dafür stehen vor allem zwei Möglichkeiten zur Verfügung:

  1. Wenn Serverfunktionen aus einem UI-Thread aufgerufen werden, verwenden Sie immer ein asynchrones Muster im öffentlichen Oberflächenbereich und in der Implementierung des Servers.

  2. Rufen Sie die Funktionen des Servers aus einem Hintergrundthread in der quergeladenen Anwendung auf.

Windows-Runtime asynchron auf dem Server

Angesichts der prozessübergreifenden Art des Anwendungsmodells führen Aufrufe des Servers zu mehr Aufwand als Code, der ausschließlich In-Process ausgeführt wird. Normalerweise ist es sicher, eine einfache Eigenschaft aufzurufen, die einen In-Memory-Wert zurückgibt, da sie schnell genug ausgeführt wird, dass das Blockieren des UI-Threads kein Problem darstellt. Jeder Aufruf, der E/A jeglicher Art umfasst (dies umfasst alle Dateiverarbeitungs- und Datenbankabrufe) kann den aufrufenden UI-Thread möglicherweise blockieren und dazu führen, dass die Anwendung aufgrund von fehlender Reaktionsfähigkeit beendet wird. Darüber hinaus wird aus Leistungsgründen von Eigenschaftsaufrufen für Objekte in dieser Anwendungsarchitektur abgeraten. Dies wird im folgenden Abschnitt ausführlicher erläutert.

Ein ordnungsgemäß implementierter Server implementiert in der Regel Aufrufe direkt aus UI-Threads über das asynchrone Windows-Runtime-Muster. Dies kann mithilfe dieses Musters implementiert werden. Zunächst wird die Deklaration (wieder aus dem begleiten Beispiel) erklärt:

public IAsyncOperation<int> FindElementAsync(int input)

Dadurch wird ein asynchroner Windows-Runtime-Vorgang deklariert, der eine ganze Zahl zurückgibt. Die Implementierung des asynchronen Vorgangs hat normalerweise die Form:

return Task<int>.Run( () =>
{
    int retval = ...
    // execute some potentially long-running code here 
}).AsAsyncOperation<int>();

Hinweis Es ist geschieht häufig, dass beim Schreiben der Implementierung einige andere potenziell zeitintensive Vorgänge abgewartet werden. Falls dies der Fall ist, muss der Task.Run-Code deklariert werden:

return Task<int>.Run(async () =>
{
    int retval = ...
    // execute some potentially long-running code here 
    await ... // some other WinRT async operation or Task
}).AsAsyncOperation<int>();

Clients dieser asynchronen Methode können diesen Vorgang wie jeden anderen aysnchronen Windows-Runtime-Vorgang warten.

Aufrufen von Serverfunktionen aus einem Hintergrundthread einer Anwendung

Da es typisch ist, dass sowohl Client als auch Server von derselben Organisation geschrieben werden, kann eine Programmierregel eingeführt werden, dass alle Aufrufe an den Server von einem Hintergrundthread in der quergeladenen Anwendung ausgeführt werden. Ein direkter Aufruf, der einen oder mehrere Datenbatches vom Server sammelt, kann aus einem Hintergrundthread erfolgen. Wenn die Ergebnisse vollständig abgerufen werden, kann der Datenbatch, der sich im Anwendungsvorgang im Arbeitsspeicher befindet, in der Regel direkt aus dem UI-Thread abgerufen werden. C#-Objekte sind natürlich agil zwischen Hintergrundthreads und UI-Threads, die für diese Art von Aufrufmuster besonders nützlich sind.

Erstellen und Bereitstellen des Windows-Runtime-Proxys

Da der IPC-Ansatz das Marshalling von Windows-Runtime-Schnittstellen zwischen zwei Prozessen umfasst, muss ein global registrierter Windows-Runtime-Proxy und Stub verwendet werden.

Erstellen des Proxy in Visual Studio

Der Prozess zum Erstellen und Registrieren von Proxys und Stubs für die Verwendung in einem normalen UWP-App-Paket wird im Thema Auslösen von Ereignissen in Windows-Runtime-Komponenten beschrieben. Die in diesem Artikel beschriebenen Schritte sind komplizierter als der unten beschriebene Prozess, da der Proxy/Stub im Anwendungspaket registriert wird (im Gegensatz zur globalen Registrierung).

Schritt 1: Erstellen Sie mithilfe der Projektmappe für das Desktopkomponentenprojekt ein Proxy-/Stub-Projekt in Visual Studio:

Lösung > Die > Projekt > Visual C++ > Win32 Console Select DLL Option hinzufügen.

Für die folgenden Schritte wird davon ausgegangen, dass die Serverkomponente MyWinRTComponent heißt.

Schritt 3: Löschen Sie alle CPP/H-Dateien aus dem Projekt.

Schritt 4: Der vorherige Abschnitt "Definieren des Vertrags" enthält einen Post-Build-Befehl, der winmdidl.exe, midl.exe, mdmerge.exe usw. ausführt. Eine der Ausgaben aus dem Midl-Schritt dieses Postbuildbefehls generiert vier wichtige Ausgaben:

a) Dlldata.c

b) Eine Headerdatei (z. B. MyWinRTComponent.h)

c) Eine *_i.c-Datei (z. B. MyWinRTComponent_i.c)

d) Eine *_p.c-Datei (z. B. MyWinRTComponent_p.c)

Schritt 5: Fügen Sie diese vier generierten Dateien zum Projekt "MyWinRTProxy" hinzu.

Schritt 6: Fügen Sie eine Def-Datei zum Projekt „MyWinRTProxy“ hinzu (Projekt > Neues Element hinzufügen> Code > Module-Definitionsdatei) und aktualisieren Sie den Inhalt wie folgt:

LIBRARY MyWinRTComponent.Proxies.dll

EXPORTS

DllCanUnloadNow PRIVAT

DllGetClassObject PRIVAT

DllRegisterServer PRIVAT

DllUnregisterServer PRIVAT

Schritt 7: Öffnen Sie Eigenschaften für das Projekt "MyWinRTProxy":

Configuration Properties > General > Target Name :

MyWinRTComponent.Proxies

C/C++ > Präprozessordefinitionen > Hinzufügen

"WIN32;_WINDOWS; REGISTER_PROXY_DLL"

C/C++ > Vorkompilierter Header: Wählen Sie „Nicht vorkompilierten Header verwenden“ aus.

Linker > General > Ignore Import Library : Wählen Sie „Ja“

Linker > Input > Additional Dependencies : rpcrt4.lib; runtimeobject.lib hinzufügen

Linker > Windows Metadata > Generate Windows Metadata : Wählen Sie „Nein“

Schritt 8: Erstellen Sie das Projekt "MyWinRTProxy".

Bereitstellen des Proxys

Der Proxy muss global registriert werden. Die einfachste Möglichkeit dafür besteht darin, den DllRegisterServer-Installationsvorgang auf der Proxy-DLL aufzurufen. Da das Feature nur Server unterstützt, die für x86 (d. h. keine 64-Bit-Unterstützung) erstellt wurden, besteht die einfachste Konfiguration darin, einen 32-Bit-Server, einen 32-Bit-Proxy und eine quergeladene 32-Bit-Anwendung zu verwenden. Der Proxy befindet sich normalerweise neben der Implementierungs-Winmd für die Desktopkomponente.

Ein weiterer Konfigurationsschritt muss ausgeführt werden. Damit der quergeladene Prozess den Proxy laden und ausführen kann, muss das Verzeichnis für ALL_APPLICATION_PACKAGES als "lesen/ausführen" gekennzeichnet sein. Dies erfolgt über das Befehlszeilentool icacls.exe. Dieser Befehl sollte im Verzeichnis ausgeführt werden, in dem sich die Implementierungs-Winmd und der Proxy-/Stub-DLL befindet:

icacls . /T /grant *S-1-15-2-1:RX

Muster und Leistung

Es ist sehr wichtig, dass die Leistung des prozessübergreifenden Transports sorgfältig überwacht wird. Ein prozessübergreifender Aufruf ist mindestens doppelt so aufwendig wie ein Prozessaufruf. Das Erstellen von "chatty"-Konversationen, die prozessübergreifend ausgeführt werden, oder wiederholte Übertragungen großer Objekte wie Bitmapbilder können zu unerwarteten und unerwünschten Problemen mit der Anwendungsleistung führen.

Hier ist eine nicht abschließende Liste der zu berücksichtigenden Punkte:

  • Synchrone Methodenaufrufe vom UI-Thread der Anwendung an den Server sollten immer vermieden werden. Rufen Sie die Methode aus einem Hintergrundthread in der Anwendung auf, und verwenden Sie dann CoreWindowDispatcher, um die Ergebnisse bei Bedarf in den UI-Thread zu bekommen.

  • Das Aufrufen asynchroner Vorgänge aus einem Anwendungs-UI-Thread ist sicher, berücksichtigen Sie aber die unten beschriebenen Leistungsprobleme.

  • Die Massenübertragung von Ergebnissen reduziert die prozessübergreifende Kommunikation. Dies wird normalerweise mithilfe des Windows-Runtime-Array-Konstrukts durchgeführt.

  • Das Zurückgeben von List<T>, bei dem T ein Objekt aus einem asynchronen Vorgang oder Eigenschaftsabruf ist, führt zu einer Menge prozessübergreifender Kommunikation. Angenommen, Sie geben einList<People>-Objekt zurück. Jeder Iterationsdurchlauf ist ein prozessübergreifender Aufruf. Jedes People-Objekt wird durch einen Proxy dargestellt, und jeder Aufruf einer Methode oder Eigenschaft für dieses einzelne Objekt führt zu einem prozessübergreifenden Aufruf. Ein "unschuldiges" List<People>-Objekt mit einem großen Count führt also zu einer großen Anzahl langsamer Aufrufe. Bessere Leistungsergebnisse erhält man durch die Massenübertragung von Inhaltsstrukturen in einem Array. Beispiel:

struct PersonStruct
{
    String LastName;
    String FirstName;
    int Age;
   // etc.
}

Geben Sie dann PersonStruct[] anstelle von List<PersonObject> zurück. Dadurch werden alle Daten in einem prozessübergreifenden "Hop" erfasst.

Wie bei allen Leistungsüberlegungen ist Messung und Prüfung von entscheidender Bedeutung. Im Idealfall sollte Telemetrie in die verschiedenen Vorgänge eingefügt werden, um zu ermitteln, wie lange sie dauern. Es ist wichtig, einen Bereich zu messen: Wie lange dauert es beispielsweise, alle People-Objekte für eine bestimmte Abfrage in der quergeladenen Anwendung zu konsumieren?

Eine weitere Technik ist das Testen von Variablenlasten. Dazu können Leistungstest-Hooks in die Anwendung eingefügt werden, die variable Verzögerungslasten in die Serververarbeitung einführen. Dies kann verschiedene Arten von Lasten und die Reaktion der Anwendung auf unterschiedliche Serverleistung simulieren. Das Beispiel veranschaulicht, wie Zeitverzögerungen mithilfe der richtigen asynchronen Techniken in Code eingefügt werden. Die genaue Dauer der Verzögerung, die injiziert wird, und das Ausmaß der Randomisierung, die in diese künstliche Last eingefügt werden soll, variiert je nach Anwendungsdesign und der Umgebung, in der die Anwendung ausgeführt werden soll.

Entwicklungsprozess

Wenn Sie Änderungen am Server vornehmen, müssen Sie sicherstellen, dass zuvor ausgeführte Instanzen nicht mehr laufen. COM wird den Prozess schließlich aufräumen, aber der Rundown-Timer dauert länger, als für die iterative Entwicklung effizient ist. Das Beenden zuvor ausgeführten Instanz ist daher ein normaler Schritt während der Entwicklung. Dies erfordert, dass der Entwickler nachverfolgt, welche dllhost-Instanz den Server hostet.

Der Serverprozess kann mithilfe des Task-Managers oder anderer Drittanbieter-Apps gefunden und beendet werden. Das Befehlszeilentool TaskList.exe ist ebenfalls enthalten und verfügt über flexible Syntax, z. B.:

Befehl Aktion
tasklist Listet alle ausgeführten Prozesse in ungefährer Reihenfolge der Erstellungszeit auf, wobei sich die zuletzt erstellten Prozesse ganz unten befinden.
tasklist /FI "IMAGENAME eq dllhost.exe" /M Listet Informationen zu allen dllhost.exe-Instanzen auf. Der Schalter "/M" listet die Module auf, die sie geladen haben.
tasklist /FI "PID eq 12564" /M Sie können diese Option verwenden, um die dllhost.exe abzufragen, wenn Sie ihre PID kennen.

Die Modulliste für einen Brokerserver sollte clrhost.dll in der Liste der geladenen Module auflisten.

Ressourcen