MFC 调用静态链接 MFC 的规则 DLL

简语:

最近学习了生成静态链接的dll及其调用,写一下笔录和大家分享,有错误的地方欢迎大家指出来

开发环境

VS2015

开发语言

C++

开发步骤

以mfc的dll创建为例,先说明一下win32的dll和mfc的dll在支持C上,win32可能比较好,实现的过程是一样的。

构建MFC的DLL项目

  • 新建MFC DLL项目


    image.png
  • 这边选择带静态链接MFC的规则DLL


    image.png
  • 默认生成了,头文件和源文件,右键def文件


    image.png
  • MyDLL.h 个人是将整个类导出,也可以单独导出某个或者几个函数
    导出函数是 :__declspec(dllimport) void(类型) __stdcall 函数名
    导出类具体参考msdn
// MyDLL.h : MyDLL DLL 的主头文件
#pragma once
#ifndef __AFXWIN_H__
    #error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif

#include "resource.h"       // 主符号

#define EXPORT __declspec(dllimport)  //这边是导出的宏定义


/**
* 接口测试接口回调
* @param code 初始化成功返回code码
*/
typedef void(*TestCallBack)(int code);

/**
* 测试结果接口回调
* @param result 返回测试结果
*/
typedef void(*TestResultCallBack)(const char* result);

/**
*
*/
typedef struct TestInfo {
    int No;
    const char* name;
    const char* content;
} TestInfo;

// CMyDLLApp
// 有关此类实现的信息,请参阅 MyDLL.cpp
// 对外导出 CMyDllApp类


class EXPORT CMyDLLApp      //将整个类导出,也可以单独导出单个函数
{
// 重写
public:

    TestCallBack testCallBack;
    TestResultCallBack resultCallBack;
    TestInfo info;
    char* id;

    void __stdcall InitTest(TestInfo const& info, char* id);
    void __stdcall SetTestCallBack(TestCallBack testCallBack);
    void __stdcall SetResultCallBack(TestResultCallBack resultCallBack);
    char* __stdcall GetTestInfoCode();

    void OnCalculate();
    void DoTest();
};
  • MyDLL.cpp 这边是逻辑实现
// MyDLL.cpp : 定义 DLL 的初始化例程。
//

#include "stdafx.h"
#include "MyDLL.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

void CMyDLLApp::InitTest(TestInfo const & info, char * id)
{
    this->info = info;
    this->id = id;
}

void CMyDLLApp::SetTestCallBack(TestCallBack testCallBack)
{
    this->testCallBack = testCallBack;
    OnCalculate();
}

void CMyDLLApp::SetResultCallBack(TestResultCallBack resultCallBack)
{
    this->resultCallBack = resultCallBack;
    DoTest();
}

char * CMyDLLApp::GetTestInfoCode()
{
    return "1234";
}

void CMyDLLApp::OnCalculate()
{
    int lastCode = info.No + 111;
    testCallBack(lastCode);
}

void CMyDLLApp::DoTest()
{
    char buf[256] = {0};
    sprintf_s(buf, "the result is %s and %s", info.name, info.content);
    resultCallBack(buf);
}
  • MyDLL.def文件 因为导出整个类,这边可以不用声明
; MyDLL.def : 声明 DLL 的模块参数。

LIBRARY  

EXPORTS
    ; 此处可以是显式导出
  • 最后编译生成lib和dll文件


    image.png

新建MFC调用客户端项目

  • 这边是新建对话框的项目


    image.png
  • 在新建项目下,建立一个目录这边是libDll,在改目录下分别建立inc和lib目录


    image.png

    然后将MyDLL.h头文件拷贝到inc目录,将dll项目生成的MyDLL.dll和MyDLL.lib两个文件拷贝到lib下

  • 然后进行配置,项目属性-->链接器 -->常规,配置附加库目录,这边是配置外来库的文件目录(libDLL/lib 将外来库文件放置这)


    image.png
  • 接着配置lib目录,项目属性-->C++ -->常规,配置附加包含目录,这边是配置外来附加头文件目录


    image.png

    然后再配置库名称,项目属性->链接器-->输入,配置附加依赖项,即把要添加的lib,名称加进去


    image.png

    到此依赖的静态链接库都配置好了。
  • 引用
    MFCTestDLLDlg.h 引入依赖的库和对应的头文件#include "MyDLL.h" #pragma comment(lib, "MyDLL.lib")
