How can I unit-test weakly named COM visible classes - c#

I have a COM visible class (marked with [ComVisible(true)] and registered with RegAsm) in C# that I want to unit-test.
My assembly is an unsigned/weakly named dll (and it has to remain this). The class that I want to test has a private member that is not COM visible.
My problem is when I want to instantiate this class in a test it fails with this message:
Test method %MyTestMethod% threw exception
System.EnterpriesServices.RegistreationException: The assembly
%MyAssembly% does not have a strong name.
So how can I use the unit-test framework on an unsigned/weakly named dll?
using System.EnterpriseServices;
using System.Runtime.InteropServices;
[ComVisible( true )]
public class MyComClass: ServicedComponent, IMyClass
{
//SomeAttribute
private MyNonComClass nonCom;
public MyComClass( MyNonComClass _nonCom )
{
this.nonCom = _nonCom;
}
}

As Hans Passant suggested when I removed the ServicedComponent class from inheritance, the unit-tests are running as supposed.

Related

How do I access static members of a c# library from c++ code?

I have been given a library written in C# and I need to use it in a C++ project. The C# library has been exported to a .tlb type library, which I can successfully import into my C++ project by using the #import directive.
Being utterly unfamiliar with COM I can't for the life of me figure out how to get at static member functions on any classes. Here's how I access it in C#:
void Function()
{
StaticClass.StaticMethod();
}
And then you get into the C++ side, what gets generated in the .tlh file is:
struct __declspec(uuid("some big long thing"))
/* dual interface */_StaticClass;
//long while later
_COM_SMARTPTR_TYPEDEF(_StaticClass, __uuidof(_StaticClass));
So I'm trying to figure out how to get use of the static class and haven't had any luck with Google. The only example anywhere else in any other project I have access to gives me something similar to this:
_StaticClassPtr s = _StaticClassPtr(__uuidof(_StaticClass));
but the example I have isn't for a static class anyway.
Basically I'm stuck with nowhere to even really start. This fails with "Unhandled exception at in <executable>: Microsoft C++ exception: _com_error at memory location <location>"
Edit: Since #dxiv informed me static methods aren't usable with COM interop, there's another option marked 'obsolete' that does not use static members -- problem is I get exactly the same exception when I construct the instance with similar syntax:
IInstanceClassPtr p = _IInstanceClassPtr(__uuidof(_InstanceClass));
The same exception is thrown, "_com_error at memory location"
Reading your question you would like to use C# using COM Interop. What I done is something like this. Starting from the assumption that COM is the acronym of Component Object Model and to use it you need an instantiable, local or remote, object. The example below creates an "in-proc" instance of the CLR object.
Create an interface which is exposed to COM:
namespace MyNamespace
{
/// <summary>
/// Provides an entry point for COM clients.
/// </summary>
[ComVisible(true)]
[Guid("A9E6D7FE-34FD-4A6B-9EB2-DC91F4AE567B")]
public interface IMyAccessor
{
void ExecuteStaticMethod();
// add anything else like methods, property,
}
}
then implement the interface in a C# class:
namespace MyNamespace
{
/// <summary>
/// The implementation of IMyAccessor.
/// </summary>
[ComVisible(true)]
[Guid("C65A7F81-641C-4F17-B34A-DEB88B4158E8")]
[ProgId("MyCompany.MyAccessor")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMyAccessor))]
public sealed class MyAccessor: IMyAccessor
{
public void ExecuteStaticMethod() { StaticClass.StaticMethod(); }
}
}
export the TLB and import it in C++ project (MyAccessor is only a name I used here) in the header file of your C++ class using the following clause:
#import "MyAccessor.tlb"
Within the class header add a line like the following:
MyNamespace::IMyAccessorPtr m_IMyAccessor;
And in the class implementation use the following:
HRESULT hr = m_IMyAccessor.CreateInstance(__uuidof(MyNamespace::MyAccessor));
if (FAILED(hr))
{
// do something if failed
}
m_IMyAccessor->ExecuteStaticMethod(); // this will execute your static method in C#
NOTE: when exporting the TLB use the correct switchs. In an x64 environment (/win64) must be used to have the right pointer size: normally tlbexp returns pointer usable in a 32bit environment. This is important if you want to extend the class with more sofistcated methods.
NOTE 2: if the returned HRESULT from CreateInstance is something like "class not registered", remember to execute the registration of the TLB wih REGTLIB.

Shallow understanding of COM interop leads to TYPE_E_CANTLOADLIBRARY exception - where's the mistake?

