c++dllc++builderdynamic-linkingc++builder-xe7

How to link a DLL to my main project? (Getting unresolved external error)


I'm still learning some uses of the C++ language.

So, I decided to create my library (dynamic) and import it to my project. I've followed some steps of a tutorial in the internet, but I aways have the error of unresolved external...

Let me go to the DLL project:

File1.cpp:

#include "MathFuncsDll.h"
#include <stdexcept>

using namespace std;

namespace MathFuncs
{
    double MyMathFuncs::Add(double a, double b)
    {
        return a + b;
    }

    double MyMathFuncs::Subtract(double a, double b)
    {
        return a - b;
    }

    double MyMathFuncs::Multiply(double a, double b)
    {
        return a * b;
    }

    double MyMathFuncs::Divide(double a, double b)
    {
        if (b == 0)
        {
            throw invalid_argument("b cannot be zero!");
        }

        return a / b;
    }
}

MathFuncs.h:

#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport)
#else
#define MATHFUNCSDLL_API __declspec(dllimport)
#endif

namespace MathFuncs
{
    // This class is exported from the MathFuncsDll.dll
    class MyMathFuncs
    {
    public:
        // Returns a + b
        static MATHFUNCSDLL_API double Add(double a, double b);

        // Returns a - b
        static MATHFUNCSDLL_API double Subtract(double a, double b);

        // Returns a * b
        static MATHFUNCSDLL_API double Multiply(double a, double b);

        // Returns a / b
        // Throws const std::invalid_argument& if b is 0
        static MATHFUNCSDLL_API double Divide(double a, double b);
    };
}

Result: Succeedfully compilled (Got Project1.dll and Project1.lib files).

Started a new console application with the following details:

File1.cpp:

// MyExecRefsDll.cpp
// compile with: /EHsc /link MathFuncsDll.lib

#include <iostream>
#include <Windows.h>

#include "MathFuncsDll.h"

using namespace std;

int main()
{
    double a = 7.4;
    int b = 99;

    try {

        LoadLibrary(TEXT("MathFuncsDll.dll")); // Also tried without TEXT();

        cout << "a + b = " <<
            MathFuncs::MyMathFuncs::Add(a, b) << endl;
        cout << "a - b = " <<
            MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
        cout << "a * b = " <<
            MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
        cout << "a / b = " <<
            MathFuncs::MyMathFuncs::Divide(a, b) << endl;

        try
        {
            cout << "a / 0 = " <<
                MathFuncs::MyMathFuncs::Divide(a, 0) << endl;
        }
        catch (const invalid_argument &e)
        {
            cout << "Caught exception: " << e.what() << endl;
        }
    }
    catch (...){
            cout << "Problem when loading dll file" << endl;
    }

    system("pause");

    return 0;
}

PS.:

I also tried without the LoadLibrary() function.

Something I also tried: ->Added the .lib, .h, .dll files in the project;

->Added the .lib, .h, .dll files in the same folder of the console application folder;

->Added the .lib, .h, .dll files in the references of theproject (C++ shared options).

What I think: The MathFuncsDLL.h is being read by the compiler, once it finds the functions/classes when I'm writing the code of the main program.

Problems I got untill now:

[ilink32 Error] Error: Unresolved external 'MathFuncs::MyMathFuncs::Add(double, double)' referenced from C:\USERS\MAURO\DESKTOP\PROJETO\WIN32\DEBUG\FILE1.OBJ

[ilink32 Error] Error: Unresolved external 'MathFuncs::MyMathFuncs::Subtract(double, double)' referenced from C:\USERS\MAURO\DESKTOP\PROJETO\WIN32\DEBUG\FILE1.OBJ

[ilink32 Error] Error: Unresolved external 'MathFuncs::MyMathFuncs::Multiply(double, double)' referenced from C:\USERS\MAURO\DESKTOP\PROJETO\WIN32\DEBUG\FILE1.OBJ

[ilink32 Error] Error: Unresolved external 'MathFuncs::MyMathFuncs::Divide(double, double)' referenced from C:\USERS\MAURO\DESKTOP\PROJETO\WIN32\DEBUG\FILE1.OBJ

Details of the compiler: -> C++ builder XE7.

Since now, thanks a lot.


