Today, C++ seems to be a second class citizen in Visual Studio 2005. While C# is more elegant and easier than C++/MC++, there are instances when Visual C++ might be an indispensable tool. If you are trying to build an assembly that exposes functionality for both managed and unmanaged code, then Visual C++ might be your only option. The only function behind this example is ADS authentication to give some meaning to this article.
Creating the Multipurpose DLL
Here are the steps needed to create such a project. Start with an ATL project that creates a DLL. Add a simple ATL object with a dual interface. In my case, I have an
ADSAuthenticate COM class with a
IADSAuthenticate interface. There is only one function in this interface:
STDMETHOD(IsAuthorized)(BSTR bstrDomain, BSTR bstrName,
BSTR bstrPassword, VARIANT_BOOL* pvbResult);
To spice things up, I've added a similar function as a C export API for use by legacy clients. I've added a new file dllexportapi.h (below) to the project and defined
AUTHORIZATION_EXPORTS at the project level (or stdafx.h file).
#define AUTHORIZATION_API __declspec(dllexport)
#define AUTHORIZATION_API __declspec(dllimport)
extern "C" AUTHORIZATION_API BOOL _stdcall IsUserAuthorized
In the .def file, add your exported function (
IsUserAuthorized). It should look like this:
; ADSAuthentication.def : Declares the module parameters.
For this particular project, the whole idea is to funnel the unmanaged call to the managed call that would authorize against the current ADS directory.
The most complex task is making this native DLL become an assembly since Visual Studio does not offer a tool to add a managed class.
To do so, start by adding a regular C++ class. Name it
ManagedAuthentication. Select the ManagedAuthentication.cpp in the solution explorer and change the file properties as described below for all configurations:
On the Configuration Properties->C/C++ ->General page:
Select Debug Information Format: C7 Compatible(/Z7)
Compile with Common Language Runtime: Common Language Runtime Support (/clr)
On the Configuration Properties->C/C++ ->Code Generation page:
Enable Minimal Rebuild: NO
Enable C++ Exceptions:Yes With SHE Exceptions(/EHa)
Runtime Library : Multi-threaded DLL (/MD)
On the Configuration Properties->C/C++ ->Precompiled Headers page:
Create/Use Precompiled header: Not Using Precompiled Headers
Under the project properties->Common Properties->References
Add the needed assemblies, in this case
Now we are going to the workhorse of this project, add the right headers to the ManagedAuthentication.cpp file:
using namespace System;
using namespace System::DirectoryServices;
using namespace System::Runtime::InteropServices;
Add the method to handle the authentication:
(String^ domain, String^ user, String^ password)
String^ domainAndUsername = domain + "\\" + user;
DirectoryEntry^ entry = gcnew DirectoryEntry(String::Empty,
// Bind to the native AdsObject to force authentication.
return entry->NativeObject != nullptr;
if (entry != nullptr)
Finally provide the implementation for the C export function:
BOOL _stdcall IsUserAuthorized(wchar_t* szDomain,
wchar_t* szUser, wchar_t* szPassword)
bool bres = false;
String^ user = Marshal::PtrToStringUni((IntPtr)szUser);
String^ domain = Marshal::PtrToStringUni((IntPtr)szDomain);
String^ password = Marshal::PtrToStringUni((IntPtr)szPassword);
bres = ManagedAuthentication::IsAuthorized(domain, user, password);
Points of Interest
Don't forget to register the DLL like a COM server using regsvr32 ADSAuthentication.dll to get the most out of its exposed interfaces. There are 2 testing projects included in the solution to call the DLL in different ways.