IDE /skipping incompatible xxx_d.dll when searching for -lxxx_d

news2024/11/19 15:24:23

文章目录

  • 概述
  • 场景复现
  • 用以测试的代码
  • 编译器位数不匹配导致?
  • 保持编译器类型一致
  • 再验证编译器位数的影响
  • MingW下调用OS的库咋不告警?
    • 以mingW下使用winSocket为例
    • MingW下网络编程的头文件分析
    • 该环境下链接的ws2_32库文件在哪里?
    • mingW为啥可以兼容window下的动态库

概述

该部分内容,是从《IDE/在Qt Creator (pro文件) 下DLL动态库的部署和加载问题分析》中独立出来的。
如下是首次遇到该问题时的记录,大约是5年前了。现在只能看出,当时调用DLL调用者程序使用的是 mingW 编译器,至于是引用的哪个动态库,以及这个动态库是使用MSVC还是使用MingW 编译的,都已无从知晓。
E:/Qt/Qt5.12.9/Tools/mingw730_64/bin/…/lib/gcc/x86_64-w64-mingw32/7.3.0/…/…/…/…/x86_64-w64-mingw32/bin/ld.exe: skipping incompatible D:\MMM\bin/xxx_d.dll when searching for -lxxx_d
当时只简单记录了猜测:如上问题是发生在从32位切换到64位程序时,因此可能是动态库位数不一致导致的。
后来在编写《Qt Quick /将C/C++中的枚举定义导出到Qml中》的 Demo 时,我又遇到了类似错误,因此来整理了此篇文章。

场景复现

编写《Qt Quick /将C/C++中的枚举定义导出到Qml中》的 Demo 时, 我先构建了名为DLL_Of_C 的动态库工程,然后使用之前的某个名为QmlA的项目部署和调用它。
我在起初犯了两个乌龙错误,
1、没有注意到 DLL_Of_C项目, 是默认x86平台而不是x64平台。
2、这个以前的项目 QmlA 实际上是使用的 Qt Creator + Qt_5_12_8_MinGW_64_bit 集成开发环境,我却误以为它是使用的 Qt Creator + MSVC 141集成开发环境。
当时在QmlA项目中pro工程文件中的相关配置如下。这个配置并无任何问题。

# 设置文件生成路径 /该路径包含其依赖的dll文件
DESTDIR = ../bin
# 设置依赖的动态链接库 
LIBS += -L../bin -lDLL_Of_C

在上述境况下,编译报错,
在这里插入图片描述
: -1: error: skipping incompatible …\bin/DLL_Of_C.dll when searching for -lDLL_Of_C

最开始我走的是老路,怀疑是不是路径写法啥的不对啊?哈哈,于是从翻出了概述中的那段草稿。原来一毛一样的错误提示,早些年就遇到过,还做了记录,只是没有验证。要注意,incompatible 并不是找不到,而是找到了但不匹配!
incompatible
adj. 不相容的,不能共存的;不能和谐相处的,合不来的;不兼容的,互斥的 n. 互不相容的人或事物
从 “不兼容的” 这个方向上继续分析,终于又发现了第二个乌龙错误:DLL是使用MSCV编译的,而QmlA项目实际上使用了MingW工具集,不是MSVC。

用以测试的代码

近来不忙,于是编写了单独的测试代码来验证和解决此问题。

动态库,
使用 VS2017 - VC++ - Windows 桌面 - 动态库链接(DLL),新建名为 DllByMsvc 项目。配置不使用预编译头,不关注dllmian实现。配置项目属性 - 常规 - 输出目录为 …/bin/ ;配置项目属性 - C/C++ - 预处理器 - 预处理器定义,增加 COMM_LIBRARY 宏定义;视测试需要选择平台为 x64 或 x86。

//dll_c_if.h
//#pragma once //确保头文件只被编译一次,防止重复包含 /仅VS适用
#ifndef DLL_C_IF_H_
#define DLL_C_IF_H_

#include <string>

