problem in explicit linking of DLL

I have made a sample Win32 DLL(not MFC) without using .DEF file(I do not have to include a header file thus).To test that i have made a sample application.It loads the DLL using explicit linking(using LoadLibrary() and GetProcAddress()).To make it work I copied the dll to the folder of the exe.Now, the dll is being loaded(the handle returnd by LoadLibrary() is not null).But the GetProcAddress() is returning a null pointer.I have run the dumpbin utility also to check things.It seems to work fine.So no prob in DLL i guess.

Please help.If u need I can mail the source code.




Answer this question

problem in explicit linking of DLL

  • PhilNick

    In addition to extern "C", you still need to export your functions, either by using a DEF file or by using __declspec(dllexport).


  • Goldenarowana

    Too much information.  Is CACULATORDLL_EXPORTS defined for the build of your dll

    It should be.  This makes CACULATOR_API = __declspec(dllexport) .

    Check to see whether CACULATOR_EXPORTS defined is for the DLL project (check Project Properties->C/C++->Preprocessor->Preprocessor Definitions

    Note that GetProcAddress must spell the exported function exactly using the name used by the compiler, and this is not always obvious since it depends on whether functions are C or C++, and their calling conventions.

    When you have a DLL, you can determine what functions it exports by using dumpbin -exports

    Example:

    // This is an example of an exported function, with C++ name mangling and

    // parameter passing per standard calling convention

    BOB13_API int CALLBACK fnbob13_cpp_stdcall(void)

    {

    return 42;

    }

    // This is an example of an exported function, with C++ name managling and

    // parameter passing per C calling convention

    BOB13_API int fnbob13_cpp_cdecl(void)

    {

    return 42;

    }

    // This is an example of an exported function, without C++ name managling and

    // parameter passing per standard calling convention

    extern "C" BOB13_API int CALLBACK fnbob13_c_stdcall(void)

    {

    return 42;

    }

    // This is an example of an exported function, without C++ name managling and

    // parameter passing per C calling convention

    extern "C" BOB13_API int fnbob13_c_cdecl(void)

    {

    return 42;

    }

    dumpbin -exports bob13.dll

             3    2 0001101E fnbob13_cpp_cdecl@@YAHXZ
             4    3 000110FF fnbob13_cpp_stdcall@@YGHXZ
             7    5 00011104 _fnbob13_c_stdcall@0
             6    6 00011122 fnbob13_c_cdecl

    Brian

     

     


  • Beskur

    Thanks Brian, CALLBACK was the culprit.

    one more doubt i have. Right now i have to copy the dll to the folder in which exe is there. is there any other way to do it. Can the code pick up the dll from the release folder of the DLL and get the updated dll everytime.

    i have already made the following settings in the project->properties (i m using VS2005)

    1. in c/c++-> Additional include directories i have added the dll project release folder path

    2.in linker -> additional library directories i have added the same dll release folder path

    3.in linker->input->additional dependencies, i have added the lib file..."CaculatorDll.lib".

    Q2 . Another ques pls... Suppose i change my dll..rebuild it...copy the new dll to the folder of the exe...Now if i try to run my exe, it shows error...i have to rebuild my exe to make it run..Y is it so

    Thanks for the solution,

    Vishal



  • OceanDude

    Ur proposed solution did not seem to help..so i m puttin up the source code.However the .def file version of the same dll is workin.

    the dll project : CaculatorDll

    CaculatorDll.cpp

    // CaculatorDll.cpp : Defines the entry point for the DLL application.

    //

    #include "stdafx.h"

    #include "CaculatorDll.h"

    #ifdef _MANAGED

    #pragma managed(push, off)

    #endif

    BOOL APIENTRY DllMain( HMODULE hModule,

    DWORD ul_reason_for_call,

    LPVOID lpReserved

    )

    {

    switch (ul_reason_for_call)

    {

    case DLL_PROCESS_ATTACH:

    case DLL_THREAD_ATTACH:

    case DLL_THREAD_DETACH:

    case DLL_PROCESS_DETACH:

    break;

    }

    return TRUE;

    }

    #ifdef _MANAGED

    #pragma managed(pop)

    #endif

    // This is an example of an exported variable

    CACULATORDLL_API int dllVar=0;

    CACULATORDLL_API MyClass* pMyClass=NULL;

    // This is an example of an exported function.

    CACULATORDLL_API int CALLBACK Add(int a,int b)

    {

    return a+b;

    }

    CACULATORDLL_API int CALLBACK Subtract(int a,int b)

    {

    return a-b;

    }

    CACULATORDLL_API int CALLBACK Multiply(int a,int b)

    {

    return a*b;

    }

    CACULATORDLL_API float CALLBACK Divide(int a,int b)

    {

    return a/(float)b;

    }

    // This is the constructor of a class that has been exported.

    // see CaculatorDll.h for the class definition

    MyClass::MyClass()

    {

    return;

    }

    int MyClass::GetInt()

    {

    return iData;

    }

    float MyClass::GetFloat()

    {

    return fData;

    }

    void MyClass::SetFloat(float f)

    {

    fData=f;

    }

    void MyClass::SetInt(int i)

    {

    iData=i;

    }

    the CaculatorDll.h file :

    // The following ifdef block is the standard way of creating macros which make exporting

    // from a DLL simpler. All files within this DLL are compiled with the CACULATORDLL_EXPORTS

    // symbol defined on the command line. this symbol should not be defined on any project

    // that uses this DLL. This way any other project whose source files include this file see

    // CACULATORDLL_API functions as being imported from a DLL, whereas this DLL sees symbols

    // defined with this macro as being exported.

    #ifdef CACULATORDLL_EXPORTS

    #define CACULATORDLL_API __declspec(dllexport)

    #else

    #define CACULATORDLL_API __declspec(dllimport)

    #endif

    // This class is exported from the CaculatorDll.dll

    class CACULATORDLL_API MyClass {

    private:

    int iData;

    float fData;

    public:

    MyClass(void);

    // TODO: add your methods here.

    int GetInt();

    float GetFloat();

    void SetInt(int);

    void SetFloat(float);

    };

    extern CACULATORDLL_API int dllVar;

    extern CACULATORDLL_API MyClass* pMyClass;

    #ifdef __cplusplus

    extern "C" {

    #endif

    CACULATORDLL_API int CALLBACK Add(int,int);

    CACULATORDLL_API int CALLBACK Subtract(int,int);

    CACULATORDLL_API int CALLBACK Multiply(int,int);

    CACULATORDLL_API float CALLBACK Divide(int,int);

    #ifdef __cplusplus

    }

    #endif

    The testCalculateDll project : test application for testing the above dll

    i copy the dll made above to the debug folder of the testCalculateDll project

    the testCalculateDll.cpp file

    // testCalculateDll.cpp : Defines the entry point for the application.

    //

    #include "stdafx.h"

    #include "testCalculateDll.h"

    #define MAX_LOADSTRING 100

    // Global Variables:

    HINSTANCE hInst; // current instance

    TCHAR szTitle[MAX_LOADSTRING]; // The title bar text

    TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name

    // Forward declarations of functions included in this code module:

    ATOM MyRegisterClass(HINSTANCE hInstance);

    BOOL InitInstance(HINSTANCE, int);

    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

    INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

    typedef int (CALLBACK* LPFUNCINT)(int,int);

    typedef float (CALLBACK* LPFUNCFLOAT)(int,int);

    int APIENTRY _tWinMain(HINSTANCE hInstance,

    HINSTANCE hPrevInstance,

    LPTSTR lpCmdLine,

    int nCmdShow)

    {

    UNREFERENCED_PARAMETER(hPrevInstance);

    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    MSG msg;

    HACCEL hAccelTable;

    // Initialize global strings

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

    LoadString(hInstance, IDC_TESTCALCULATEDLL, szWindowClass, MAX_LOADSTRING);

    MyRegisterClass(hInstance);

    // Perform application initialization:

    if (!InitInstance (hInstance, nCmdShow))

    {

    return FALSE;

    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTCALCULATEDLL));

    // Main message loop:

    while (GetMessage(&msg, NULL, 0, 0))

    {

    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

    {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

    }

    }

    return (int) msg.wParam;

    }

    //

    // FUNCTION: MyRegisterClass()

    //

    // PURPOSE: Registers the window class.

    //

    // COMMENTS:

    //

    // This function and its usage are only necessary if you want this code

    // to be compatible with Win32 systems prior to the 'RegisterClassEx'

    // function that was added to Windows 95. It is important to call this function

    // so that the application will get 'well formed' small icons associated

    // with it.

    //

    ATOM MyRegisterClass(HINSTANCE hInstance)

    {

    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;

    wcex.lpfnWndProc = WndProc;

    wcex.cbClsExtra = 0;

    wcex.cbWndExtra = 0;

    wcex.hInstance = hInstance;

    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTCALCULATEDLL));

    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TESTCALCULATEDLL);

    wcex.lpszClassName = szWindowClass;

    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);

    }

    //

    // FUNCTION: InitInstance(HINSTANCE, int)

    //

    // PURPOSE: Saves instance handle and creates main window

    //

    // COMMENTS:

    //

    // In this function, we save the instance handle in a global variable and

    // create and display the main program window.

    //

    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

    {

    HWND hWnd;

    hInst = hInstance; // Store instance handle in our global variable

    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

    CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (!hWnd)

    {

    return FALSE;

    }

    ShowWindow(hWnd, nCmdShow);

    UpdateWindow(hWnd);

    return TRUE;

    }

    //

    // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)

    //

    // PURPOSE: Processes messages for the main window.

    //

    // WM_COMMAND - process the application menu

    // WM_PAINT - Paint the main window

    // WM_DESTROY - post a quit message and return

    //

    //

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    {

    int wmId, wmEvent;

    PAINTSTRUCT ps;

    HDC hdc;

    static HMODULE hDll;

    static LPFUNCINT lpAdd;

    static LPFUNCINT lpSubtract;

    static LPFUNCINT lpMultiply;

    static LPFUNCFLOAT lpDivide;

    static LPWSTR szBuffer=(LPWSTR)malloc(100);

    switch (message)

    {

    case WM_CREATE:

    hDll=LoadLibrary(TEXT("CaculatorDll.dll"));

    if(hDll!=NULL)

    {

    lpAdd=(LPFUNCINT)GetProcAddress(hDll,"Add");

    lpSubtract=(LPFUNCINT)GetProcAddress(hDll,"Subtract");

    lpMultiply=(LPFUNCINT)GetProcAddress(hDll,"Multiply");

    lpDivide=(LPFUNCFLOAT)GetProcAddress(hDll,"Divide");

    if(!lpAdd)

    {

    MessageBox(hWnd,TEXT("Unable to import Add()"),TEXT("PROBLEM"),MB_OK);

    }

    if(!lpMultiply )

    {

    MessageBox(hWnd,TEXT("Unable to import Multilpy()"),TEXT("PROBLEM"),MB_OK);

    }

    if(!lpDivide)

    {

    MessageBox(hWnd,TEXT("Unable to import Divide()"),TEXT("PROBLEM"),MB_OK);

    }

    if(!lpSubtract)

    {

    MessageBox(hWnd,TEXT("Unable to import Subtract()"),TEXT("PROBLEM"),MB_OK);

    }

    }

    else

    {

    MessageBox(hWnd,TEXT("Unable to import Dll"),TEXT("PROBLEM"),MB_OK);

    FreeLibrary(hDll);

    }

    InvalidateRect(hWnd,NULL,TRUE);

    break;

    case WM_COMMAND:

    wmId = LOWORD(wParam);

    wmEvent = HIWORD(wParam);

    // Parse the menu selections:

    switch (wmId)

    {

    case IDM_ABOUT:

    DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);

    break;

    case IDM_EXIT:

    DestroyWindow(hWnd);

    break;

    default:

    return DefWindowProc(hWnd, message, wParam, lParam);

    }

    break;

    case WM_PAINT:

    hdc = BeginPaint(hWnd, &ps);

    // TODO: Add any drawing code here...

    TextOut(hdc,0,0,szBuffer,wsprintf(szBuffer,TEXT("5+2 = %d"),lpAdd(5,2)));

    TextOut(hdc,0,20,szBuffer,wsprintf(szBuffer,TEXT("5-2 = %d"),lpSubtract(5,2)));

    TextOut(hdc,0,30,szBuffer,wsprintf(szBuffer,TEXT("5*2 = %d"),lpMultiply(5,2)));

    TextOut(hdc,0,40,szBuffer,wsprintf(szBuffer,TEXT("5/2 = %f"),lpDivide(5,2)));

    EndPaint(hWnd, &ps);

    break;

    case WM_DESTROY:

    PostQuitMessage(0);

    FreeLibrary(hDll);

    break;

    default:

    return DefWindowProc(hWnd, message, wParam, lParam);

    }

    return 0;

    }

    // Message handler for about box.

    INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

    {

    UNREFERENCED_PARAMETER(lParam);

    switch (message)

    {

    case WM_INITDIALOG:

    return (INT_PTR)TRUE;

    case WM_COMMAND:

    if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)

    {

    EndDialog(hDlg, LOWORD(wParam));

    return (INT_PTR)TRUE;

    }

    break;

    }

    return (INT_PTR)FALSE;

    }



  • rupakg

    Then your problem is the CALLBACK macro: it is adding __stdcall to your exported function declarations, making them adhere to the standard calling convention and making them have a different name (that GetProcAddress must use).

    If you use Windows APIs, like CreateWindow, to call back into your functions, like the window procedure (WndProc), you need the Standard calling convention.  This carries extra name mangling (the number of bytes is appended to the name, and an underscore is prepended). 

    For DLL exports however, you have a choice of using __stdcall or not.   Try them both, and you will never have confusion again...

    Brian


  • Steve Russell

    Hi!

    Most typical problem is that you forget to use extern "C". If you use C++ linkage (default) then your function will have extra name characters after it. Try add extern "C" before exported functions.

    If not works - send your code, I'll try to figure out what's the problem is.



  • Alex Feldstein

    You set things up right in terms of setting up the library dependency in your project build.  For the runtime dependency, let LoadLibrary pick the location according to its search rules.  Here is the documentation for LoadLibrary.

    If it were me, I would add a custom build step (under project properties) to copy the dll from the its release or debug directory to the same directory as the exe (under release debug). Use the $(ConfigurationName) macro to resolve to "debug" or "release" and the $(TargetDir) macro to resolve to the EXE location.

    The second issue is addressed by making your dll project a dependency of the exe project.  So when the dll project changes, the IDE will relink the exe project.

    Brian

     


  • spherule

    i m afraid i m using __declspec(dllexport) method only..u can see my source code..i hav put it up..the .def file method seems to work..

  • Ischyrus

    the dumpbin utility is giving me this output :

    Dump of file C:\Documents and Settings\t-vsriva\My Documents\Visual Studio 2005\
    Projects\experiments\CaculatorDll\release\CaculatorDll.dll

    File Type: DLL

    Section contains the following exports for CaculatorDll.dll

    00000000 characteristics
    43EF5B4C time date stamp Sun Feb 12 21:29:08 2006
    0.00 version
    1 ordinal base
    12 number of functions
    12 number of names

    ordinal hint RVA name

    1 0 00001080 0MyClass@@QAE@XZ = 0MyClass@@QAE@XZ (public: __thi
    scall MyClass::MyClass(void))
    2 1 00001000
    4MyClass@@QAEAAV0@ABV0@@Z = 4MyClass@@QAEAAV0@ABV0
    @@Z (public: class MyClass & __thiscall MyClass::operator=(class MyClass const &
    ))
    3 2 000010A0
    GetFloat@MyClass@@QAEMXZ = GetFloat@MyClass@@QAEMXZ
    (public: float __thiscall MyClass::GetFloat(void))
    4 3 00001090
    GetInt@MyClass@@QAEHXZ = GetInt@MyClass@@QAEHXZ (pub
    lic: int __thiscall MyClass::GetInt(void))
    5 4 000010B0
    SetFloat@MyClass@@QAEXM@Z = SetFloat@MyClass@@QAEXM@
    Z (public: void __thiscall MyClass::SetFloat(float))
    6 5 000010C0
    SetInt@MyClass@@QAEXH@Z = SetInt@MyClass@@QAEXH@Z (p
    ublic: void __thiscall MyClass::SetInt(int))
    7 6 00003348
    dllVar@@3HA = dllVar@@3HA (int dllVar)
    8 7 0000334C
    pMyClass@@3PAVMyClass@@A = pMyClass@@3PAVMyClass@@A
    (class MyClass * pMyClass)
    9 8 00001030
    _Add@8 = _Add@8
    10 9 00001060 _Divide@8 = _Divide@8
    11 A 00001050 _Multiply@8 = _Multiply@8
    12 B 00001040 _Subtract@8 = _Subtract@8

    Summary

    1000 .data
    1000 .rdata
    1000 .reloc
    1000 .rsrc
    1000 .text

    C:\Program Files\Microsoft Visual Studio 8\VC>



  • problem in explicit linking of DLL