How can I create an instance for a class in c# dll that I imported in a c++ project? I have imported the c# library to c++ by following the instructions given here. I can call the functions using the public interface.
[EDIT] I want to set values for the attributes of the class in c# dll through c++ and to pass it to the dll. So that I can skip so many set functions in c# dll. If I can create object for the class, I will set the values using the object and pass the object to c# dll.
In the link you provided, in the c++ client code description
// CPPClient.cpp: Defines the entry point for the console application.
// C++ client that calls a managed DLL.
#include "stdafx.h"
#include "tchar.h"
// Import the type library.
#import "..\ManagedDLL\bin\Debug\ManagedDLL.tlb" raw_interfaces_only
using namespace ManagedDLL;
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
ICalculatorPtr pICalc(__uuidof(ManagedClass));
long lResult = 0;
// Call the Add method.
pICalc->Add(5, 10, &lResult);
wprintf(L"The result is %d\n", lResult);
// Uninitialize COM.
CoUninitialize();
return 0;
}
the creation of the pointer, pICalc is the pretty much the creation of the object of the class. Created in the line ICalculatorPtr pICalc(__uuidof(ManagedClass));
Related
I am trying to follow tutorial calling code from C++ into C#.
I followed the coding part correctly.
But when I run the code I get exception:
System.DllNotFoundException: 'Unable to load DLL 'SampleNativeLib': The specified module could not be found. (Exception from HRESULT: 0x8007007E)'
I create C# library as simple application file.
And C++ as dynamic .dll.
In the original tutorial (I also have full project file of it) in the C# project in the references there are no references to C++ .dll.
I would like to ask how C++ is referenced to C#?
The code of C#
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace ObjectPinning {
class Program {
[DllImport("SampleNativeLib")]
static extern int SetData([MarshalAs(UnmanagedType.LPArray)] int[] darray);
[DllImport("SampleNativeLib")]
static extern int DoCalc();
static void Main(string[] args) {
var data = Enumerable.Range(0, 10).Select(i => i + 1).ToArray();
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
Console.WriteLine(SetData(data));
Console.WriteLine(DoCalc());
Console.ReadLine();
GC.Collect();//Clean up any garbage object
Console.WriteLine(DoCalc());
handle.Free();
}
}
}
The code of C++:
// SampleNativeLib.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
int* g_pData;
extern "C" __declspec(dllexport) int WINAPI DoCalc() {
int sum = 0;
for(int i = 0; i < 10; i++)
sum += g_pData[i];
return sum;
}
extern "C" __declspec(dllexport) int WINAPI SetData(int* data) {
g_pData = data;
return DoCalc();
}
My project file:
Download
Teachers File:
Download
My file
Teacher File
The exception you get is most likely because you didn't add the dll to the C# executable path. In the first step the application searching the dependencies in the exe directory, if not found, then it goes through the Window environment variables. That is why we don't need to copy the dlls when we use some of the Windows API function, because if you look at at your PATH variable, you'll have %SystemRoot% variable which links to your windows folder.
In C# you can't really reference C++ projects as you already know when adding another C# project, since you calling from managed code to unmanaged/native code.
Important note when you building your own C++ dll, make sure the compiler environment set to x32 bit, otherwise you'll get some other exceptions when calling C++ code from C#.
What actually expose the C++ function to allow for it consumed by other languages, is the:
extern "C" __declspec(dllexport)
On the signature of the function. So that gives you the power to do almost everything you can do with C++ also in C#/Java.
I am following the example in How to call a managed DLL from native Visual C++ code in Visual Studio.NET or in Visual Studio 2005 to call a .NET DLL from native C++ code. The code for C# looks like this:
public class StringExample: IStringExample
{
public string returnString(string input)
{
return input;
}
}
I followed the example's steps to build, register the COM assembly, and export the type library (.tlb) from the C# code.
In C++ code, I am trying to use code similar to the following:
#import "..\StringExample\bin\Debug\StringExample.tlb" raw_interfaces_only
using namespace StringExample;
void abc()
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
IStringExample ptr(__uuidof(StringExample));
BSTR bstrInput = L"hello world";
BSTR bstrOutput =L"";
ptr->returnString(bstrInput, &bstrOutput);
TCHAR* szOutput = (TCHAR *)_bstr_t(bstrOutput);
// Uninitialize COM.
CoUninitialize();
}
However, bstrOutput is empty. Moreover, I need to convert the bstrOutput to TCHAR* to pass it to a different api.
Is there an error in the variable initialization? Is there a different way to pass string variables between .NET and C++?
I have a DLL made in C++ that is then used in a C# program. the C# program references the DLL and i call the classes and can use the functions defined in the DLL.
Now i want to add other functions to the DLL so i can run C++ code in C#. I Follow the example of how the other functions in the DLL are defined; .h and .cpp and i create a new function that executes code. I put it in the same places as the previously define functions, but when i put the DLL in my C# code my function is not there. when i create an instance of the DLL class my function is not a part of it.
I put it in the .h as a public function of the class and gave it code to run in the .cpp but its not found.
Why can't my C# program see the function i made even though its following the same suite as the other functions?
UPDATE
code.h:
#pragma once
#include <windows.h>
#include <PvDisplayWnd.h>
#include <vfw.h>
#include "NPvResult.h"
#include "NPvDeviceInfo.h"
#include "NPvBuffer.h"
#include <string>
using namespace System;
using namespace System::Runtime::InteropServices;
public ref class NPvDisplayWnd
{
public :
NPvDisplayWnd();
bool Handle();
NPvResult^ ShowModeless(long locx, long locy, long x, long y);
NPvResult^ ShowModal();
NPvResult^ NPvDisplayWnd::Create();
int^ Display( NPvBuffer^ aBuffer, int^ x);
NPvResult^ Work( const std::string afilename, unsigned short asizex, unsigned short asizey, unsigned short aBPP, double aFPS);
NPvResult^ Close();
NPvResult^ DoEvents();
}
code.cpp:
#include "NPvDisplayWnd.h"
NPvDisplayWnd::NPvDisplayWnd(){code}
NPvResult^ NPvDisplayWnd::Create(){code}
bool NPvDisplayWnd::Handle()
NPvResult^ NPvDisplayWnd::ShowModeless(long locx, long locy, long x, long y){code}
NPvResult^ NPvDisplayWnd::ShowModal(){code}
int^ NPvDisplayWnd::Display( NPvBuffer^ aBuffer, int^ x){code}
NPvResult^ NPvDisplayWnd::DoEvents(){code}
NPvResult^ NPvDisplayWnd::Work( const std::string aFileName, unsigned short aSizeX, unsigned short aSizeY, unsigned short aBPP, double aFPS){code}
NPvResult^ NPvDisplayWnd::Close(){code}
The function that i added was the Work() function. if i build this and place it in my C# code, it sees all the functions except work. To make sure i was using the right .dll, i changed the name of ShowModeless() to ShowModeless__F() and rebuilt it and added it to my C# and the change carried over, but i still didn't see my Work() function.
On Windows, you must explicitly export symbols in order for users to be able to access them via DLLs. This is done using the __declspec(export) syntax. This is needed during compilation and linking of the DLL.
Now that we know you're using C++/CLI instead of regular C++, we can see a problem. Work takes a std::string. But Work is intended to be a managed function, and managed code cannot take a std::string as a parameter. You need to take a managed string type instead.
A native C++ class cannot be imported in C#. You need to utilize C++/CLI feature in existing C++ DLL so that managed clients (like C#) can import the classes.
namespace XX
{
public ref class YourClass{...};
}
It will export managed class YourClass under XX namespace. You need to use /CLR compiler flag.
You may compile set of CPP files as managed, and set of other files as unmanaged. Managed classes cannot contain unmanaged stuff (unless it is declared pointer).
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,