Loading WinRT component without referencing the DLL/assembly
Asked Answered
D

1

5

I have been experimenting with WinRT components using C#, C++/CX and WRL in C++. So far I managed to do everything I tried even if compared to COM some stuff have changed and are either confusing or frustrating.

The last thing I'm trying and that so far I couldn't do is a basic architecture pattern of COM. I simply want to create an instance of a WRL component without referencing the DLL in the project which is using the component. As far as I remember this is the basic behavior of COM, provide the GUID of the CoClass of the COM, the using program knows only about the interface and CoCreateInstance will dynamically loads the COM and create an instance attached to the interface you're requesting.

I cannot find how to do that with WRL. I have defined few simple interfaces and I cannot even find them in the registry. However as it is possible with COM to use object without the registry and that there is now window metadata I suppose that this is the reason.

Does someone out there knows if this is not a restriction of WinRT (which would make it a very poor architecture...) or in the case it is possible how to achieve late binding with WRL.

To make it clear, in the calling program I would like to only provide the information of the interface (this can be the .h) then I need to be able to create an instance of the WinRT component using its GUID or a moniker name. This is an architecture pattern I have used in C++/COM, C# and Java as you can write an application and support new features without touching a line of the application, not even recompiling it.

Thanks O. Rouit

Disastrous answered 23/12, 2012 at 6:57 Comment(3)
I don't know the answer to your question, but even if you manage to dynamically instantiate WinRT components, I'm not sure how you'd add new features without installing a new version of your app. You won't be able to put new files to install folder any other way and that's the only location from which executable code can be loaded by the app.Mouldy
Sounds like windows store is not suitable for a professional usage at all...Disastrous
What exactly are you trying to achieve? There might be a way to still do it within the existing limitations. Interaction between multiple applications through contracts can be a replacement for no application extensibility.Mouldy
G
7

So far it's unclear what you're actually trying to do. WRL is just a C++ helper library for WinRT, if you're using C++ (think of it as WRL-is-to-WinRT-as-ATL-is-to-COM).

Let's take C++, since that's the 'most raw' and has no vm or runtime like C# or JS. You're trying to instantiate a WinRT object that you didn't link with? If so, that's simple - ActivateInstance(activateableclassid). The real question is what are you trying to instantiate? If it's a 1st party (inbox/Windows) component, it should just work. This is very much like COM, where there ACID is like a CLSID and ActivateInstance() is like CoCreateInstance().

If you're trying to instantiate a 3rd party WinRT component (not shipped w/Windows), it's a little simpler. The ONLY way to register 3rd party WinRT components is by including them in your package. Packages are isolated from each other, so you cannot have, e.g. AngryBirds deliver a FOO WinRT object and use it in an app in the Scrabble package. Unlike COM where registration is machine wide (or in the rare even you register under HKCU instead of HKLM, user-wide), WinRT 3rd party (package'd) WinRT objects are only registered for use by apps in that package.(1)

(1) That's a little white lie. Technically your application can instantiate WinRT components provided with Windows and WinRT components provided by packages in your package graph. You can make a Framework package which includes WinRT objects, and have your application's package dependent on that. Windows figures our your package depends on the framework and thus your 'package graph' has 2 packages in it -- your app's package and the framework package. BUT you cannot submit Framework packages to the Store. Local development and enterprise/side-loading deployments can do this (they don't go through the Store and thus don't need to meet Store submission criteria), but any application package submitted to the Store can only depend on the Windows provided frameworks (WinJS, VCLibs, PlayReady/DRM). That's how PlayReady works - it's a Framework package containing (among other things) some WinRT objects; if your application's package declares a on it your package graph will contain 2 packages (your app's package + playready's package), and ActivateInstance() can resolve ACIDs across the union of packages in your package graph.

There's a lot of protections built into the application model. This is one of them. This prevents 'COM Hell' and 'DLL Hell' - everything's fine, then 6 months later you install app Y and for no apparent reason app X no longer works. The new appmodel is designed to prevent that scenario.

Another protection is the limitation of where package'd WinRT objects can be found. Even if you put a file in your app's local folder (e.g. ApplicationData.current.localFolder), ActivateInstance() won't find it. WinRT doesn't look under AppData (or oodles of other places) for registered WinRT objects -- only those in your package graph (and 1st party (Windows) components provided with the OS, e.g. StorageFolder).

provide the GUID of the CoClass of the COM, the using program knows only about the interface and CoCreateInstance will dynamically load the COM and create an instance attached to the interface you're requesting. The WinRT equivalent is to provide the ACID of the runtimeclass of the WinRT [component], the using program knows only about the interface and ActivateInstance() will dynamically load the WinRT [component] and create an instance attached to the interface you're requesting.

The caveat is, the WinRT component's implementation must be registered in your package graph, i.e. must be listed in your app's package's AppXManifest.xml, or a dependency's AppXManifest.xml.

That's at the most raw level, if you deal directly with WinRT at the ABI level. C++ projections are equivalent, just more convenient syntax. The CLR and JS runtimes provide their own additions and variations of enforcement (e.g. search for "windows 8 javascript local vs web compartment") but they just further color the behavior. They can't overrule the underlying OS' baseline behavior.

As Damir noted

You won't be able to put new files to install folder any other way and that's the only location from which executable code can be loaded by the app. "Executable code" comes in varying forms, depending on your definition.

WinRT components can be loaded from other places (Windows provided WinRT components, dependent packages e.g. the PlayReady framework package).

(Non-WinRT, non-COM) 'native' DLLs can be loaded from other places (executable's directory, System32, etc). See Search Order for Windows store apps.

.NET assemblies in DLLs have their own similar restrictions e.g. Assembly.Load().

Some people consider Javascript "code", and there's the whole local vs web compartment thing to further color where code can be resolved from.

As a general rule, you can dynamically load code in a variety of ways, but it needs to be known code. There's no supported way you can create e.g. a C++ app that will load DLLs (WinRT or non-WinRT) that's not known at Store submission time. You can write an app using optional (conditionally loaded) code, you can have 'plugins' -- but they need to be in your package when you submit to the Store.

You can't build e.g. Outlook as is today, where Outlook is submitted to the Store with an open-ended plugin model and sometime later install an OutlookNiftyCalendar add-on which Outlook could find and use. Not in Windows 8.

(there are some ways you can bend this rule a bit in a Javascript app via web compartment, but that's a complex topic in its own right, and even that has limits both hard and soft/policy. And irrelevant if you're not writing a Javascript applications)

Gough answered 24/12, 2012 at 1:3 Comment(4)
I clearly understand that WRL is ATL of WinRT and that ActiveInstance is CoCreateInstance. I have a very simple WRL component and a unit test program. My problem was that I couldn't find how to register it! Your explanation is very good. I'm currently evaluating WinRT to see how it can be used in an enterprise environment. The fact that third party are local to a package sounds like the registry free deployment of a COM (after XP SP2). Is it possible to test this with a UnitTest project or do I need to create a package for that?Disastrous
If I understand well, it is possible to extend applications when working on a development machine or in the case of a company deployment that doesn't go through the Windows Store. Windows store are monolithic and un-extendable application, isn't? However, is it possible to update a Windows store app, adding new components through the submission process? That would be. App.exx module1.dll module2.dll The new module2.dll wouldn't be present in the first deployment and is not linked to App.exe. Is that sceanrio possible? Thanks again for you exhaustive explanation!Disastrous
Yes user1466502, one can repeatedly submit package updates to the Store that extend the app's behavior. This could be via DLLs per additional functionality, or it could just be additional functions inside your .exe or any other factoring. After all, the package as a whole is updated so however you slice and dice your code is your call.Gough
To your earlier question, yes your WinRT bits are registered when your package is deployed by virtue of your WinRT module bits are listed in your AppxManifest.xml. The deployment stack handles this for you. This does have similiarities in overall feel as RegFreeCOM - using a declarative model you specify your component(s), and then at runtime your app has access to your bits. The details differ of course but the net's similar - the bits are limited to just your app and don't affect others on the systemGough

© 2022 - 2024 — McMap. All rights reserved.