Solution

  • You use of LoadLibrary() is wrong and useless. You are not passing the returned module handle to GetProcAddress() to load the DLL functions dynamically. So remove the call to LoadLibrary().

    Your console code is trying to link statically to the DLL functions. To resolve the references, you need to add the DLL's .lib file to your console project, either in the Project Manager, or via a #pragma comment(lib, Project1.lib) statement in your code. It is not enough for the .lib file to be present in the console project's folder.

    That being said, your DLL should not be trying to export namespaced class static functions to begin with. Export flat C-style standalone functions instead. Your header can provide a namespaced wrapper class for use in C++, just don't export it.

    It is also not safe to throw exceptions (especially class-based exceptions) over the DLL boundary, either. You need to get rid of that completely. In the case of Divide(), either make the caller validate that it never passes b=0, or else change the signature of Divide() to return a bool indicating success/failure and use a separate output parameter to return the division result.

    Try something more like this:

    MathFuncsDll.cpp:

    #define MATHFUNCSDLL_EXPORTS
    #include "MathFuncsDll.h"
    
    double MathFuncs_Add(double a, double b)
    {
        return a + b;
    }
    
    double MathFuncs_Subtract(double a, double b)
    {
        return a - b;
    }
    
    double MathFuncs_Multiply(double a, double b)
    {
        return a * b;
    }
    
    double MathFuncs_Divide(double a, double b)
    {
        return a / b;
    }
    /* alternatively:
    bool MathFuncs_Divide(double a, double b, double *result)
    {
        if (b == 0) return false;
        if (result) *result = a / b;
        return true;
    }
    */
    

    MathFuncsDll.h:

    #ifndef MathFuncsDllH
    #define MathFuncsDllH
    
    #ifdef MATHFUNCSDLL_EXPORTS
    #define MATHFUNCSDLL_API __declspec(dllexport)
    #else
    #define MATHFUNCSDLL_API __declspec(dllimport)
    #endif
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    // Returns a + b
    MATHFUNCSDLL_API double MathFuncs_Add(double a, double b);
    
    // Returns a - b
    MATHFUNCSDLL_API double MathFuncs_Subtract(double a, double b);
    
    // Returns a * b
    MATHFUNCSDLL_API double MathFuncs_Multiply(double a, double b);
    
    // Returns a / b
    MATHFUNCSDLL_API double MathFuncs_Divide(double a, double b);
    // alternatively: bool MathFuncs_Divide(double a, double b, double *result);
    
    #ifdef __cplusplus
    }
    
    #include <stdexcept>
    
    namespace MathFuncs
    {
        class MyMathFuncs
        {
        public:
            static Add(double a, double b) { return MathFuncs_Add(a, b); }
            static double Subtract(double a, double b) { return MathFuncs_Subtract(a, b); }
            static double Multiply(double a, double b) { return MathFuncs_Multiply(a, b); }
            static double Divide(double a, double b)
            {
                if (b == 0)
                    throw std::invalid_argument("b cannot be zero!");
                return MathFuncs_Divide(a, b);
                /* alternatively:
                double result;
                if (!MathFuncs_Divide(a, b, &result))
                    throw std::invalid_argument("b cannot be zero!");
                return result;
                */
            }
        };
    }
    #endif
    
    #endif
    

    File1.cpp:

    #include <windows.h>
    #include <iostream>
    
    // if you don't add the DLL .lib file to the project using the Project Manager,
    // uncomment this statement ...  either way, you really should rename your DLL
    // project to something more meaningful then "Project1" ...
    // #pragma comment(lib, "Project1.lib")
    
    #include "MathFuncsDll.h"
    
    int main()
    {
        double a = 7.4;
        int b = 99;
    
        try
        {
            std::cout << "a + b = " << MathFuncs::MyMathFuncs::Add(a, b) << std::endl;
            std::cout << "a - b = " << MathFuncs::MyMathFuncs::Subtract(a, b) << std::endl;
            std::cout << "a * b = " << MathFuncs::MyMathFuncs::Multiply(a, b) << std::endl;
            std::cout << "a / b = " << MathFuncs::MyMathFuncs::Divide(a, b) << std::endl;
    
            try
            {
                std::cout << "a / 0 = " << MathFuncs::MyMathFuncs::Divide(a, 0) << std::endl;
            }
            catch (const std::invalid_argument &e)
            {
                std::cout << "Caught exception: " << e.what() << std::endl;
            }
        }
        catch (...)
        {
            std::cout << "Problem when loading dll file" << std::endl;
        }
    
        system("pause");
    
        return 0;
    }