2017-02-23 63 views
0

我正在编写必须从mpusbapi.dll文件中导入其某些函数的类。不幸的是,当我尝试包括ActuatorControl.h在主文件中,我得到了LNK2005已经定义的错误,在VS 2015中使用Microchip的mpusbapi.h会产生“LNK2005已定义”错误

Error LNK2005 
"unsigned long (__cdecl* MPUSBGetConfigurationDescriptor)(void *,unsigned char,void *,unsigned long,unsigned long *)" ([email protected]@[email protected]) 
    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

请编辑您的问题以提供[mcve]。 –

+1

我编辑了它,并包含一个可验证的例子。它可以被编译,并会给你错误 –

+0

我在'ActuatorControl.h'看到一个问题一个标题不应该有'#包括“stdafx.h”' – drescherjm

回答

1

所以我看了看mpusbapi.h和示例应用程序(from here, MCHPFUSB 1.3)。

我打算假设您在运行时动态加载函数LoadLibrary,因为它看起来它们的二进制文件是用Borland编译器构建的,因此存根LIB将不会像VS中那样工作(除非你有一个VS版本,或者只想manually generate it)。


简短的回答是:现在的标题并不适合从多个文件中包含。它在他们简单的示例应用程序中有所作为,但这只是关于它,所以你必须推出自己的应用程序。尽管他们的“文件”的影响,为了保持你的理智,你会想要查看mpusbapi.h作为更多的示例比任何通常有用的。


中等答案是:头部设计的确很差。特别是它定义了全局变量,因此每个包含它的源文件都会定义这些变量,从而导致错误。他们逃避的原因是:他们用Borland的工具构建了所有的东西,可能包括他们的例子,并且与大多数其他编译器不同,多重定义只是Borland的链接器的警告。所以他们可能会忽略警告,因为它仍然工作。现在,您在使用更理想的链接器时付出代价。 (另外,图书馆是很老了,我不记得VS做了关于多个定义早在2004年或每当所以它也有可能是它用来接受这一点)


长的答案/解决方案是:该头文件中的内容不是函数声明或类型定义,它们是函数指针变量的实际定义(您负责使用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使得在标题定义他们的错误)。
  • 当程序启动时,与上述LoadDLL()实现一样,使用LoadLibrary/GetProcAddress填充函数指针。

因此,尽一切可能漂浮你的船。这里有一个快速和肮脏的十岁上下的方式来完成这项工作:

  1. 创建一些源文件(或许你把LoadDLL()或等值的相同)和复制+粘贴当前所有这些函数指针的定义mpusbapi.h。这个来源现在将定义这些全局变量。
  2. 编辑mpusbapi.h并在每个定义的前面加上extern,使其成为仅仅是一个宣言,如:

    extern DWORD (*MPUSBGetDeviceCount)(PCHAR pVID_PID); 
    
  3. 您现在可以包括您的固定mpusbapi.h任何地方,你需要使用这些。

  4. 请确保并拨打LoadDLL()或任何与LoadLibrary/GetProcAddress加载并初始化所有全局函数指针,然后再使用它们中的任何一个。

而且应该这样做。如果你想以不同的方式组织你的代码,把它们全部包装到一个类中,可能使这些成员变量和委托给它们,或者生成和使用LIB,而不是手动加载所有的函数等,直到前面,只要因为您已完成一般步骤,并且不会在由多个源文件(或者根本不包括在内)中包含的标题中定义全局变量。

+1

感谢您的全面解答。我接受了你的建议,并没有使用mpusbapi.h文件。我做了我自己的运行时连接与mpusbapi.dll,它的工作! –

+1

@EdwardH请注意,您可能希望经历为VS创建相应的LIB的过程,特别是如果您计划在不同的程序中再次使用此API。它将为您节省手动执行所有导入操作的麻烦(导入与DLL对应的LIB实际上只是为您执行所有'LoadLibrary' /'GetProcAddress'的存根库)。然后,您可以使用* _mpusbapi.h *并像往常一样调用函数。唯一使用这种导入库的方法是,如果DLL不存在,程序将无法启动,而不能在运行时检测到。 –