我正在编写必须从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;
}
执行器控件
#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() {
}
最佳答案
因此,我看了mpusbapi.h和示例应用程序(from here, MCHPFUSB 1.3)。
我假设您正在使用LoadLibrary
在运行时动态加载函数,因为它们的二进制文件似乎是使用Borland的编译器构建的,因此存根LIB在VS中无法按原样工作(除非您拥有VS版本,或者只想manually generate it)。
简短的答案是:标头原样确实不适合包含在多个文件中。它可以在他们的简单示例应用程序中工作,但仅此而已,因此您必须自己动手。尽管有其“文档”的含义,但为了保持理智,您将想将mpusbapi.h视为一个示例,而不是通常有用的任何示例。
中等答案是:标头确实设计不佳。特别是,它定义了全局变量,因此包含它的每个源文件都将定义这些变量,从而也将导致您的错误。他们之所以无法使用它,是因为:他们使用Borland的工具构建了所有内容,可能包括其示例,并且与大多数其他编译器不同,多重定义只是Borland链接器的警告。因此他们可能会忽略警告,因为它仍然有效。现在,当您使用更精巧的链接器时,您就可以支付价格。 (而且该库非常老,我不记得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)。
现在,就像我说的那样,有很多选择,但是它们都有相同的目标:
一次定义这些函数指针。
在标头中声明它们,以便您可以在其他地方使用它们(mpusbapi.h犯了在标头中定义它们的错误)。
程序启动时,如上述
LoadLibrary
实现中那样,在GetProcAddress
/ LoadDLL()
中填充函数指针。所以,只要您的船浮起就行。这是完成工作的一种快速而肮脏的方法:
创建一些源文件(也许与您放入
LoadDLL()
或等效文件的源文件相同),然后从当前mpusbapi.h中复制并粘贴所有这些函数指针定义。现在,将在这些来源中定义这些来源。编辑mpusbapi.h并在每个定义之前添加
extern
,使其仅成为声明,例如:extern DWORD (*MPUSBGetDeviceCount)(PCHAR pVID_PID);
现在,您可以在需要使用固定的mpusbapi.h的任何地方使用它们了。
在使用任何全局函数指针之前,请确保先调用
LoadDLL()
或使用LoadLibrary
/ GetProcAddress
进行任何操作,以加载并初始化所有全局函数指针一次。那应该做到的。如果您想以不同的方式组织代码,请将其全部包装在一个类中,可能使那些成员var并委派给他们,或者生成并使用LIB而不是手动加载所有函数等,那么就继续吧,只要当您完成常规步骤时,不要在多个源文件(或根本上没有)包含的标头中定义全局变量。
关于c++ - 在VS 2015中使用Microchip的mpusbapi.h会产生“LNK2005已定义”错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42405762/