调用非托管
介绍
我们编写的依赖于 .NET 框架的每段代码都称为托管代码。之所以称为托管代码,是因为框架提供了安全性和垃圾收集功能。但是,C# 为我们提供了调用非托管代码的可能性,这些代码要么隐藏在 DLL 中,要么完全用其他编程语言(如C或C++)编写。COM组件和Win32 API也属于非托管代码类别。
当 .NET 框架诞生时,需要提供对调用和集成非托管代码到 C# 应用程序的支持。这不仅适用于 C#,还适用于所有依赖 .NET 框架的语言。
这种支持以两种主要服务的形式提供。第一种是InteropServices,第二种是Platform Invoke Services。
有四个标准,您需要满足其中一个标准才能使用非托管代码。
- 识别 DLL 中的函数(指定函数名称和包含该函数的 DLL)
- 创建一个类来保存/分组 DLL
- 在托管代码中创建原型
- 调用 DLL 函数。
简单消息框
消息框来自 user32.dll 。它允许您显示一个带有标题和消息的小消息框。此DLL提供的功能和功能比消息框强大得多,但对于此演示来说,它应该足够了。
为了使用 DLL,我们需要使用以下行启动我们的应用程序。
using System.Runtime.InteropServices;
这为我们提供了能够加载指定 DLL 的DllImport函数。
DllImport("user32.dll")
此语句将 DLL 加载到我们的内存中。我们的命名空间几乎可以操作消息框并显示我们的消息了。
我们需要定义消息框的结构,它需要与 DLL 中的完全相同。
static extern int MessageBoxA(int hWnd, string strMsg,
string strCaption,uint iType);
hWnd将为我们提供窗口所有者的句柄。strMsg是我们想要在窗口中显示的消息;它是来自 DLL 的 LPCTSTR 类型。strCaption是我们消息框的标题,它与strMsg 的类型相同。最后,我们有iType ,它是一个无符号整数,它控制对话框的内容和行为。
iType变体的一些示例包括:
- 0x00000002L -> 为我们提供了中止、重试、忽略按钮。
- 0x00000006L -> 为我们提供取消、重试、忽略按钮。
- 0x00000004L -> 为我们提供“是”、“否”按钮。
完成这些之后,剩下要做的就是完成我们的Main函数并调用外部代码。
static void Main(string[] args)
{
MessageBoxA(0,
"Hello, Pluralsight!",
"Welcome to the written guides!",
Convert.ToUInt32(0x00000002L));
}
结果如下所示。
正如您从列表中回忆的那样,值0x00000002L会产生三个按钮:中止、重试和忽略。由于这是一个演示,我们并不真正关心单击这些按钮时会发生什么。但您需要知道您应该适当地处理每个事件,否则可能会导致应用程序崩溃。单击按钮时,消息框会返回一个整数,这就是您根据单击的按钮处理相关事件的方式。Convert.ToUInt32 ()函数是必需的,因为参数类型是unit,但文档将示例值保存在long数据类型中,而这些数据类型不能直接转换。
我们来看一个这些按钮的返回值的例子,我们需要将代码修改成这样:
int buttonPushed = MessageBoxA(0,
"Hello, Pluralsight!",
"Welcome to the written guides!",
Convert.ToUInt32(0x00000002L));
Console.WriteLine($"The button push resulted in value: {buttonPushed}");
按下“中止”按钮将产生以下输出。
The button push resulted in value: 3
按下“重试”按钮会得到以下输出。
The button push resulted in value: 4
最后,但并非最不重要的一点是,忽略给我们以下输出。
The button push resulted in value: 5
请注意开发人员常犯的一个错误:他们不阅读 API 文档,并且将不兼容或不隐式兼容的参数传递给来自非托管代码的函数或类。这会导致应用程序崩溃和许多麻烦。节省一些时间,务必阅读文档。
C++ 非托管代码包装器
本指南的这一部分将向您展示如何调用非托管 C++ 代码并围绕它构建项目。您需要在 Visual Studio 中安装用于 C++ 的 CLI 包,请注意,此示例仅在 Visual Studio 2017 中进行了测试。
我们需要创建一个名为Core的新项目;这应该是一个空的Visual C++项目。生成项目后,您需要右键单击项目转到属性。在常规选项卡上,将配置类型更改为静态库 (.lib)。然后,在C/C++ - 预编译头下,将预编译头设置为不使用预编译头。
现在,右键单击解决方案资源管理器中的Core ,并添加一个名为Worker.h的新头文件。这基本上是一个有薪水的工人。
#pragma once
namespace Core
{
class Worker
{
public:
const char* m_Name;
private:
float m_Salary;
public:
Worker(const char* name, float salary);
void raiseSalary(float amount);
inline float GetSalary() const { return m_Salary; };
};
}
We need another header file called Core.h. This is for further extensibility and you can skip creating this file without repercussions. But, if you do create it, make sure you include the following two lines in it.
#pragma once
#include "Worker.h"
Last, but not least, we need real code to power our app.
#include "Worker.h"
#include <iostream>
namespace Core
{
Worker::Worker(const char* name, float salary)
: m_Name(name), m_Salary(salary)
{
std::cout << "Successfully created Worker object!" << std::endl;
}
void Worker::raiseSalary(float amount)
{
m_Salary *= amount;
std::cout << "Raised salary of Worker" << m_Name << " to " << m_Salary << std::endl;
}
}
Now we need a new project. Right-Click on our project and add a new project called Wrapper. This should be a Class Library under Visual C++ - CLR.
First, we define our Worker.h header file.
#pragma once
#include "ManagedObject.h"
#include "../Core/Core.h"
using namespace System;
namespace CLI
{
public ref class Worker : public ManagedObject<Core::Worker>
{
public:
Worker(String^ name, float salary);
void raiseSalary(float amount);
property float Salary
{
public:
float get()
{
return m_Salary->GetSalary();
}
private:
void set(float value)
{
}
}
};
}
Our ManagedObject.h gives us the flexibility to further extend the app. It also provides a means for the destructor to the Garbage Collector to clean up after the app execution. This is a template that can be found on the internet, as it is very common to use it.
#pragma once
using namespace System;
namespace CLI {
template<class T>
public ref class ManagedObject
{
protected:
T* m_Instance;
public:
ManagedObject(T* instance)
: m_Instance(instance)
{
}
virtual ~ManagedObject()
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
!ManagedObject()
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
T* GetInstance()
{
return m_Instance;
}
};
}
using namespace System::Runtime::InteropServices;
static const char* string_to_char_array(String^ string)
{
const char* str = (const char*)(Marshal::StringToHGlobalAnsi(string)).<span class="h
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~