NativeWindowsDLL.zip
This article briefly explains what a native Windows Dynamic Link Library
(DLL) is, shows how to create a DLL using C++, how to consume it in C# and
then explains how DLLs work. There are many types of DLLs but all of them
are native DLLs, except the term native should only be used for DLLs that do
not have a Type Library (COM) or CLI Metadata (.Net Class Library). If it is
possible to create a reference to a DLL using the Visual Studio Add
References window then the DLL should not be called a
native DLL. When a C# program calls a native DLL it must
use the DllImportAttribute class (DllImport for short), as described here.
Use of DllImportAttribute/DllImport is also called Pinvoke.
A native Windows DLL is not a .Net Class Library. A .Net Class Library is
a DLL with CLI Metadata and other additional things. A native DLL can only
have unmanaged code; in other words, a compiled native DLL has native (x86)
machine instructions but cannot have Common Intermediate Language (CLI)
virtual machine instructions (CLI virtual machine instructions are also
called Microsoft Intermediate Language (MSIL) instructions). There are many
other articles explaining .Net Class Libraries using C# and other .Net
languages. Unless a .Net Class Library is built using C++ it cannot have
native (x86) machine instructions in it. To create a .Net Class Library
using C++ see my article
Managed C++ Wrapper For Unmanaged Code.
Understanding native DLLs is not a requirement for C# and Common Language
Infrastructure programming. (CLI is often called .Net.) This article is
intended for C# programmers unfamiliar with C++ that want to understand
native DLLs and are brave enough to explore them. This article assumes you
at least know how to create and use a C# Console Application project. I
intend to explain in this article how to create a C++ DLL project you can
use for educational purposes even if you are unfamiliar with C++.
What a Dynamic Link Library Is
The DLL file is separate from other executable files. The "Dynamic Link"
portion of Dynamic Link Library refers to the fact that the functions and
variables are linked dynamically. In contrast, a static library contains
unmanaged code that is combined with other unmanaged code into a single
executable. Programmers unfamiliar with C++ and unmanaged code might have
difficulty understanding the concept of static libraries and static linking.
When code that is statically linked is changed, every executable that uses
that code must be updated. The majority of the Windows API exist in native
Windows DLLs. Imagine how complicated it would be to update Windows if
every Windows program (executable) had to be updated for
every patch or fix for Windows.
Note that when a DLL project is built, there is a file with a "lib"
extension created that is used by C/C++ projects but not by C#.
A DLL is a library of:
- an optional entry-point function (DllMain) for the DLL
- exported functions and data (variables)
- internal functions and data
The DllMain entry-point function is called once when each of the
following happens to the DLL:
- a process loads the the DLL
- a process unloads the the DLL
- a thread loads the DLL
- a thread unloads the DLL
This provides the DLL the opportuinty to initialize and uninitialize
itself as needed. Note that there are limits to what can be or should be
done in the DllMain function; Windows API functions cannot be called in the
DllMain function except for those in Kernel32.dll. These limitations only
apply to the DllMain function. The DllMain function must return TRUE unless
it intends to indicate that the DLL has failed to load properly.
Creating the DLL
We will begin by creating the C++ DLL project. In Visual Studio start
with:
"File" | "New" | "Project..."
You will then get the familiar "New Project" window as in the following:

Then in the "New Project" window:
- In the left pane under "Installed Templates" (the default) expand "Visual
C++".
- In the middle pane select "Win32 Project".
- Near the bottom give the project a name: I used "SampleNativeDLL".
- Change the Location if you need to or want to.
- Click "OK".
You will then get the "Welcome to the Win32 Application Wizard" window as
in the following:

Just click "Next >". The next window will be the "Application Settings"
window as in the following:

