How to unit test a class defined in a Metro executable - mstest

Using VS11 RC. I have a client executable written in C++ that I am trying to test with a C# Unit Test Library (Metro Style apps) project. The test fails with exception Class not registered HRESULT 0x80040154 (REGDB_E_CLASSNOTRG); I believe this is related to a warning in the test project: The executable 'Client.exe' is specified as the implementation for winmd file 'C:.Client.winmd'. Only in-process servers are supported for generation of registration information in the app manifest. You will need to specify the out-of-process server registration information in the app manifest.
It appears to me that MSTest is looking for the class as a COM object, and that the warning is telling me that I need to specify it as an out-of-process server. Unfortunately, I don't think I want to do that as it is an executable, and even if that is what I want, I can't figure out how to do it.
I could simply move the class under test into a different project, but I don't want to do that without a reason better than "I can't get this to work".
Is there any way to unit test a class defined in a Metro executable?

The code to be tested needs to be moved into a DLL.
A native Windows Runtime type must be defined in a DLL in order to be activatable (this effectively means "instantiable through Windows Runtime"). Your executable doesn't need to go through Windows Runtime to instantiate types that it defines itself, but your unit test executable does need to go through Windows Runtime to instantiate those types.
I would suggest defining everything that you can in a DLL, and keeping your EXE as small as possible: have it just bootstrap your application, and put all of your real code in DLLs.

Related

Registration-Free WinRT component for DLL

I am trying to use Registration-free WinRT by adding an application manifest following https://blogs.windows.com/windowsdeveloper/2019/04/30/enhancing-non-packaged-desktop-apps-using-windows-runtime-components/ to my DLL which is used as a in-proc COM server.
However, I get REGDB_E_CLASSNOTREG when the runtime class is instantiated. I used mt.exe to make sure the manifest is embedded. I am on Win-10 2004. Is this supposed to be working for win32 in-proc DLL servers? Is there an API call to register the component manually?
It appears that the Dll activation context is not used as active activation context by the loader as found in https://stackoverflow.com/a/48308884 .
Instead we have to save the context and activate it before instantiating our runtime class as shown below:
In DllMain:
if (dwReason == DLL_PROCESS_ATTACH)
{
GetCurrentActCtx(&hActCtx);
}
And before instantiating the Runtime class:
ActivateActCtx(hActCtx, &cookie);
Class1 c;
DeactivateActCtx(0, cookie);
auto x = c.MyProperty();
By manually using the activation context API along with the embedded manifest in DLL, i can use winrt components without registration.
Make sure all dependencies related to WinRT component are present. Dependencies can be other required DLLs or VS runtime DLLs etc.
One problem I faced was: VCRUNTIME140_APP.dll not found, so I copied VC runtime libraries from the following location:
C:\program files
(x86)\msbuild\15.0.net.netnative\15.0.24211\x64\ilc\lib\MSCRT
to the output folder, post this FFmpegInteropX component got loaded into application (I think this can be solved by installing Microsoft.VCRTForwarders.140 nuget, but haven't tried it yet)

How to find the type_info symbol without using /export (for unit tests)

Context:
We've implemented SEH/C++ exception handing in LLVM some time ago, to make our JIT'ted code compatible with native exceptions. As part of the exception handling code generation, internal information about type_info is necessary, because it's used in the exception handling tables. (More specifically, it's used in the RTTI TypeDescriptor)
In normal applications, type_info is internally added by the Microsoft linker to executables. However, the symbol is hidden by default. To expose it, you can use /EXPORT:??_7type_info##6B# in the linker, which basically exposes the symbol so that we can use it for our bidding.
Problem:
In standard executables, this approach works just fine. However, we're also using the Microsoft Unit Test framework, which generates a DLL. Herein lies the problem, because the MS test runner (exe) isn't compiled with the '/EXPORT' flag above.
The result is that once the unit test is invoked, it generates an error (Symbol not found).
Question:
In short, we're looking for a solution that allows us to run our unit tests from within the MS test runner.
We've tried to lookup the symbol manually, so far without results.

