如何构建暴露 Win32 API 和本机 COM 对象的程序集






3.69/5 (8投票s)
在 DLL 库中进行托管和非托管调用。

引言
现在,C++ 在 Visual Studio 2005 中似乎是二等公民。虽然 C# 比 C++/MC++ 更优雅且更容易,但在某些情况下,Visual C++ 可能是必不可少的工具。如果您正在尝试构建一个程序集,该程序集为托管代码和非托管代码公开功能,那么 Visual C++ 可能是您的唯一选择。此示例背后的唯一功能是 ADS 身份验证,以便使本文具有一定的意义。
创建多用途 DLL
以下是创建此类项目所需的步骤。 从创建 DLL 的 ATL 项目开始。 添加一个带有双接口的简单 ATL 对象。 在我的例子中,我有一个带有 IADSAuthenticate
接口的 ADSAuthenticate
COM 类。 此接口中只有一个函数
STDMETHOD(IsAuthorized)(BSTR bstrDomain, BSTR bstrName,
BSTR bstrPassword, VARIANT_BOOL* pvbResult);
为了使事情更加有趣,我添加了一个类似的函数作为 C 导出 API,供旧版客户端使用。 我在项目中添加了一个新文件 dllexportapi.h(如下),并在项目级别(或 stdafx.h 文件)定义了 AUTHORIZATION_EXPORTS
。
#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);
在 .def 文件中,添加您导出的函数 (IsUserAuthorized
)。 它应该看起来像这样
; ADSAuthentication.def : Declares the module parameters.
LIBRARY "ADSAuthentication.DLL"
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
IsUserAuthorized
对于这个特定的项目,整个想法是将非托管调用引导到托管调用,后者将针对当前的 ADS 目录进行授权。
最复杂的任务是使这个原生 DLL 成为程序集,因为 Visual Studio 没有提供添加托管类的工具。
为此,首先添加一个常规 C++ 类。 将其命名为 ManagedAuthentication
。 在解决方案资源管理器中选择 ManagedAuthentication.cpp,并按照如下所述更改所有配置的文件属性
在“配置属性”->“C/C++”->“常规”页面上
选择“调试信息格式”:“C7 兼容(/Z7)”
使用公共语言运行时编译: 公共语言运行时支持 (/clr)
在“配置属性”->“C/C++”->“代码生成”页面上
启用最小重新生成: 否
启用 C++ 异常: 是,带 SHE 异常 (/EHa)
运行时库 : 多线程 DLL (/MD)
在“配置属性”->“C/C++”->“预编译头”页面上
创建/使用预编译头: 不使用预编译头
在“项目属性”->“通用属性”->“引用”下
添加所需的程序集,在本例中为 System.DirectoryServices
。
现在我们将要处理这个项目的主力,将正确的标头添加到 ManagedAuthentication.cpp 文件
#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"
添加处理身份验证的方法
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;
}
最后,提供 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;
}
关注点
不要忘记像 COM 服务器一样使用 regsvr32 注册 DLL ADSAuthentication.dll,以充分利用其公开的接口。 解决方案中包含 2 个测试项目,用于以不同的方式调用 DLL。