Creating my first dll

I am relatively new to the dll stuff but do know more c embedded programming than c++. Anyways I created a simple dll project in VS 2008 with the MFC DLL Wizard.

I am also adding in 2 existing files, labels.h and labels.cpp from a sdk for a dymo label printer. What I am doing is trying to use the functions in the labels.cpp file by including the labels.h in the h file created by my new project.

Is this the right way to expose the existing functions in the added file labels.cpp? The project builds without a issue and produces a dll but the function does not exist and I am assuming I have a linking error missing and possibly the extra files I am adding are not exposed to the entry point of the dll?

Could someone provide a little guidance here?

Thanks,

Tom



I'm guessing that you haven't exported your new functions using __declespec(export)

1
2
3
4
5
6
7
8
9
10
11
// MyDLL.h

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

extern MYDLL_API int nmydll;

MYDLL_API int fnmydll(void);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
// mydll.cpp

// etc

#include "mydll.h"

// This is an example of an exported variable
MYDLL_API int nmydll=0;

// This is an example of an exported function.
MYDLL_API int fnmydll(void)
{
	return 42;
}


Etc

Or you can go old school and use a def file?


Andy

PS Also see

Exporting from a DLL Using __declspec(dllexport)
http://msdn.microsoft.com/en-us/library/a90k134d.aspx

And for an example of exporting static class members.

Walkthrough: Creating and Using a Dynamic Link Library (C++)
http://msdn.microsoft.com/en-us/library/vstudio/ms235636.aspx

And for def files

Exporting from a DLL Using DEF Files
http://msdn.microsoft.com/en-us/library/d91k01sh.aspx

Etc

Exporting from a DLL
http://msdn.microsoft.com/en-us/library/z4zxe9k8.aspx

What is a DLL?
http://support.microsoft.com/kb/815065
(includes assorted useful info and links)
Last edited on
1
2
3
4
5
6
#define CALLTYPE __stdcall
#define FNCINFO extern "C" __declspec(dllexport)
FNCINFO int CALLTYPE myfunction()
{
    return 0xfaf;
}

This way, GetProcAddress will load your function properly, which is the "standard-most" way to call a function.

If you want to use dllimport too, just remove the ' extern "C" ' from the definition.

Without ' extern "C" ', compiler-dependent name mangling will happen, which won't allow you to use the library on two different compilers.

The way I've shown above is the one I use for all of my DLL-plugin-based projects, in combo with LoadLibrary/GetProcAddress.
Last edited on
Good point about the calling conventions.

The code I posted above is from a Visual Studio 2010 generated project. As I was focusing on the export issue, I looked through the lack of calling convention... Not very good!!

I always hand code my exported functions; usually with the predefined APIENTRY (from windef.h) for the calling convention, rather than my own #define.

Andy
Last edited on
I usually prefer to see (and manipulate?) what the value of the defines are, without needing to change APIENTRY (which, frankly, will FUCK UP windows.h if edited) in case you want an actual change.

The biggest change in this context is the 'extern "C" ', which will "convert" (when possible) your functions from C++ to C naming and style.

If you're natively using C, you don't need it, but if you want to overload functions, you cannot use extern "C", because of this:

1
2
FNCINFO void CALLTYPE myoverload(void*) {}
FNCINFO void CALLTYPE myoverload(int) {}


What happens when you use C++ naming? (Without extern "C")
Two functions will be generated.
To avoid confusions, every function name will contain details about its parameters and return value.
As an example, the first overload may become @4as@myoverload and other gibberishy things you may not understand on the fly.

The second one will have a different gibberishing.

What happens with C naming? (with extern "C")
Two functions cannot be generated: C does not support overloading.
You must separate them in two different functions:
myoverload1 and myoverload2.
Their actual name will be kept for stdcall, and if cdecl their name will have an underscore (_) before their name.
Example:

FNCINFO int __stdcall myfnc() {}
Name -> myfnc

FNCINFO int __cdecl myfnc() {}
Name -> _myfnc

You must take them into account yourself when using GetProcAddress.
If you use dllimport, the compiler/linker will handle it for you.