We have a .Net assembly, A, that makes use of a class Foo from a COM library, B.
We've created an interop for B (Interop.b.dll) and A makes use of Foo via the interop.
If I decompile Interop.b.dll I can see the following interface defined within it:
using System.Runtime.InteropServices;
namespace Interop.b
{
[Guid("SOME-GUID")]
[CoClass(typeof (FooClass))]
[ComImport]
public interface Foo : _Foo
{
}
}
In the references settings of .Net assembly A, I have the option to embed the interop types for Interop.b.dll. If I set this to true the interfaces defined within Interop.b.dll are embedded within A.dll. If I then decompile A.dll I can see the same interface as I found in the interop:
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Interop.b
{
[CompilerGenerated]
[Guid("SOME-GUID")]
[CoClass(typeof (object))]
[TypeIdentifier]
[ComImport]
public interface Foo : _Foo
{
}
}
Except that it's not the same. In this version we have [TypeIdentifier] as an additional attribute and the parameter of the CoClass attribute has changed from typeof(FooClass) to typeof(object).
I note that Interop.b.dll contains the type FooClass, which I believe is a wrapper class for the COM type Foo that is responsible for marshalling parameter types between .Net and COM, but this type has not been embedded in A.dll.
Within A.dll, Foo is used like this:
using Interop.b;
namespace My.Product
{
public class AClass: IAClass
{
private Foo LocalFoo { get; }
public AClass()
{
LocalFoo = new Foo();
}
}
}
On a clean install, this fails with the following exception:
System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' to interface type 'Interop.b.Foo'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{SOME-GUID}' failed due to the following error: Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)).
(Interop.b.dll has been installed to the GAC by the product installer)
On a development machine, reinstalling the product and running from installed binaries fails in the same way. Subsequently recompiling the product, which overwrites the installed binaries, and running from either code (in debug) or the fresh binaries then works.
Now, I believe the error is that the code in A.dll should be instantiating FooClass rather than Foo. i.e. something like this:
LocalFoo = new FooClass();
...because Foo is an interface (in the interop) and FooClass is the thing that handles the marshalling of the parameter types between .Net and COM.
So, questions:
1) Am I correct that the code should be instantiating FooClass rather than Foo?
2) Why does it work at all on a dev machine?
3) Why is the embedded interface using typeof(object) instead of typeof(FooClass)?
4) What is the benefit of embedding the interfaces from the interop in A.dll when we will still need Interop.b.dll on the target machine to allow us to make use of FooClass?

Using C# code in C++ (COM) Dll import not working quite right

