1.继续动态库
1.1 使用 .def 文件来引用动态库的导出库
1.新建一个空的解决方案,添加一个 MyDllLib 项目,创建一个 MyDllLib.cpp 文件,一个 MyDllLib.h 文件;
cpp 源文件
int MyAdd(int a, int b)
{
return a+b;
}
int MySub(int a, int b)
{
return a-b;
}
h 头文件
#ifndef _MYDLLLIB_H_
#define _MYDLLLIB_H_
int MyAdd(int a, int b);
int MySub(int a, int b);
#endif //_MYDLLLIB_H_
2.在 MyDllLib 项目的源文件中添加一个 模块定义文件.def ,可以在 代码 选项中找到,在其中添加:
LIBRARY "MyDllLib"
EXPORTS
MyAdd @1
MySub @2
3.在创建一个 Test 项目,进行动态库的引用;
main.cpp
#include <iostream>
using namespace std;
#include "../MyDllLib/MyDllLib.h"
#pragma comment(lib, "../Debug/MyDllLib.lib")
int main()
{
cout << MyAdd(1, 10) << endl;
return 0;
}
4.将该库的编译风格设置为C,使其可以在 .c 文件中使用;
#ifndef _MYDLLLIB_H_
#define _MYDLLLIB_H_
#ifdef __cplusplus
extern "C" int Add(int a, int b);
extern "C" int Sub(int a, int b);
#else
int Add(int a, int b);
int Sub(int a, int b);
#endif // __cplusplus
#endif //_MYDLLLIB_H_
1.2 显式运行时加载
1.这可使得我们无需调用 MyDllLib.h 以及其生成的导出库 lib ;
2.需要一个函数指针来作为函数的承接口;
3.对于 GetProcAddress 函数,第一个参数是动态库的句柄,第二个参数是函数的名称;
4.使用完动态库句柄之后我们需要将其释放掉;
#include <iostream>
using namespace std;
#include <windows.h>
// 定义一个函数指针用来作为库句柄调用函数
typedef int (*PFUNC)(int, int);
int main()
{
HMODULE lib_handle = ::LoadLibrary(L"../Debug/MyDllLib.dll");
if(lib_handle != 0)
{
PFUNC pFun = (PFUNC)::GetProcAddress(lib_handle, "MyAdd");
cout << pFun(2, 5) << endl;
::FreeModule(lib_handle);
lib_handle = 0;
}
system("pause");
return 0;
}
- c语言 文件同样适用(C语言中没有 :: 全局作用域符号);
#include <stdio.h>
#include <windows.h>
// 定义一个函数指针用来作为库句柄调用函数
typedef int (*PFUNC)(int, int);
int main()
{
HMODULE lib_handle = LoadLibrary(L"../Debug/MyDllLib.dll");
PFUNC pFun = 0;
if(lib_handle != 0)
{
PFUNC pFun = (PFUNC)GetProcAddress(lib_handle, "Add");
printf("%d\n", pFun(1, 5));
FreeModule(lib_handle);
lib_handle = 0;
}
return 0;
}
2.动态共享库
2.1 我们希望多个进程可以共用一个动态库,这时候我们就需要动态共享库
1.创建一个新的空白解决方案,添加一个 dll 项目,不勾选 空项目 ;
2.在源文件中添加一个 Share.cpp 源文件;
#include <stdafx.h>
int a = 0;
void SetValue(int b)
{
a = b;
}
int GetValue()
{
return a;
}
3.添加一个 Share.h 文件,里面装着两个函数声明;
#ifndef _SHARE_H_
#define _SHARE_H_
void SetValue(int b);
int GetValue();
#endif //_SHARE_H_
4.添加一个 模块定义文件.def ,进行动态库的函数导出;
LIBRARY SHARE
EXPORTS
GetValue @1
SetValue @2
4.生成动态库文件 Share.dll 以及导出函数文件 Share.lib ;
5.给该解决方案添加一个基于对话框的MFC应用程序,向对话框上添加两个按钮,一个 Edit 控件,并给 Edit 控件添加变量,变量类型为 Value 型,变量为 DWORD m_sValue ;
6.给 SetValue 按钮添加点击处理函数,获得 a 的值,并将 a 的值放到MessageBox上;
void CSHAREDPROCESSDlg::OnBnClickedButton2()
{
// SetValue按钮
m_sValue = GetValue();
CString str;
str.Format(_T("%d"), m_sValue);
MessageBox(str);
}
7.给 GetValue 按钮添加点击处理函数,从对话框上取数,并将取下来的数赋值给 a ;
void CSHAREDPROCESSDlg::OnBnClickedButton1()
{
// GetValue按钮
UpdateData(TRUE);
SetValue(m_sValue);
}
8.给对话框类引用动态库的导出库文件;
// 调用动态库
#include "../SharedDll/SharedDll.h"
#pragma comment(lib, "../Debug/SharedDll.lib")
9.运行该MFC应用程序,在 Edit 上输入一串数字,点击 GetValue 将a的值更改为 Edit 上的值,在点击 SetValue 按钮将a的值用MessageBox显示出来;
2.2 我们运行两个生成的exe文件,在一个上设置a的值,在另一个上获得a的值,发现获得的值并不是我们设置的a的值,这是因为我们没有将库中的变量a进行共享
1.在 Share.cpp 文件中使用 pragma data_seg 将变量a进行共享(补上一句,创建时未勾选空项目,那么我们需要在每一个源文件中引用 stdafx.h 头文件);
#include "stdafx.h"
#pragma data_seg("MySeg")
int a = 0;
#pragma data_seg()
void SetValue(int b)
{
a = b;
}
int GetValue()
{
return a;
}
2.在 .def 文件中添加:
LIBRARY SHARE
EXPORTS
GetValue @1
SetValue @2
SECTIONS
MySeg SHARED
3.重新生成动态库,再运行两个exe文件,即可实现在一个对话框上可以获得另一个对话框上设置的a的值;
3.网络
3.1 概念
网络是各种连在一起的可以互相通信的设备的集合。
3.2 UDP用户数据报文协议
3.3 创建一个基于UPD的客户端
1.创建一个基于UDP的客户端分为5步:加载库->创建通信接口套接字socket->将自己的信息与套接字进行绑定->收发数据->关闭套接字->卸载库;
2.新建一个空白解决方案,在其中添加一个Win32控制台应用程序,添加一个源文件 ClientMain.cpp ;
3.首先加载库: WSAStartup 函数;加载库的过程可以在该函数的帮助文档中获取,我们将示例代码复制到该文件中;
4.创建一个套接字: socket ;其第一个参数是遵循的地址规则(IPv4或者IPv6等),第二个参数是传送什么样的数据(本次选择数据报文格式),第三个参数是传输的协议(本次选择UDP);
5.将自己的信息与套接字进行绑定: bind ;首先我们需要将自己的信息存储到一个结构体中,该结构体包括要传送数据的端口号、自己遵循的IP协议、自己的IP地址;
6.传输数据: sendto ;该函数第一个参数本机的套接字,第二个参数是传送的数据的地址,第三个参数是传送的数据的大小,第四个是标记(本次置为零),第五个是需要接收端的信息(本次虚拟一个),最后一个是接收端的地址的大小(用来区别是哪一个地址结构体);
7.关闭库: WSACleanup ;
#include <iostream>
using namespace std;
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 1.加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
// 2.创建套接字
SOCKET socket_client = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_client == INVALID_SOCKET)
{
cout << "error code: " << WSAGetLastError() << endl;
WSACleanup();
return -1;
}
// 3.绑定套接字与自己的端口
sockaddr_in addr_client; // 自己的信息
addr_client.sin_family = AF_INET; // 自己的IP地址是IPv4 还是IPv6
addr_client.sin_port = htons(4567); // 端口号
addr_client.sin_addr.S_un.S_addr = inet_addr("192.168.1.25"); // 自己的IP地址
if(SOCKET_ERROR == ::bind(socket_client, (const sockaddr*)&addr_client, sizeof(sockaddr_in)))
{
cout << "error code: " << WSAGetLastError() << endl;
::closesocket(socket_client);
WSACleanup();
return -1;
}
// 4.传输数据
char sz_buffer[1024] = {0};
cin >> sz_buffer;
sockaddr_in addr_server;
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(4568);
addr_server.sin_addr.S_un.S_addr = inet_addr("192.168.1.26");
int n_send_size = ::sendto(socket_client, sz_buffer, 1024, 0, (const sockaddr*)&addr_server, sizeof(sockaddr_in));
if(n_send_size <= 0)
{
cout << "error code: " << WSAGetLastError() << endl;
}
// 5.关闭库
WSACleanup();
system("pause");
return 0;
}