To understand which function signature is used by a C++-decorated-name function, use UnDecorateSymbolName from DbgHelp.h : http://msdn.microsoft.com/en-us/library/windows/desktop/ms681400(v=vs.85).aspx

This will also give you custom classes' names (as they will be included in the full name).

It's all simpler than it looks like, big posts doesn't mean hard things.
I just want to be clean to explain these things.
These informations mostly apply to MSVC.
Thanks for the responses.

The generated stuff I had from creating the project looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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 DYMOLABEL_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 
// DYMOLABEL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef DYMOLABEL_EXPORTS
#define DYMOLABEL_API __declspec(dllexport)
#else
#define DYMOLABEL_API __declspec(dllimport)
#endif

// This class is exported from the DymoLabel.dll
class DYMOLABEL_API CDymoLabel {
public:
	CDymoLabel(void);
	// TODO: add your methods here.
	
};

extern DYMOLABEL_API int nDymoLabel;

DYMOLABEL_API int fnDymoLabel(void);


So all of that could be replaced with this?

1
2
3
4
5
6
#define CALLTYPE __stdcall
#define FNCINFO extern "C" __declspec(dllexport)
FNCINFO int CALLTYPE myfunction()
{
    return 0xfaf;
}


Also, I am not calling this from another c++ app, its a xojo app but even using this as an example it throws me an error stating the function entry cannot be resolved. But I do want to make sure I am doing this correctly. I kind of envision that from a command prompt I could call this function, correct?

Thanks
I don't think so, not so easily.
The bad thing is, I don't know how to handle classes.
But, unless you make your own program, you won't be able to call it from a cmd prompt, because the CMD don't know which parameters you want, the return value, the calling convention...

Only thing I know, for classes you should remove the ' extern "C" '.
And about xojo... I don't know anything about it /:
Yea its never that easy:) I know from embedded work I have done before that I had done cmd line stuff with that before but not sure if its the same way that I should go for a dll.

xojo just uses the api call or std call stuff so its pretty straight forward.

I am just confused on how to expose the functions I want.

Thanks.
What do you want to export, Classes or Functions?
WHAT will call your functions, Your program or Another program?

These are trivial questions, and below the reasons:

1. IDK about classes </lol>
2. If you call it from your own program, it will be certainly easier.
If another program (I think "xojo" can be "Another program") you should check this program's documentation infos for developers.
I guess I just want to export the functions I need which are just 2 of them. And yes it will be my own app calling these functions from the dll, so it should be easy enough.

here is the relevant code I want to call:

from the h file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ILblInfo : public COleDispatchDriver
{
public:
	ILblInfo() {}		// Calls COleDispatchDriver default constructor
	ILblInfo(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
	ILblInfo(const ILblInfo& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
	CString GetLabelName();
	CString GetPaperName();
	long GetPaperWidth();
	long GetPaperHeight();
	long GetBitmapWidth();
	long GetBitmapHeight();
	long GetLabelCount();
	long GetLabelWidth();
	long GetLabelHeight();
	BOOL GetModified();
};


and the cpp 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
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
/////////////////////////////////////////////////////////////////////////////
// ILblInfo properties

/////////////////////////////////////////////////////////////////////////////
// ILblInfo operations

CString ILblInfo::GetLabelName()
{
	CString result;
	InvokeHelper(0x1, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
	return result;
}

CString ILblInfo::GetPaperName()
{
	CString result;
	InvokeHelper(0x2, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetPaperWidth()
{
	long result;
	InvokeHelper(0x3, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetPaperHeight()
{
	long result;
	InvokeHelper(0x4, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetBitmapWidth()
{
	long result;
	InvokeHelper(0x5, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetBitmapHeight()
{
	long result;
	InvokeHelper(0x6, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetLabelCount()
{
	long result;
	InvokeHelper(0x7, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetLabelWidth()
{
	long result;
	InvokeHelper(0x8, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

long ILblInfo::GetLabelHeight()
{
	long result;
	InvokeHelper(0x9, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
	return result;
}

BOOL ILblInfo::GetModified()
{
	BOOL result;
	InvokeHelper(0xa, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
	return result;
}


At least for now I just want to call the GetPaperName function to see if it works.

Thanks.
Exporting a class from a DLL is just a case of marking the class using __declspec(export) (usually using a #define) just like the wizard generated code shows.

1
2
3
4
5
6
7
8
9
10
11
#ifdef DYMOLABEL_EXPORTS
#define DYMOLABEL_API __declspec(dllexport)
#else
#define DYMOLABEL_API __declspec(dllimport)
#endif

class DYMOLABEL_API ILblInfo : public COleDispatchDriver
{
public:

    // etc 


A class exported from a DLL can only inherit from and use classes which are also exported. Here you're inheriting from COleDispatchDriver, a class class exported by the MFC DLL, so you will need to create an MFC DLL project. Unless you have reasons to do otherwise, you should create an MFC DLL project of type "Regular DLL using shared MFC DLL".

Note that your app must also be using the MFC shared DLL; this is so the app, your DLL, and the MFC DLL (which exports the COleDispatchDriver class) are all working with the same CRT memory allocator. You could make your DLL an MFC extension DLL, if required for other reasons, but you should not link it statically to MFC.

(I assume you know that the app and DLL must be both debug or both release; you can not mix them.)

Also, make sure that both app and DLL are build for Unicode or Multi-byte. But this won't cause problems when you run; it will prevent linking.

Andy

PS I only have MFC source for Visual C++ 2008, but I suspect this holds for later versions. The definitions for CString and COleDispatchDriver class does not include an export statement. This is because they are exporting the class using a def file.
Last edited on
If you have full access to both DLL and Executable, my approach would be the following:

DLL:

Exports.h
1
2
3
4
5
6
7
8
9
10
#include "LabelInfo.h"
// I have to guess you'll call it LabelInfo.h ?

#define CALLTYPE __stdcall
#define FNCINFO extern "C" __declspec(dllexport)

// We will call GetLabelInfo to generate a ILblInfo
FNCINFO ILblInfo* CALLTYPE GetLabelInfo(LPDISPATCH pDispatch = 0);
// And we will call FreeLabelInfo to free it
FNCINFO void CALLTYPE FreeLabelInfo(ILblInfo* iLbl);


Exports.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <new>
#include "Exports.h"

// Allocate it, check if pDispatch has a value, to choose which constructor to use
FNCINFO ILblInfo* CALLTYPE GetLabelInfo(LPDISPATCH pDispatch)
{
    if(pDispatch)
        return new (std::nothrow) ILblInfo(pDispatch); // if cannot allocate, return 0.
    return new (std::nothrow) ILblInfo(); // if cannot allocate, return 0.
}

// Free it
FNCINFO void CALLTYPE FreeLabelInfo(ILblInfo* iLbl)
{
    if(iLbl)
        delete iLbl;
}


LabelInfo.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*/ Your includes /*/
class ILblInfo : public COleDispatchDriver
{
public:
	ILblInfo() {}		// Calls COleDispatchDriver default constructor
	ILblInfo(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
	ILblInfo(const ILblInfo& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}
        virtual ~ILblInfo() { /*/ use this destructor /*/ }
// Attributes
public:

// Operations
public:
	virtual CString GetLabelName();
	virtual CString GetPaperName();
	virtual long GetPaperWidth();
	virtual long GetPaperHeight();
	virtual long GetBitmapWidth();
	virtual long GetBitmapHeight();
	virtual long GetLabelCount();
	virtual long GetLabelWidth();
	virtual long GetLabelHeight();
	virtual BOOL GetModified();
};


And you won't need to edit your "LabelInfo.cpp" file.

In your executable file, instead:

LabelInfo.h
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
/*/ This is almost a copy of the above LabelInfo.h file, but... /*/
/*/ Include Windows.h and what defines COleDispatchDriver /*/

#define CALLTYPE __stdcall
// We don't need FNCINFO.
class ILblInfo : public COleDispatchDriver
{
public:
        virtual ~ILblInfo() = 0;
// You don't need to store attributes.
// You can show them, or you can NOT show them and keep them hidden.

	virtual CString GetLabelName() = 0;
	virtual CString GetPaperName() = 0;
	virtual long GetPaperWidth() = 0;
	virtual long GetPaperHeight() = 0;
	virtual long GetBitmapWidth() = 0;
	virtual long GetBitmapHeight() = 0;
	virtual long GetLabelCount() = 0;
	virtual long GetLabelWidth() = 0;
	virtual long GetLabelHeight() = 0;
	virtual BOOL GetModified() = 0;
};

typedef ILblInfo* (CALLTYPE* GetLabelInfo_)(LPDISPATCH = 0); // underscore, to make sure this is just a TYPEDEF.
typedef void (CALLTYPE* FreeLabelInfo_)(ILblInfo*); // Same. They are just types. 

The differences are:

1. You only need virtual functions.
Using non-virtual functions may cause incongruences.

2. " = 0; " at the end of all functions.
This means there is a function named like that, but it has no body at the time (The body is hidden in the DLL.
DLL freed, you cannot use ILblInfo anymore).

3. I added some typedefs. They must match the DLL's version.

You MUST NOT add LabelInfo.cpp to your EXE project.

Main.cpp
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
#include <windows.h>
#include "LabelInfo.h"

int main()
{
    HMODULE myDll = LoadLibrary("my_dll_file.dll");
    if(!myDll)
        return 1; // failure: Cannot find DLL file, or DLL entrypoint failed.
    GetLabelInfo_ GetLabelInfo = GetProcAddress(myDll,"GetLabelInfo");
    FreeLabelInfo_ FreeLabelInfo = GetProcAddress(myDll,"FreeLabelInfo");
    if(!GetLabelInfo || !FreeLabelInfo)
    {
        FreeLibrary(myDll);
        return 1; // failure: Cannot find GetLabelInfo or FreeLabelInfo
    }
    
    ILblInfo* label_info = GetLabelInfo(my_constructor_parameter);
    if(label_info)
    {
        /*/ use it /*/
        FreeLabelInfo(label_info);
        label_info = 0;
    }
    
    FreeLibrary(myDll);
    myDll = 0;
    GetLabelInfo = 0;
    FreeLabelInfo = 0;
    return 0;
}


Careful: A type mismatch will not be notified by the compiler.
You must be careful on your own to match all typedefs.
After this first part, if you want to add a function, it's not a big trouble as hopefully you can see.
Just make sure the declaration's order is the same:

1
2
3
4
5
6
7
// DLL project
class myclass {
    virtual ~myclass();
    virtual void myclass_a();
    virtual void myclass_b();
    virtual void myclass_c();
};


1
2
3
4
5
6
7
8
// EXE project
class myclass {
    virtual ~myclass() = 0;
    virtual void myclass_a() = 0;
    virtual void myclass_c() = 0; // !!! Failure !!!
// Calling this function or a function after this one will probably crash!
    virtual void myclass_b() = 0; // ^
};


In case you want to take another approach, it could be this, but I cannot help you besides this portion of code.
It will also (probably) require dllimport in the Exe's project.

1
2
3
4
5
6
// Header file
#define CALLTYPE
#define FNCINFO __declspec(dllexport)
class FNCINFO ILblInfo : public COleDispatchDriver {
    /*/ ... /*/
};
I would not take EssGeEich's function based approach.

As the MFC DLL is involved in the mix, it is better to export the class in the same way as the MFC DLL does so everything behaves consistently. (Your class is inheriting from a class which is exported by the MFC DLL.)

And you need to (a) make the DLL an MFC DLL and (b) ensure your app is also using the shared MFC DLL, as I said above.

Andy
Last edited on
That was one of my thoughts, but as he said he's making both DLL and EXE, I thought it was ok like that.

But you probably want to follow andywestken or someone else, I really never got into MFC/ATL (I'm quite new to some COM things too).
@tsrdatatech

Out of interest, why are you using the MFC COleDispatchDriver mechanism rather than, say, Direct-to-COM ?

I assume your app was already using MFC.

Andy
Last edited on
Topic archived. No new replies allowed.