Best practice when creating DLL

Pages: 12
hi all..
I am using C++ visual studio 2008 and I am creating a custom control class in a DLL. What is the best programming practice to do that? Is it to reference the DLL in the project's property or using LoadLibrary() and GetProcAddress()??

Thanks
Last edited on
The easiest thing for anyone wanting to use your custom control is to link to the dll statically, that is, have an export lib for the linker to use.

Having to use LoadLibrary() / GetProcAddress(), while certainly doable, is a lot of extra brain calories expended, in my opinion of course.
The absolute BEST practice is to of course document your code; in multiple languages if possible.
closed account (N85iE3v7)
Sometimes it is necessary to use LoadLibrary, for example, if in your application you have different dlls for specific targets. Could it be a DLL for a specific hardware. Some developers do not supply the lib files, so it is always good to know how to link the DLL dynamically.

I agree, it is a real pain to do it though.

As Computergeek01 explained, documentation is everything for someone using a library, if possible even supply the example of how to dynamically import the functions.
Last edited on
Up till now, i am referencing the dll in the projects' properties. I do have several dlls to use but it is not working. I dont know if the problem in linking the dlls or in specifying the #include files.
Besides i don't have a .lib files generated.
Which is better? using lib files, static DLLs or dynamic DLLs?
Thanks for your help
closed account (N85iE3v7)
If you had a lib you could load it by using a #pragma statement. If you do not have a lib file, then you will need to use function pointers. If you have different DLLs for different settings, perhaps loading them dynamically is better, by allowing the user to choose which one to load.

What did you use to link them ?A pragma statement of loaded it dynamically ? If you use the latter it won't work without a lib file. Also, did you mapped them properly ? Please paste some code here.
Sorry for inconvenience, I noticed that there is .lib generated but all placed in the main debug folder of the whole solution project. I am not using neither #pragma nor a function pointers. I am just adding the DLL to the project that needs it. I am adding it in the property of the project in reference option.
Regarding the different DLLs, each DLL is used for certain purpose but not setting and sometime a dll may need to refer another dll (is this possible??!!!)
In the code, I didnt use any thing special for loading or mapping dlls. The code is set of classes (.h and .cpp):
class A, B and they belong to DLL1
class C, D that belong to DLL2
the 3rd file is not able to notice the above defined classes

Since I had .lib generated, do I need to use #pragma ??
By the way, I removed the DLL reference in the project properties, it didn't work. It gave errors in the manifest and the linker and that is due to un-referencing the needed DLL

Please I really need your help. Since 3 days ago I'm not able to do anything before resolving this issue
Last edited on
freddie.. zlogdan.. please helppp
any updates from any interested geek ?
closed account (N85iE3v7)
Sorry, I have no Visual Studio here with me now, a few days off work.

With classes A and B you generate DLL 1. The same goes for C and D. And you have DLL2. If your 3rd project uses them, you will need to do


 
#pragma comment(lib, "yourlib.lib") 


The in VS C++ settings you have to add the path to the linker to find where this lib is. You do that in the linker are of your properties settings. Additional libs I think. The linker must know where to find the libs.

Go straight here to this site, http://www.tenouk.com/cnwin32tutorials.html, it teaches you everything about using DLLs.

@ahbazzi: could you post the header file(s) for DLL 1? And could you post the code that's not working while trying to use DLL 1? (And for DLL 2 if you have time)

Your file for class A should look something like this (the headers for the other classes should be similar):

1
2
3
4
5
6
7
8
9
10
11
//A.h
#ifdef DLL_1_EXPORTS
   #define DLL_1_API __declspec(dllexport)
#else
   #define DLL_1_API __declspec(dllimport)
#endif

class DLL_1_API A
{
...
};


For the project creating the DLL, DLL_1_EXPORTS should be #define d. That way, it __declspec(dllexport)s the class. In the project that's trying to use the DLL, it shouldn't have DLL_1_EXPORTS #define d so that way, when it includes A.h, it knows to import the class from the DLL.

Since you're exporting classes, you'll have to do some trickery to get them working if you decide to go the LoadLibrary/GetProcAddress route. If you go that route, you would have to write two extra (global) functions for each class: one that returns a pointer to an instance of that class and one that takes in the pointer and deletes it. You'll have to do it that way because you can't call GetProcAddress on all the methods and then bind it to the implementation of a class.