As the title implies, I am having difficulty calling C# code from C++. A bit of context: there is an API call in C# (that does not exist in the C++ version of the API) and I need to integrate it into a much larger C++ project.
I have been reading over this SO post, and made the most headway with the COM method.
My C# dll compiles*. After copying the resulting dll and tlb files into the appropriate C++ project directory, I open an admin cmd prompt in the same C++ project directory and run:
regasm MyFirstDll.dll /codebase /tlb
(*I compile it as a Class Library, Assembly name: MyFirstDll, Default namespace: MyMethods, Assembly Information... -> Make assembly Com-Visible is checked, Build->Register for COM interop is also checked.)
Using the Object Browser, I am able to see the class I defined, as well as a method with the appropriate args and signature.
Screenshot of it showing up in Object Browser
The issue I am experiencing is with the method call (and not the class). Even though the method is visible in the Object Browser, in my code it is not recognized as a method of the object.
class "ATL::_NoAddRefReleaseOnCComPtr" has no member "Add"
Here is my code:
MyFirstDll C# Project:
using System;
using System.Runtime.InteropServices;
//
namespace MyMethods
{
// Interface declaration.
[Guid("8a0f4457-b08f-4124-bc49-18fe11cb108e")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface Easy_Math
{
[DispId(1)]
long Add(long i, long j);
};
}
namespace MyMethods
{
[Guid("0cb5e240-0df8-4a13-87dc-50823a395ec1")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyMethods.AddClass")]
public class AddClass: Easy_Math
{
public AddClass() { }
[ComVisible(true)]
public long Add(long i, long j)
{
return (i + j);
}
}
}
NateMath.h:
#include <atlcomcli.h>
#import "MyFirstDll.tlb"
class NateMath
{
public:
NateMath(void);
~NateMath(void);
long Add(long a, long b);
private:
CComPtr<MyFirstDll::AddClass> adder;
};
NateMath.cpp:
#include "stdafx.h"
#include <atlcomcli.h>
#import "MyFirstDll.tlb"
#include "NateMath.h"
NateMath::NateMath(void)
{
CoInitialize(NULL);
adder.CoCreateInstance(__uuidof(MyFirstDll::AddClass));
}
NateMath::~NateMath(void)
{
CoUninitialize();
}
long NateMath::Add(long a, long b) {
return adder->Add(a,b);
}
The issue being that in "return adder->Add(a,b)" (NateMath.cpp) Add(a,b) shows up red with class "ATL::_NoAddRefReleaseOnCComPtr" has no member "Add"
This is because you are trying to use your class name in CComPtr instead of the interface. With COM, all methods are defined on an interface, not on the class that implements an interfaces.
You can CoCreateInstance(__uuidof(YourClass)) because the intent is to create an instance of YourClass (which is identified by the GUID expressed by __uuidof(YourClass)). However, YourClass in the C++ is a dummy struct that's only present so that you can read the uuid -- the definition of YourClass in the C++ generated from the #import is empty and will always be empty.
To fix this, use CComPtr<YourInterface>. This tells the C++ that you want to communicate with the referenced object via that interface. Here's a rule to remember: The type argument to CComPtr and CComQIPtr must always be a COM interface. That COM interface can either be an explicitly-defined interface, or it can be the "class interface" which was automatically produced by .NET.
Speaking of class interfaces: If you had used ClassInterfaceType.AutoDual instead of None, you could have used a CComPtr<_YourClass> (note the leading underscore -- _YourClass is the class interface, whereas YourClass would be the class. I would recommend doing it the way you already have, however.

VSTO Unit Testing Office AddIn in C# .NET via RequestComAddInAutomationService

I have worked and read through various StackOverflow questions and other tutorials and documentation over the past weeks (N.B. some of them below) to try and find a way of unit testing a VSTO AddIn.
Unfortunately, it always results in an E_NOINTERFACE exception in my tests.
The code I am using is below - one extract of the ThisAddin partial class overriding the RequestComAddinAutomationService, another describing the test utility interface, the test itself, as well as an additional assembly extract proving that the AddIn assembly and its internals are visible to the test.
My question is: why is this not working? I am pretty sure that this follows the generally accepted practices of VSTO testing.
If the below is not possible anymore, how should one go about testing VSTO? Is .NET remoting/IPC the only solution?
ThisAddin.cs
public partial class ThisAddin
{
#region Testing Utilities
private AddinHelper _comAddinObject;
protected override object RequestComAddInAutomationService()
{
// This is being called when I debug/run my project, but not when I run the tests
return _comAddinObject ?? (_comAddinObject = new AddinHelper());
}
#endregion
}
#region Testing Utilities
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IAddinHelper
{
Presentation GetPresentation();
string GetString();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IAddinHelper))]
public class AddinHelper : StandardOleMarshalObject, IAddinHelper
{
public Presentation GetPresentation()
{
return Globals.ThisAddin... [...];
}
public string GetString()
{
return "Hello World!";
}
}
#endregion
AssemblyInfo.cs
[assembly: InternalsVisibleTo("MyProject.Tests")]
MyUnitTest.cs (has a reference to MyProject)
[TestClass]
public class BasicTest
{
[TestMethod]
public void TestInstantiates()
{
var application = new Application();
var doc = application.Presentations.Open(#"C:\Presentation.pptx",
MsoTriState.msoFalse, MsoTriState.msoFalse, MsoTriState.msoFalse);
var comAddins = application.COMAddIns;
var comAddin = comAddins.Item("MyProject"); // Returns okay
var comObject = (IAddinHelper) comAddin.Object; // Exception occurs
Assert.AreEqual(true, true); // Never reached
doc.Close();
}
}
Furthermore, the project's settings are set to "Register for COM interop" and Visual Studio runs elevated without errors - running it as non-admin results in the DLLs not being registered; therefore, I also know that the COM objects are registered.
Resulting Exception
An exception of type 'System.InvalidCastException' occurred in MyProject.Tests.dll but was not handled in user code
Additional information: Unable to cast COM object of type 'System.__ComObject' to interface type 'MyProject.IAddinHelper'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{59977378-CC79-3B27-9093-82CD7A05CF74}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
StackOverflow
How to call VSTO class from other c# project
Unit Testing VSTO projects
VSTO Add-ins, COMAddIns and RequestComAddInAutomationService (Register for COM Interop doesn't change anything)
Why cannot I cast my COM object to the interface it implements in C#? (the issue is likely not STAThread related)
https://sqa.stackexchange.com/questions/2545/how-do-i-unit-test-word-addin-written-in-c
Microsoft
https://blogs.msdn.microsoft.com/varsha/2010/08/17/writing-automated-test-cases-for-vsto-application/ <-- Even with this supposedly working sample project, I am getting the exact same error.
https://msdn.microsoft.com/en-us/library/bb608621.aspx
General workflow background: https://msdn.microsoft.com/en-us/library/bb386298.aspx
I honestly don't know how one is supposed to test VSTO Add-Ins without this working.
Solution
The Project with methods to be Tested has to use the PIA Microsoft.Office.Interop.PowerPoint via the .Net reference tab.
In the Unit Test Project you have to use the Microsoft Powerpoint 1X.0 Object Library via the COM reference tab - its an ActiveX.
The confusing thing is in Solution Explorer they are both called: Microsoft.Office.Interop.Powerpoint
When you specify the correct References you'll see this error on the line where the exception occurs:
Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
To solve that simply add a .Net reference to the Microsoft.CSharp.dll in the Unit Test project.
Next we will run into the error you're seeing.
Firstly add a unique GUID to the Interface & class (this will overcome the error):
[ComVisible(true)]
[Guid("B523844E-1A41-4118-A0F0-FDFA7BCD77C9")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IAddinHelper
{
string GetPresentation();
string GetString();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("A523844E-1A41-4118-A0F0-FDFA7BCD77C9")]
[ComSourceInterfaces(typeof(IAddinHelper))]
public class AddinHelper : StandardOleMarshalObject, IAddinHelper
Secondly temporarily make the private AddinHelper _comAddinObject; public in Scope (you can do your [assembly: InternalsVisibleTo("MyProject.Tests")] later when its working).
Thirdly, check that Powerpoint has not disabled the COM add-in. This sometimes happens silently, without the Office App complaining.
Lastly, make sure Register for COM is ticked.
Viola:
Ref: I pulled my hair out working this out years ago helping this fellow SO'r: Moq & Interop Types: works in VS2012, fails in VS2010?

MEF not loading a references again

I have a interface as follows:
[InheritedExport(typeof(ITransform))]
public interface ITransform
{...}
Now, I have two classes:
namespace ProjectA
{
public class Transform:ITransform {....}
}
And
namespace ProjectB
{
public class Transform:ITransform {....}
}
I am using DirectoryCatalog for loading each parts. Each project is compiled and their binaries(build output) location is given as an input to DirectoryCatalog for further composition.
The code for fetching ITransform parts is as follows:
public static class ExtensionFactory
{
public static ITransform GetExtension(string extensionPath)
{
IEnumerable<ITransform> extensions = null;
try
{
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(extensionPath));
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(catalog);
extensions = container.GetExportedValues<ITransform>();
return extensions.FirstOrDefault();
}
catch (Exception ex) {........}
return extensions.FirstOrDefault();
}
}
I have another project ProjectXYZ(auto generated by third party tool(Altova Mapforce 2012 SP1)).
For ProjectA:
namespace ProjectXYZ
{
public classA{...}
}
For ProjectB:
namespace ProjectXYZ
{
public classA{...}
public classB{...}
}
ProjectA.Transform uses ProjectXYZ.ClassA, whereas ProjectB.Transform uses ProjectXYZ.ClassB from another implementation of ProjectXYZ. The implementation and classes of ProjectXYZ varies across for different implementation of ITransform. The classes in ProjectXYZ are automatically generated through some third-party tools, which I need to use directly. So, I cannot make any changes to ProjectXYZ.
So, when first time MEF loads ProjectA.Transform, it also loads ProjectXYZ to be used as a reference for ProjectA. When ProjectB.Transform is getting loaded/exported, then as ProjectXYZ being already in MEF memory, it uses the ProjectXYZ reference available from ProjectA. Thus, when ProjectB.Transform is executing, it searches for ProjectXYZ.ClassB, which it does not gets as MEF has load ProjectXYZ reference available in ProjectA.
How to resolve this problem. The MEF loads the parts correctly, but it does not load the supporting dll's references in a desired manner. I have also tried PartCreationPolicy attribute, but the results are same.
It not a MEF problem. The problem is in the loading model of .NET. (or better the way you're objects are loaded by .net)
When MEF loads it returns the correct objects. But when looking for class ProjectXYZ when projectB is loaded there is already a ProjectXYZ dll loaded with the correct assembly name projectB is referring to. And the loader the dll actually referenced by projectB is not loaded.

Categories