i'm converting C++ to C++/CLI and would like to expose some managed classes as COM objects. In C# it was easy and setting [ComVisible] & inheriting from interface (also ComVisible) did the job.
However C++ project build as C++/CLI does not export DllRegisterServer.
Here is sample project (started from CLR Console Application project in VS 2008).
#include "stdafx.h"
using namespace System;
using namespace System::Runtime::InteropServices;
[ComVisible(true)]
[Guid("E3CF8A18-E4A0-4bc3-894E-E9C8648DC1F0")]
[InterfaceType(ComInterfaceType::InterfaceIsDual)]
public interface class ITestInterface
{
void TestMethod();
};
[ComVisible(true)]
[Guid("1514adf6-7cb0-4561-9fbb-b75c0467149b")]
public ref class CliComClass : ITestInterface
{
public:
virtual void TestMethod()
{
}
};
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello World");
return 0;
}
When I run regsvr32 on output .exe I got error saying DllRegisterServer was not found. I've tried google for some help but with no success.
You need to use TlbExp instead, TlbExp is the tool use to export managed classes to COM, it will read the assembly find the ComVisible type and register them.
Related
I'm trying to call my C# dll from a C++ client, so far I have the dll all setup and in my registry (I can create and call it from the power shell for example).
The problem I'm having is that I can't call it from my C++ code.
My C# interface:
namespace MyInterop
{
[Guid("BE507380-1997-4BC0-AF01-EE5D3D537E6B"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyDotNetInterface
{
void ShowCOMDialog();
}
}
My C# class that implements the interface:
namespace MyInterop
{
[Guid("38939B1E-461C-4825-80BB-725DC7A88836"), ClassInterface(ClassInterfaceType.None)]
public class MyDotNetClass : IMyDotNetInterface
{
public MyDotNetClass()
{ }
public void ShowCOMDialog()
{
MessageBox.Show("I am a" +
" Managed DotNET C# COM Object Dialog");
}
}
}
Very simple as I'm just testing at the moment. I now import the tlb into my C++ file
#import "<Path to file>\MyInterop.tlb" raw_interfaces_only
Finally I try to call it:
HRESULT hr = CoInitialize(NULL);
MyInterop::IMyDotNetInterfacePtr MyDotNetClass(__uuidof(MyInterop::MyDotNetClass));
MyDotNetClass->ShowCOMDialog();
CoUninitialize();
But, VS is telling me that ShowCOMDialog is not a member of my interface. Have I missed something?
By declaring raw_interfaces_only, you have surpressed the generation of wrapper functions, as indicated in this link. And since your interface is based on IDispatch, you are forced to call your interface methods indirectly via IDispatch's Invoke.
Suggestions:
Change your interface type to ComInterfaceType.InterfaceIsDual or ComInterfaceType.InterfaceIsIUnknown
Remove raw_interfaces_only in order to work with the generated wrapper functions.
I am working on WP8 project that includes class library project as C# source code and Windows Runtime Component as C++ source code. Does anyone know whether or not it is possible to create such C# class library which would reference Windows Runtime Component? The ultimate result should be .NET assembly and .WIMND/.DLL runtime component that can be used for application. Currently I cannot build class library because it doesn't see Windows Runtime Component, even though I added it to the project.
More specific. I have, say, MyNs.MyClass.MyMethod() which is defined in C++ runtime component and used from C# class library. Currently I cannot compile C# due to missing method although I have windows runtime component project attached to the same solution.
Although I am butting in because this is not my area, I tried Googling for "c# call windows runtime component". There seem to be many hits/examples, e.g. the first one is https://msdn.microsoft.com/en-us/library/hh755833.aspx.
Does that not help you?
I solved this by adding reference to Windows runtime component manually into the C# class library .csproj file as follows
...
<ItemGroup>
<Reference Include="WindowsRuntimeComponent.winmd" />
</ItemGroup>
...
I managed to make a C++ WRL project and use a class in that project from a C# project by adding a reference in the normal way. The Wrl project (not C++/CX, which also works) was made using some WRL template that I found somewhere on the web. The wrl project required me to make a .idl to define the interface, and produced its .dll and .winmd. Here is some code for those who are battling with this type of thing:
The Wrl class:
#include "pch.h"
#include "WrlTestClass2_h.h"
#include <wrl.h>
using namespace Microsoft::WRL;
using namespace Windows::Foundation;
namespace ABI
{
namespace WrlTestClass2
{
class WinRTClass: public RuntimeClass<IWinRTClass>
{
InspectableClass(RuntimeClass_WrlTestClass2_WinRTClass, BaseTrust)
public:
WinRTClass()
{
}
// http://msdn.microsoft.com/en-us/library/jj155856.aspx
// Walkthrough: Creating a Basic Windows Runtime Component Using WRL
HRESULT __stdcall Add(_In_ int a, _In_ int b, _Out_ int* value)
{
if (value == nullptr)
{
return E_POINTER;
}
*value = a + b;
return S_OK;
}
};
ActivatableClass(WinRTClass);
}
}
The C# code that uses this class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
namespace CSharpClientToWrl
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
WrlTestClass2.WinRTClass _winRtTestClass = new WrlTestClass2.WinRTClass();
int _answer = _winRtTestClass.Add(4, 6);
Assert.AreEqual(_answer, 10);
}
}
}
The .idl file of the wrl project:
import "inspectable.idl"; import "Windows.Foundation.idl";
#define COMPONENT_VERSION 1.0
namespace WrlTestClass2 {
interface IWinRTClass;
runtimeclass WinRTClass;
[uuid(0be9429f-2c7a-40e8-bb0a-85bcb1749367), version(COMPONENT_VERSION)]
interface IWinRTClass : IInspectable
{ // http://msdn.microsoft.com/en-us/library/jj155856.aspx // Walkthrough: Creating a Basic Windows Runtime Component Using WRL HRESULT Add([in] int a, [in] int b, [out, retval] int* value);
}
[version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)]
runtimeclass WinRTClass
{
[default] interface IWinRTClass;
} }
Here is my other question that led into this one as reference also:
How to call managed C++ methods from Un-managed C++
I have successfully created a C# COM File. Now I need a simple explanation on how to implement it in unmanaged C++.
I am following this example but the c++ part is weak.
http://www.codeproject.com/Articles/7859/Building-COM-Objects-in-C
Here is my COM file
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace cSharpRiJHarn
{
[Guid("ED1483A3-000A-41f5-B1BC-5235F5897872")]
public interface DBCOM_Interface
{
[DispId(1)]
String encrypt(string s);
[DispId(2)]
String decrpyt(string s);
}
[Guid("A6BCEC1D-B60C-4c97-B9AD-1FE72642A9F8"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface DBCOM_Events
{
}
[Guid("7C13A8C6-4230-445f-8C77-0CA5EDECDCB5"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(DBCOM_Events))]
public class RijndaelLink : DBCOM_Interface
{
public String encrypt(String s)
{
return Rijndael.EncryptString(s);
}
public String decrpyt(String s)
{
return Rijndael.DecryptString(s);
}
}
}
I just want a VERY basic example on using this with unmanaged code.
Please include in your answers:
Do I need to include the project or just the source files of the COM
Do I need to add a reference
A very basic example of passing a string and printing it out with cout.
Thanks for your help!
The first thing you need to do is properly define the COM object in .NET, to be used by the unmanaged world (C++ or other). Here is a decent definition:
namespace cSharpRiJHarn
{
[Guid("ED1483A3-000A-41f5-B1BC-5235F5897872")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
public interface IRijndaelLink
{
string encrypt(string s);
string decrypt(string s);
}
[Guid("7C13A8C6-4230-445f-8C77-0CA5EDECDCB5")]
[ComVisible(true)]
public class RijndaelLink : IRijndaelLink
{
public string encrypt(string s)
{
return Rijndael.EncryptString(s);
}
public string decrypt(string s)
{
return Rijndael.DecryptString(s);
}
}
}
Next, you need to register this .NET assembly for COM using the RegAsm tool. I suggest also you build a Type Library (.TLB) with it, something like this (I suppose you build the whole stuff for X86, not X64):
c:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe YourAssembly.dll /tlb:YourAssembly.tlb /codebase
Please adapt to your actual path. Also check the codebase arg as you may not need this in production.
This will build a .TLB file with both the interface and the class inside. It works because we added the ComVisible attribute. You will also note I have not defined a Dispatch or Dual interface because in this sample, I don't need COM Automation (VB, VBA) nor any scripting language (VBScript, JScript) support, only IUnknown interfaces which are much easier to use in plain C/C++ than IDispatch interfaces.
Now, there is an easy way to import that in the unmanaged c++ world using a Microsoft specific C++ extension: #import Directive, similar to Add References in .NET. Here is a sample Console Application that uses the COM Object:
#include "stdafx.h"
#import "c:\MyPathToTheTlb\YourAssembly.tlb" // import the COM TLB
using namespace YourAssembly;
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL); // needed to enter COM space
IRijndaelLinkPtr ptr(__uuidof(RijndaelLink)); // create the COM Object with the desired interface
_bstr_t s = ptr->encrypt("hello"); // call the function
printf("%S", (LPWSTR)s); // for example
CoUninitialize();
return 0;
}
You will notice the #import directive also create cool wrappers (_bstr_t, as .NET String will be exported as Automation BSTR here, even for IUnknown interfaces) for string handling, so it's no really big deal.
This is not the only way all this can work, but that's IMHO one of the most simple.
How does an unmanaged c++ .dll know where the location of a managed c# .dll is?
Some context:
I have a c++ .dll that imports a type library (.tlb) and inside one of the c++ functions, I instantiate a pointer to the functions inside the c# .dll. Then, using that pointer, I can call the c# functions in c++. I would like to know how c++ .dll know where the c# .dll is? Further, is there a better way to do this type of coding?
Does the .tlb need to be in the same directory as the c# .dll?
One way to accomplishing the above is to register the C# dll file with the Microsoft Windows Registry using the regasm command. This command EXE is included with distributions of Visual Studios. An example use of the command follows:
regasm NameofC#DLL.dll /tlb:NameofC#DLL.tlb
Once you have registered it in the registry you will need to install it to the global assembly cache (GAC) using the gacutil command. This is also included with distributions of Visual Studios. An example use of the command follows:
gacutil /i NameofC#DLL.dll
Once these steps are completed your C++ code will be able to find the C# dll assuming your DLL files are constructed similar to the following:
[C#]
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace MyNameSpace
{
/// <summary>
/// Interface for C++ DLL. This exposes the functions used inside the dll
/// Make sure the return types, function names, and argument types match the class
/// </summary>
[ComVisible(true)]
[Guid("CBA208F2-E43B-4958-97C7-C24EA5A213DE")]
public interface IMyClass
{
int Function1();
int Function2();
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("579091E6-83A1-4aa5-89A7-F432AB2A57E3")]
[ComVisible(true)]
public class MyClass : IMyClass
{
public MyClass()
{
//Constructor
}
public int Function1()
{
//Do something in C#
return an integer;
}
public int Function2()
{
//Do something else in C#
return an integer;
}
}//End Class MyClass
}//End namespace MyNameSpace
Everywhere you see a GUID being used, that is a randomly generated global identifier used to identify your C# code. This number can be randomly generated using the GUID creation tool provided with Visual Studios under the "Tool menu" and the "Create GUID" option. Select Registry format and press "New GUID". Then just press copy and paste it where the GUID needs to be (Remove the brackets!)
[C++]
#include <windows.h>
#include "stdafx.h"
#include <cstdlib>
#pragma warning (disable: 4278)
#import <mscorlib.tlb> raw_interfaces_only
#include <stdio.h>
//This path needs to be valid at compile time. The file does not need to be there in runtime when using the DLL after compile.
#import "C:\\...\\NameofC#DLL.tlb" no_namespace named_guids
extern "C" _declspec(dllexport) int _Function1()
{
int result = 0;
IMyClass *CSharpInterface = NULL;
//Open interface to C#
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_MyClass, NULL, CLSCTX_INPROC_SERVER,
IID_IMyClass, reinterpret_cast<void**>(&CSharpInterface));
//Call Function1 C# method
result = CSharpInterface->Function1();
//Close interface
CoUninitialize();
//Return result
return result;
}
The required TLB file at compile time can be generated using the tlbexp command also included with visual studios.An example use of the command follows:
tlbexp NameofC#DLL.dll
If you do not specify a path it will default to the following path:
C:\Program Files\Microsoft Visual Studio 9.0\VC
There a several places you can mess this up and the C# DLL call will fail.
Regards,
SeaMossDesign
Maybe I'm missing something, but you can create a custom CLR host and invoke a method from C# without pointer. Check ICLRRuntimeHost::ExecuteInDefaultAppDomain out.
I've been working with trying to link up some c++ code and wrap it inside a COM object to access via C#. I created an atl project and added a simple method such as Add(double a, double b). The following is the code from my atl.h file:
// atl.h : Declaration of the Catl
#pragma once
#include "resource.h" // main symbols
#include "com_i.h"
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif
// Catl
class ATL_NO_VTABLE Catl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<Catl, &CLSID_atl>,
public Iatl
{
public:
Catl()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_ATL)
DECLARE_NOT_AGGREGATABLE(Catl)
BEGIN_COM_MAP(Catl)
COM_INTERFACE_ENTRY(Iatl)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHOD(Add)(DOUBLE a, DOUBLE b);
};
OBJECT_ENTRY_AUTO(__uuidof(atl), Catl)
The following is from the atl.cpp file
// atl.cpp : Implementation of Catl
#include "stdafx.h"
#include "atl.h"
STDMETHODIMP Catl::Add(DOUBLE a, DOUBLE b)
{
// TODO: Add your implementation code here
return a + b;
}
Inside my c# file I'm calling the dll... after i referenced it... it sees the dll but not the methods assigned. which is my problem. Heres the code from program.cs
sing System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace sharpdll
{
class Program
{
[DllImport("com.dll")]
public static extern double Add(double a, double b);
static void Main(string[] args)
{
Add(2, 3);
}
}
}
Debugging breaks at Add(2, 3);
Says "Unable to find an entry point named 'Add' in DLL 'com.dll'."
Any ideas?
DllImport is for PInvoke (to native Win32 dlls).
You want COM Interop.
Register your ATL com object, then Add a reference to it, as you would to any .Net or COM component.
An alternative to ATL, you can expose your C++ functionality through C++/CLI.
Hope this helps,