0

I am writing class that has to import some of its functions from mpusbapi.dll file. Unfortunately, when I try to include ActuatorControl.h in the main file, I was given a LNK2005 already defined error,

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

The rest of the functions that were obtained from the mpusbapi.dll got the same message. I have included the guards for both mpusbapi.h and ActuatorControl.h I wrote this code in Visual Studio 2015.

The mpusbapi.h and mpusbapi.dll are obtained from Microchip Technology Incorporated.

Can some help me with this? I have been on it for hours. Here is my code.

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() {

}
Jason C
  • 38,729
  • 14
  • 126
  • 182
Edward H
  • 21
  • 1
  • Please [edit] your question to provide a [mcve]. – Baum mit Augen Feb 23 '17 at 02:08
  • 1
    I have edited it and included a verifiable example. It can be compiled and will give you the error – Edward H Feb 23 '17 at 02:23
  • I see a problem in `ActuatorControl.h` a header should never have `#include "stdafx.h"` – drescherjm Feb 23 '17 at 02:43
  • Thanks for the help. I just tried it and there is no difference because I configured my VC to not included preprocessed header. – Edward H Feb 23 '17 at 03:00
  • The OP has improved this question greatly with edits and I believe it deserves a few more upvotes, as it is now a well-formed question with a good example regarding a frustratingly poorly-designed API header. – Jason C Feb 23 '17 at 17:38

1 Answers1

1

So I took a look at mpusbapi.h and the example applications (from here, MCHPFUSB 1.3).

I'm going under the assumption that you are dynamically loading the functions at runtime with LoadLibrary, as it appears their binaries were built with Borland's compiler, and thus the stub LIB won't work as-is in VS (unless you have a VS build, or want to just manually generate it).


The short answer is: The header as-is isn't really suitable to be included from multiple files. It sort of works in their simple example applications but that's about it, so you'll have to roll your own. Despite the implications of their "documentation", to maintain your sanity you'll want to view mpusbapi.h as more of an example than anything generally useful.


The medium answer is: The header is really poorly designed. In particular it defines global variables, thus every source file that includes it will have those defined, and thus, your error. The reason they got away with it is this: They built everything with Borland's tools, likely including their examples, and unlike most other compilers, multiple definitions are merely a warning with Borland's linker. So they probably ignored the warnings, since it still worked. Now you pay the price when you use a saner linker. (Also the library is very old, I don't remember what VS did about multiple definitions back in 2004 or whenever so it's also possible that it used to accept this.)


The long answer / solution is: The things in that header aren't function declarations or typedefs, they're actual definitions of function pointer variables (which you are responsible for initializing with GetProcAddress). You have a lot of options but basically you're just going to want to start from scratch, using the function signatures given in the current header as a guide.

First, from the example applications (and I know you know this, but for the benefit of the general reader), here is how to load them dynamically. This applies in general cases as well:

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

Note that, looking at that implementation and comparing it to the header, it doesn't actually load all of the functions from the DLL. It only loads the one that that specific example application needed. So don't forget to load the rest of the functions you use.

So (and again assuming manual run-time linking, if you've got a VS lib you can just skip all this, link to the LIB, and include _mpusbapi.h [note the underscore] but not mpusbapi.h).

Now, like I said there's a lot of options, but they all have the same goals:

  • Define those function pointers once.
  • Declare them in a header so you can use them elsewhere (mpusbapi.h makes the mistake of defining them in the header).
  • When your program starts, populate the function pointers with LoadLibrary/GetProcAddress as in the LoadDLL() implementation above.

So do whatever floats your boat. Here's one quick and dirty-ish way to get the job done:

  1. Create some source file (perhaps the same one you put LoadDLL() or equivalent in) and copy + paste all those function pointer definitions from the current mpusbapi.h into it. That source is now where those globals will be defined.
  2. Edit mpusbapi.h and add extern in front of each definition, so that it merely becomes a declaration, e.g.:

    extern DWORD (*MPUSBGetDeviceCount)(PCHAR pVID_PID);
    
  3. You may now include your fixed mpusbapi.h anywhere you need to use those.

  4. Be sure and call LoadDLL() or whatever with LoadLibrary/GetProcAddress to load and initialize all your global function pointers once before you use any of them.

And that should do it. If you want to organize your code differently, wrap it all up in a class, possibly making those member vars and delegating to them, or generate and use a LIB instead of manually loading all the functions, etc., go right ahead, as long as you get the general steps done and don't define globals in a header that is included by multiple source files (or at all, in general).

Community
  • 1
  • 1
Jason C
  • 38,729
  • 14
  • 126
  • 182
  • 1
    Thanks for that comprehensive answer. I took your advice and not used mpusbapi.h file. I did my own run-time linking with the mpusbapi.dll and it worked! – Edward H Feb 23 '17 at 07:59
  • 1
    @EdwardH Note that you may want to go through the process of creating a corresponding LIB for VS anyways, especially if you plan on using this API again in a different program. It'll save you the trouble of having to manually do all the imports (import LIBs that correspond to DLLs are really just stub libraries that do all the `LoadLibrary`/`GetProcAddress` for you). Then you can just use *_mpusbapi.h* and call the functions as usual. The only con to using an import library like that is the program will fail to start if the DLL is not present, rather than you being able to detect at runtime. – Jason C Feb 23 '17 at 17:41