前情提要
众所周知,在Windows中访问底层网络需要用到WinPcap这个工具
比如,Wireshark(网络封包分析软件)就用到了WinPcap
那么,如何在Qt平台中使用WinPcap呢?
安装 WinPcap for Qt
众所周知,C++想要使用库,就必须提供.h .lib .dll等文件,那么只要下载对应文件即可
目的是安装相关驱动和dll,安装完成之后基于winpcap的应用程序才能够正常运行
解压即可得到 Include & Lib 文件夹
From windows下安装配置winpcap - sunnycs - 博客园
接下来,我们需要在Qt中引用这些文件(假设解压目录:E:\WpdPack)
在.pro文件中添加:
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个报错:
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.h の pcap/pcap.h の pcap-stdinc.h
发现了令人震惊的一幕:
#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
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 从其下方排除即可
为了最大限度避免修改头文件
我们需要用到另一个性质:
头文件一半为了防止重复包含,一般会写上:
#ifndef STH_H
#define STH_H
...
#endif
所以头文件不会二次引用
也就是说,只要在 那个罪恶的 #define 之前 写下 #include<stdio.h>:
#include <stdio.h>
#include "pcap.h"
这样 pcap.h 中的 #include <stdio.h> 就不会生效 也不会产生替换了
而其他位置的snprintf vsnprintf 替换并无大碍(我猜的),因为都有定义
或者
#include <QMainWindow>
#include "pcap.h" //或者让<QMainWindow>(里面可能包含stdio)置于pacp.h前面,阻止对stdio.h中某些函数的重定义
peace