1
votes

I am very new in C++ world and this kind of task. I am having a hard time to resolve this issue. Any help is kindly appreciated!

I have created a regular MFC DLL that is statically linked to MFC. (This is the first time i ever created a MFC project)

MFC DLL Header File Content

// Serial.h

#pragma once

#ifndef __AFXWIN_H__
    #error "include 'pch.h' before including this file for PCH"
#endif

#include "resource.h"       // main symbols

class SerialApp : public CWinApp
{
public:
    SerialApp();
    __declspec(dllexport) void __cdecl SerialApp::Init();

public:
    virtual BOOL InitInstance();
    DECLARE_MESSAGE_MAP()
};

MFC DLL Definition File Content

// Serial.cpp

#pragma once
#include "pch.h"
#include "framework.h"
#include "Serial.h"
#include <iostream>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

BEGIN_MESSAGE_MAP(SerialApp, CWinApp)
END_MESSAGE_MAP()

SerialApp::SerialApp()
{
}

SerialApp theApp;

BOOL SerialApp::InitInstance()
{
    CWinApp::InitInstance();
    return TRUE;
}

extern "C" 
{
    __declspec(dllexport) void __cdecl SerialApp::Init()
    {
        AfxDebugBreak();
        std::cout << "Succeded";
    }
}

Win32 Console Application Content

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

int main()
{   
    //Load the DLL
    HMODULE lib = LoadLibrary(L"D:\\C++ PROJECTS\\serial\\Debug\\Serial.dll");

    //Create the function
    typedef void(*Init_t)(void);
    Init_t Serial = (Init_t)GetProcAddress(lib, "Init");
    std::cout << GetLastError(); // This output's 127

    if (!Serial) {
        //ERROR.  Handle it.
    }

    __declspec(dllimport) void __cdecl Init(void);
    //Init(); //LNK2019 unresolved external symbol "__declspec(dllimport) void __cdecl Init(void)"

    std::getchar();
}

When i use this approach:

 __declspec(dllimport) void __cdecl Init(void);
 Init();

//I am getting the linker error LNK2019 unresolved external symbol "__declspec(dllimport) void __cdecl Init(void)"

When i use this approach:

typedef void(*Init_t)(void);

Init_t Serial = (Init_t)GetProcAddress(lib, "Init");
std::cout << GetLastError();

//I am getting error code 127 from the GetLastError() function. (The specified procedure could not be found)

When i use this approach:

#include "Serial.h"

int main()
{   
    SerialApp pSerial;
    pSerial.Init();
    std::getchar();
}

//Serial.h forces me to include 'pch.h' before itself. if i include 'pch.h' into Win32 Console App before Serial.h It end's up with about 1000's of errors.

For Win32 Console Application Project:

1. C/C++ -> General -> Additional Include Directories is set to: D:\C++ PROJECTS\serial\Serial

2. Linker -> Input -> Additional Dependencies is set to : Serial.lib

3. Both Serial.lib and Serial.dll are included in the project and in the Debug folder.

4. Both MFC Dll Project and Win32 Console Projects are compiled in Debug x86 mode.

Questions

  1. What i am doing wrong?
  2. Is this even possible?

Thank you!

1
__declspec(dllexport) void __cdecl SerialApp::Init() Is that a typo? The function that you try to __declspec(dllimport) is a free function, not a member of SerialApp.dxiv
For me none of them is a typo:) But it might be a typo. Can you please explain what i am doing wrong? How can i make the function a member of the class? Is the header declaration not enough to make it a member of the class? class SerialApp : public CWinApp { public: SerialApp(); __declspec(dllexport) void __cdecl SerialApp::Init(); Thank you!swartkatt
It's really simple, in main() you declare a function named Init(), for which you did not provide an implementation (definition), so the linker fails. The member function Init() is a different function. It can be invoked through a class instance (eg pSerialApp->Init()), or using the class name (ie SerialApp::Init()) if it was a static member function). Init() will generate code and try to link to function named Init(), that it not member of any class. It's the same as ::Init() (which is often used to tell the compiler to call a function in the global namespace rather the class).Constantine Georgiou
Thank you Constantine! How can i create a class instance of Serial.dll inside the Win32 Console Application? Being honest, i didn't understand what you mean. Can you please provide me an example? By doing __declspec(dllimport) void __cdecl Init(void); inside the main() function, I thought and hoped that the compiler can understand that i want to use the Init() method inside the Serial.lib and Serial.dll that i provide. > What is the problem with this part? typedef void(*Init_t)(void); Init_t Serial = (Init_t)GetProcAddress(lib, "Init");swartkatt
@swartkatt You cannot import a member function like SerialApp::Init to a non-MFC app, because that app does not know about the SerialApp class, and cannot know about it since SerialApp is derived from CWinApp which is an MFC class, while your app is non-MFC. If you want to export a free function from the DLL, then make it __declspec(dllexport) void __cdecl Init(void); outside the SerialApp class, or any class for that matter. As for C#, which you only brought into the picture in the last comment, you cannot directly import any C++ classes or member functions, anyway.dxiv

1 Answers

1
votes

The question is resolved by @dxiv's comment:

You cannot import a member function like SerialApp::Init to a non-MFC app, because that app does not know about the SerialApp class, and cannot know about it since SerialApp is derived from CWinApp which is an MFC class, while your app is non-MFC. If you want to export a free function from the DLL, then make it __declspec(dllexport) void __cdecl Init(void); outside the SerialApp class, or any class for that matter.

1. I removed the declaration __declspec(dllexport) void __cdecl SerialApp::Init(); from the header file.

2. Rewrite the definition of Init() in the Serial.cpp file as:

extern "C" 
{
    __declspec(dllexport) void __cdecl Init()
    {
        std::cout << "Succeded";
    }
}

3. I call the function in Win32 Console App as:

int main()
{
    //Load the DLL
    HMODULE lib = LoadLibrary(L"D:\\C++ PROJECTS\\serial\\Debug\\Serial.dll");

    //Create the function
    typedef void(*Init_t)(void); // <- You can name Init_t whatever you want.
    Init_t Serial = (Init_t)GetProcAddress(lib, "Init");

    if (!Serial) {
        std::cout << GetLastError();
        return 1;
    }

    Serial();   //<- Init() Function Call from Serial.dll
    
    std::getchar();
    return 0;
}

And all worked great!

Thank you everyone, who make time to read and answer my Question!