虽然lain已经写了一个例子,但我还是会把它发出来以防万一...
编写一个包装器以访问您自己的库的过程与访问标准 .Net 库之一的过程相同。
以下是在名为CsharpProject的项目中的示例C#类代码:
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++接口中的导出宏来完成。未管理的接口头文件必须包含未管理的内容(或者由预处理器过滤掉)。
#pragma once
#include <string>
#ifdef CSHARPWRAPPER_EXPORTS
#define EXPORT_SPEC __declspec( dllexport )
#else
#define EXPORT_SPEC __declspec( dllimport )
#endif
EXPORT_SPEC std::string GetDisplayString(const char * pName, int iValue);
你可以创建一个内部头文件,以便在管理库文件中包含。这将添加using namespace
语句,并可以包括所需的辅助函数。
托管C ++接口头文件(CsharpInterface.h):
#pragma once
#include <string>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace CsharpProject;
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 "CppInterface.h"
void main() {
std::string strDisplayString = GetDisplayString("Test", 123);
printf("%s\n", strDisplayString.c_str());
}