Is it correct to load the assembly manifest directly into the application context?

I'm currently trying to get a rather tangled scheme running with registration free COM.
It's not that it is not working, it's that I have hit a kind of confusing situation, where it seems I should active the manifest of the assembly dependency directly in the application context, rather than have the application context point at the dependent assembly.
It is rather easy to explain by the example project MS themselves publish:
Normally, you have an application, an app manifest, a (server-)dll and it's assembly manifest. These correspond to what the example gives:
client.exe
client.exe.manifest (This one points to SideBySide.X as dependentAssembly)
SideBySide.dll
SideBySide.X.manifest
Now, one standard case is to embed the client application manifest inside the client executable, and then use the DLL and its external manifest file.
Now, if for whatever reason the correct application manifest isn't known at compile time, you can load a manifest file at runtime via the Activation Context API.
And this is where it gets confusing:
According to the article, the client application now directly switches its Activation Context to the assembly manifest:
If you look at the _tmain function in client.cpp ... a new section of code that initializes the activation context as follows:
actCtx.lpSource = "SideBySide.X.manifest";
I have cross checked this, and it would also work to dynamically load a file that contains the info from client.exe.manifest, i.e. just the reference to SideBySide.X, and continue with this Activation Context - which would also correspond to the ActCtx in use when we embed a correct application manifest into the executable.
That is, actCtx.lpSource = "client.exe.manifest"; would also work.
TL;DR What is the implication, if any, of directly activating an Activation Context "containing" an assembly manifest inside application code.
Is this how it's supposed to be done when loading manifests from files? (And if so, why can't we directly embed the assembly manifest into the executable, when it is known at compile time.)
Note: (This should really be a comment to #Eric Brown's answer, but it's getting rather lengthy)
The linked article does a decent job of explaining the two RT_MANIFESTresource types, but with regard to regFreeCOm, it leaves a few loose ends. I'll throw in some quotes that jumped at me:
ISOLATIONAWARE_MANIFEST_RESOURCE_ID is used primarily for DLLs. It
should be used if the dll wants private dependencies other than the
process default. ...
the NT library loader checks to see if the
dll has a resource of type RT_MANIFEST, ID
ISOLATIONAWARE_MANIFEST_RESOURCE_ID. If it does, the loader calls
CreateActCtx with the resource, and use the generated activation
context to probe the dll's static dependencies.
What I understand this to mean is that the only point of RT_MANIFEST/2 is for the static DLL dependency loader to find the correct resource to use for resolving DLL dependencies. (Not COM dependencies, see below.)
Sometimes, you want to use the activation context outside of probing
the dll's static dependencies. You can define macro
ISOLATION_AWARE_ENABLED when you compile the module.
When ISOLATION_AWARE_ENABLED is defined, Windows re-defines certain
APIs. For example LoadLibraryExW is redefined to
IsolationAwareLoadLibraryExW.
... Not all APIs affected by activation context are wrapped. For example,
..., and neither is any of the COM APIs.
So, to sum up: I think the RT_MANIFEST mechanism is mostly orthogonal to regFreeCOM as COM doesn't care at all where it's activation context comes from and there is no built-in help for regFreeCOM wrt. Isolation Awareness.
Yes, this is how it's supposed to be done, and you can embed the assembly manifest into the executable (well, the resource section). That's what the RT_MANIFEST resource type is for. There are two default types of manifest resources
Process manifests, with ID CREATEPROCESS_MANIFEST_RESOURCE_ID (1), used during process creation,
Isolation-Aware manifests, with ID ISOLATIONAWARE_MANIFEST_RESOURCE_ID (2), used during DLL loading.
There are a couple of uses (primarily around Click-once deployment) that illustrate the ability to embed registration-free COM manifests in subsidiary DLLs, using RT_MANIFEST. In particular, if a DLL has a CREATEPROCESS_MANIFEST_RESOURCE_ID, that manifest will be used as well.
An example seems like a good thing here.
Given
client.exe
sidebyside.dll
If sidebyside.dll has an RT_MANIFEST resource with ID 1 (CREATEPROCESS_MANIFEST_RESOURCE_ID) which has the appropriate registration-free COM entries in it, and client.exe has an RT_MANIFEST resource with ID 1 that has a <file> entry for sidebyside.dll, then Win32 will automatically handle the registration-free COM management.
Part 8 of the example article strongly implies this, and I've seen it done in a number of in-house projects.

createObject in VB6 does fails when running exe

I have a VB6 program which tries to run a DLL written in C#.
This DLL has a COM interface so I can create an object of a class in it with "CreateObject".
The problem is that it runs and works well when I run it from the VB6 IDE, but when I make an EXE and try to run it, it throws the exception:
"Automation error. The system cannot find the file specified (-2147024894)."
Why is it happening and how can i solve it?
Look at Project, References in the IDE and look which dll or ocx file belongs to the object you are referencing with CreateObject (the Object Manager might also help to find out).
This dll file must be available when the exe is compiled, too. Usually, you need to have it registered with regsvr32.exe.
A technique I use to figure issues of this type is to open the add reference dialog in Visual Basic 6. I scroll the list of available COM Libraries and see if the problem DLL is listed. If it is then CreateObject should work, you should be able to assign it do a variant variant and use late binding to access it's members.
In addition try temporally set a reference to the variable and instead of using CreateObject use the = New and see what error messages, if any, it gives you. Generally I found them to be more informative then the ones thrown by CreateObject.
Finally it would help if you post the reason why you are choosing to use CreateObject instead of setting of a reference. If the DLL is a known object that will be continually used by the program then a reference should be set and early binding generally used.
Finally it may be that the error is resulting from a dependency of the C# COM DLL not the DLL itself. If for example I was to take a Com Library and properly register it but it relies on the COM Library Widget2000 and it NOT registered then it will throw the automation error. Especially if you are testing the EXE in it's installed environment and not the environment in which you complied it.
For example suppose I have a CAD program written in VB6 and I have source tree that begins with MyCAD. THe exe is in MyCAD/MainEXE and the shape library is in MyCAD/ShapeLibrary. I run the IDE everything is fine. Then I make my setup and goto my test machine and install it and it error on the creation of shapelibrary.
The first thing I would do it check if MainEXE will run straight out of the MainEXE directory of my source tree. That test will eliminate whether it is a install issue or a quirk of the IDE vs complied version. Then I would look at the setup and see what not being registered. Also look at either the source for the C# library or the setup for the library and see what dependencies it needs. Since it a complied COM DLL you should be able to use a dependency walker tool to see what COM references it needs. Finally make sure the correct version of the .NET framework is installed.
If you are compiling the C# DLL on your test machine - make sure you have ticked the register for COM Interop setting. If you are not compiling on the same machine you need to run RegAsm with the /codebase option.
try compiling it as an installer and include the dll/com that you use in the compilation of the installer package so that the dll/com that you use will be include in the compilation of your exe.., and install it in the windows not just copy past it.

Avoid loading .Net Dlls in a C++/CLI project?

I have a project written in C++/CLI. Some of the types there are in managed code, and some are in completely native code. Let's say I have the produced DLL on a machine that dosen't have any version of the .Net framework installed, is there a way that another, native application will link with my "mixed-mode" Dll and use only the native types? I've noticed that the minute I add the "/clr" switch, my Dll automatically depends on several .Net Framework Dlls (mscorjit, mscoree etc.), and when I actually try to use the 100% native types defined in it, the application still tries to load those .Net Framework Dlls (even though I don't use the framework in that part of the code).
So, is it possible to avoid loading those Dlls in such case? (as I see it, the other option is to create another, native project, that will contain all of the native types, without the managed ones).
Thanks
No. When you load a mixed mode assembly (/clr), right after DllMain runs, the .cctor runs and initializes the framework, if it hasn't already been setup for the application.
Without this, there would be a big hit as soon as you called a function that required a managed API. For details, see "Initialization of Mixed Assemblies" on MSDN.
The best option would be to make your native API a separate DLL, and have the mixed mode assembly a separate project, so you can load it separately if required.

Resources