This beginner article might trigger you to open your Visual Studio's Visual C++ IDE for the first time and build a usable DLL out of it, especially if you never opened it before you were scared of its complexity.
Introduction
Just bravely open your Visual C++ IDE (mine is in VS2013), punch several lines of C code (not even C++), make a super easy usable DLL and test it in a little C# app.
Background
If you have been a "business application developer" for a long time (decades?), you probably already built up a sense on not to touch "low level" code.
"low level code is for building the OS, device drivers, DB engines, 3D simulations, real time weapon controllers whatsoever..."
"I can use Java, VB, C# or even script to do everything, as long as there is a wrapper class giving me access to lower level functions".
Those are very common thoughts.
Sometimes, your curiosity does pass your anxiety and fright. But no matter how hard you search, any VC++ examples, even for beginners, will drag you into new jargon worlds: Win32 APIs, .H (header) file, MFC, ATL, Message pump, pointer of pointer (**ppMyVar
, what the hell!), COM+, STA, MTA... This is one main reason stopping lots of you even bother to open the VC++ IDE.
In this article and the example code, we will never ever touch those buzzwords, "super easy", I mean it pal! Let's start now.
Using the Code
The code is very simple. I highly recommend you NOT to copy/paste. I suggest you start the IDE, create the solution and file, punch the code line by line all by yourself, manually. This way, you will have a deeper feeling about it (and enjoy it hopefully).
Here are the steps:
- Open the VS (mine is VS2013, other version should be very similar). Create a new Visual C++ project. Follow my red marks below (use your own file path, but name the project as
SuperEasyDLL
):
- In the newly created project structure, add a new item as below:
- The new item is a C++ File (.cpp). Just follow my highlighted text:
- Now let's punch several lines here:
(Although I recommend you to punch code yourself, but still, just in case you're a copy/paste maniac like myself ;-)
int iMySuperEasyOutput;
extern "C" __declspec(dllexport) void AddingFifty(int iInput);
extern "C" __declspec(dllexport) int GetMySuperEasyOutput();
void AddingFifty(int iInput)
{
iMySuperEasyOutput = iInput + 50;
}
int GetMySuperEasyOutput()
{
return iMySuperEasyOutput;
}
As you can see above, no class, no construction, no struct
, no Header #include
, no nothing but pure basic code. All it does is very simple: Exposing 2 functions. One is to add 50
to input integer, the other is to output the result.
Yes. Those __declspec(dllexport)
keywords are for exporting the functions. You have to guarantee that exported function name and parameter(s) are exactly the same as you actually implemented in the code.
See? I did try hard not to use jargons.
- Make sure
SuperEasyDLL
is compiled as "DLL", not "EXE". To do that, right click the project, go to properties, and pick "DLL" (as highlighted below):
- Build the project, you can see your SuperEasyDLL.dll in the debug folder.
- Now let's create the C# winform test project. Name it "
SuperEasyDLLWinform
". One dummy form, one dummy button. Don't even bother to change the default name. Punch the three pieces of red circled code as below, because those three pieces are not generated by IDE.
(For your convenience, three pieces are here.)
using System.Runtime.InteropServices;
[DllImport("SuperEasyDLL.dll")]
public static extern int GetMySuperEasyOutput();
[DllImport("SuperEasyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddingFifty(int iTestInput);
AddingFifty(50);
int iResult = GetMySuperEasyOutput();
MessageBox.Show(iResult.ToString());
As you can see now, in our dummy winform app, we are using DLLImport
keywords which obviously are trying to use the exported functions we built in DLL.
- Make sure winform app EXE can find our DLL easily. Just copy SuperEasyDLL.dll from step(6) to winform debug folder (where the SuperEasyDLLWinform.exe sits).
- Beautiful! It's so smart by knowing how to add 50!
Follow the above 9 steps in several minutes, and you are done your 1st VC++ IDE project.
Points of Interest
- Is the VC++ code punched above real C++? You might doubt. No and Yes. It doesn't use any C++ features, those super duper cool OO stuff right? No. I choose not to use any of those in this example. "mean and lean", that's what I strive for. But you know what, I heard some pundits said "C" is a subset of "C++", and lots of people agree. So in this perspective, the above code is sort of C++.
- The DLL we built could also be called "unmanaged DLL", "native DLL". It means that it doesn't have any .NET framework involved (automatic memory/object gabage collection for example). It also doesn't have any MFC, ATL, COM+ involved. So I call it pure Win32 DLL.
- .NET C# app calling unmanaged DLL/native DLL has a specific terminology: P/Invoke (platform invoke). If you are interested, you can find tons of information on it. To me, I just call it
DllImport
method, just for myself.
The above POIs are already good enough for this beginner article. Below, I'm going to hit a couple of "beginner+" points, if it's too much for you, please skip... - Remember in our code (both in C# and VC++), you see this:
extern "C"
This is to tell the compiler that, when it compiles the code, the function behind the descriptor should use C standard naming mangling. What is naming mangling? You gulped (because I did that LOL).
Basically, any function names in IDE are human readable friendly (because we named them of course!). But the compiler will compile them differently. C++ has a standard, C has another. So to keep things simple (C# calling native C DLL), we tell the VS IDE to use C standard naming mangling for both projects.
To see the compiled mangled name, you first open Developer Command Prompt. It's just a DOS command line under VS environment.
in the DOS prompt screen, type the command:
dumpin /exports "C:\data\projtest\SuperEasyDLL\Debug\SuperEasyDLL.dll" (change your path accordingly).
Red marked exported name with the extern "C" naming mangling.
Red marked exported name without the extern "C" naming mangling.
- In our little Winform C# code, you can see this:
[DllImport("SuperEasyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
The "CallingConvention.Cdecl
" is to tell the compiler that this function call is going to clean the stack from C# client side. I guess the compiler will add some boilerplate binary code around it once compiled. I'm not an expert and I don't really know. What I really know is without this, the app will pop error complaining about some sort of stack issues.
People always say that from 0 to 1 is a big step, but we are just from 0 to 0.1.
But hey, a tiny step is still a step moving forward.
Hope you get your hands dirty and like it just as I did.
My next beginner article for anyone who wants to know a little more on VC++ is here:
History
- 8th September, 2017: Initial version