Context
Im working on a project designed to send certain commands to a device. Each device can be interfaced with a dll (e.g. deviceADll.h, deviceBDll.h) and the Dll's were not programmed by me, nor can I modify them in any way. I am in charge of integrating DeviceB to the project, with minimal changes to the structure of the project. I know the structure may not be optimal and/or well designed, so I am willing to take suggestion concerning that matter as a last resort solution.
Since the devices are very similar, all Dll functions have the same name, and often the same prototype.
Also because of this, I made a parent class (Device_ts.h), from which DeviceA_ts.h and DeviceB_ts.h inherit (I also have a factory class for the Devices, but I don't think that it's relevant to my problem).
Problem
The problem occurs when I try to include both Dlls: the project compiles, but I get a
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
followed by a
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
and a
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
Has anyone experienced a similar situation? Should I ignore those warning or will I not be able to call DeviceB.h
functions since their definitions are ignored?
I am using Visual Studio 2010, the Device_ts.h
library I am writing is a static library and all the project's parameters (e.g. /MD, include directories, dependencies, MFC, etc) are set properly from what I found in my research for this problem.
Code
The include and code looks like this (I will only show one of the functions that cause the warning since I get the same error on 50 functions):
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_
This is a good use-case for manual DLL loading, using LoadLibrary()
and GetProcAddress()
.
You'll have to manage a function pointer for each function looked up this way, which is a bit of a pain, but bypassing the OS's dll loading gives you a lot of flexibility.
Also note that you do not need to link against the DLL when using this method, the dll binding is 100% runtime, and the linker is not involved at all.
Here's an example:
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;
}
Edit: Basically, I suggest you treat the device DLLs as plugins, rather than dynamic libraries.