通过C++/CLR封装的方式使非托管的C++、VB6.0调用.Net托管代码

通常.Net的dll只能被加载到对应的虚拟机中运行和调用,而无法直接被低版本的.Net或C++和VB6.0等非托管代码调用。但是实际项目开发过程中我们为了兼容,不得不同时支持这些非托管代码或低版本的运行时。实际上微软提供了多种方式可以实现这种需求,如进程间通讯、COM/ActiveX、C++/CLR。本文主要介绍通过C++/CLR包装方式,实现非托管代码访问托管C#.Net调用。简单调用顺序如下图所示。

 

目标C#.Net类库

假设我们有一个目标C#.Net类库,提供以下两个api:

1
2
public bool CreateConnection()
public string GetData(string key)

 

创建C++/CLR包装类库

该类库是一个可以跟C#.Net dll互通的公共语言类库,跟C#.Net dll的区别在于他是用c++编写的,对外暴露c语言类api。非托管代码通过P/Invoke的方式实现函数调用。

  • 在Visual Studio新建一个C++/CLR类库

  • 引用目标C#.Net类库

项目右击->属性->公共属性->引用->添加NetApi.dll进入引用

 

  •  编写api的包装类ApiWrapper

这个包装类用于封装对C#.Net dll的调用,本例中维护了目标api类的一个实例,只是简单实现了功能,实际项目中应注意需要更多的异常处理。

NetApiClr.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// NetApiClr.h
 
#pragma once
 
using namespace System;
 
namespace NetApiClr {
 
    public ref class ApiWrapper
    {
    public:
        ApiWrapper();
        bool CreateConnection();
        String ^ GetData(String ^);
 
    private:
        NetApi::DataHelper ^ dataHelper = nullptr;
    };
}

 NetApiClr.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// This is the main DLL file.
 
#include "stdafx.h"
 
#include "NetApiClr.h"
 
NetApiClr::ApiWrapper::ApiWrapper()
{
    dataHelper = gcnew NetApi::DataHelper();
}
 
bool NetApiClr::ApiWrapper::CreateConnection(){
    return dataHelper->CreateConnection();
}
 
String ^ NetApiClr::ApiWrapper::GetData(String ^ key)
{
    return dataHelper->GetData(key);
}
  •  编写api入口

这个入库对外暴露了c语言类的api,以便三方程序可以像调用win32接口那样通过P/Invoke的方式调用这些api。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "stdafx.h"
#include "NetApiClr.h"
#include <msclr/gcroot.h>
 
using namespace System;
using namespace std;
using namespace Runtime::InteropServices;
 
ref class ManagedGlobals abstract sealed {
public:
    static NetApiClr::ApiWrapper ^ apiWrapper = gcnew NetApiClr::ApiWrapper();
};
 
extern  "C" _declspec(dllexport) bool _stdcall CreateConnection()
{
    return ManagedGlobals::apiWrapper->CreateConnection();
}
 
extern  "C" _declspec(dllexport) char* _stdcall GetData(char* cKey)
{
    String^ key = gcnew String(cKey);
    String^ strResult = ManagedGlobals::apiWrapper->GetData(key);
    char* cResult =
        (char*)(Marshal::StringToHGlobalAnsi(strResult)).ToPointer();
    return cResult;
}
  •  编写并配置DEF文件

模块定义或 DEF 文件 (*.def) 是一种文本文件,其中包含一个或多个描述 DLL 各种属性的模块语句。

NetApiClr.def

1
2
3
4
5
6
LIBRARY      "NetApiClr"
 
EXPORTS
    ; Explicit exports can go here
   CreateConnection @1
    GetData @2

 

配置DEF文件

项目右击->属性->配置属性->连接器->输入->在模块定义文件选择中填写你编写的描述文件

C++ demo

  • 定义api函数指针
1
2
typedef bool (WINAPI *pCreateConnection)();
typedef char* (WINAPI *pGetData)(char*);
  • 加载C++/CLR dll和函数
1
2
3
4
5
6
//load dll
HINSTANCE hInst=LoadLibrary(_T("NetApiClr.dll"));<br>//import api
pCreateConnection FuncCreateConnection = nullptr;
pGetData FuncGetData = nullptr;
FuncCreateConnection = (pCreateConnection)GetProcAddress(hInst, "CreateConnection");
FuncGetData = (pGetData)GetProcAddress(hInst, "GetData");
  • 调用函数
1
2
3
4
5
6
7
8
if (FuncCreateConnection())
{
    ::MessageBoxA(NULL, FuncGetData("abcd"), "Demo Code", MB_OK);
}
else
{
    ::MessageBoxA(NULL, "ERROR", "Demo Code", MB_OK);
}
  • 释放dll句柄
1
2
//free dll
FreeLibrary(hInst);

VB6.0 demo

  • 加载C++/CLR dll并定义api
1
2
Public Declare Function CreateConnection Lib "NetApiClr.dll" () As Boolean
Public Declare Function GetData Lib "NetApiClr.dll" (ByVal message As String) As String
  • 调用api
1
2
3
4
5
If CreateConnection Then
    MsgBox GetData("abcd")
Else
    MsgBox "Error"
End If

 

参考:

 

转载请注明出处: https://www.cnblogs.com/keitsi/p/17102345.html

posted @   keitsi  阅读(385)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示