If you post the code you're using, maybe we can figure out what's going wrong.

EDIT:

If you have a lib, you do not have to use a #pragma for your code to use it. Under the project properties->Configuration Properties->Linker->Input->Additional Dependencies, set where the .lib is and your project would recognize it.

I think it's important to mention that the .lib generated from a Visual Studio Static Library project is different from the .lib generated from a Dynamic Library project. Don't be fooled by the fact that they have the same file extension; they are both different things. The .lib generated from a Static Library project (I will call it a slib from now on) contains the actual implementation whereas the .lib generated along with a Dynamic Library (dll) (I will call it a dlib from now on) only contains stubs of the implementation. If you are to link your project with a slib, that's it, you can run your program and it will not need a dll. If you are to link your project with a dlib, your project will run correctly only if that library's dll is in the same directory as your program's exe or if it is in the system PATH.

I think the easiest solution for you, ahbazzi, is to configure your projects as static libraries. For this, go to project properties->Configuration Properties->General and set the Configuration Type to static library. When you compile your projects, each will output a .lib (or slib as above). Then, in your project that you want to use your libraries, link with both these .libs. See the instructions above as to how to link with a .lib. I haven't used the #pragma approach, myself.
Last edited on
closed account (N85iE3v7)
I think the OP has actual DLLs as targets. The lib files for are required to import the functions from the DLL. Also, by using a .def file one will not need the to __declspec(dllexport) or __declspec(dllimport).
I think the OP has actual DLLs as targets.

If that's the case, then the OP can change the configuration type of the projects to dynamic library. Setting up the linker options to link with the .lib generated with a dynamic library is the same as linking with a static .lib.

by using a .def file one will not need the to __declspec(dllexport) or __declspec(dllimport)

Using dllexport is hands-down the most convenient and least error prone way to export a class to a dll.
I think that having DLLs on which the whole program will run is better and more professional than having libraries only. Please correct me if I'm wrong. As zlogdan stated, my target is to have DLLs.

Anyway, each DLLs I created is generating .lib file and all those files (i.e. .dll, .lib and .exe) are placed in the same directory.

The properties of the DLL1 and DLL2 projects are:
- Configuration Type: Dynamic Library (.dll)
- Additional Included Directories: null

The 3rd project is also a DLL that uses that above 2 DLLs. The property of DLL3 is:
- I referenced the above 2 DLLs in the Common Properties.
- Configuration Type: Dynamic Library (.dll)
- Additional Included Directories: I added the path of the header files of DLL1 & DLL2 classes

My problem till now is in this 3rd dll. It is not able to resolve the class defined in DLL1 and thus giving a lot of errors upon compilation

Note: In all projects I'm not using precompiled headers (actually I don't know what those precompiled headers are used for)

here is parts of the classes headers of the DLLs:

DLL1:

Class1: BsolWindow.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "windows.h"

namespace BsolControls {
	class BsolWindow {

	private:
		...
	
	public:
		__declspec(dllexport) BsolWindow (LPCWSTR ClassName);
		...
	};
}


Class2: BsoleditBox.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "windows.h"
#ifndef bsolwindow_h
#define bsolwindow_h
#include "BsolWindow.h"
#endif;


namespace BsolControls {
	class BsolCtrlCls {
	public:
		...

	};

	class BsolEditBox {
	private:
		...
	
	public:
		__declspec(dllexport) BsolEditBox (LPCWSTR , int , int , int , int);
	};
}


DLL2:

Class3: BsolLinkList.h
1
2
3
4
5
6
7
8
9
#include "windows.h"

class BsolLinkList {
private:
	...
public:
	__declspec(dllexport) BsolLinkList ();
	...
};



DLL3 (using DLL1 and DLL2):

Class4: BsolForm.h
1
2
3
4
5
6
7
8
9
10
11
12
#include "BsolEditBox.h"
#include "BsolLinkList.h"


class BsolForm  {
private:
	...

public:
	__declspec(dllexport) BsolForm (LPCWSTR formName);
	...
};


BsolForm.cpp:
1
2
3
4
5
6
7
8
9
10
11
#include "BsolForm.h"

