65.9K
CodeProject 正在变化。 阅读更多。
Home

一个超级简单的VC++ IDE开发的DLL,特别适合初次尝试者

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (21投票s)

2017年9月8日

CPOL

6分钟阅读

viewsIcon

23378

downloadIcon

319

VC++ IDE 中易于使用的 DLL

引言

大胆地打开你的Visual C++ IDE(我的版本是VS2013),敲几行C代码(甚至不是C++),创建一个超级简单的可用DLL,然后在小的C#应用程序中进行测试。

背景

如果你是一名“业务应用程序开发人员”很长一段时间(几十年?),你可能已经形成了一种感觉,即不要触碰“底层”代码。

“底层代码是用来构建操作系统、设备驱动程序、数据库引擎、3D模拟、实时武器控制器等等……”

“我可以利用Java、VB、C#甚至脚本来完成所有事情,只要有一个封装类提供对低级函数的访问即可。”

这些是非常普遍的想法。

有时候,你的好奇心会克服你的焦虑和恐惧。但无论你如何努力搜索,任何VC++的例子,即使是为初学者准备的,都会把你带入新的术语世界:Win32 API、.H(头文件)、MFC、ATL、消息循环、双重指针(**ppMyVar,这是什么玩意儿!)、COM+、STA、MTA…… 这是阻止许多人甚至懒得打开VC++ IDE的主要原因之一。

在这篇文章和示例代码中,我们永远不会触及这些术语,“超级简单”,我是认真的,伙计!让我们现在开始吧。

Using the Code

代码非常简单。我强烈建议你不要复制/粘贴。我建议你启动IDE,创建解决方案和文件,一行一行地手动敲入代码。这样,你将对此有更深刻的体会(并希望享受它)。

以下是步骤:

  1. 打开VS(我的版本是VS2013,其他版本应该非常相似)。创建一个新的Visual C++项目。按照我下面的红色标记进行操作(使用你自己的文件路径,但项目名称应为SuperEasyDLL)。

  2. 在新建的项目结构中,添加一个新项,如下所示:

  3. 新项是C++文件(.cpp)。只需按照我高亮显示的文本进行操作。

  4. 现在我们在这里敲入几行代码:

    (尽管我建议你自己敲代码,但还是以防万一你和我一样是个复制粘贴狂 ;-)

    int iMySuperEasyOutput;
    extern "C" __declspec(dllexport) void AddingFifty(int iInput);
    extern "C" __declspec(dllexport) int GetMySuperEasyOutput();
    
    void AddingFifty(int iInput)
    {
       iMySuperEasyOutput = iInput + 50;
    }
    
    int GetMySuperEasyOutput()
    {
       return iMySuperEasyOutput;
    }

    如上所示,没有类,没有构造函数,没有struct,没有头文件`#include`,没有其他任何东西,只有纯粹的基础代码。它所做的一切都非常简单:暴露两个函数。一个函数将输入整数加上50,另一个函数输出结果。

    是的。那些__declspec(dllexport)关键字用于导出函数。你必须确保导出的函数名称和参数与你在代码中实际实现的完全相同。

    看?我确实努力避免使用术语。

  5. 确保SuperEasyDLL被编译为“DLL”,而不是“EXE”。要做到这一点,右键单击项目,转到属性,然后选择“DLL”(如下图所示):

  6. 构建项目,你可以在debug文件夹中看到你的SuperEasyDLL.dll

  7. 现在让我们创建C# winform测试项目。命名为“SuperEasyDLLWinform”。一个虚拟的窗体,一个虚拟的按钮。甚至不要费心去更改默认名称。敲入以下三个红圈的代码,因为这三部分代码不是IDE生成的。

    (为了方便你,这里是三部分代码。)

    using System.Runtime.InteropServices;
    [DllImport("SuperEasyDLL.dll")] 
    public static extern int GetMySuperEasyOutput(); 
    [DllImport("SuperEasyDLL.dll", CallingConvention = CallingConvention.Cdecl)] 
    public static extern void AddingFifty(int iTestInput);
    //input is 50
    AddingFifty(50);
    int iResult = GetMySuperEasyOutput();
                
    //output is 100
    MessageBox.Show(iResult.ToString());

    现在我们可以看到,在我们的虚拟winform应用程序中,我们正在使用DLLImport关键字,这显然是在尝试使用我们在DLL中构建的导出函数。

  8. 确保winform应用程序的EXE能够轻松找到我们的DLL。只需将SuperEasyDLL.dll从步骤(6)复制到winform的debug文件夹(即SuperEasyDLLWinform.exe所在的位置)。

  9. 太棒了!它很聪明,知道如何加50!

按照以上9个步骤,几分钟内,你就完成了你的第一个VC++ IDE项目。

关注点

  1. 上面敲入的VC++代码是真正的C++吗?你可能会怀疑。不是,也不是。它没有使用任何C++特性,那些超级酷的面向对象的东西,对吧?没有。我选择在这个例子中不使用任何一个。“精简而高效”,这就是我所追求的。但是你知道吗,我听说有些专家说“C”是“C++”的子集,很多人都同意。所以从这个角度来看,上面的代码可以说是C++。
  2. 我们构建的DLL也可以称为“非托管DLL”、“原生DLL”。这意味着它没有任何.NET框架的参与(例如自动内存/对象垃圾回收)。它也没有任何MFC、ATL、COM+的参与。所以我称之为纯Win32 DLL。
  3. .NET C#应用程序调用非托管DLL/原生DLL有一个特定的术语:P/Invoke(平台调用)。如果你有兴趣,可以找到大量关于它的信息。对我来说,我只是自己称它为DllImport方法。
    以上提到的要点对于这篇入门文章来说已经足够了。下面,我将介绍几个“入门+”的点,如果这对你来说太多了,请跳过…
  4. 还记得我们在代码(C#和VC++)中看到的这个吗
    extern "C"

    这是告诉编译器,在编译代码时,描述符后面的函数应该使用C标准的名称修饰。什么是名称修饰?你倒吸一口凉气(因为我也这样做过,哈哈)。

    基本上,IDE中的任何函数名都对人类友好(因为我们自己命名的,当然!)。但编译器会以不同的方式编译它们。C++有自己的标准,C有自己的标准。所以为了保持简单(C#调用原生C DLL),我们告诉VS IDE为两个项目使用C标准的名称修饰。

    要查看编译后的修饰名称,你需要先打开Developer Command Prompt。这只是VS环境下的一个DOS命令行。

    在DOS提示符屏幕中,输入命令

    dumpin /exports "C:\data\projtest\SuperEasyDLL\Debug\SuperEasyDLL.dll" (请根据你的实际路径进行更改)。

    红色标记的是带有`extern "C"`名称修饰的导出名称。

    红色标记的是没有`extern "C"`名称修饰的导出名称。

  5. 在我们的小Winform C#代码中,你可以看到这个:
    [DllImport("SuperEasyDLL.dll", CallingConvention = CallingConvention.Cdecl)]

    CallingConvention.Cdecl”是告诉编译器,这个函数调用将从C#客户端清除堆栈。我猜编译器编译后会在周围添加一些样板二进制代码。我不是专家,我也不太清楚。我真正知道的是,如果没有这个,应用程序会弹出错误,抱怨某种堆栈问题。

人们常说从0到1是一个大跨步,但我们只是从0到0.1。

但是,嘿,一小步也是前进的一步。

希望你动手实践了,并且像我一样喜欢它。

我的下一篇面向想了解更多VC++内容的初学者文章在这里:

历史

  • 2017年9月8日:初始版本
© . All rights reserved.