在VS 2015中使用Microchip的mpusbapi.h会产生"LNK2005 Already defined"错误



我正在编写类,必须从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()实现。

所以要做任何使船漂浮的事情。这是完成工作的一种快速而肮脏的方法:

  1. 创建一些源文件(也许是将LoadDLL()或等效的同一文件)和 copy paste paste 所有这些函数指针定义从当前 mpusbapi.h 中。该来源现在是将这些全球群体定义的地方。
  2. edit mpusbapi.h 并在每个定义的前面添加 extern,因此它仅变成声明,例如:

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

  4. 确定并致电LoadDLL()或使用LoadLibrary/GetProcAddress的任何内容加载和初始化所有全局功能指针,然后再使用其中任何一个。

那应该做到。如果您想以不同的方式组织代码,请将其全部包裹在一堂课中,可能使这些成员vars并委派给它们,或者生成和使用lib,而不是手动加载所有功能,等等当您完成一般步骤时,并且均未在标题中定义全球,该标题由多个源文件包含(或者通常是一般的)。

最新更新