In the "Application Settings" window:
- For "Application type" select "DLL". The "Additional options" will
change.
- For "Additional options" select "Export symbols". You will get very
little sample code without this.
- Leave all the other settings as the default setting.
- Click "Finish".
The project will be generated and since we selected "Export symbols" the
generated code will include additional sample code. The sample code will
show how to define:
- a variable (nSampleNativeDLL)
- a function that is outside of a class (fnSampleNativeDLL)
- a class (CSampleNativeDLL)
And how to export them for use by other applications.
A class cannot be exported from a DLL if the DLL is to be used by any
language other than C++. So remove the CSampleNativeDLL class. To remove the
CSampleNativeDLL class, in the "SampleNativeDLL.h" file remove:
// This class is exported from the SampleNativeDLL.dll
class SAMPLENATIVEDLL_API CSampleNativeDLL {
public:
CSampleNativeDLL(void);
// TODO: add your methods here.
};
In the "SampleNativeDLL.cpp" file remove:
// This is the constructor of a class that has been exported.
// see SampleNativeDLL.h for the class definition
CSampleNativeDLL::CSampleNativeDLL()
{
return;
}
We can now tell C++ that we want the exported function and variable to be
accessible to the C language, because then they will be accessible to other
languages too. To do that, near the top of the "SampleNativeDLL.cpp" file,
after the two #include statements, add the following line:
extern "C" {
Then at the bottom of the file add a line with "}". In other words, the
top of the "SampleNativeDLL.cpp" file should look like the following:
// SampleNativeDLL.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "SampleNativeDLL.h"
extern "C" {
Be sure to put the closing "}" at the bottom. Then do the same for the
"SampleNativeDLL.h" file except since there are no #include statements in
the SampleNativeDLL.h file, the extern "C" can go just below the comments at
the top of the file. Then put the closing "}" at the bottom.
Creating the Console Application
Next we will create a C# application to use the DLL.We will keep it very
simple so we will make it a Console applicaiton. I will assume you know how
to do that. Be sure to change the language in the left
(templates) pane back to C# from C++.
After creating the Console project, add the following two lines to the
"Program" class:
[System.Runtime.InteropServices.DllImport("SampleNativeDLL.dll")]
public static extern int fnSampleNativeDLL();
Then add the following line to the "Main" nethod:
Console.WriteLine(fnSampleNativeDLL());
If you build and run the program now then during execution you will get
the error that the DLL could not be loaded. That is because the Console
Application does not know where the DLL is at. There are many possible
solutions but for this article we will simply copy the DLL. There are many
ways to do that but here is one. In Windows Explorer go to the directory
where the DLL project is at. There will be one subdirectory called "Debug";
go to it and you will see the DLL; copy it. Then go to the directory where
the Console Application project is at. Go to the "bin" subdirectory and then
the "Debug" subdirectory of that. Paste the DLL there. Now when you execute
the Console Application you should get "42" written to the console. That
will happen because the Console Application is calling the fnSampleNativeDLL
function in the DLL and that function is returning 42.
Explanation of the DLL Code
If you look at the generated project it will look as in the following:

The "ReadMe.txt" file is created by Visual Studio when the project is
generated to describe the generated files. You can ignore the "External
Dependencies". The remaining two folders are "Header Files" and "Source
Files". For the purposes of this article we will ignore the files
"targetver.h", "stdafx.h" and "stdafx.cpp".
The following is the contents of the "SampleNativeDLL.h" file
after the modifications I described previously:
extern "C" {
// 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 SAMPLENATIVEDLL_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
// SAMPLENATIVEDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef SAMPLENATIVEDLL_EXPORTS
#define SAMPLENATIVEDLL_API __declspec(dllexport)
#else
#define SAMPLENATIVEDLL_API __declspec(dllimport)
#endif
extern SAMPLENATIVEDLL_API int nSampleNativeDLL;
SAMPLENATIVEDLL_API int fnSampleNativeDLL(void);
}
Originally the primary need for header files is the result of the C
language requiring that functions and variables be defined (in the file)
before use. So a header file has the signatures (prototypes) of functions.
Header files have what is required by any C/C++ file to use the functions
defined in the header. The explanation of header files is not directly
relevant to DLLs. What is relevant is that the "SampleNativeDLL.h" file has
the following in it:
SAMPLENATIVEDLL_API int fnSampleNativeDLL(void);
That is the signature (prototype) of the fnSampleNativeDLL function. Note
the use of the "SAMPLENATIVEDLL_API" macro. After the compiler processes the
"SAMPLENATIVEDLL_API" macro the fnSampleNativeDLL function is defined as:
__declspec(dllexport) int fnSampleNativeDLL(void);
The purpose of "__declspec(dllexport)" is to export
the function for use by other programs. A "__declspec(dllimport)" would be
used to import a function that is exported from a DLL. In
C#, DllImportAttribute (DllImport for short) essentially serves the same
purpose as "__declspec(dllimport)" in C/C++.
The following is the contents of the "SampleNativeDLL.cpp" file
after the modifications I described previously:
// SampleNativeDLL.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "SampleNativeDLL.h"
extern "C" {
// This is an example of an exported variable
SAMPLENATIVEDLL_API int nSampleNativeDLL=0;
// This is an example of an exported function.
SAMPLENATIVEDLL_API int fnSampleNativeDLL(void)
{
return 42;
}
}
Note that that implements the "fnSampleNativeDLL" function. The function
just returns the number "42".
The only file remainng to be described is "dllmain.cpp". The following
is the contents of it:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
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;
}
It has one function called DllMain that has previously been described.
The contents of the SampleNativeDLL.cpp and dllmain.cpp files could be
combined into one file; they are made separate for our convenience.