BsolForm::BsolForm(LPCWSTR name_P) {
	...
	this->prntWnd = BsolWindow::BsolWindow (); // this line gives error
	this->itemHandel = BsolLinkList::BsolLinkList (); // no error
	BsolEditBox edtBx_v = BsolEditBox::BsolEditBox (); // also gives error
	...
}

...


The winmain procedure is called from a 4th project whose output is the .exe file.

Important note:
Before creating the DLL3, the program was running normally and the main project was loading DLL1 properly without using neither #pragma nor LoadLibrary/GetProcAddress.


This is the my application's whole architecture till now (it will be growing and growing later on). I'll be waiting your feedback in order to know what and where to modify
1
2
3
this->prntWnd = BsolWindow::BsolWindow (); // this line gives error
this->itemHandel = BsolLinkList::BsolLinkList (); // no error
BsolEditBox edtBx_v = BsolEditBox::BsolEditBox (); // also gives error 

What are the errors those lines give you? Do you have constructors for BsolWindow and BsolEditBox that take no parameters? The ones you've shown take parameters (BsolWindow takes a LPCWSTR and BsolEditBox takes a LPCWSTR and four ints). Maybe this is the source of your error.

I think that having DLLs on which the whole program will run is better and more professional than having libraries only. Please correct me if I'm wrong.

Using "libraries only" (that is, static linking) is not less professional than using DLLs. Static linking results in having a more bloated exe because it contains all the implementations. Static linking also makes it so the exe does not depend on anything else to run functions from the library (i.e. your exe is self-contained). Using DLLs (dynamic linking) reduces the size of your exe but now your exe requires those DLLs to run. Dynamic linking has of course some other advantages (such as when you statically link and the library changes somehow, you have to recompile your exe whereas if you dynamically link you may not have to recompile). Both linking methods have different advantages over each other. You can't say one is more professional or better than the other.
I had stated above that:
My problem till now is in this 3rd dll. It is not able to resolve the class defined in DLL1 and thus giving a lot of errors upon compilation

In the architecture posted above, you can notice that:
DLL1 contains classes BsolWindow and BsolEditBox
DLL2 contains class BsolLinkList

In the class BsolForms, I had just coded the class constructor (i.e. BsolForm). However, as per the errors below, the class BsolForm is recognizing BsolLinkList but not BsolWindow nor BsolEditBox.

Below is the code of BsolForm (.h and .cpp) so you can match the line number of the errors and you will notice that I'm getting error on every line containing BsolWindow or BsolEditBox but not BsolLinkList.

Maybe my problem is in #include and not DLLs because logically, DLLs will give errors upon linking if not loaded or referenced correctly and not upon compilation.

BsolForm.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "BsolEditBox.h"
#include "BsolLinkList.h"


class BsolForm  {
	private:
		LPCWSTR			formsName;
		BsolWindow *	formsWnd;
		BsolWindow *	prntWnd;
		BsolLinkList *	itemHandel;

	public:
		__declspec(dllexport) BsolForm (LPCWSTR formName);
		__declspec(dllexport) BsolWindow * getFormsWnd ();
		__declspec(dllexport) BsolWindow * getFormsPrntWnd ();
		__declspec(dllexport) void setFormsWnd (BsolWindow * wnd);
		__declspec(dllexport) void setFormsPrntWnd (BsolWindow * prntWnd);
		__declspec(dllexport) void addChildWnd (BsolWindow * childWnd);
		__declspec(dllexport) void addEditBox (BsolEditBox * edtBx);
};


BsolForm.cpp

1
2
3
4
5
6
7
8
9
#include "BsolForm.h"

BsolForm::BsolForm(LPCWSTR formName_P) {
	this->formsName = formName_P;
	this->formsWnd = BsolWindow::BsolWindow (formName_P);
	this->prntWnd = NULL;//BsolWindow::BsolWindow ();
	this->itemHandel = BsolLinkList::BsolLinkList ();
	BsolEditBox edtBx_v = BsolEditBox::BsolEditBox (formsName_P, 0, 0, 20, 10);
}


Note: even when this->prntWnd is set to null, it is still giving error