#ifdef COMM_LIBRARY
#ifdef _WIN32 
#define COMM_API_EXPORT /*extern "C"*/ __declspec(dllexport) 
#else
#define COMM_API_EXPORT __attribute__((visibility("default")))
#endif
#else
#ifdef _WIN32
#define COMM_API_EXPORT /*extern "C"*/ __declspec(dllimport)
#else
#define COMM_API_EXPORT __attribute__((visibility("hidden")))
#endif
#endif

//接口中使用了非C的std::string //若强行声明 extern "C" 会有编译告警
//#ifdef __cplusplus
//extern "C" {
//#endif

    enum EnumRegID {
        ID_E_REG_M = 10,
        ID_E_REG_N,
        ID_E_REG_P
    };

    //dll导出的接口
    COMM_API_EXPORT std::string RegTable_Name(EnumRegID u16RegID);

//#ifdef __cplusplus
//}
//#endif

#endif // DLL_C_IF_H_
//dll_c_if.cpp
#include "dll_c_if.h"

//函数接口实现
std::string RegTable_Name(EnumRegID u16RegID)
{
    return "river.qu@" + std::to_string(u16RegID);
}

可执行程序,
在 Qt Creator 新建 Qt 控制台应用程序, 项目名 UseDllInQt。视测试需求配置为mingW64或msvc64编译套件。

//pro文件节选
HEADERS += \
    dll_c_if.h

# 设置文件生成路径 /该路径包含其依赖的dll文件
DESTDIR = ../bin

# 设置依赖的动态链接库
LIBS += -L../bin -lDllByMsvc
//mian.cpp
#include <QCoreApplication>
#include <QDebug>
#include "dll_c_if.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug()<< QString::fromLocal8Bit(RegTable_Name(ID_E_REG_N).c_str());

    return a.exec();
}

相关目录配置如下,根目录 E:\DLLTest,其下设子目录:
bin目录;库项目 DllByMsvc;Qt 控制台应用程序项目目录 UseDllInQt;两个工程的输出目录都配置指向 bin 目录。

编译器位数不匹配导致?

按照场景复现中提到的那样,假设此时我还没有意识到所有的错误。首先我将 DllByMsvc 项目配置为 x86平台进行编译生成。项目 UseDllIQt 使用 mingW64套件,执行编译。
在这里插入图片描述
假设我现在意识到了生产DLL的编译器和调用DLL的编译器,其位数是不一样的,并怀疑它。于是乎,我修改 DllByMsvc 工程的目标平台为 x64 并重新生成,再重新编译使用它的 UseDllIQt 项目。
在这里插入图片描述
依然存在编译警告,但此处不再提示 incompatible 那样的内容。

仅针对我遇到的上述问题,测试分析至此,可以断定:
编译错误 “跳过 incompatible 不兼容的动态库”,其主要原因在于 Dll实现者 与DLL使用者,在编译器平台位数上的不一致。但同时,即使位数是一致的,若编译器类型不一致,也有有编译错误,而且是更加隐晦的提示。

写在前面,
解决标题中问题的最终方案是:使得 <DLL实现项目> 与 <DLL使用项目>,其编译器平台位数和编译器类型保持一致。 下文是得出此结论的过程。

保持编译器类型一致

如下,在 UseDllInQt 项目 - Buid & Run 下双击 MSVC2015_64,即可切换到对应的Kits上,(我这里没有安装Qt的MSVC2017库)
在这里插入图片描述
使用 VS2017平台x64重新编译生成 DllByMsvc 项目,在 UseDllInQt 项目(已选用MSVC编译器)中部署调用,使用前文配置的 LIBS,编译情况如下,
: -1: error: LNK1146: 没有用选项“/LIBPATH:”指定的参数
这又是闹哪门子,
经过排查发现,实际的pro文件中,我画蛇添足,将 LIBS += -L…/bin -lDllByMsvc 错误的写成了 LIBS += -L …/bin -lDllByMsvc,问题在于-L 和 …/bin 路径之间是不可以有空格。从这里我们也可基本猜测,LIBS使用 -L参数,与直接使用LIBPATH编写配置,其效果应是一致的。修改后,再次重新编译,没有告警。

