• 在C#调用C++的DLL方法(二)生成托管的DLL


    写操作之前,还是扼要的说一下托管与非托管C++的区别好了,其实我也并没有深入了解过托管C++的特点所在,其最大的特征就是可以由系统来调试回收相关的代码资源,跟C#的特性一样,只是编程风格跟C++类似而已,因此,这决定了C#与托管C++是可以完美结合在一起的。托管C++生成的dll跟C#生成的dll应该说是没区别的,之所以产生托管C++这种怪物,完全是因为微软在极力推崇C#,必须要兼顾不同语言间交互。

    好吧,接下来正经的写一下过程。先摆出目的:我手上有一个C++写的类(NativeClass),想在C#下调用这个类,可是C#是没有简单的像Dllimport这样的方法获取非托管C++ dll里的类。我的解决方法是,生成一个托管C++的dll,然后在C#下引用这个dll。因为托管代码与非托管代码是不能在一个文件里混编的,所以我必须将非托管C++写的NativeClass用托管C++的手段封装一下,然后生成一个dll,以供C#调用。


    下面直接方法步骤:

    一、建立CLR类库工程

    image


    二、一个非托管C++的例子

    我手上有一个用非托管C++写的类NativeClass,它本身是属于另外一个非托管C++工程,现在我直接将这个类文件拷贝到本工程的目录下去。如果是其他比较大型的类,必要将NativeClass.h里#include到的其他文件也一并拷贝到本新建工程目录下,然后将这些文件添加到VS的资源管理器下,这里的意思是非托管C++中的include的文件,不是说托管C++中的。如下图所示:

    image

    上图中,除了NativeClass.h和NativeClass.cpp文件是我添加进去的,其他都是工程自带的东西,其中ManageClass.h及ManageClass.cpp是要生成dll所动用到的东西。暂时先不管,我们看一下NativeClass.h和NativeClass.cpp里的内容:

    NativeClass.h代码:

    #pragma once 
    
    class NativeClass 
    { 
    private:                                                                                                                                                                                                                                                                                                                                                                                                                       
        int nCount; 
    
    public: 
        NativeClass(void); 
        ~NativeClass(void);
        int GetCount(void); 
        void Increase(void); 
        void Clear(void);
    };

    NativeClass.cpp代码:

    #include "stdafx.h"
    #include "NativeClass.h"
    
    NativeClass::NativeClass(void) 
    { 
        this->nCount = 0; 
    } 
    NativeClass::~NativeClass(void) 
    { 
    } 
    int NativeClass::GetCount(void) 
    { 
        return this->nCount; 
    } 
    void NativeClass::Increase(void) 
    { 
        this->nCount++; 
    } 
    void NativeClass::Clear(void) 
    { 
        this->nCount = 0; 
    }

    非托管C++的类很简单。


    三、封装成托管C++的内容

    这一步是很关键的,之所以有这么一步,是因为托管C++与非托管C++没法混编,于是乎我用托管代码将上面的NativeClass类封装了一下,本来按规范而言我应该将函数声明与实现分开写,但为了简便,只在ManageClass.h里作修改,虽然没有用到ManageClass.cpp,但无论如何也别将这个文件删除,否则是没法生成dll的。我的封装代码如下:

    // ManageClass.h 
    #pragma once 
    #include "NativeClass.h" 
    using namespace System; 
    namespace ManageClass { 
        public ref class NativeClassEx 
        { 
            // TODO: 在此处添加此类的方法。 
        private: 
            NativeClass * m_pnClass; 
        public: 
            NativeClassEx(void) 
            { 
                this->m_pnClass = new NativeClass(); 
            } 
            ~NativeClassEx(void) 
            { 
                delete this->m_pnClass; 
            } 
            int GetCount(void) 
            { 
                return this->m_pnClass->GetCount(); 
            } 
            void Increase(void) 
            { 
                this->m_pnClass->Increase(); 
            } 
            void Clear(void) 
            { 
                this->m_pnClass->Clear(); 
            } 
        protected: 
            !NativeClassEx(void) 
            { 
                delete this->m_pnClass; 
            } 
        }; 
    }

    四、生成托管C++的dll

    其实到了这一步就结了,你直接点编译,就会在解决方案目录下的Debug文件夹里生成ManageClass.dll了,务必要看清,经过封装后,我新的类名是叫NativeClassEx,请在使用时注意一下。


    五、项目测试dll

    调用托管C++的dll跟调用C#的dll没任何区别,新建一个测试工程(我用的是WinForm的窗体工程),名字叫DllTest,在解决方案资源管理器里将刚刚生成的那个ManageClass.dll添加到引用里,使用using ManageClass,然后你就可以用了,其测试代码就几句话:

    NativeClassEx testCalss = new NativeClassEx(); 
    Debug.WriteLine("GetCount : " + testCalss.GetCount().ToString()); 
    testCalss.Increase(); 
    testCalss.Increase(); 
    testCalss.Increase(); 
    Debug.WriteLine("GetCount : " + testCalss.GetCount().ToString()); 
    testCalss.Clear(); 
    Debug.WriteLine("GetCount : " + testCalss.GetCount().ToString());

    编译一下,看输出窗口,类还是完美运行得了的。


    六、注意事项

    1、尽管C#与托管C++很大程度上兼容,但还是要注意基本类型外的对齐问题,像结构体、string类这些,最好入口参数除了基本类型其他都别用,这点请参考我上一篇文章;

    2、我尝试用托管C++封装我写OpenCV类,类里再调用了OpenCV的dll(即C#调用托管dll,托管dll调用非托管dll),编译通过,但实际运行不行,里面有什么问题暂时不清楚;

    3、建议,没什么事别用这种方法来调用类,C#中调用dll的函数才是最具保障的。

    4、用前记得先编译好dll,并确保添加了引用,可能会有一些关于CPU类型选择的warning,请诸位自力更生了。

  • 相关阅读:
    C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
    C#进阶系列——WebApi 接口测试工具:WebApiTestClient
    Web API在OWIN下实现OAuth
    C#进阶系列——WebApi 跨域问题解决方案:CORS
    C#进阶系列——WebApi 身份认证解决方案:Basic基础认证
    C#进阶系列——WebApi 异常处理解决方案
    python标准库介绍——13 types 模块详解
    python标准库介绍——12 time 模块详解
    python标准库介绍——11 atexit 模块详解
    python标准库介绍——10 sys 模块详解
  • 原文地址:https://www.cnblogs.com/stemon/p/4246165.html
Copyright © 2020-2023  润新知