The errors I'm getting are:

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
1
1>Compiling...
1>BsolForm.cpp
1>c:\...\bsolutionsapps\bsolform\bsolform.h(8) : error C2143: syntax error : missing ';' before '*'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(8) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(8) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(9) : error C2143: syntax error : missing ';' before '*'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(9) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(9) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(14) : error C2143: syntax error : missing ';' before '*'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(14) : error C2071: 'BsolForm::BsolWindow' : illegal storage class
1>c:\...\bsolutionsapps\bsolform\bsolform.h(14) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(14) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(14) : warning C4183: 'getFormsWnd': missing return type; assumed to be a member function returning 'int'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(15) : error C2143: syntax error : missing ';' before '*'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(15) : error C2071: 'BsolForm::BsolWindow' : illegal storage class
1>c:\...\bsolutionsapps\bsolform\bsolform.h(15) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(15) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\...\bsolutionsapps\bsolform\bsolform.h(15) : warning C4183: 'getFormsPrntWnd': missing return type; assumed to be a member function returning 'int'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(16) : error C2061: syntax error : identifier 'BsolWindow'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(17) : error C2061: syntax error : identifier 'BsolWindow'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(18) : error C2061: syntax error : identifier 'BsolWindow'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(19) : error C2061: syntax error : identifier 'BsolEditBox'
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(5) : error C2039: 'formsWnd' : is not a member of 'BsolForm'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(5) : see declaration of 'BsolForm'
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(5) : error C2653: 'BsolWindow' : is not a class or namespace name
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(5) : error C3861: 'BsolWindow': identifier not found
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(6) : error C2039: 'prntWnd' : is not a member of 'BsolForm'
1>c:\...\bsolutionsapps\bsolform\bsolform.h(5) : see declaration of 'BsolForm'
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(7) : error C2440: '=' : cannot convert from 'BsolLinkList' to 'BsolLinkList *'
1>        No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(8) : error C2065: 'BsolEditBox' : undeclared identifier
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(8) : error C2146: syntax error : missing ';' before identifier 'edtBx_v'
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(8) : error C2065: 'edtBx_v' : undeclared identifier
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(8) : error C2653: 'BsolEditBox' : is not a class or namespace name
1>c:\...\bsolutionsapps\bsolform\bsolform.cpp(8) : error C3861: 'BsolEditBox': identifier not found
1>c:\...\BSolutionsApps\BsolForm\Debug\BuildLog.htm"
1>BsolForm - 28 error(s), 2 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========


Note: I didn't fix the error on line 7 in .cpp file just to show you a good evidence that the compiler is recognizing BsolLinkList class but not the other 2 classes :)

Hope that you can help because I'm not able to find any clue for this issue and I had being stuck at this point for more than 2 weeks
Last edited on
Both BsolWindow and BsolEditBox are in a namespace (BsolControls). In BsolForm.h and BsolForm.cpp, wherever you see BsolWindow or BsolEditBox, you should do the following:

1
2
3
BsolControls::BsolWindow *	formsWnd;
...
__declspec(dllexport) void addEditBox (BsolControls::BsolEditBox * edtBx);

Those lines are just examples but you see how that can fix your errors. You could also add the following line above your class definition in BsolForm.h:

using namespace BsolControls;

I wish I would've caught that namespace earlier when you posted BsolEditBox.h and BsolWindow.h.

EDIT:
Another thing is is you're always using __declspec(dllexport). This is incorrect. When you compile DLL3, it will think it will have to dllexport BsolLinkList, BsolEditBox, and BsolWindow. What it needs to do is __declspec(dllimport) them instead.

To get around this, in DLL1 enter the following into the preprocessor options
DLL_1_EXPORTS

and do the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "windows.h"

#ifdef DLL_1_EXPORTS
   #define DLL_1_API __declspec(dllexport)
#else
   #define DLL_1_API __declspec(dllimport)
#endif

namespace BsolControls {
	class BsolWindow {

	private:
		...
	
	public:
		DLL_1_API BsolWindow (LPCWSTR ClassName);
		...
	};
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "windows.h"
#ifndef bsolwindow_h
#define bsolwindow_h
#include "BsolWindow.h"
#endif;


namespace BsolControls {
	class BsolCtrlCls {
	public:
		...

	};

	class BsolEditBox {
	private:
		...
	
