我正在编写类,必须从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:UsersEdward HarsonoDesktopLinear ActuatorLinear ActuatorLinear Actuator.obj
1
从mpusbapi.dll获得的其余函数收到相同的消息。我包括mpusbapi.h和ActuatorControl.h的警卫。
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() {
}
,所以我看了 mpusbapi.h 和示例应用程序(从此处,MCHPFUSB 1.3)。
我将在使用LoadLibrary
的运行时动态加载功能的假设,因为看来它们的二进制文件是使用Borland的编译器构建的,因此Stub Lib在VS中无法正常工作(除非您具有VS构建,或者只想手动生成它)。
简短的答案是:标题AS-IS并不真正适合从多个文件中包含。它在他们简单的示例应用程序中起作用,但仅此而已,因此您必须自己滚动。尽管它们的"文档"有影响,但要维护您的理智,您将要查看 mpusbapi.h 比通常有用的任何事物都更多。。
中等答案是:标头的设计确实很差。特别是它定义了全局变量,因此每个包含在内的源文件都将具有定义的源文件,从而将其错误。他们之所以摆脱它的原因是:他们使用Borland的工具(可能包括示例)构建了所有内容,并且与大多数其他编译器不同,多个定义只是对Borland的Linker的警告。因此,他们可能忽略了警告,因为它仍然有效。现在,当您使用Saner Linker时,您要付出代价。(也很旧,我不记得VS在2004年或任何时候对多个定义做了什么,因此它也有可能接受它。)
长答案/解决方案是:标头中的事物不是函数声明或typedefs,它们是功能指针变量的实际定义(您负责使用GetProcAddress
初始化)。您有很多选择,但基本上,您只想从头开始启动,使用当前标题中给出的功能签名作为指南。
首先,从示例应用程序中(我知道您知道这一点,但是为了一般读者的好处),这是如何动态加载它们的方法。这在一般情况下也适用:
void LoadDLL(void)
{
libHandle = NULL;
libHandle = LoadLibrary("mpusbapi");
if(libHandle == NULL)
{
printf("Error loading mpusbapi.dllrn");
}
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 Errorrn");
}//end if else
}//end LoadDLL
请注意,查看该实现并将其与标题进行比较,它实际上并没有加载DLL中的所有功能。它仅加载该特定示例应用程序所需的一个。因此,不要忘记加载您使用的其余功能。
so(再次假设手动运行时链接,如果您有vs lib,则可以跳过所有这些,链接到lib,并包括 _mpusbapi.h ] 但不是mpusbapi.h )。
现在,就像我说的那样,有很多选择,但是它们都有相同的目标:
- 定义那些函数指针一次。
- 在标题中声明它们,因此您可以在其他地方使用它们( mpusbapi.h 将其错误定义在标题中的错误)。
- 当您的程序启动时,用
LoadLibrary
/GetProcAddress
填充函数指针,如上述LoadDLL()
实现。
所以要做任何使船漂浮的事情。这是完成工作的一种快速而肮脏的方法:
- 创建一些源文件(也许是将
LoadDLL()
或等效的同一文件)和 copy paste paste 所有这些函数指针定义从当前 mpusbapi.h 中。该来源现在是将这些全球群体定义的地方。 edit mpusbapi.h 并在每个定义的前面添加
extern
,因此它仅变成声明,例如:extern DWORD (*MPUSBGetDeviceCount)(PCHAR pVID_PID);
您现在可以在需要使用这些的任何地方包括固定的 mpusbapi.h 。
- 确定并致电
LoadDLL()
或使用LoadLibrary
/GetProcAddress
的任何内容加载和初始化所有全局功能指针,然后再使用其中任何一个。
那应该做到。如果您想以不同的方式组织代码,请将其全部包裹在一堂课中,可能使这些成员vars并委派给它们,或者生成和使用lib,而不是手动加载所有功能,等等当您完成一般步骤时,并且均未在标题中定义全球,该标题由多个源文件包含(或者通常是一般的)。