Introduction
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).
#ifdef AUTHORIZATION_EXPORTS
#define AUTHORIZATION_API __declspec(dllexport)
#else
#define AUTHORIZATION_API __declspec(dllimport)
#endif
extern "C" AUTHORIZATION_API BOOL _stdcall IsUserAuthorized
(wchar_t* szDomain,
In the .def file, add your exported function (IsUserAuthorized
). It should look like this:
; ADSAuthentication.def : Declares the module parameters.
LIBRARY "ADSAuthentication.DLL"
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
IsUserAuthorized
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 System.DirectoryServices
.
Now we are going to the workhorse of this project, add the right headers to the ManagedAuthentication.cpp file:
#include "StdAfx.h"
#include "dllexportapi.h"
#include <atlstr.h>
using namespace System;
using namespace System::DirectoryServices;
using namespace System::Runtime::InteropServices;
#include <atlstr.h>
#include "ManagedAuthentication.h"
Add the method to handle the authentication:
bool ManagedAuthentication::IsAuthorized
(String^ domain, String^ user, String^ password)
{
String^ domainAndUsername = domain + "\\" + user;
DirectoryEntry^ entry = gcnew DirectoryEntry(String::Empty,
domainAndUsername,
password);
try
{
return entry->NativeObject != nullptr;
}
catch (...)
{
}
finally
{
if (entry != nullptr)
entry->Close();
}
return false;
}
Finally provide the implementation for the C export function:
BOOL _stdcall IsUserAuthorized(wchar_t* szDomain,
wchar_t* szUser, wchar_t* szPassword)
{
bool bres = false;
try
{
String^ user = Marshal::PtrToStringUni((IntPtr)szUser);
String^ domain = Marshal::PtrToStringUni((IntPtr)szDomain);
String^ password = Marshal::PtrToStringUni((IntPtr)szPassword);
bres = ManagedAuthentication::IsAuthorized(domain, user, password);
}
catch (...)
{
}
return (BOOL)bres;
}
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.