WinPcap in Qt - _snprintf等函数冲突定义问题

本文最后更新于:2021年11月30日 晚上

前情提要

众所周知,在Windows中访问底层网络需要用到WinPcap这个工具

比如,Wireshark(网络封包分析软件)就用到了WinPcap

那么,如何在Qt平台中使用WinPcap呢?

安装 WinPcap for Qt

众所周知,C++想要使用库,就必须提供.h .lib .dll等文件,那么只要下载对应文件即可

WinPcap官网

1.首先下载安装winpcap.exe

目的是安装相关驱动和dll,安装完成之后基于winpcap的应用程序才能够正常运行

2.下载winpcap的开发包,头问文件和库文件

解压即可得到 Include & Lib 文件夹

From windows下安装配置winpcap - sunnycs - 博客园

接下来,我们需要在Qt中引用这些文件(假设解压目录:E:\WpdPack)

.pro文件中添加:

1
2
3
INCLUDEPATH += E:/WpdPack/Include
LIBS += E:/WpdPack/Lib/x64/wpcap.lib \
E:/WpdPack/Lib/x64/Packet.lib #假设是x64程序 若为x86程序 观察Lib目录下的文件

用以引入头文件和库文件路径

然后在mainwindow.h文件中引入头文件"pcap.h"以测试是否成功安装

正文:Error

果不其然,琳琅满目的30个报错:

1
2
3
4
E:\Qt5.14.2\Tools\mingw730_64\x86_64-w64-mingw32\include\stdio.h:735: 
error: conflicting declaration of 'int _snprintf(char*, size_t, const char*, ...)' with 'C' linkage
_CRTIMP int __cdecl _snprintf(char * __restrict__ _Dest,size_t _Count,const char * __restrict__ _Format,...) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;
^~~~~~~~~

不过仔细观察

错误多为:conflicting declaration (定义冲突)& has not been declared (未声明)

且翻来覆去就那几个函数:_snprintf() _vsnprintf()

而且而且,貌似都跟 stdio.h 有关

列文虎克

等一下,我们引入的是 pcap.h 头文件,为什么会引发标准库错误

まさか、真実はいつも一つ

让我们进入 pcap.hpcap/pcap.hpcap-stdinc.h

发现了令人震惊的一幕:

1
2
3
4
5
#if _MSC_VER < 1500
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strdup _strdup
#endif

这个头文件 居然对 snprintf & vsnprintf 函数进行了#define 替换

众所周知,#define 的作用域是该语句到文件尾

好巧不巧,该语句后正好有 #include<stdio.h>

stdio.h 中又同时定义了 snprintf _snprintf & vsnprintf _vsnprintf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int vsnprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, va_list __local_argv)
{
return __ms_vsnprintf (__stream, __n, __format, __local_argv);
}

int snprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, ...)
{
register int __retval;
__builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
__retval = __ms_vsnprintf (__stream, __n, __format, __local_argv);
__builtin_va_end( __local_argv );
return __retval;
}

__attribute__((__format__ (ms_printf, 3, 4))) __MINGW_ATTRIB_NONNULL(3)
_CRTIMP int __cdecl _snprintf(char * __restrict__ _Dest,size_t _Count,const char * __restrict__ _Format,...) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;
__attribute__((__format__ (ms_printf, 3, 0))) __MINGW_ATTRIB_NONNULL(3)
_CRTIMP int __cdecl _vsnprintf(char * __restrict__ _Dest,size_t _Count,const char * __restrict__ _Format,va_list _Args) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;

导致 snprintf vsnprintf 被替换为 _ 下划线版本 导致重定义

同时导致<QMainWindow>中引用了 stdio.h 的标准库文件 都出现了snprintf vsnprintf 未定义问题

对策

既然 #define 的作用域是垂直向下的,那么只要把 stdio.h 从其下方排除即可

为了最大限度避免修改头文件

我们需要用到另一个性质:

头文件一半为了防止重复包含,一般会写上:

1
2
3
4
#ifndef STH_H
#define STH_H
...
#endif

所以头文件不会二次引用

也就是说,只要在 那个罪恶的 #define 之前 写下 #include<stdio.h>

1
2
#include <stdio.h>
#include "pcap.h"

这样 pcap.h 中的 #include <stdio.h> 就不会生效 也不会产生替换了

而其他位置的snprintf vsnprintf 替换并无大碍(我猜的),因为都有定义

或者

1
2
#include <QMainWindow>
#include "pcap.h" //或者让<QMainWindow>(里面可能包含stdio)置于pacp.h前面,阻止对stdio.h中某些函数的重定义

peace

Reference

使用Winpcap示例程序的时候,会出现很多错误(转)_天道酬勤-CSDN博客


WinPcap in Qt - _snprintf等函数冲突定义问题
https://mrbeancpp.github.io/2021/11/30/WinPcap-in-Qt-_snprintf等函数冲突定义问题/
作者
MrBeanC
发布于
2021年11月30日
许可协议