Explicit loading static class members form DLL

hi,
how do I load those functioin from my exe.

here is my code:

HEADER OF DLL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma once

// MathFuncsDll.h

	extern "C" class MyMathFuncs
	{
	public:
		// Returns a + b
		static __declspec(dllexport) double Add(double a, double b);

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

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

		// Returns a / b
		// Throws DivideByZeroException if b is 0
		static __declspec(dllexport) double Divide(double a, double b);
	};
 extern "C" __declspec(dllexport) void f();


CPP OF DLL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// MathFuncsDll.cpp
// compile with: /EHsc /LD

#include "Dll.h"
#include <stdexcept>
#include <iostream>
using namespace std;

    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 new invalid_argument("b cannot be zero!");
        }

        return a / b;
    }

void f()
{
	cout << "hello world" << endl;
}


EXE FILE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <Windows.h>
using namespace std;

typedef void (__cdecl* ptr)(void);
typedef double (__cdecl* ptr2)(double, double);

int main()
{
	HINSTANCE hDLL = LoadLibraryA("Dll.dll");
	if(!hDLL)
	{
		FreeLibrary(hDLL);
		cout << "DLL not found...";
		cin.ignore();
		exit(1);
	}
	else
	{
		double a = 3.3, b = 4.4, c;
		ptr func = (ptr)GetProcAddress(hDLL, "f");
		ptr2 add = (ptr2)GetProcAddress(hDLL, "MyMathFuncs::Add");

		if(!func)
		{
			FreeLibrary(hDLL);
			cout << "error binding to function f...";
			cin.ignore();
			exit(2);
		}
		else
		{
			if(!add)
			{
				FreeLibrary(hDLL);
				cout << "error binding to function Add...";
				cin.ignore();
				exit(2);
			}
		}
		func();
		c = add(a, b);
		FreeLibrary(hDLL);
		cout << "all OK...";
	}
	cin.ignore();
	return 0;
}

PROGRAM OUTPUT while runing exe:
error binding to function Add...
so binding to function f works but not binding to function Add.