	public:
		DLL_1_API BsolEditBox (LPCWSTR , int , int , int , int);
	};
}


and similarly for DLL2. That way, when you include the .h files in DLL3, it will dllimport the functions as opposed to trying to dllexport them.
Last edited on
Shacktar.... thanks for your help very much..
The problem caused by the namespace is solved and your remark for the DLL import/export was very usefull although i didnt understand the role of __declspec(dllexport) and __declspec(dllimport).

now i have another small question:
what should the window style be in order to get a main frame window and then create a child window

thanks in advance
although i didnt understand the role of __declspec(dllexport) and __declspec(dllimport)

Do you understand them now, though? Just in case you still don't, let me explain.

When you have __declspec(dllexport) in front of a class or function, this says to the compiler that you want to expose this class or function to the user of this DLL.

Now, let's say you want to create a DLL with two functions, one called "intAdd" which adds two integers and one called "intMultiply" that multiplies two integers. The header file for your DLL will look like this:

1
2
__declspec(dllexport) int intAdd(int x, int y);
__declspec(dllexport) int intMultiply(int x, int y)


Let's say you have a customer and you have provided them your DLL as well as the above header file (and a .lib)...They wouldn't be able to use your DLL!

This is because __declspec(dllexport) tells the compiler that you want to expose these functions. But that's not what the customer wanted! They wanted to use the functions from the DLL. Now Your customer is angry because they're getting linker errors about how intAdd and intMultiply aren't defined anywhere.

To fix this, you send the customer a new header file that looks like this:

1
2
__declspec(dllimport) int intAdd(int x, int y);
__declspec(dllimport) int intMultiply(int x, int y)


Now their project compiles fine and they're able to use your DLL. What __declspec(dllimport) does is it tells the compiler that the function is defined in the DLL and not in the current project. Your customer is happy and you get paid.

However, you have a slight problem. You now have two header files instead of one for your DLL (one for you, one for your customer). This can be a big deal when your DLL has more than two exposed functions (or classes). To get around this we use the preprocessor #ifdef trick I showed you above. Let's say you add MY_DLL_EXPORTS to the preprocessor input in your project (it's safe to say your customer won't have the same thing defined).

You now change your header file to this:

1
2
3
4
5
6
7
8
#ifdef MY_DLL_EXPORTS
     #define MY_DLL_API __declspec(dllexport)
#else
     #define MY_DLL_API __declspec(dllimport)
#endif

MY_DLL_API int intAdd(int x, int y);
MY_DLL_API int intMultiply(int x, int y);


When you compile your DLL with the above change, the preprocessor will place __declspec(dllexport) in front of the functions and when your customer compiles the DLL, the preprocessor will place __declspec(dllimport) in front of the functions. Now, you can create a DLL for your customer, and they will be able to use the DLL (without linker errors) all with only one header file! Mission accomplished.

what should the window style be in order to get a main frame window and then create a child window

Well, it's been a while since I've used Win32 for window creation, but I would suspect that your main frame window can have any style, as long as it looks like how you want it. You might want to give the main frame window a WS_CLIPCHILDREN style attribute so it doesn't waste time drawing things behind any child windows. The child window, however, should have WS_CHILD (or WS_CHILDWINDOW) as part of its style attribute set.

Note that you need to pipe style attributes together (using the | character).

e.g. say you want a window to be a child window with a title bar (caption), a system menu (this is the minimize/maximize and close buttons), a horizontal scroll bar, and you want it to receive focus if you press TAB. The style will look like this (the style attributes can be in any order):

WS_CHILD | WS_CAPTION | WS_SYSMENU | WS_HSCROLL | WS_TABSTOP
Thanks very much for your elaborated explanation. The part of #ifdef was already clear to me. While the role of __declspec(dllexport) and __declspec(dllimport) was ambiguous to me. But after your explanation, things are getting clearer.

Regarding the main frame and its child window, I was already using WS_CHILD but the problem was that I wasn't passing a parent window HWND in the create function. Now this problem is fixed as well.

Again, I would like to thank you hoping that you will keep following my questions and clarifications since I have a lot of questions to be raised.
You're welcome!. I'll answer any question I can (and I'm sure other forum goers would be eager to help as well). Of course, if the question is about a different topic then you should create a new thread for it.
Pages: 12