附加的对LIBS配置插入几句,
DllByMsvc 项目设置输出目录后,不仅会将 DLL_Of_C.dll 文件生成在该目录下,DllByMsvc.lib、DllByMsvc.ilk、DllByMsvc.exp 等编译结果都生成在了该目录下。其中在编译阶段,起到作用的是 DllByMsvc.lib 而不是 DllByMsvcdll 文件。
也即在在msvc编译器下, pro工程文件中配置的 LIBS,其作用是告诉链接器在指定的库文件路径中查找所需的库文件。
相关更细致的讲解,可参考 《IDE/在Qt Creator (pro文件) 下DLL动态库的部署和加载问题分析》中的章节。

至此,我们已经明晰并解决了 skipping incompatible xxx_d.dll when searching for -lxxx_d 问题。

再验证编译器位数的影响

在上一节的测试基础(UseDllInQt 项目使用Qt Creator + MSVC 64 编译器)上,将DLL工程修改回使用x86平台,重新编译生成DLL实现项目和DLL使用项目,异常如下,
在这里插入图片描述
DLL_Of_C.lib(DLL_Of_C.dll): -1: error: LNK1112: 模块计算机类型“X86”与目标计算机类型“x64冲突

要注意的是,不同于本文一开始的原始场景,此时 DLL实现项目和DLL使用项目编译器类型是一样的,仅仅是编译器的平台位数不一致。此时的编译报错,也与原始场景中完全不一致。
因此,导致 “skipping incompatible xxx_d.dll when searching for -lxxx_d” 编译的原因,应该描述为:它是 DLL实现项目和DLL使用项目编译器类型及编译器平台位数都不一致的综合结果。
在我的草稿中,还记录着一些内容,说是有网友在Linux上的一些实践,只是编译器类型不一致的情况下就会出现 skipping incompatible 类似的编译错误,我这里没有实际验证过,仅备案。

MingW下调用OS的库咋不告警?

我比较清晰的记着,几年前我是在 Qt Creator + MingW 集成开发环境下实践过Windows网络编程的,只需要在 pro 中包含 ws2_32 库即可,也使用过 user32 等系统库。当时还留下过疑问:
为啥Windows OS的库能在MingW下调用,而没有本文中描述的那些问题。我自己在MSCV下编写的库,事事就这么多?

以mingW下使用winSocket为例

如下,新建名为 WinProgramInMingW 的工程,使用 Qt_5_12_8_MinGW_64_bit 开发套件。示例将试图在该项目下使用 Windows 网络编程类和接口。为了更好的说明,先故意制造个错误,
在这里插入图片描述
很容易知道,这是因为没有在 pro 下配置包含WSADATA实现的库。可修改pro文件,

# 设置依赖的动态链接库
LIBS += -L../bin    \   #自定义库目录
        #-lDllByMsvc \  #MSVC编译的自定库文件
        -lws2_32        #OS库文件

如上,引用 ws2_32 库后,将编译正常,不会出现 skipping incompatible 等问题。以前不知道原因,总觉很奇怪。接下来,我们将从<windows.h>头文件和该项目的Makefile文件下手,来分析为啥不告警。

MingW下网络编程的头文件分析

先看看MingW环境下的 windows.h 头文件
在main.cpp下的头文件包含行上执行跳转,可以发现该 windows.h 文件位于 D:\Qt\Qt5.12.8\Tools\mingw730_64\x86_64-w64-mingw32\include 目录下,并不是系统目录下的,也不是VS安装目录下的。打开一些包含网络编程的VS下的项目,查看同名文件,会发现他们位于,C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um、C:\Program Files (x86)\Windows Kits\8.1\Include\um 等Windows SDK的安装目录。Windows SDK 可以随VS安装,也可以独立安装,要详细了解它们,可以参考 《IDE/记录VS2015&WinSDK安装过程中增删的系统组件和环境变量》、《IDE/Windows SDK /Windows CDB调试器安装和使用总结》等文章。姑且先分析到这。

我们在 WSADATA 这个结构类型上跳转,发现其位于,
D:\Qt\Qt5.12.8\Tools\mingw730_64\x86_64-w64-mingw32\include\psdk_inc 的 _wsadata.h 头文件中。而我们在Windows编程中常用的 winsock.h 、 winsock2.h 位于 D:\Qt\Qt5.12.8\Tools\mingw730_64\x86_64-w64-mingw32\include 目录下。也就是说,MingW为了提供Windows网络编程能力,它自己定义了全部相关头文件,这样说可能有点欠妥,请继续往后看。

基于上述头文件的分析,我想说,难不成MingW自己也实现了Windows的套接字编程?

该环境下链接的ws2_32库文件在哪里?

我想知道该环境下链接的ws2_32库文件是系统的还是MingW自己的? 打开该项目下的 Makefile.Debug 文件,查找 ws2_32 文件,只有一行,
LIBS = -L…\bin -lws2_32 D:\Qt\Qt5.12.8\5.12.8\mingw73_64\lib\libQt5Cored.a
由于不是全路径,这并不能告诉我 ws2_32 到底是哪里的。

MinGW(Minimalist GNU for Windows)是一个用于Windows平台的开源软件开发工具集,它使用GNU工具链,包括GCC编译器和一系列的工具。它的目标是使开发人员能够在Windows平台上使用GNU工具和编译器来编译和构建应用程序。
我在D:\Qt\Qt5.12.8\Tools\mingw730_64下搜索ws2_32库文件,可见 D:\Qt\Qt5.12.8\Tools\mingw730_64\x86_64-w64-mingw32\lib 存在一个 libws2_32.a 文件。可是我们知道,后缀.a的文件,是静态库文件,不是我们使用的动态库。至于这里的.a是不是真正的静态库文件,还是说他是与MSVC动态链接库的.lib引导文件相似,在这里我还拿不准,我后续可能会在 《IDE/在Qt Creator (pro文件) 下DLL动态库的部署和加载问题分析》继续整理它。

如果使用系统Everything搜索,可在系统几十个目录中看到它,有lib后缀有dll后缀。其中几个主要的:
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.14393.0\um\x64\WS2_32.Lib、C:\Windows\System32\ws2_32.dll
但我还是不知道,我的项目中到底链接了哪一个。还好,我想起了 Dependency Walker 依赖分析工具,
在这里插入图片描述
通过上述分析可以看出来,我引用的 ws2_32最终是链接到了 C:\Windows\System32\ws2_32.dll 操作系统下边的库,不是mingW的库,MingW也确实没有找到。也就是说,MinGW并没有重新实现套接字库,而是通过与系统的动态库进行链接来使用Windows的套接字编程接口。但这是如何做到的,我自己用MSVC开发的库,可以做到吗?

mingW为啥可以兼容window下的动态库

经过分析,我推测这种兼容性的实现主要基于如下两点:

  • 基于Windows操作系统的开放性和兼容性要求,Microsoft为其他编译器和工具链提供了一些兼容性支持,以确保它们可以在Windows系统中正常使用。
  • 对于像ws2_32.dll这样的系统库,在MinGW等工具链中使用时,通常会提供与MSVC兼容的封装和适配层。这些封装层的作用是将MinGW使用的编译和链接规范转换为与ws2_32.dll兼容的调用方式,以实现与Windows套接字编程接口的交互。
    这种兼容性是通过适当的编译选项、链接设置和调用约定的处理来实现的。MinGW编译器和工具链通过特定的设置和封装层,确保在编译和链接时正确地引用和调用ws2_32.dll中的函数和符号,以实现套接字编程。

虽然Windows系统的库,也极有可能是通过MSVC等工具生成的,毕竟是一家子嘛。但是人家是系统的,人家威望高,而MinGW的目标之一就是在Windows上运行和使用这些系统库,所以作为MingW的开发者,它必须去对这些系统的库做一些封装和适配。而我们自己通过MSVC编译生成的库,是不可能有这种待遇的。分析到这里,我终于有点释怀了,这太复杂,我有点力所不及。

即使MingW套件的开发者,已经尽力的去保证MinGW工具链可以去兼容ws2_32.dll等系统库,但由于编译器和工具链之间的差异,以及特定的编译和链接规范,可能会存在一些细微的差异和限制。因此,在使用MinGW或其他工具链时,建议确保适当的编译选项和设置,以及正确的库文件和版本,以确保与ws2_32.dll的兼容性和正确的套接字编程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/726485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL自治平台建设的内核原理及实践(下)

总第566篇 2023年 第018篇 本文整理自美团技术沙龙第75期的主题分享《美团数据库攻防演练建设实践》&#xff0c;系超大规模数据库集群保稳系列&#xff08;内含4个议题的PPT及视频&#xff09;的第4篇文章。 本文作者在演讲后根据同学们的反馈&#xff0c;补充了很多技术细节&…

【Web狗自虐系列1】Pwn入门之初级ROP

0x0 栈介绍 栈式一种典型的后进先出的数据结构&#xff0c;其操作主要有压栈(push)与出栈(pop)两种操作 压栈与出栈都是操作的栈顶 高级语言在运行时都会被转换为汇编程序&#xff0c;在汇编程序运行过程中&#xff0c;充分利用了这一数据结构。每个程序在运行时都有虚拟地址…

国产化适配再进一步,融云完成欧拉、TDSQL、优炫等多方适配

近期&#xff0c;融云完成了与开源操作系统欧拉&#xff08;openEuler&#xff09;、企业级数据库 TDSQL 和优炫的适配工作&#xff0c;国产化上下游生态适配之路再次迈进坚实一步。关注【融云 RongCloud】&#xff0c;了解协同办公平台更多干货。 欧拉&#xff08;openEuler&a…

DoTween 学习

部分参考&#xff1a;DOTween中文详解&#xff08;持续更新&#xff09;_一条爱玩游戏的咸鱼的博客-CSDN博客 官方文档&#xff1a;DOTween - Documentation (demigiant.com) 什么是Tween&#xff08;补间&#xff09; 补间&#xff0c;一般指补间动画&#xff0c;例如uni…

Ceph集群的部署

一、Ceph集群的部署 1、集群环境 1.1 集群架构 主机名业务IP存储IP服务器配置系统类型集群角色ceph-mon1-deploy172.17.10.61/16192.168.10.61/242C/4GUbuntu1804mondeploy(部署节点)ceph-mon2172.17.10.62/16192.168.10.62/242C/4GUbuntu1804mon(监控节点)ceph-mon3172.17.…

fun函数方法体=返回值,kotlin

fun函数方法体返回值&#xff0c;kotlin var str: String "fly"fun main(args: Array<String>) {println(getMyString())println(getMyInt())str "phil"println(getMyString())println(getMyInt()) }fun getMyInt(): Int {return if (str.equals(&…

javaweb学习4

作业 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script type"text/javascript">//动态绑定表单提交window.onloadfunction (){//得到form2的dom对象var fo…

多元分类预测 | Matlab基于北方苍鹰优化深度置信网络(NGO-DBN)的分类预测,多输入模型,NGO-DBN分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab基于北方苍鹰优化深度置信网络(NGO-DBN)的分类预测,多输入模型,NGO-DBN分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可…

在IDEA中如何查看svn版本提交历史记录

1.查看svn版本历史记录方法一 2.查看svn版本历史记录方法二 ①拉取远程库代码 ②提交本地代码到远程 ③查看历史记录 ④回退代码 3.查看历史版本记录的提交 4.选择上图中某次提交记录再次选图中序号4

python接口自动化(十九)--Json 数据处理---实战(详解)

