您现在的位置是:网站首页> C/C++

C++第三方库&开源软件

  • C/C++
  • 2025-08-27
  • 1089人已阅读
摘要

C++第三方库&开源软件

SMFL游戏界面库

安装生成 C 和 C++ 应用所需的组件

CEF开源库

mosquitto MQTT服务

Serial-Studio一个开源串口神器

IM开源即时通讯软件

ImGui一个轻量级、跨平台的即时模式(Immediate Mode)GUI 库 


SMFL游戏界面库

点击下载库

点击查看Visual Studio 2017配置SMFL

点击查看SMFL 教程&个人笔记



安装生成 C 和 C++ 应用所需的组件

点击下载



CEF开源库

github开源地址

Windows 10 下使用Visual Studio 2017 编译CEF SDK

查看使用教程

二进制库下载

~B[D)TGXBJSWH951QIFZK2O.png

下载后的文件名如下所示:

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,其目录结构如下图所示

YZ2KR1SLDFEUDCK50VI{OKE.png


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所在目录如下图所示:

WV77%_ZF1%(8]WZL2$PA[PO.png

程序运行逻辑


运行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协议,所以是可以商用的。

1.png




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;

}




















上一篇:QT视频教程

下一篇:C/C++同行者

Top