您的位置:1010cc时时彩经典版 > 1010cc时时彩经典版 > 使用mfc扩大dll完结插件效果,程序中落到实处插

使用mfc扩大dll完结插件效果,程序中落到实处插

发布时间:2019-10-01 03:39编辑:1010cc时时彩经典版浏览(57)

    golangplugin功能用的比较少,官方的示例只有返回函数,而没有返回对象。但是实际应用中OOP可以极大提高程序的质量,经过我自己试验,发现plugin是可以返回对象的。

    概述

    本文要解决的问题是,使用mfc设计具有对话框界面的程序,并且支持插件(数量不限),并且每个插件都可以有自己的界面,并且主程序和插件之间要能(通过接口)双向传递数据。

    在.NET框架下的C#语言,和其他.NET语言一样提供了很多强大的特性和机制。其中一些是全新的,而有些则是从以前的语言和平台上照搬过来的。然而,这种巧妙的结合产生了一些有趣的方法可以用来解决我们的问题。这篇文章将讲述如何利用这些奇妙的特性,用插件(plug-ins)机制建立可扩展的解决方案。后面也将提供一个简要的例子,你甚至可以用这个东西来替换那些已经在很多系统中广泛使用的独立的程序。在一个系统中,可能有很多程序经常需要进行数据处理。可能其中有一个程序用于处理雇员的信息,而另一个用来管理客户关系。

        我们在前面的文章中提到了两中实现晚绑定的方式,那么在用的时候是否发现有什么不同呢?

    Kotlin 知识梳理系列文章

    Kotlin 知识梳理(1) - Kotlin 基础
    Kotlin 知识梳理(2) - 函数的定义与调用
    Kotlin 知识梳理(3) - 类、对象和接口
    Kotlin 知识梳理(4) - 数据类、类委托 及 object 关键字
    Kotlin 知识梳理(5) - lambda 表达式和成员引用
    Kotlin 知识梳理(6) - Kotlin 的可空性
    Kotlin 知识梳理(7) - Kotlin 的类型系统
    Kotlin 知识梳理(8) - 运算符重载及其他约定
    Kotlin 知识梳理(9) - 委托属性
    Kotlin 知识梳理(10) - 高阶函数:Lambda 作为形参或返回值
    Kotlin 知识梳理(11) - 内联函数
    Kotlin 知识梳理(12) - 泛型类型参数


    返回类型

    我试验的返回对象的类型是空接口,然后用类型断言转换成含有一组约定方法的接口。

    开发环境

    windows 10

    Visual Studio 2010

    在大多数情况下,系统总是被设计为很多个独立的程序,他们之间很少有交互,经常使用复制代码的办法来共享。而实际上这样的情况可以把那些程序设计为插件,再用一个单一的程序来管理这些插件。这种设计可以让我们更好的在不同的解决方案中共享公用的方法,提供统一的感观。

        是的,我们会很容易的发现Activator.CreateInstance()创建的对象,我们只能访问他的访问级别为public的方法,但是我们仅仅小小的统一下手脚,使用BindingFlags.NonPublic|BindingFlags.Instance就可以获得该对象的internal,private级别的变量。但是往往后一种方式我们也是不经常采取的,因为我们需要稳定的成员支持,而私有类型的成员往往是程序中最容易更新的,假如一个程序集更新后,我们使用这种方式调用即会发生错误。

    一、本文概要

    本文是对<<Kotlin in Action>>的学习笔记,如果需要运行相应的代码可以访问在线环境 try.kotlinlang.org,这部分的思维导图为:

    图片 1

    Kotlin中,我们可以通过 调用自己代码中定义的函数,来实现 特定语言结构。这些功能与 特定的函数命名 相关,而不是与特定的类型绑定。例如,如果在你的类中定义了一个名为plus的特殊方法,那么按照约定,就可以在该类的实例上使用 运算符,这种技术称为 约定

    因为由类实现的接口集是固定的,而Kotlin不能为了实现其他接口而修改现有的类,因此一般 通过扩展函数的机制 来为现有的类增添新的 约定方法,从而适应任何现有的Java类。

    实例

    我们首先在主程序中定义一个约定返回接口:

    type ResObj interface { Say int}
    

    插件中的对象定义和返回接口的函数:

    //对象定义type ObjType struct { //内部封装数据 Name string}//Say 方法的实现func (p *ObjType) Say int { fmt.Printf("%s say: %sn", p.Name, s) return len}//返回函数func New(arg string) interface{}{ return &ObjType{Name:arg}}
    

    在主程序中调用plugin

     p, err := plugin.Open(pluginPathName) if err != nil { panic } f, err := p.Lookup if err != nil { panic } fn, ok := f.(func interface{}) if ok == false { panic("func type error: New.") } //New 返回空接口 obj0 := fn //把空接口转换成`ResObj`接口 obj1, ok := obj0. if ok == false { panic("Type error: ResObj.") } //调用接口的方法 obj1.Say("Hello friend!")
    

    结束

    解决方案

    主程序为任意mfc应用程序,插件为mfc扩展dll,动态链接。插件提供创建和销毁窗口的接口函数,创建接口创建窗口对象后将窗口指针或句柄返回给主程序,主程序使用完毕后调用销毁接口销毁窗口对象。主程序和插件之间使用消息或额外的接口函数传递数据。

    图片一是一个例子程序的截图。用户界面和其他常见的程序没有什么不同。整个窗体被垂直的分割为两块。左边的窗格是个树形菜单,用于显示插件列表,在每个插件的分支下面,列出了这个插件所管理的数据。而右边的窗格则用于编辑左边被选中的插件的数据。各个插件提供各自的编辑数据的界面。图片一展示了一个精巧的工作区。

        今天刚刚过完五一长假回学校(自己给自己放了十天假,哈哈),今天就和同学们一起学习一下插件编程,既然上面我提到了基本安全(稳定)的方式,就一定会用到Activator.CreateInstance,今天我在博客里一共做两个试验,一个是Winfrom的插件体系的构建,一个是控制台的插件体系的构建,当然都是很简单的那种。

    二、重载算术运算符

    Kotlin中,使用约定的最直接的例子就是 算术运算符,在Java中,全套的算术运算符只能用于基本数据类型, 运算符可以与String一起使用。下面,我们看一下在Kotlin中,如何使用算术运算符来完成一些其它的事情。

    操作步骤

    本章将创建一个例程,可动态加载插件。

    开始

    1.Winfrom小实验(实验要求:)

    2.1 重载二元运算符

    假设已经有一个数据类Point,它包含两个成员变量,分别是x,y点的坐标值,我们希望通过算术运算符 对两个Point对象相加之后,能够得到一个新的Point对象,它的成员变量x,y为原有两个Point对象的x,y之和。

    图片 2

    运行结果为:

    图片 3

    在上面的代码中,我们为Point类定义了一个扩展函数plus,这样当我们调用first second,实际上执行的是first.plus(second)方法来得到一个新的Point对象。这里需要注意的是:用于重载运算符的所有函数都需要 用 operator 关键字来标记,用来表示你打算 把这个函数作为相应的约定的实现

    所有可重载的二元算术运算符如下,自定义类型的运算符,基本上和标准数字类型的运算符有着相同的优先级。

    • a * btimes
    • a / bdiv
    • a % bmod
    • a bplus
    • a - bminus

    1、创建工程

    主程序为任意mfc应用程序,插件为mfc扩展dll。

    图片 4

    图片 5

    图片 6

    笔者在制作这个例程时将主程序做成了基于对话框的程序,并且将主程序和插件放入了同一个解决方案里,但这些都不是必须的。

    那么,主程序必须能够加载插件,然后和这些插件进行通信,这样才能实现我们的设计。所有这些的实现可以有很多不同的方法,仅取决于开发者选择的语言和平台。如果选择的是C#和.NET,那么反射(reflection)机制可以用来加载插件,并且其接口和抽象类可以用于和插件通信。

    1. 动态的加载菜单项

    2. 动态的绑定菜单单击事件

    运算符函数和 Java

    • 当从Java调用Kotlin运算符非常容易,只需要像普通函数一样调用即可,例如上面的plus方法。
    • 当从Kotlin调用Java的时候,对于与Kotlin约定匹配的函数(不要求使用operator修饰符,但是参数需要匹配名称和数量)都可以使用运算符语言来调用。如果Java类定义了一个满足需求的函数,但是起了一个不同的名称,可以通过定义一个扩展函数来修正这个函数名用来替代现有的Java方法。

    2、在插件中定义继承CWnd的类,实现插件的界面显示功能

    先在资源中添加formview类型的资源,资源ID使用默认的IDD_FORMVIEW。

    图片 7

    然后资源上右键添加类,得到一个继承CDialogEx的类,类名我设的是TestDlg。

    图片 8

    下面是vs自动生成的TestDlg.h

    #pragma once
    #include "Resource.h"
    
    // TestDlg 对话框
    
    class TestDlg : public CDialogEx
    {
        DECLARE_DYNAMIC(TestDlg)
    
    public:
        TestDlg(CWnd* pParent = NULL);   // 标准构造函数
        virtual ~TestDlg();
    
    // 对话框数据
        enum { IDD = IDD_FORMVIEW };//louObaichu:如果在这行报错提示IDD_FORMVIEW未定义,则需要#include "Resource.h"
    
    protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    
        DECLARE_MESSAGE_MAP()
    };
    

    再然后在资源中添加控件,修改TestDlg的代码,实现插件的功能。例程只有一个静态控件,显示插件的名字。

    图片 9

    上述步骤可以满足一般需求,但实际上资源的类型并不限于formview,甚至资源都不是必须的,如有特殊需要完全可以直接定义继承CWnd的类。

    为了更好的理解主程序和插件之间的通信,可以先了解一下设计模式。设计模式最早由Erich Gamma提出1,它利用架构和对象思想来实现通用的通信模型。不管组件是否具有不同的输入和输出,只要他们有相似的结构。设计模式可以帮助开发者利用广受证明的面向对象理论来解决问题。事实上它就是描述解决方案的语言,而不用管问题的具体细节或者编程语言的细节。设计模式策略的关键点在于如何把整个解决方案根据功能来分解,这种分解是通过把主程序的不同功能分开执行而完成的。这样主程序和子程序之间的通信可以通过设计良好的接口来完成。通过这种分解我们立即可以得到这两个好处:第一,软件项目被分成较小的不相干的单位,工作流程的设计可以更容易,而较小的代码片断意味着代码更容易建立和维护。第二个好处在于改变程序行为的时候并不会关系到主程序的运行,主程序不用关心子程序如何,他们之间只要有通用的通讯机制就足够了。

    现在我们来整理一下思路:动态的加载当然需要使用反射,前面的文章我们提到了先要加载程序集,然后获得程序集的相关类型的对象,通过对象来访问相应的方法,现在我们可以确定应该是主程序(我在这里称之为宿主程序)下载插件程序集,但是问题来了,即便是加载了我们又怎么去约束我们要实用的插件方法呢。难道所有的插件方法都需要吗?当然不是,这时候我们就用到了插件接口来约束,也就是为插件程序来制定调用规则。

    没有用于位运算的特殊运算符

    Kotlin没有为标准数字类型IntLong等定义任何位运算符,因此也不允许你为自定类型定义它们。相反,它使用中缀调用语法的函数,可以为自定义类型定义相似的函数,下面我们为Point添加一个and,用于执行位运算。

    图片 10

    运行结果为:

    图片 11

    这里我们不再使用operator关键字来声明,而是用infix来定义一个中缀调用语法的函数,其它执行位运算的函数包括:shlshrushrandorxorinv

    3、在插件中定义接口函数,并声明为dll导出函数

    <pre name="code" class="cpp">// Plugin1.cpp : 定义 DLL 的初始化例程。
    #include "stdafx.h"
    #include "TestDlg.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    extern HINSTANCE g_hDll;//DLL的模块句柄。可在DllMain中获得。
    
    extern "C" __declspec(dllexport)
    CWnd* DLLAPI_Create(CWnd *pobjWnd)
    {//创建一个插件,以pobjWnd为父窗口,返回其指针。
        HINSTANCE hRes=AfxGetResourceHandle();
        TestDlg *pobjNew=new TestDlg();
    
        AfxSetResourceHandle(g_hDll);//设置当前资源模块句柄。如果不设置且不同模块间资源ID有冲突,则pobjNew无法正确创建。
        pobjNew->Create(TestDlg::IDD,pobjWnd);
        AfxSetResourceHandle(hRes);
        return pobjNew;
    }
    
    extern "C" __declspec(dllexport)
    int DLLAPI_Destroy(CWnd *pobjWnd)
    {
        TestDlg *pobjDes=(TestDlg *)pobjWnd;
        pobjDes->DestroyWindow();
        delete pobjDes;
        return 0;
    }
    

    例程的销毁接口将pobjWnd转换成了子类再操作,也可以直接操作pobjWnd,但必须确保所有被重写的操作在TestDlg的所有父类中都定义为虚函数。

    如果有多个插件,所有插件的接口函数原型和名字必须相同。主程序加载插件时也必须指定同样的函数原型和名字。

    建立接口

    现在我们已经有了个大概思路,改程序该分为三部分:宿主程序,约定接口,插件程序。

    2.2 重载复合赋值运算符

    当在定义像plus这样的函数,Kotlin不止支持 号运算,也支持像 =这样的 复合赋值运算符

    图片 12

    需要注意,这个只对于可变变量有效,也就是first要声明为var。在一些情况下,定义 =运算符可以 修改使用它的变量所引用的对象,但不会重新分配引用,将一个元素添加到可变集合,就是一个很好的例子:

    图片 13

    如果你定义了一个返回值为Unit,名为plusAssign的函数,Kotlin将会在用到 =运算符的地方使用它,其它二元运算符也有命名相似的对应函数:minusAssigntimesAssign等。

    当在代码中用到 =的时候,理论上plusplusAssign都可能会被调用,如果两个函数都有定义并且适用,那么编译器就会报错,例如下面这样的定义:

    图片 14

    编译时的错误为:

    图片 15

    解决方法有两种:

    • 使用 不可变 val 代替可变 var 来修饰first,这样plus运算符就不再适用。
    • 不要同时为一个类添加plusplusAssign运算。如果一个类是 不可变的,那就应该只提供返回一个新值的运算;如果一个类是 可变的,例如构建器,那么只需要提供plusAssign和类似的运算符就够了。

    Kotlin的标准库支持集合的这两种方法:

    • -运算符总是返回一个新的集合
    • =-=运算符用于可变集合时,始终在一个地方修改它们;而它们用于只读集合时,会返回一个修改过的副本。

    作为它们的运算数,可以使用单个元素,也可以使用元素类型一致的其它集合:

    图片 16

    运行结果为:

    图片 17

    4、主程序加/卸载插件

    当主程序需要加载插件时,使用LoadLibrary和GetProcAddress函数获得接口函数的指针。需要显示插件界面时,通过创建接口创建插件,再使用CWnd的方法调整其位置和显隐。同理可通过销毁接口销毁插件,使用FreeLibrary卸载dll。

    例程通过控件输入插件的路径名,再加载插件并显示。下面是例程窗口类的源码:

    // MainProDlg.h : 头文件
    //
    #pragma once
    #include "afxeditbrowsectrl.h"
    #include "afxwin.h"
    // CMainProDlg 对话框
    class CMainProDlg : public CDialogEx
    {
    // 构造
    public:
        CMainProDlg(CWnd* pParent = NULL);  // 标准构造函数
    
    // 对话框数据
        enum { IDD = IDD_MAINPRO_DIALOG };
    
        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    // 实现
    protected:
        HICON m_hIcon;
    
        // 生成的消息映射函数
        virtual BOOL OnInitDialog();
        afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
        afx_msg void OnPaint();
        afx_msg HCURSOR OnQueryDragIcon();
        DECLARE_MESSAGE_MAP()
    public:
        HMODULE m_hPlugin;
        CWnd* (*m_pfunCreate)(CWnd *pobjWnd);
        int (*m_pfunDestroy)(CWnd *pobjWnd);
        CWnd *m_pobjPlugin;//插件的界面
    
        int LoadPlugin(CString strPlugin);
        int FreePlugin();
        int ResetCtrls(int iWidth,int iHeight);
    
        CMFCEditBrowseCtrl m_EditPlugin;
        CButton m_ButtonLoad;
        afx_msg void OnBnClickedButton1();
        afx_msg void OnSize(UINT nType, int cx, int cy);
        afx_msg void OnDestroy();
    };
    
    
    
    // MainProDlg.cpp : 实现文件
    //
    #include "stdafx.h"
    #include "MainPro.h"
    #include "MainProDlg.h"
    #include "afxdialogex.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    #define HEIGHT 30
    #define WIDTH  60
    // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
    class CAboutDlg : public CDialogEx
    {
    public:
        CAboutDlg();
    
    // 对话框数据
        enum { IDD = IDD_ABOUTBOX };
    
        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    
    // 实现
    protected:
        DECLARE_MESSAGE_MAP()
    };
    
    CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
    {
    }
    
    void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
    }
    
    BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
    END_MESSAGE_MAP()
    // CMainProDlg 对话框
    CMainProDlg::CMainProDlg(CWnd* pParent /*=NULL*/)
        : CDialogEx(CMainProDlg::IDD, pParent)
    {
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
        m_pobjPlugin=NULL;
        m_hPlugin=NULL;
        m_pfunCreate=NULL;
        m_pfunDestroy=NULL;
    }
    
    void CMainProDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
        DDX_Control(pDX, IDC_MFCEDITBROWSE1, m_EditPlugin);
        DDX_Control(pDX, IDC_BUTTON1, m_ButtonLoad);
    }
    
    BEGIN_MESSAGE_MAP(CMainProDlg, CDialogEx)
        ON_WM_SYSCOMMAND()
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_BN_CLICKED(IDC_BUTTON1, &CMainProDlg::OnBnClickedButton1)
        ON_WM_SIZE()
        ON_WM_DESTROY()
    END_MESSAGE_MAP()
    // CMainProDlg 消息处理程序
    BOOL CMainProDlg::OnInitDialog()
    {
        CDialogEx::OnInitDialog();
    
        // 将“关于...”菜单项添加到系统菜单中。
    
        // IDM_ABOUTBOX 必须在系统命令范围内。
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);
    
        CMenu* pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != NULL)
        {
            BOOL bNameValid;
            CString strAboutMenu;
            bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
            ASSERT(bNameValid);
            if (!strAboutMenu.IsEmpty())
            {
                pSysMenu->AppendMenu(MF_SEPARATOR);
                pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
            }
        }
    
        // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
        //  执行此操作
        SetIcon(m_hIcon, TRUE);         // 设置大图标
        SetIcon(m_hIcon, FALSE);        // 设置小图标
    
        // TODO: 在此添加额外的初始化代码//[
        CRect objRect;
        this->GetClientRect(objRect);
        this->ResetCtrls(objRect.Width(),objRect.Height());
        m_EditPlugin.EnableFileBrowseButton(NULL,L"动态链接库 (*.dll)|*.dll|所有文件 (*.*)|*.*||");
        //]
        return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
    }
    
    void CMainProDlg::OnSysCommand(UINT nID, LPARAM lParam)
    {
        if ((nID & 0xFFF0) == IDM_ABOUTBOX)
        {
            CAboutDlg dlgAbout;
            dlgAbout.DoModal();
        }
        else
        {
            CDialogEx::OnSysCommand(nID, lParam);
        }
    }
    // 如果向对话框添加最小化按钮,则需要下面的代码
    //  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
    //  这将由框架自动完成。
    void CMainProDlg::OnPaint()
    {
        if (IsIconic())
        {
            CPaintDC dc(this); // 用于绘制的设备上下文
    
            SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    
            // 使图标在工作区矩形中居中
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon   1) / 2;
            int y = (rect.Height() - cyIcon   1) / 2;
    
            // 绘制图标
            dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
            CDialogEx::OnPaint();
        }
    }
    //当用户拖动最小化窗口时系统调用此函数取得光标
    //显示。
    HCURSOR CMainProDlg::OnQueryDragIcon()
    {
        return static_cast<HCURSOR>(m_hIcon);
    }
    
    void CMainProDlg::OnBnClickedButton1()
    {
        // TODO: 在此添加控件通知处理程序代码
        int i;
        CString strPlugin;
    
        m_EditPlugin.GetWindowText(strPlugin);
        FreePlugin();
        i=this->LoadPlugin(strPlugin);
        if(i<0) this->MessageBox(L"error!");
    
        return;
    }
    
    int CMainProDlg::LoadPlugin(CString strPlugin)
    {
        int iRet=-1,i;
        CRect objRect;
    
        this->GetClientRect(objRect);
    
        m_EditPlugin.GetWindowText(strPlugin);
        m_hPlugin=LoadLibrary(strPlugin);
        if(m_hPlugin==NULL) goto _Exit;
    
        m_pfunCreate=(CWnd* (*)(CWnd *pobjWnd))GetProcAddress(m_hPlugin,"DLLAPI_Create");
        m_pfunDestroy=(int (*)(CWnd *pobjWnd))GetProcAddress(m_hPlugin,"DLLAPI_Destroy");
        if(m_pfunCreate==NULL||m_pfunDestroy==NULL) goto _Exit;
    
        m_pobjPlugin=m_pfunCreate(this);
        this->ResetCtrls(objRect.Width(),objRect.Height());
        m_pobjPlugin->ShowWindow(1);
    
        iRet=0;
    _Exit:
        if(iRet<0) FreePlugin();
        return iRet;
    }
    
    int CMainProDlg::FreePlugin()
    {
        int iRet=0,i;
    
        if(m_hPlugin==NULL) goto _Exit;
    
        if(m_pobjPlugin)
        {
            m_pobjPlugin->DestroyWindow();
            m_pfunDestroy(m_pobjPlugin);
            m_pobjPlugin=NULL;
        }
        FreeLibrary(m_hPlugin);
        m_hPlugin=NULL;
        m_pfunCreate=NULL;
        m_pfunDestroy=NULL;
    
    _Exit:
        return iRet;
    }
    
    void CMainProDlg::OnSize(UINT nType, int cx, int cy)
    {
        CDialogEx::OnSize(nType, cx, cy);
        // TODO: 在此处添加消息处理程序代码//[
        ResetCtrls(cx,cy);
        //]
    }
    
    int CMainProDlg::ResetCtrls(int iWidth,int iHeight)
    {
        if(m_EditPlugin.GetSafeHwnd())
        {
            if(iWidth>WIDTH&&iHeight>HEIGHT)
            {
                m_EditPlugin.MoveWindow(0,0,iWidth-WIDTH,HEIGHT);
                m_EditPlugin.ShowWindow(1);
            }
            else m_EditPlugin.ShowWindow(0);
        }
        if(m_ButtonLoad.GetSafeHwnd())
        {
            if(iWidth>WIDTH&&iHeight>HEIGHT)
            {
                m_ButtonLoad.MoveWindow(iWidth-WIDTH,0,WIDTH,HEIGHT);
                m_ButtonLoad.ShowWindow(1);
            }
            else m_ButtonLoad.ShowWindow(0);
        }
        if(m_pobjPlugin)
        {
            if(iWidth>WIDTH&&iHeight>HEIGHT)
            {
                m_pobjPlugin->MoveWindow(0,HEIGHT,iWidth,iHeight);
                m_pobjPlugin->ShowWindow(1);
            }
            else m_pobjPlugin->ShowWindow(0);
        }
        return 0;
    }
    
    void CMainProDlg::OnDestroy()
    {
        CDialogEx::OnDestroy();
        // TODO: 在此处添加消息处理程序代码//[
        FreePlugin();
        //]
    }
    

    在C#程序中,接口是用来定义一个类的功能的。接口定义了预期的方法,属性,事件信息。为了使用接口,每个具体的函数必须严格按照接口的定义完成所描述的功能。列表一展示了上面例子程序的接口:IPlug。这个接口定义了四个方法:GetData,GetEditControl,Save和Print。这四个定义并没有描述具体是怎么完成的,但是他们保证了这个类支持IPlug接口,也就是保证支持这些方法的调用。

    为了我们在今后可以编译管理我们将一些有关管理插件的程序集都已XML的方式进行管理,这样将不会遗漏和便于修改。

    2.3 重载一元运算符

    重载一元运算的过程和前面看到的方式相同:用预先定义的一个名称来声明函数,并用修饰符operator标记。下面的例子中重载了-a运算符:

    图片 18

    运行结果为:

    图片 19

    所有可重载的一元算法运算符包括:

    • aunaryPlus
    • -aunaryMinus
    • !anot
    • a/a inc
    • --a/a--dec

    当你定义incdec函数来重载自增和自减的运算符时,编译器自动支持与普通数字类型的前缀、后缀自增运算符相同的语义。例如后缀运算会先返回变量的值,然后才执行 操作。

    5、编译生成插件和主程序

    运行结果如图所示:

    图片 20

    定制属性

    1 <Assemblypaths> 
    2     <oper type="toUpper" path="G:C#测试程序集toUpper.dll"/>
    3     <oper type="toLower" path="G:C#测试程序集toLower.dll"/>
    4 </Assemblypaths> 
    

    三、重载比较运算符

    与算术运算符一样,在Kotlin中,可以对任何对象使用比较运算符(==!=><),而不仅仅限于基本数据类型。

    一个DLL包含多个插件

    使用mfc扩展dll可以实现此效果,但必须设计更复杂的接口。最简单的做法是,增加一个load接口,主程序加载dll完毕后调用该接口函数获得所有插件的信息,同时创建接口增加一个参数,指明插件的标识符。因为所有插件都继承CWnd,所以不影响使用。

    ---------------------------EOB-------------------------

    在查看代码之前,讨论总是先得转移到属性定制上面.属性定制是.NET提供的一个非常棒的新特性之一,属性对于所有的编程语言都是一种通用的结构.举个例子,一个函数用于标识可访问权限的public,private,或者protect标志就是这个函数的一个属性.属性定制之所以如此让人兴奋,那是因为编程人员将不再只能从语言本身提供的有限的属性集中选择.一个定制的属性其实也是一个类,它从System.Attribute继承,它的代码被允许是自我描述的.属性定制可以应用于绝大多数结构中,包括C#里面的类,方法,事件,域和属性等等.示例代码片断定义了两个定制的属性:PlugDisplayNameAttribute和PlugDescriptionAttribute,所有的插件内部的类必须支持这两个属性.列表二是用于定义PlugDisplayNameAttribute的类.这个属性用于显示插件节点的内容.在程序运行的时候,主程序将可以利用反射(reflection)来取得属性值.

    在这个Xml文件里保存着插件实现接口成员的功能以及插件的保存路径。
    到了这里我们肯定要定义一个约定接口程序集了,具体实现如下:

    3.1 等号运算符,equals

    如果在Kotlin中使用==/!=运算符,它将被转换成equals方法的调用,和其他运算符不同的是,==!=可以用于可空运算数,比较a == b会检查a是否为飞空,如果不是就调用a.equals(b),完整的调用如下所示:

    a?.equals(b) ?: (b == null)
    

    对于data修饰的数据类,equals的实现将会由编译器自动生成,如果需要手动实现,可以参考下面的做法:

    图片 21

    • 比较是否指向同一对象的引用,如果是,那么直接返回true
    • 类型如果不同,直接返回false
    • 比较作为判断依据的字段

    equals函数之所以被标记为override,这是因为这个方法的实现是在Any类中定义的,而operator关键字在基本方法中已经标记了。同时,equals不能实现为扩展函数,因为继承自Any类的实现始终优先于扩展函数。

    本文由1010cc时时彩经典版发布于1010cc时时彩经典版,转载请注明出处:使用mfc扩大dll完结插件效果,程序中落到实处插

    关键词: