【问题标题】:Linker warning "second definition ignored" when including two libraries with same function names包含具有相同函数名的两个库时,链接器警告“忽略第二个定义”
【发布时间】:2018-01-27 14:05:25
【问题描述】:

上下文

我正在从事一个旨在向设备发送某些命令的项目。每个设备都可以与一个 dll 接口(例如 deviceADll.h、deviceBDll.h),这些 Dll 不是我编写的,我也不能以任何方式修改它们。我负责将 DeviceB 集成到项目中,对项目结构的改动很小。我知道结构可能不是最优的和/或设计良好的,所以我愿意就此问题提出建议作为最后的解决方案。

由于设备非常相似,所有 Dll 函数都具有相同的名称,并且通常具有相同的原型。

也正因为如此,我创建了一个父类(Device_ts.h),DeviceA_ts.h 和 DeviceB_ts.h 都从该类继承(我也有一个 Devices 的工厂类,但我认为它与我的问题)。

问题

当我尝试包含两个 Dll 时出现问题:项目编译,但我得到一个

Warning 60 warning LNK4006: Connect@12 already defined in DeviceA.lib(DeviceA.dll); second definition ignored C:\project_path\DeviceB.lib(DeviceB.dll) Project_Name

后跟一个

Warning 61 warning LNK4006: __imp__Connect@12 already defined in DeviceA.lib(DeviceA.dll); second definition ignored C:\project_path\DeviceB.lib(DeviceB.dll) Project_Name

还有一个

Warning 62 warning LNK4221: This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library C:\project_path\DeviceB.lib(DeviceB.dll) Project_Name

有没有人遇到过类似的情况?我应该忽略这些警告还是因为它们的定义被忽略而无法调用 DeviceB.h 函数?

我正在使用 Visual Studio 2010,我正在编写的 Device_ts.h 库是一个静态库,并且所有项目的参数(例如 /MD、包含目录、依赖项、MFC 等)都根据我在我的研究这个问题。

代码

包含和代码如下所示(我将只显示导致警告的函数之一,因为我在 50 个函数上遇到相同的错误):

DeviceADll.h

#ifndef DEVICEA_H__
#define DEVICEA_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

namespace DeviceA
{
// some struct definition that don't cause the linker warnings
//...

// function definitions
extern "C" HANDLE PASCAL EXPORT Connect( HANDLE h_devA, const char *ip);
// ...
} // namespace DeviceA

DeviceBDll.h

#ifndef DEVICEB_H__
#define DEVICEB_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

namespace DeviceB
{
// some struct definition that don't cause the linker warnings
//...

// function definitions
extern "C" HANDLE PASCAL EXPORT Connect( HANDLE h_devB, const char *ip);
// ...
} // namespace DeviceB

Device_ts.h

#ifndef DEVICE_FCT_H_
#define DEVICE_FCT_H_
#ifndef EXPORT
#define EXPORT
#endif

#if _MSC_VER > 1000
#pragma once
#endif

#include "DeviceADll.h"
#include "DeviceBDll.h"

class CDevice {
public:
    virtual BOOL Connect(char *ip_addr) = 0;
};
#endif DEVICE_FCT_H_

【问题讨论】:

  • 您是否有权访问DeviceADll 和/或DeviceBDll 的静态链接版本?如果是这样,您可以自己制作两个 DLL,每个链接到一个设备库,并且只导出不冲突的符号。
  • Dll 是由另一个团队开发的,因此它们必须保留为 Dll,这样未来的版本就不必在每个新版本中重新编写。通常情况下不会那么糟糕,但是这些文件每个有 5000 行,所以它真的不理想
  • 离题:注意那些双下划线。任何带有双下划线的标识符都保留给库实现使用。

标签: c++ visual-studio dll linker-warning


【解决方案1】:

这是手动加载 DLL 的一个很好的用例,使用 LoadLibrary()GetProcAddress()

您必须为以这种方式查找的每个函数管理一个函数指针,这有点麻烦,但绕过操作系统的 dll 加载会给您带来很大的灵活性。

另外注意,使用该方法时不需要对DLL进行链接,dll绑定是100%运行时,完全不涉及链接器。

这是一个例子:

typedef void (*connect_fn)(HANDLE, const char*);

connect_fn connect_a;
connect_fn connect_b;

int main()
{
  HINSTANCE dll_a = LoadLibrary("path_to_dll_a.dll");
  HINSTANCE dll_b = LoadLibrary("path_to_dll_b.dll");

  if (!dll_a || !dll_b) {
    return 1;
  }

  connect_a = (connect_fn)GetProcAddress(dll_a , "Connect");
  connect_b = (connect_fn)GetProcAddress(dll_b , "Connect");

  // connect_a and connect_b can now be used.
  return 0;
}

编辑:基本上,我建议您将设备 DLL 视为插件,而不是动态库。

【讨论】:

  • 感谢您的回答。我知道加载 Dll 是在“运行时”而不是编译时完成的。由于库(Device_ts.h)是静态库,它还能加载 Dll 吗?另外我想我应该将每个 Dll 加载到它们关联的子类(DeviceA_ts.h 和 DeviceB_ts.h)中,对吧?
  • @Max Dan Laplace 在“正常” DLL 使用期间,仍然会在编译时执行一些部分链接(构建了一个地址表,操作系统将在 dll 加载时归档该地址表)。这就是我所说的 DLL 的编译时链接。
  • 就在每个类中加载而言,这是一个取决于很多因素的设计决策。例如,如果所有 dll 都具有相同的符号,那么我将创建一个通用的 DeviceType 类,该类在其构造函数中接收 DLL 路径,并作为注入的依赖项传递给每个设备。
猜你喜欢
  • 1970-01-01
  • 2021-03-09
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多