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,
Related
I have found the DllExport Project on GitHub while searching for a way to use C# .NET Core 3 Code from Plain C/C++. My goal is being able to compile the C# to any dynamic library and use it on both Linux or Windows with dlopen (dlopen for Windows).
C# Library I'm trying to create:
using RGiesecke.DllExport;
using System;
namespace CSharpPart
{
public class Test
{
[DllExport]
public static int _add(int a, int b)
{
return a + b;
}
}
}
C/C++ Code where I'm trying to Reference the C# Library:
#include "CPartCMake.h"
#include "dlfcn.h"
#include "cstring"
using namespace std;
int main()
{
const char* pemodule = "path_to_dll\\CSharpPart.dll";
void* handle = dlopen(pemodule, RTLD_NOW);
typedef int(_cdecl* _add)(int a, int b);
_add pAdd = (_add)dlsym(handle, "_add");
cout << pAdd(5, 7);
return 0;
}
The Call to dlopen returns a non NULL handle which I guess means that the Library itself can be opened. dlsym return a NULL. When viewing the C# DLL through DLL Export Viewer it can be confirmed that the DLL doesn't contain the _add entry.
I tried Wrapping the C# project in an C++/CLI Project like proposed here but it doesn't seems to work with .NET Core.
I read there is also a possibility with using COM interop but I didn't really understood what it is about and I'm not sure if it would work cross-platform like.
I'm afraid that DLLExport just isn't yet compatible with .NET Core as well. Is there any way of achieving my goal like I described it?
I am doing a POC where I need to invoke a c# method from a c++ application.
So I did the following:
Create a c# library.
Create a c++/cli library wrapping the dll from step 1.
Creating a c++ library that would consume cli wrapper.
Creating an unmanaged console application that would use c++ dll output from step 3.
Guys,
I have done a lot of searches in last days but did not get any useful help.
Created the c# project with output: DotNetLogger.dll
Created the c++/cli project with output: CliLogger.dll
Also referenced DotNetLogger.dll
Also exported a method.
Create C++ project with output CppLogger.dll
and so on.
Also,
C# project setting: v4.6.2
C++/CLI project settings: /CLR enabled with v4.6.2
C++ project setting: no /clr
namespace DotNetLogger
{
public class ManagedLogger
{
public void Ping()
{
//Do something
}
}
}
using namespace System;
using namespace DotNetLogger;
namespace CliLogger {
public ref class LoggerBridge
{
public:
ManagedLogger^ obj;
LoggerBridge() {
obj = gcnew ManagedLogger();
}
void Result() {
return obj->Ping();
}
};
}
__declspec(dllexport) void AreYouThere() {
CliLogger::LoggerBridge obj;
return obj.Result();
}
#pragma once
#include "D:\Logger_POC_NoWorks\InteropLogger\CliLogger\CliLogger.h"
__declspec(dllimport) void AreYouThere();
class UnmanagedLogger {
public:
void InvokeResult() {
return AreYouThere();
}
};
#include "pch.h"
#include <iostream>
#include "D:\Logger_POC_NoWorks\InteropLogger\CppLogger\CppLogger.h"
int main()
{
UnmanagedLogger uLogger;
uLogger.InvokeResult();
}
Expected Result:
The console application shall build successfully in VS 2017.
But I am getting compiler errors;
Warning C4273 'AreYouThere': inconsistent dll linkage ServiceCode d:\logger_poc_noworks\interoplogger\cpplogger\cpplogger.h 5
Error C2871 'DotNetLogger': a namespace with this name does not exist ServiceCode d:\logger_poc_noworks\interoplogger\clilogger\clilogger.h 4
Error C2039 'LoggerBridge': is not a member of 'CliLogger' ServiceCode d:\logger_poc_noworks\interoplogger\clilogger\clilogger.h 21
And many cascading errors.
I'm guessing your second block of code is clilogger.h?
You don't need to include cliLogger.h in your 3rd block of code as you are only using AreYouThere which you have already declared, this declaration also conflicts with the declaration from the header (hence the inconsistent dll linkage warning). This header also contains CLI code which will produce errors in your pure c++ file (probably the cause of your 'LoggerBridge': is not a member of 'CliLogger' error).
The #include directive is resolved by the pre-processor, which just is a pure text processor unaware of actual C++ (or C) semantics. So this piece of code
#include "D:\Logger_POC_NoWorks\InteropLogger\CliLogger\CliLogger.h"
__declspec(dllimport) void AreYouThere();
class UnmanagedLogger { /* ... */ }
will result in
using namespace System;
using namespace DotNetLogger;
namespace CliLogger { /* ... */ }
__declspec(dllexport) void AreYouThere()
{
CliLogger::LoggerBridge obj;
return obj.Result();
}
__declspec(dllimport) void AreYouThere();
class UnmanagedLogger { /* ... */ }
being presented to compiler after pre-processing, which will result in two problems:
Unmanaged compiler still sees managed code (e. g. public ref class LoggerBridge, which is invalid in pure unmanaged C++).
You get the incompatible declaration of AreYouThere, first with dllexport, then second time with dllimport.
So you need a separate header only containing unmanaged code and managing the import/export problem.
Typically, you'd have something like:
#ifdef INSIDE_MY_DLL_IMPLEMENTATION // invent up some better name yourself...
#define DLL_IMPORT __declspec(dllexport)
#else
#define DLL_IMPORT __declspec(dllimport)
#endif
DLL_IMPORT void areYouThere();
In referencing project, you'd just include the header then, inside DLL code, you'd have instead:
#define INSIDE_MY_DLL_IMPLEMENTATION // must precede, of course...
#include "new_interface_file.h"
Generalising now: You can use this pattern for any DLL, managed or purely unmanaged alike. For the latter case, you could even consider different compilers (e. g. __attribute__((dllimport)) for GCC, although you might need some extra work as GCC attributes don't precede, but follow function declaration). Solely: different C++ implementations (compilers) most likely come with incompatible ABI, so you should fall back to a pure C interface if you plan multiple compiler compatibility:
#ifdef __cplusplus
extern "C"
{
#endif
// ...
#ifdef __cplusplus
}
#endif
I need to import a dll made in c# from c++
All the examples I found import a single function from a dll, but as far as I know, you cannot export a single function from c# without exporting the whole class (I'm a bit of a c# newbie though)
I am using node gyp to compile it (I'm building a node module for my web app using v8)
Here is my c# code:
using System;
using System.IO;
using System.Runtime.InteropServices;
[ComVisible(true)]
public class Hello
{
public static void Main()
{
Console.WriteLine("Hello World!");
string createText = "Hello World" + Environment.NewLine;
File.WriteAllText(".\\asd.txt", createText);
}
}
And this is my c++ header file:
#ifndef ASDLIB_H
#define ASDLIB_H
#define ASD_IMPORT __declspec(dllimport)
#define STDCALL __stdcall
class ASD_IMPORT Hello{
public:
STDCALL static void ASD_IMPORT Main();
};
#endif // ASDLIB_H
I normally do this kind of stuff by creating a static C++ CLR Wrapper Lib.
These are the steps I'm normally using (although I don't use it very often):
Here minimal example with using Visual Studio:
1. Create managed C# .NET library Project
Let name it HelloLibManaged
with one file Hello.cs and following content:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HelloLibManaged
{
public class Hello
{
public void Print()
{
System.Console.WriteLine("Hello from Managed Lib");
}
}
}
Build this lib with x86 or x64 , just dont Any CPU
2. Create a new C++ CLR static lib project within same solution.
Let's name it HelloLibCpp
Add a reference to HelloLibManaged project via Project->Add Reference
Remove automatic created existing .h/.cpp files from Project, and create these 2 files:
HelloUnmanaged.h
#pragma once
namespace hello_managed {
class Hello
{
public:
void Print();
};
}
and
HelloUnmanaged.cpp:
#include "HelloUnmanaged.h"
namespace hello_managed
{
void Hello::Print()
{
HelloLibManaged::Hello^ hello = gcnew HelloLibManaged::Hello();
hello->Print();
}
}
Und Project Properties -> General specify static lib.
In the Build settings specify x86 / x64 - use the same build type as building Managed lib from Step 1.
Now you can build the dll and use it in your other unmanaged C++ projects.
For more advanced stuff, like return types and methods with parameters you have to do Type-Marshalling between managed/unmanaged code. There are many resources online with more information about Type-Conversion between managed/unmanaged code. Keyword: Marshaling.
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;
} }
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.