// MFCTestDLLDlg.h : 头文件
#pragma once
#include "MyDLL.h"
#pragma comment(lib, "MyDLL.lib")

// CMFCTestDLLDlg 对话框
class CMFCTestDLLDlg : public CDialogEx
{
// 构造
public:
    CMFCTestDLLDlg(CWnd* pParent = NULL);   // 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MFCTESTDLL_DIALOG };
#endif

    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:
    afx_msg void OnBnClickedButton1();
    CMyDLLApp MyDllApp;
    afx_msg void OnBnClickedButton2();
        //因为静态的方法无法使用非静态的成员,所以更新UI,采用消息发送的形式
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
        //两个回调传递的方法必须是静态的,否则会报错
    static void TestCallback(int code);
    static void ResultCallBack(const char* result);
        CStatic m_static1;
       CStatic m_static2;
};

MFCTestDLLDlg.cpp

// MFCTestDLLDlg.cpp : 实现文件
#include "stdafx.h"
#include "MFCTestDLL.h"
#include "MFCTestDLLDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define WM_UI_MESSAGE WM_USER + 105


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CMFCTestDLLDlg 对话框


CMFCTestDLLDlg::CMFCTestDLLDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_MFCTESTDLL_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMFCTestDLLDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CMFCTestDLLDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON1, &CMFCTestDLLDlg::OnBnClickedButton1)
    ON_BN_CLICKED(IDC_BUTTON2, &CMFCTestDLLDlg::OnBnClickedButton2)
    ON_MESSAGE(WM_UI_MESSAGE, OnMyMessage)
END_MESSAGE_MAP()

//消息接收方法
LRESULT CMFCTestDLLDlg::OnMyMessage(WPARAM wParam, LPARAM lParam) {
    if (wParam == 0)
    {
        CString* result = (CString*)lParam;
        MessageBox(result->GetBuffer(), MB_OK);
    }
    return 0;
}


// CMFCTestDLLDlg 消息处理程序

BOOL CMFCTestDLLDlg::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: 在此添加额外的初始化代码
    TestInfo info;
    info.No = 100;
    info.name ="jack";
    info.content = "this is test dll";
    MyDllApp.InitTest(info, "8888");

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CMFCTestDLLDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CMFCTestDLLDlg::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 CMFCTestDLLDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

void CMFCTestDLLDlg::OnBnClickedButton1()
{
    MyDllApp.SetTestCallBack(TestCallback);

}

void CMFCTestDLLDlg::OnBnClickedButton2()
{
    MyDllApp.SetResultCallBack(ResultCallBack);
}

void CMFCTestDLLDlg::TestCallback(int code)
{
    CString *codeCs = new CString();
    codeCs->Format(L"get the code is %d",code);
    ::PostMessage(AfxGetApp()->m_pMainWnd->m_hWnd, WM_UI_MESSAGE, NULL, (LPARAM)(codeCs));
}
void CMFCTestDLLDlg::ResultCallBack(const char * result)
{
    CString *codeCs = new CString();
    codeCs->Format(L"get the code is %s", result);
    ::PostMessage(AfxGetApp()->m_pMainWnd->m_hWnd, WM_UI_MESSAGE, NULL, (LPARAM)(codeCs));
}

到此整个项目over了,但是还存在几个问题,一个是字符串回传的时候,会出现乱码;一个是编译会提示警告出现宏重定义;

参考:
https://www.cnblogs.com/19910101zj/p/4611695.html
http://www.jb51.net/article/56602.htm
http://blog.csdn.net/tajon1226/article/details/55190247

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容