简介 上一篇说了关于json数据处理&#xff0c;是为了断言方便&#xff0c;这篇就带各位小伙伴实战一下。首先捋一下思路&#xff0c;然后根据思路一步一步的去实现和实战&#xff0c;不要一开始就盲目的动手和无头苍蝇一样到处乱撞&#xff0c;撞得头破血流后而放弃了。不仅什么…

模块联邦实践

在聊模块联邦之前&#xff0c;我们先了解下在多个项目下&#xff0c;前端模块如何复用的 跨项目模块复用方案 1、npm 包包管理工具对于前端应用来说不可或缺&#xff0c;我们可以将模块上传到 npm 包&#xff0c;在需要的项目中引入&#xff0c;以此来复用一些公用模块。 2、mo…

基于CANN的AI推理最佳实践丨多路极致性能目标检测应用设计解密

当前人工智能领域&#xff0c;最热门的无疑是以ChatGPT为代表的各种“新贵”大模型&#xff0c;它们高高在上&#xff0c;让你无法触及。但在人们的日常生活中&#xff0c;实际应用需求最大的还是以Yolo模型为代表的目标检测“豪强”&#xff0c;它们每天都在以各种方式落地、应…

javascript匿名函数之立即调用函数

今天在看youtube的前端代码时发现了一个很奇怪的写法&#xff0c;从来没见过&#xff0c;代码如下&#xff1a; (function(e, c, l, f, g, h, k) {var d window;d.getInitialData function() {var b window;b.ytcsi && b.ytcsi.tick("pr", null, "&…

TensorFlow基础和入门案例

TensorFlow简介 TensorFlow是目前主流深度学习框架之一&#xff0c;其库中几乎包含了所有机器学习和深度学习相关的辅助函数和封装类&#xff0c;官方文档如下图所示。在其框架下做各种神经网络算法的开发可以极大减轻工作量&#xff0c;在入门阶段可以不需要深入理解相关优化…

spring拦截器参数及多拦截器执行顺序讲解

1.拦截器中的参数 2.多拦截器执行顺序 如果全部返回true&#xff0c;则按照流程全部执行 如果3返回false&#xff0c;123的preHandler会执行&#xff0c;123的postHandler都不会执行&#xff0c;但是return为true的2和1的after会执行 如果2返回false 12的preHandler会执行 pos…

sql 模糊查询与查询时间范围 起止时间

上代码 <select id"page" resultType"com.sky.entity.Orders">select * from orders<where><if test"number!null and number!">and number like concat(%,#{number},%)</if><if test"phone!null and phone!&q…

选择排序、归并排序、快速排序

1.选择排序 选择排序算法的实现思路有点类似插入排序&#xff0c;也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素&#xff0c;将其放到已排序区间的末尾。 Java代码实现如下。 ps&#xff1a;选择排序的最好情况时间复杂度、最坏情况和平均情况…

vue子组件监听不到父组件数据变化;子组件获取不到父组件的异步数据

当父子组件嵌套使用时created 和mounted 生命周期的执行顺序是父created -> 子created -> 子mounted -> 父mounted&#xff0c;但是这只是针对同步代码&#xff0c;当生命周期中有异步接口时&#xff0c;那么就会变成父created未执行完 -> 子created -> 子mount…

web安全php基础_魔术常量__FUNCTION__与__METHOD__的区别

PHP 魔术常量 PHP 向它运行的任何脚本提供了大量的预定义常量。 不过很多常量都是由不同的扩展库定义的&#xff0c;只有在加载了这些扩展库时才会出现&#xff0c;或者动态加载后&#xff0c;或者在编译时已经包括进去了。 有八个魔术常量它们的值随着它们在代码中的位置改…

安装Pytorch及配置Pycharm

PyTorch是一个基于Torch的Python开源机器学习库&#xff0c;用于自然语言处理等应用程序。它主要由Facebookd的人工智能小组开发&#xff0c;不仅能够 实现强大的GPU加速&#xff0c;同时还支持动态神经网络&#xff0c;这一点是现在很多主流框架如TensorFlow都不支持的。 本文…