您现在的位置是:网站首页> C/C++
C++第三方库&开源软件
- C/C++
- 2025-08-27
- 1089人已阅读
C++第三方库&开源软件
ImGui一个轻量级、跨平台的即时模式(Immediate Mode)GUI 库
SMFL游戏界面库
安装生成 C 和 C++ 应用所需的组件
CEF开源库
Windows 10 下使用Visual Studio 2017 编译CEF SDK
下载后的文件名如下所示:
cef_binary_3.2785.1466.g80e473e_windows32.tar.tar
cef_binary_3.2785.1466.g80e473e_windows64.tar.tar
可见:只有 Debug、Release、Resources 三个文件夹中的某些文件存在差异。所以,可将32位的Debug、Release、Resources重命名为Debug32、Release32、Resources32;64位的Debug、Release、Resources重命名为Debug64、Release64、Resources64。然后,将两个文件夹合并。
笔者将两个文件夹合并到了W:\libCEF\v3.2785.1466\unzip,其目录结构如下图所示
StdAfx.h
#define STR(x) #x
#define STR2(x) STR(x)
#define INCLUDE(f) STR2(PATH(f))
//包含 libCEF 的头文件
//下面的宏定义,##之前是 libCEF include 目录相对于本文件的相对路径
#define PATH(f) ../../libCEF/v3.2785.1466/unzip/include/##f
#define USING_CEF_SHARED 1
#include INCLUDE(cef_app.h)
#include INCLUDE(cef_client.h)
#undef PATH
#include "cefSimple.h"
//下面的宏定义是 libCEF 库文件相对于 vc 项目文件(*.vcproj/*.vcxproj)的相对路径
#define PATH "../../libCEF/v3.2785.1466/"
//连接库文件 libcef.lib
#ifdef _WIN64
#ifdef _DEBUG
#define PATH1 "unzip/Debug64/"
#else
#define PATH1 "unzip/Release64/"
#endif
#else
#ifdef _DEBUG
#define PATH1 "unzip/Debug32/"
#else
#define PATH1 "unzip/Release32/"
#endif
#endif
#pragma comment(lib,PATH PATH1 "libcef.lib")
#undef PATH1
//连接库文件 libcef_dll_wrapper.lib
#define PATH1 "libcef_dll_wrapper/bin/"
#if _MSC_VER==1500 //VC++9.0(VC2008)
#define PATH2 "vc2008"
#elif _MSC_VER==1600 //VC++10.0(VC2010)
#define PATH2 "vc2010"
#elif _MSC_VER==1700 //VC++11.0(VC2012)
#define PATH2 "vc2012"
#elif _MSC_VER==1800 //VC++12.0(VC2013)
#define PATH2 "vc2013"
#elif _MSC_VER==1900 //VC++14.0(VC2015)
#define PATH2 "vc2015"
#else
#error 未知的 VC++ 编译器
#endif
#ifdef _WIN64
#define PATH3 "-x64"
#else
#define PATH3 "-Win32"
#endif
#ifdef _DEBUG
#ifdef _UNICODE
#define PATH4 "-DU/"
#else
#define PATH4 "-DA/"
#endif
#else
#ifdef _UNICODE
#define PATH4 "-RU/"
#else
#define PATH4 "-RA/"
#endif
#endif
#ifdef _MT
#ifdef _DLL //使用多线程 DLL 版的 C 函数库
#define PATH5 "libcef_dll_wrapperD.lib"
#else //使用多线程版的 C 函数库
#define PATH5 "libcef_dll_wrapperT.lib"
#endif
#else //使用单线程版的 C 函数库
#define PATH5 "libcef_dll_wrapperS.lib"
#endif
#pragma comment(lib,PATH PATH1 PATH2 PATH3 PATH4 PATH5)
#undef PATH1
#undef PATH2
#undef PATH3
#undef PATH4
#undef PATH5
#undef PATH
#undef STR
#undef STR2
#undef INCLUDE
CWinApp
BOOL CmfcCEFApp::InitInstance()
{
CWinApp::InitInstance();
_tsetlocale(LC_ALL,_T(""));
{//CEF 初始化
CefMainArgs ma(m_hInstance);
CefRefPtr<SimpleApp> app(new SimpleApp());
int nCEP = CefExecuteProcess(ma,app.get(),NULL);
if(nCEP >= 0) { exit(nCEP); }
CefSettings settings;
CefString(&settings.locale) = L"zh-CN";
settings.no_sandbox = 1;
settings.multi_threaded_message_loop = 1;
CefInitialize(ma, settings, app.get(),NULL);
}
{
CmfcCEFDlg dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
}
CefShutdown(); //退出 CEF
return FALSE;
}
Dialog
BOOL CmfcCEFDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CefWindowInfo wi;
RECT rc;
GetClientRect(&rc);
wi.SetAsChild(m_hWnd,rc);
CefBrowserSettings bs;
CefBrowserHost::CreateBrowser(wi,m_Handler.get()
,L"www.sina.com",bs,NULL);
return TRUE;
}
void CmfcCEFDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
HWND hWnd = m_Handler->m_hWndBrowser;
if(hWnd)
{
RECT rc;
GetClientRect(&rc);
::MoveWindow(hWnd,0,0,rc.right,rc.bottom,FALSE);
Invalidate();
}
}
编译mfcCEF成功后,先不要急着运行程序。因为mfcCEF.exe运行时需要一些文件,需要把它们复制到mfcCEF.exe所在目录。
假如mfcCEF.exe是32位的Release版,则
1、复制图1.3中Release32文件夹内的所有文件到mfcCEF.exe所在目录。注意:*.lib有259M,运行时不需要这些文件,所以就不用复制了。
2、复制图1.3中Resources32文件夹内的所有文件到mfcCEF.exe所在目录。
文件复制完成后,mfcCEF.exe所在目录如下图所示:
程序运行逻辑
运行mfcCEF,代码的执行顺序如下:
1、执行 CmfcCEFApp::InitInstance,调用 CefInitialize 初始化 CEF;
2、CmfcCEFApp::InitInstance 里的 dlg.DoModal() 显示程序主界面;
3、程序主界面被创建时,执行 CmfcCEFDlg::OnInitDialog,调用 CefBrowserHost::CreateBrowser 创建浏览器窗口;
4、浏览器窗口创建完毕后,执行 SimpleHandler::OnAfterCreated,获得浏览器窗口的句柄 m_hWndBrowser;
5、浏览器窗口是程序主界面窗口的子窗口。调整主界面大小时,将执行 CmfcCEFDlg::OnSize。OnSize 函数里移动浏览器窗口(句柄为 m_hWndBrowser)使其占满主界面窗口的客户区;
6、用户退出主界面,CmfcCEFApp::InitInstance 里的 dlg.DoModal() 将返回。同时对象 dlg 将被析构,其成员变量m_Handler 也将被析构。CefRefPtr<SimpleHandler> 使用了计数功能,m_Handler 析构时计数值减一后等于零,new SimpleHandler() 创建的对象将被自动 delete;
7、CmfcCEFApp::InitInstance 里的 CefShutdown() 被执行,退出 CEF;
8、CmfcCEFApp::InitInstance 返回 FALSE,整个程序不进入消息循环,而是结束。
下面说明一下对象new SimpleApp()的生命周期:
1、CefRefPtr<SimpleApp> app(new SimpleApp()) 后对象的计数值为 1;
2、CefInitialize(ma, settings, app.get(),NULL) 后对象的计数值为2;
3、app 析构后对象的计数值为 1;
4、CefShutdown() 后,app 的计数值为 0,对象自动被 delete。
mosquitto MQTT服务
客户端可用开放包
eclipse-paho-mqtt-c-win32-1.3.9.rar
mosquitto是一款开源的MQTT消息代理(服务器)软件,实现了MQTT协议版本3.1和3.1.1,提供轻量级的,支持可发布/可订阅的的消息推送模式,使设备对设备之间的短消息通信变得简单,比如现在应用广泛的低功耗传感器,手机、嵌入式计算机、微型控制器等移动设备
Mosquitto项目还提供了用于实现MQTT客户端的C库以及非常受欢迎的mosquitto_pub和mosquitto_sub命令行MQTT客户端。
1)mosquitto:代理器主程序
2)mosquitto.conf:配置文件【路径:/etc/mosquitto】
3)mosquitto_passwd:用户密码管理工具
4)mosquitto_pub:用于发布消息的命令行客户端
5)mosquitto_sub:用于订阅消息的命令行客户端
6)mqtt:MQTT的后台进程
7)libmosquitto:客户端编译的库文件
关于连接认证、订阅认证和发布认证,Mosquitto 提供了多种机制来实现安全的 MQTT 通信:
1. 连接认证
用户名和密码:最基本的认证方式是通过客户端提供的用户名和密码进行认证。这需要在 Mosquitto 的配置文件中启用 password_file 选项,并使用 mosquitto_passwd 工具来创建和管理用户名和密码。
SSL/TLS:通过使用 SSL/TLS,可以在客户端和服务器之间建立加密的连接,同时也支持基于证书的客户端认证。这需要在配置文件中指定 cafile、certfile 和 keyfile 等选项。
2. 订阅认证和发布认证
访问控制列表(ACL):Mosquitto 允许使用访问控制列表(ACL)来限制客户端对特定主题的订阅和发布权限。ACL 规则可以在配置文件中指定,或者通过外部文件定义。每条规则指定了客户端或客户端组的订阅和发布权限。
插件:Mosquitto 支持使用插件扩展其功能,包括认证和授权。例如,mosquitto-auth-plug 插件支持多种后端存储(如 HTTP、JWT、MySQL、Redis 等),允许更灵活和强大的认证和授权机制。
实现示例
假设你想使用基于用户名和密码的认证,你需要进行以下步骤:
在 Mosquitto 的配置文件中启用密码文件:
password_file /path/to/passwordfile
使用 mosquitto_passwd 工具创建密码文件并添加用户:
mosquitto_passwd -c /path/to/passwordfile username
重启 Mosquitto 服务以应用更改。
对于更高级的认证和授权需求,你可能需要查阅 Mosquitto 的官方文档或考虑使用插件来实现。
password_file 用于 Mosquitto MQTT 代理中,存储用户名和密码信息,以便进行客户端连接认证。这个文件的内容包含了用户名和加密后的密码,每一行代表一个用户的凭证。请注意,为了安全性,密码在文件中是以加密形式存储的
johndoe:$6$+LRu4ZGJ$Y5M6Z...省略...k3R6FgH0
janedoe:$6$UMi9SDH$9aK8F...省略...3JUZP2C0
在这个示例中,johndoe 和 janedoe 是用户名,$6$+LRu4ZGJ$Y5M6Z...省略...k3R6FgH0 和 $6$UMi9SDH$9aK8F...省略...3JUZP2C0 分别是与这些用户名相关联的加密密码。
为了创建或更新这个文件,你需要使用 Mosquitto 提供的 mosquitto_passwd 工具。下面是一些基本的使用方法:
创建新的 password_file 并添加第一个用户:
mosquitto_passwd -c /path/to/passwordfile johndoe
这会提示你输入 johndoe 的密码两次来确认。
向已存在的 password_file 中添加新用户或更新现有用户的密码:
mosquitto_passwd /path/to/passwordfile janedoe
同样,这会提示你为 janedoe 输入密码两次。
请记住,每次修改 password_file 后,都需要重启 Mosquitto 服务或重新加载配置,以确保更改生效。
JS通过WebSockets访问mosquitto例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Mosquitto WebSocket Example</title>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
<script>
// 连接选项
const options = {
connectTimeout: 4000, // 超时时间
// 认证信息(如果需要)
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
username: 'username', // 替换为你的用户名
password: 'password', // 替换为你的密码
};
// 使用 WebSocket 连接到代理
const client = mqtt.connect('ws://broker-address:9001', options);
client.on('connect', function () {
console.log('Connected to Mosquitto via WebSocket!');
// 连接成功后订阅主题
client.subscribe('test/topic', function (err) {
if (!err) {
// 订阅成功后发布消息
client.publish('test/topic', 'Hello MQTT over WebSocket!');
}
});
});
// 接收消息
client.on('message', function (topic, message) {
// message 是一个 Buffer
console.log(message.toString());
// 关闭客户端
client.end();
});
// 监听错误事件
client.on('error', function (error) {
console.log('Connection failed:', error);
});
</script>
</body>
</html>
Serial-Studio一个开源串口神器
一个开源的串口项目——Serial Studio,这是一个强大的数据可视化软件,支持串口通信,串口终端,网络通信 TCP/UDP,MQTT通信协议。这个项目遵循MIT协议,所以是可以商用的。
IM开源即时通讯软件
一、FlamingoIM
FlamingoIM 是一款轻量级开源即时通讯软件,其目前有服务器端、pc 端和安卓端,三端都是 native 应用,从通信协议到界面库都自主开发,不是使用第三的库包装而成。大家平常用微信和 QQ 比较多,所以学习起来,对即时通讯本身没有业务理解负担。
https://github.com/balloonwj/flamingo
开发语言:
Server:C++
PC:C++
Android:Java
IOS:Objective-C
二、TeamTalk
TeamTalk 简称 tt,是蘑菇街开源的一款即时通讯 IM,代码地址:
https://github.com/balloonwj/TeamTalk
开发语言:
Server:C++
PC:C++
Android:Java
IOS:Objective-C
Web 管理:php
ImGui一个轻量级、跨平台的即时模式(Immediate Mode)GUI 库
从 GitHub 仓库下载最新源码:
https://github.com/ocornut/imgui
核心文件包括:
基础 UI 逻辑:imgui.cpp、imgui.h、imgui_internal.h、imgui_widgets.cpp等
后端适配(以 DirectX 11 为例):backends/imgui_impl_win32.cpp(窗口适配)、backends/imgui_impl_dx11.cpp(渲染适配)
VC:
在项目目录下创建imgui文件夹,将下载的 ImGui 核心文件和后端文件放入:
核心文件:imgui.cpp、imgui.h、imgui_demo.cpp(示例)等
后端文件:backends/imgui_impl_win32.cpp、backends/imgui_impl_win32.h、backends/imgui_impl_dx11.cpp、backends/imgui_impl_dx11.h
#include "imgui.h"
#include "backends/imgui_impl_win32.h"
#include "backends/imgui_impl_dx11.h"
#include <d3d11.h>
#include <tchar.h>
// 全局变量(DirectX 11相关)
static IDXGISwapChain* g_pSwapChain = nullptr;
static ID3D11Device* g_pd3dDevice = nullptr;
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
// 窗口过程(处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
// 让ImGui处理输入消息(鼠标、键盘等)
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
switch (msg) {
case WM_SIZE:
if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED) {
// 窗口大小改变时,重新创建渲染目标
CleanupRenderTarget();
g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
CreateRenderTarget();
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
// 初始化DirectX 11
bool CreateDeviceD3D(HWND hWnd) {
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Width = 0;
sd.BufferDesc.Height = 0;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
// 创建DX11设备和交换链
UINT createDeviceFlags = 0;
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
if (D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2,
D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK)
return false;
CreateRenderTarget();
return true;
}
// 创建渲染目标
void CreateRenderTarget() {
ID3D11Texture2D* pBackBuffer;
g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
pBackBuffer->Release();
}
// 清理渲染目标
void CleanupRenderTarget() {
if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
}
// 清理DirectX资源
void CleanupDeviceD3D() {
CleanupRenderTarget();
if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 注册窗口类
WNDCLASSEX wc = { sizeof sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L,
hInstance, nullptr, nullptr, nullptr, nullptr,
_T("ImGui Example"), nullptr
};
RegisterClassEx(&wc);
// 创建窗口
HWND hWnd = CreateWindowEx(0, wc.lpszClassName, _T("ImGui + DirectX 11 Example"),
WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr);
// 初始化DirectX
if (!CreateDeviceD3D(hWnd)) {
CleanupDeviceD3D();
UnregisterClass(wc.lpszClassName, wc.hInstance);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// 初始化ImGui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // 启用键盘导航
// 配置ImGui样式
ImGui::StyleColorsDark(); // 深色主题
// 初始化ImGui后端(Win32窗口 + DirectX 11渲染)
ImGui_ImplWin32_Init(hWnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
// 示例变量(用于UI交互)
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
// 主循环
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT) {
// 处理窗口消息
if (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
// 开始ImGui帧
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// 1. 显示ImGui自带的演示窗口(可删除)
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
// 2. 自定义窗口
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, ImGui!"); // 创建窗口
ImGui::Text("这是一个ImGui示例窗口。"); // 显示文本
ImGui::SliderFloat("浮点数滑块", &f, 0.0f, 1.0f); // 滑块控件
ImGui::ColorEdit3("背景色", (float*)&clear_color); // 颜色选择器
if (ImGui::Button("按钮")) // 按钮控件
counter++;
ImGui::SameLine();
ImGui::Text("点击次数: %d", counter);
ImGui::Checkbox("显示演示窗口", &show_demo_window); // 复选框
ImGui::Checkbox("显示另一个窗口", &show_another_window);
ImGui::End();
}
// 3. 另一个窗口
if (show_another_window) {
ImGui::Begin("另一个窗口", &show_another_window);
ImGui::Text("这是另一个窗口!");
if (ImGui::Button("关闭"))
show_another_window = false;
ImGui::End();
}
// 渲染
ImGui::Render();
const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
g_pSwapChain->Present(1, 0); // 交换缓冲区(1=垂直同步)
}
// 清理资源
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
CleanupDeviceD3D();
UnregisterClass(wc.lpszClassName, wc.hInstance);
return 0;
}