简语:
最近学习了生成静态链接的dll及其调用,写一下笔录和大家分享,有错误的地方欢迎大家指出来
开发环境
VS2015
开发语言
C++
开发步骤
以mfc的dll创建为例,先说明一下win32的dll和mfc的dll在支持C上,win32可能比较好,实现的过程是一样的。
构建MFC的DLL项目
-
新建MFC DLL项目
-
这边选择带静态链接MFC的规则DLL
-
默认生成了,头文件和源文件,右键def文件
- 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文件
新建MFC调用客户端项目
-
这边是新建对话框的项目
-
在新建项目下,建立一个目录这边是libDll,在改目录下分别建立inc和lib目录
然后将MyDLL.h头文件拷贝到inc目录,将dll项目生成的MyDLL.dll和MyDLL.lib两个文件拷贝到lib下
-
然后进行配置,项目属性-->链接器 -->常规,配置附加库目录,这边是配置外来库的文件目录(libDLL/lib 将外来库文件放置这)
-
接着配置lib目录,项目属性-->C++ -->常规,配置附加包含目录,这边是配置外来附加头文件目录
然后再配置库名称,项目属性->链接器-->输入,配置附加依赖项,即把要添加的lib,名称加进去
到此依赖的静态链接库都配置好了。 - 引用
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