Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

How To Build an Assembly that Exposes Win32 APIs and Native COM Objects

3.69/5 (8 votes)
18 Jun 2007CPOL2 min read 1   489  
Make managed and unmanaged calls into a DLL library
Screenshot - adsauthentication.png

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:

C++
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).

C++
#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, //wchar_t* szUser, wchar_t* szPassword); 

In the .def file, add your exported function (IsUserAuthorized). It should look like this:

C++
; 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:

C++
#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:

C++
bool ManagedAuthentication::IsAuthorized
	(String^ domain, String^ user, String^ password)
{
            String^ domainAndUsername = domain + "\\" + user;
            DirectoryEntry^ entry = gcnew DirectoryEntry(String::Empty,
                                                       domainAndUsername,
                                                         password);

            try
            {
                // Bind to the native AdsObject to force authentication.
                return entry->NativeObject != nullptr;

            }
            catch (...)
            {
            }
            finally
            {
                if (entry != nullptr)
                    entry->Close();
            }
            return false;
}

Finally provide the implementation for the C export function:

C++
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)