I've tryed evry possible aproach even using calling convenctions etc.. no luck.
The problems is that the exported names of class member are decorated: extern "C" does not work with classes, as they have to be C++ (there's no such thing as a C class).

Building your files into a dll (MathFuncsDll.dll) and then dumping its exports (using "link /dump /exports MathFuncsDll.dll" in a Visual Studio Command Prompt) I get

Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file MathFuncsDll.dll

File Type: DLL

  Section contains the following exports for MathFuncsDll.dll

    00000000 characteristics
    4ED1F9AF time date stamp Sun Nov 27 08:49:51 2011
        0.00 version
           1 ordinal base
           5 number of functions
           5 number of names

    ordinal hint RVA      name

          1    0 000111A4 ?Add@MyMathFuncs@@SANNN@Z = @ILT+415(?Add@MyMathFuncs@@SANNN@Z)
          2    1 0001100A ?Divide@MyMathFuncs@@SANNN@Z = @ILT+5(?Divide@MyMathFuncs@@SANNN@Z)
          3    2 00011037 ?Multiply@MyMathFuncs@@SANNN@Z = @ILT+50(?Multiply@MyMathFuncs@@SANNN@Z)
          4    3 0001108C ?Subtract@MyMathFuncs@@SANNN@Z = @ILT+135(?Subtract@MyMathFuncs@@SANNN@Z)
          5    4 000110C3 f = @ILT+190(_f)

  Summary

        1000 .data
        2000 .idata
        3000 .rdata
        1000 .reloc
        1000 .rsrc
        6000 .text
       10000 .textbss


So I need to call GetProcAddress with "?Add@MyMathFuncs@@SANNN@Z" to find MyMathFuncs::Add(). The name mangling is version specific (I'm using VC++ 2008, so you might be lucky with '2005 or 2010, but prob not otherwise)

But you can use a def file to provide a friendly alias for the name.

1
2
3
4
5
6
7
8
; MathsFuncDll.def

LIBRARY "MathFuncsDll"
EXPORTS
	MyMathFuncs_Add      = ?Add@MyMathFuncs@@SANNN@Z
	MyMathFuncs_Divide   = ?Divide@MyMathFuncs@@SANNN@Z
	MyMathFuncs_Multiply = ?Multiply@MyMathFuncs@@SANNN@Z
	MyMathFuncs_Subtract = ?Subtract@MyMathFuncs@@SANNN@Z


After rebuilding, the linker export dump shows 4 extra new entrypoints: MyMathFuncs_Add, MyMathFuncs_Divide, MyMathFuncs_Multiply, and MyMathFuncs_Subtract.

Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file MathFuncsDll.dll

File Type: DLL

  Section contains the following exports for MathFuncsDll.dll

    00000000 characteristics
    4ED1FCBB time date stamp Sun Nov 27 09:02:51 2011
        0.00 version
           1 ordinal base
           9 number of functions
           9 number of names

    ordinal hint RVA      name

          1    0 000111A4 ?Add@MyMathFuncs@@SANNN@Z = @ILT+415(?Add@MyMathFuncs@@SANNN@Z)
          2    1 0001100A ?Divide@MyMathFuncs@@SANNN@Z = @ILT+5(?Divide@MyMathFuncs@@SANNN@Z)
          3    2 00011037 ?Multiply@MyMathFuncs@@SANNN@Z = @ILT+50(?Multiply@MyMathFuncs@@SANNN@Z)
          4    3 0001108C ?Subtract@MyMathFuncs@@SANNN@Z = @ILT+135(?Subtract@MyMathFuncs@@SANNN@Z)
          5    4 000111A4 MyMathFuncs_Add = @ILT+415(?Add@MyMathFuncs@@SANNN@Z)
          6    5 0001100A MyMathFuncs_Divide = @ILT+5(?Divide@MyMathFuncs@@SANNN@Z)
          7    6 00011037 MyMathFuncs_Multiply = @ILT+50(?Multiply@MyMathFuncs@@SANNN@Z)
          8    7 0001108C MyMathFuncs_Subtract = @ILT+135(?Subtract@MyMathFuncs@@SANNN@Z)
          9    8 000110C3 f = @ILT+190(_f)

  Summary

        1000 .data
        2000 .idata
        3000 .rdata
        1000 .reloc
        1000 .rsrc
        6000 .text
       10000 .textbss


which means you can now use

ptr2 add = (ptr2)GetProcAddress(hDLL, "MyMathFuncs_Add");

to obtain a pointer to your add function.

See "Explicitly Linking to Classes in DLL's"
http://www.codeguru.com/cpp/w-p/dll/importexportissues/article.php/c123
for further information, and another approach or two.

Andy

PS I see your function pointers are declared with explicit calling convention. The functions themselves should also be delcared with (the same) explicit calling convention. This is always a good idea for DLL exports, in case the default calling convention of the DLL is different to the calling app.

Last edited on
PS For your test program, there's no point calling FreeLibrary if the load has just failed.

Also, you should avoid multiple exits from a function if you can help it.

Finally, if LoadLibrary and GetProceAddress set the "last error", so I've added code to get hold of it and convert it to a message (using FormatMessage).

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <iostream>
#include <Windows.h>
using namespace std;

typedef void (__cdecl* ptr)(void);
typedef double (__cdecl* ptr2)(double, double);

void ReportError(LPCSTR errorDesc, DWORD errorCode);

int main()
{
    DWORD ret = NOERROR;

    HINSTANCE hDLL = LoadLibraryA("MathFuncsDll.dll");
    if(!hDLL)
    {
        ret = GetLastError();
        ReportError("There was a problem loading library", ret);
    }
    else
    {
        ptr  func = NULL;
        ptr2 add  = NULL;

        func = (ptr)GetProcAddress(hDLL, "f2");
        if(NULL != func)
            add = (ptr2)GetProcAddress(hDLL, "MyMathFuncs_Add");

        if(!func || !add)
        {
            ret = GetLastError();
            const char* message = NULL;
            if(!func)
                message = "There was a problem binding to function f...";
            else if(!add)
                message = "There was a problem binding to function Add...";
            ReportError(message, ret);
        }
        else
        {
            double a = 3.3;
            double b = 4.4;

            func();
    
            double c = add(a, b);

            cout << a << " + " << b << " = " << c << endl;

            cout << "All OK...";
        }

        FreeLibrary(hDLL);
    }
    cin.ignore();
    return 0;
}

void ReportError(LPCSTR errorDesc, DWORD errorCode)
{
    const int bufferLen = 1024;

    char errorMessage[bufferLen] = {0};

    DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM
                | FORMAT_MESSAGE_IGNORE_INSERTS
                | FORMAT_MESSAGE_MAX_WIDTH_MASK;

    FormatMessageA( flags,
                    NULL,
                    errorCode,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    errorMessage,
                    bufferLen,
                    NULL );

    if(NULL != errorDesc)
        cout << errorDesc << endl
             << "Error code " << errorCode << endl
             << errorMessage << endl;
    else
        cerr << "Error code " << errorCode << endl
             << errorMessage << endl;
}
Last edited on
Hi Andy, thanks alot for clarifing this, I've spend houres and even days surfing around MSDN and other palces to learn about DLL's but your answer explained that in 2 minutes.
I'm going to learn about *.def files creation right now :D
also I didn't know about exports dumping so thanks for that too!

I have one more question:
what if header file of DLL is wrapped into namespace (or if there are more namespaces), will I in that case use the same approach as you described?
or is it using namespaces in DLL's useless?

thanks.
Works the same way, but the decorated name includes the namespaces (here Test1::Test2::...)

This the dump before adding the aliases.

Andy

Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file MathFuncsDll.dll

File Type: DLL

  Section contains the following exports for MathFuncsDll.dll

    00000000 characteristics
    4ED267D9 time date stamp Sun Nov 27 16:39:53 2011
        0.00 version
           1 ordinal base
           5 number of functions
           5 number of names

    ordinal hint RVA      name

          1    0 000110B9 ?Add@MyMathFuncs@Test2@Test1@@SANNN@Z = @ILT+180(?Add@MyMathFuncs@Test2@Test1@@SANNN@Z)
          2    1 0001124E ?Divide@MyMathFuncs@Test2@Test1@@SANNN@Z = @ILT+585(?Divide@MyMathFuncs@Test2@Test1@@SANNN@Z)
          3    2 0001103C ?Multiply@MyMathFuncs@Test2@Test1@@SANNN@Z = @ILT+55(?Multiply@MyMathFuncs@Test2@Test1@@SANNN@Z)
          4    3 00011019 ?Subtract@MyMathFuncs@Test2@Test1@@SANNN@Z = @ILT+20(?Subtract@MyMathFuncs@Test2@Test1@@SANNN@Z)
          5    4 000110C3 f = @ILT+190(_f)

  Summary

        1000 .data
        2000 .idata
        3000 .rdata
        1000 .reloc
        1000 .rsrc
        6000 .text
       10000 .textbss

Last edited on
cool :D
thanks alot for all your time Andy!
DLL's are now OWNED!
Topic archived. No new replies allowed.