我正在编写必须从mpusbapi.dll文件导入其部分功能的类。不幸的是,当我尝试在主文件中包含ActuatorControl.h时,我得到了LNK2005已经定义的错误,
Error LNK2005
"unsigned long (__cdecl* MPUSBGetConfigurationDescriptor)(void *,unsigned char,void *,unsigned long,unsigned long *)" (?MPUSBGetConfigurationDescriptor@@3P6AKPAXE0KPAK@ZA)
already defined in ActuatorControl.obj
Linear Actuator
C:\Users\Edward Harsono\Desktop\Linear Actuator\Linear Actuator\Linear Actuator.obj
1
从mpusbapi.dll获得的其他函数都得到了相同的消息。我已经为mpusbapi.h和ActuatorControl.h提供了保护。我在Visual Studio 2015中编写了这段代码。
mpusbapi.h和mpusbapi.dll来自Microchip Technology Incorporated。
有人可以帮我吗?我已经上了好几个小时了。这是我的代码。
的main.cpp
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <windows.h>
#include "ActuatorControl.h"
int main()
{
ActuatorControl x;
return 0;
}
ActuatorControl.h
#pragma once
#ifndef _ActuatorControl_H_
#define _ActuatorControl_H_
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <windows.h>
#include <Dbt.h>
#include <tchar.h>
#include "mpusbapi.h"
class ActuatorControl {
private:
//----------------Global variables used in this application--------------------------------
public:
ActuatorControl();
~ActuatorControl();
};
#endif
ActuatorControl.cpp
#include "ActuatorControl.h"
ActuatorControl::ActuatorControl() {
HMODULE DLL = LoadLibrary(L"mpusbapi.dll");
if (DLL) {
MPUSBGetDLLVersion = (DWORD(*)(void)) GetProcAddress(DLL, "_MPUSBGetDLLVersion");
}
}
ActuatorControl::~ActuatorControl() {
}
答案 0 :(得分:1)
所以我看一下mpusbapi.h和示例应用程序(from here, MCHPFUSB 1.3)。
我假设您在运行时使用LoadLibrary
动态加载函数,因为看起来它们的二进制文件是使用Borland的编译器构建的,因此存根LIB赢了&# 39;在VS中按原样工作(除非你有VS版本,或者只想manually generate it)。
简短的回答是:标题按原样并不适合包含在多个文件中。它可以在他们简单的示例应用程序中运行,但是关于它,所以你必须自己动手。尽管他们的&#34;文档&#34;的含义,为了保持你的理智,你想要查看 mpusbapi.h 作为更多的示例而不是任何东西通常很有用。
中等答案是:标题的设计非常糟糕。特别是它定义了全局变量,因此包含它的每个源文件都将定义那些,因此,您的错误。他们摆脱它的原因是:他们使用Borland的工具构建了所有东西,可能包括他们的例子,与大多数其他编译器不同,多个定义仅仅是对Borland的链接器的警告。所以他们可能忽略了警告,因为它仍然工作。现在,当您使用saner链接器时,您需要付出代价。 (此外,图书馆已经很老了,我不记得VS在2004年或之前做过多次定义的事情,所以它也有可能接受这个。)
长答案/解决方案是:该标题中的内容不是函数声明或typedef,它们是函数指针变量的实际定义(您负责使用GetProcAddress
进行初始化) 。你有很多选择,但基本上你只是想从头开始,使用当前标题中给出的功能签名作为指南。
首先,从示例应用程序(我知道你知道这一点,但为了普通读者的利益),这里是如何动态加载它们。这也适用于一般情况:
void LoadDLL(void)
{
libHandle = NULL;
libHandle = LoadLibrary("mpusbapi");
if(libHandle == NULL)
{
printf("Error loading mpusbapi.dll\r\n");
}
else
{
MPUSBGetDLLVersion=(DWORD(*)(void))\
GetProcAddress(libHandle,"_MPUSBGetDLLVersion");
MPUSBGetDeviceCount=(DWORD(*)(PCHAR))\
GetProcAddress(libHandle,"_MPUSBGetDeviceCount");
MPUSBOpen=(HANDLE(*)(DWORD,PCHAR,PCHAR,DWORD,DWORD))\
GetProcAddress(libHandle,"_MPUSBOpen");
MPUSBWrite=(DWORD(*)(HANDLE,PVOID,DWORD,PDWORD,DWORD))\
GetProcAddress(libHandle,"_MPUSBWrite");
MPUSBRead=(DWORD(*)(HANDLE,PVOID,DWORD,PDWORD,DWORD))\
GetProcAddress(libHandle,"_MPUSBRead");
MPUSBReadInt=(DWORD(*)(HANDLE,PVOID,DWORD,PDWORD,DWORD))\
GetProcAddress(libHandle,"_MPUSBReadInt");
MPUSBClose=(BOOL(*)(HANDLE))GetProcAddress(libHandle,"_MPUSBClose");
if((MPUSBGetDeviceCount == NULL) || (MPUSBOpen == NULL) ||
(MPUSBWrite == NULL) || (MPUSBRead == NULL) ||
(MPUSBClose == NULL) || (MPUSBGetDLLVersion == NULL) ||
(MPUSBReadInt == NULL))
printf("GetProcAddress Error\r\n");
}//end if else
}//end LoadDLL
请注意,查看该实现并将其与标头进行比较,它实际上并未加载DLL中的所有函数。它只加载特定示例应用程序所需的那个。因此,不要忘记加载您使用的其他功能。
所以(并再次假设手动运行时链接,如果你有一个VS lib,你可以跳过这一切,链接到LIB,并包含 _mpusbapi.h [注意下划线] 但不是mpusbapi.h )。
现在,就像我说的那样,有很多选择,但它们都有相同的目标:
LoadLibrary
实现中的GetProcAddress
/ LoadDLL()
填充函数指针。所以你的船漂浮着什么。这是完成工作的一种快速而肮脏的方式:
LoadDLL()
或等效文件相同)和复制+粘贴当前 mpusbapi.h中的所有函数指针定义进入它。那个源现在是定义那些全局变量的地方。编辑 mpusbapi.h 并在每个定义前添加extern
,以便它只是一个声明,例如:
extern DWORD (*MPUSBGetDeviceCount)(PCHAR pVID_PID);
您现在可以将固定的 mpusbapi.h 包含在您需要的任何地方。
LoadDLL()
/ LoadLibrary
之前调用GetProcAddress
或其他任何内容来加载和初始化所有全局函数指针。那应该这样做。如果你想以不同的方式组织你的代码,可以将它们全部包装在一个类中,可能使这些成员变量并委托给它们,或者生成并使用LIB而不是手动加载所有函数等等,就这样,当你完成一般步骤并且没有在多个源文件包含的标题中定义全局变量(或者通常)。