C++ MFC使用Protobuf

C++ MFC使用Protobuf

2020年10月8日 0 作者 老王

先来看看效果。
MFC Protobuf序列化效果展示

1 安装Protobuf和CMake

下载protobuf-cpp源码,写本文时最新版本为3.13.0:https://github.com/protocolbuffers/protobuf/releases
安装Protobuf
如果电脑之前没安装CMake,需要先下载安装CMake,用于编译protobuf-cpp源码。
CMake下载地址:https://cmake.org/download/
下载CMake

2 CMake编译protobuf-cpp

打开CMake软件。
Where is the source code 项选择我们下载的protobuf-cpp源码目录下的cmake文件夹。
Where to build the binaries 项选择源码编译后存放的目录。
选择编译目录
然后弹出一个对话框,叫选择生成哪种VS版本的项目,由于我用的VS2017,所以我选择的是Visual Studio 15 2017(ps:我试过选择Visual Studio 15 2017 Win64来创建64位的版本,但是后来发现生成的lib不能用,一直报错说生成的lib是32位的,所以我这儿就没选64位的版本)。
选择生成哪种VS的项目
然后点击Finish,稍等编译完成。
出现以下界面后,点击Generate。
点击Generate
然后再点击Open Project,或者直接去编译后的目录用相应版本的VS打开生成后的项目。
打开生成的工程
然后选择生成libprotobuf、libprotoc、protoc这三个项目。
生成lib文件
生成的文件如下(Debug模式下生成的文件在Debug文件夹下,Release模式下生成的文件在Release文件夹下),其中两个lib文件是c++程序需要引入的,protoc.exe用于编译.proto文件。
生成的lib文件

3 批处理生成proto文件

我们先编写一个测试用的proto文件。具体的proto的语法可以参考这篇文章

syntax = "proto3";

package School;

message Student {
  string name = 1;
  int32 id = 2;
}

我们可以用命令行的方式来进行编译proto文件,一个proto文件会编译成一个pb.h和一个pb.cc文件。命令如下:

protoc --cpp_out=./cpp message.proto

但是由于实际项目中我们会写很多个proto文件,一个一个去编译proto文件很显然是不现实的,应该交给批处理程序,由于MFC默认包含有预编译头文件,而protoc默认没有添加预编译头,这里批处理程序中也需要添加预编译头。这里直接给出批处理编译proto文件的程序(rum.bat),如下。注意要将批处理程序和protoc.exe放在同一目录下。

@echo off

rem 进入当前目录
cd /d %~dp0

if not exist .\cpp (
    md .\cpp
)
for %%i in (*.proto) do (
    protoc --cpp_out=.\cpp %%i
    rem 从这里往下都是注释,可忽略
    echo From %%i To %%~ni.pb.h %%~ni.pb.cc Successfully!  
)

rem 给.cc文件添加预编译头,若不需要,需注释掉以下部分
echo #include "pch.h" > $
for /f "delims=" %%i in ('dir /b .\cpp\*.pb.cc')do (
    copy /b $+".\cpp\%%i" $1
    move $1 ".\cpp\%%i"
)
del /f $

4 MFC项目中引用protobuf

首先创建一个MFC项目,然后选择显示所有文件。
显示所有文件
接下来我们进行项目的配置。
第一步,右键项目名,选择属性,选择 配置属性》常规》MFC的使用,选择在静态库中使用MFC
注意,配置( C ) 要选择为 所有配置 平台( P ) 要选择为所有平台。
在静态库中使用MFC
第二步,拷贝protobuf/src文件夹下的google至项目中。
google源码
然后设置项目属性,C/C++》常规》附加包含目录,添加上$(ProjectDir)。
注意,配置( C ) 要选择为 所有配置 平台( P ) 要选择为所有平台。
附加包含目录
第三步,设置预处理器,项目属性,C/C++》预处理器》预处理器定义,添加_SCL_SECURE_NO_WARNINGS
设置预处理器
第四步,创建一个lib/protobuf/x86文件夹,将之前生成的libprotobufd.lib拷贝过去,然后右键libprotobufd.lib包含在项目中。
拷贝protobuf.lib文件
包含libprotobufd.lib至项目中
第五步设置链接器。
①项目属性》链接器》常规》附加库目录 添加 $(ProjectDir)lib\protobuf\x86。
我这里没有生成64位的lib(所以我这儿配置( C )仍然选择的是所有配置,平台( P )仍然选择的所有平台),如果需要打包成64位的,建议创建一个x64的文件夹,然后把64位的lib拷贝过去。
设置附加库目录
②项目属性》链接器》输入》附加依赖项 添加libprotobufd.lib
(ps: 建议这里Debug模式配置为libprotobufd.lib,Release配置为libprotobuf.lib,因为我这儿只生成了Debug的lib文件,所以都用的libprotobufd.lib,实际项目最好改一下)
附加依赖项添加libprotobufd.lib

5 项目使用protobuf

创建src/proto文件夹,并把protoc.exe、run.bat以及我们写的proto文件拷贝至proto文件夹下。
创建src/proto文件夹
双击run.bat,生成pb.h和pb.cc文件,点击刷新,并将pb.cc和pb.h包含在项目中。
添加pb.cc和pb.h文件
至此,就可以在项目中愉快的使用protobuf啦。
序列化。

School::Student student;
student.set_name("Tom Cat");
student.set_id(999);
std::fstream output("./student.data", std::ios::out | std::ios::binary);

if (!student.SerializeToOstream(&output)) {
    MessageBoxW(_T("序列化失败"));
}
else
{
    MessageBoxW(_T("序列化成功"));
}

反序列化。

School::Student student;
std::fstream input("./student.data", std::ios::in | std::ios::binary);
if (!student.ParseFromIstream(&input)) {
    MessageBoxW(_T("读取失败"));
}
else
{
    CString msg;
    msg.Format(_T("name:%S\r\nid:%d"), student.name().c_str(), student.id());
    MessageBoxW(msg);
}

项目上传到这儿啦。
链接:https://pan.baidu.com/s/1kClVNeeWXzqJQCkKmR9G1Q
提取码:rhxr