Explicit loading static class members form DLL

Nov 27, 2011 at 7:26am
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.
Nov 27, 2011 at 9:09am
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 Nov 27, 2011 at 9:23am
Nov 27, 2011 at 10:02am
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 Nov 27, 2011 at 10:05am
Nov 27, 2011 at 3:14pm
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.
Nov 27, 2011 at 4:41pm
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 Nov 27, 2011 at 4:42pm
Nov 27, 2011 at 4:52pm
cool :D
thanks alot for all your time Andy!
DLL's are now OWNED!
Topic archived. No new replies allowed.