我有点生疏,实际上我的C ++真的很生疏。从大学一年级开始就没有触及它,所以已经有一段时间了。
无论如何,我正在做与大多数人相反的事情。从C ++调用C#代码。我在网上做了一些研究,似乎我需要创建一些托管C ++来形成一个桥梁。使用__declspec(dllexport)然后从中创建一个dll并将整个事件用作包装器。
但我的问题是 - 我真的很难找到例子。我找到了一些基本的东西,有人想将C#版本用于String.ToUpper(),但这是非常基本的,只是一个小代码片段。
任何人都有任何想法,我可以在哪里寻找更具体的东西?注意,我不想使用COM。目标是根本不触及C#代码。
答案 0 :(得分:11)
虽然lain打我写一个例子,但无论如何我都会发布它以防万一...
编写包装器以访问您自己的库的过程与访问其中一个标准.Net库的过程相同。
示例C#类代码,名为CsharpProject:
using System;
namespace CsharpProject {
public class CsharpClass {
public string Name { get; set; }
public int Value { get; set; }
public string GetDisplayString() {
return string.Format("{0}: {1}", this.Name, this.Value);
}
}
}
您将创建一个托管C ++类库项目(例如CsharpWrapper)并添加您的C#项目作为它的引用。为了使用相同的头文件供内部使用和引用项目,您需要一种方法来使用正确的declspec。这可以通过定义预处理程序指令(在本例中为CSHARPWRAPPER_EXPORTS
)并使用#ifdef
在头文件中的C / C ++接口中设置导出宏来完成。非托管接口头文件必须包含非托管内容(或者由预处理器过滤掉)。
非托管C ++接口头文件(CppInterface.h):
#pragma once
#include <string>
// Sets the interface function's decoration as export or import
#ifdef CSHARPWRAPPER_EXPORTS
#define EXPORT_SPEC __declspec( dllexport )
#else
#define EXPORT_SPEC __declspec( dllimport )
#endif
// Unmanaged interface functions must use all unmanaged types
EXPORT_SPEC std::string GetDisplayString(const char * pName, int iValue);
然后,您可以创建内部头文件,以便能够包含在托管库文件中。这将添加using namespace
语句,并且可以包含您需要的辅助函数。
托管C ++接口头文件(CsharpInterface.h):
#pragma once
#include <string>
// .Net System Namespaces
using namespace System;
using namespace System::Runtime::InteropServices;
// C# Projects
using namespace CsharpProject;
//////////////////////////////////////////////////
// String Conversion Functions
inline
String ^ ToManagedString(const char * pString) {
return Marshal::PtrToStringAnsi(IntPtr((char *) pString));
}
inline
const std::string ToStdString(String ^ strString) {
IntPtr ptrString = IntPtr::Zero;
std::string strStdString;
try {
ptrString = Marshal::StringToHGlobalAnsi(strString);
strStdString = (char *) ptrString.ToPointer();
}
finally {
if (ptrString != IntPtr::Zero) {
Marshal::FreeHGlobal(ptrString);
}
}
return strStdString;
}
然后你只需编写执行包装的接口代码。
托管C ++接口源文件(CppInterface.cpp):
#include "CppInterface.h"
#include "CsharpInterface.h"
std::string GetDisplayString(const char * pName, int iValue) {
CsharpClass ^ oCsharpObject = gcnew CsharpClass();
oCsharpObject->Name = ToManagedString(pName);
oCsharpObject->Value = iValue;
return ToStdString(oCsharpObject->GetDisplayString());
}
然后在非托管项目中包含非托管标头,告诉链接器在链接时使用生成的.lib文件,并确保.Net和包装器DLL与非托管应用程序位于同一文件夹中。
#include <stdlib.h>
// Include the wrapper header
#include "CppInterface.h"
void main() {
// Call the unmanaged wrapper function
std::string strDisplayString = GetDisplayString("Test", 123);
// Do something with it
printf("%s\n", strDisplayString.c_str());
}
答案 1 :(得分:10)
在visual studio中创建一个新的C ++ / CLI项目,并添加对C#dll的引用。假设我们有一个名为DotNetLib.dll
的C#dll,其中包含以下类:
namespace DotNetLib
{
public class Calc
{
public int Add(int a, int b)
{
return a + b;
}
}
}
现在将CLR C ++类添加到C ++ / CLI项目中:
// TestCPlusPlus.h
#pragma once
using namespace System;
using namespace DotNetLib;
namespace TestCPlusPlus {
public ref class ManagedCPlusPlus
{
public:
int Add(int a, int b)
{
Calc^ c = gcnew Calc();
int result = c->Add(a, b);
return result;
}
};
}
这将从C ++调用C#。
现在,如果需要,您可以将本机C ++类添加到C ++ / CLI项目中,该项目可以与CLR C ++类进行通信:
// Native.h
#pragma once
class Native
{
public:
Native(void);
int Add(int a, int b);
~Native(void);
};
和
// Native.cpp
#include "StdAfx.h"
#include "Native.h"
#include "TestCPlusPlus.h"
Native::Native(void)
{
}
Native::~Native(void)
{
}
int Native::Add(int a, int b)
{
TestCPlusPlus::ManagedCPlusPlus^ c = gcnew TestCPlusPlus::ManagedCPlusPlus();
return c->Add(a, b);
}
您应该能够正常地从任何其他本机C ++ dll调用Native类。
另请注意,托管C ++与C ++ / CLI不同,并且已被C ++ / CLI取代。维基百科解释得最好: