查询Active Directory(AD)而不链接“ActiveDs_TLB.pas”

时间:2014-07-15 08:24:03

标签: delphi com active-directory wmi

我想在使用Delphi(7及更高版本)开发的应用程序中查询Active Directory,但不希望在“uses”子句中包含“ActiveDs_TLB”以保持EXE大小不变。查询WMI时,可以使用IBindCtx和IMoniker接口来避免在类型库中进行链接(请参阅How do I use WMI with Delphi without drastically increasing the application's file size?获取解决方案)。

执行AD查询时是否可以这样做?我的情况我想要检索“IADsUser”和“IADsComputer”。我知道我可以通过手动将所需的定义从“ActiveDs_TLB”复制到我的程序或使用LDAP查询来减少EXE大小,但我更喜欢类似于为WMI描述的解决方案。

2 个答案:

答案 0 :(得分:3)

我不是Active Directory专家,但我刚创建了两个D7控制台应用程序,一个使用ActiveDS_TLB.Pas类型库访问WnNTSystemInfo对象,另一个使用后期绑定做同样的事情,即从AD获取ComputerName

首先,后期绑定一个:

program ActiveDSLBConsole;
{$APPTYPE CONSOLE}
uses
  SysUtils, ActiveX, ComObj;
var
  SI : OleVariant;
  S : String;
begin
  CoInitialize(Nil);
  SI := CreateOleObject('WinNTSystemInfo');
  S := SI.ComputerName;
  writeln(S);
  readln;
end.

(上面写了上面最长的是检查注册表的名称 要创建的对象)

无论如何,我希望这表明,是的,您可以通过后期绑定查询AD,并且这个最小的示例将让您开始以这种方式查询AD。

使用ActiveDS_Tlb的等效AD控制台应用程序是

program ActiveDSConsole;
{$APPTYPE CONSOLE}
uses
  SysUtils, ActiveX, ActiveDS_Tlb;
var
  SI : IADsWinNTSystemInfo;
  S : String;
begin
  CoInitialize(Nil);
  SI := CoWinNTSystemInfo.Create;
  S := SI.ComputerName;
  writeln(S);
  readln;
end.

这些.Exe大小为

ActiveDSConsole   :  390144 bytes
ActiveDSLBConsole :   87552 bytes (late bound)

因此显然有相当多的代码可以支持使用 tlb对象,但都不是很大。

FWIW,上面重写为极简主义VCL应用程序的Button1Click处理程序,提供了Exe大小 的

using ActiveDS_TLB : 396288 bytes
late bound         : 392704 bytes

这两者之间的差异对我来说似乎相当微不足道,但有一个明确的 在最小的D7控制台应用程序中后期绑定的大小优势。你的旅费可能会改变, 如果你赦免混合的比喻,那么最好“吮吸它并看到”。

顺便说一下,后期绑定的优点是你不必总是为接口方法中的每个参数提供参数。您可以使用这种特殊语法调用方法,以便编译器得到增强,以允许(在D2中添加自动化支持时)它知道包含后期绑定自动化对象的变体:

(来自MS Word后期绑定示例)

Table := MSWord.ActiveDocument.Tables.Add(Range:= MSWord.Selection.Range, NumRows:= Rows, NumColumns:= Columns, DefaultTableBehavior:= wdWord9TableBehavior, AutoFitBehavior:= wdAutoFitFixed);

答案 1 :(得分:1)

马丁的答案填补了缺失的部分。以下是如何使用后期绑定查询IADsUser的示例:

program GetUserObjectPath;

{$APPTYPE CONSOLE}

uses SysUtils, ActiveX, ComObj;

function GetObject (const Name: WideString) : IDispatch;

var
    Moniker : IMoniker;
    Eaten : Integer;
    BindContext : IBindCtx;

begin
    OleCheck (CreateBindCtx (0, BindContext));
    OleCheck (MkParseDisplayName (BindContext, PWideChar (Name), Eaten,
                                  Moniker));
    OleCheck (Moniker.BindToObject (BindContext, NIL, IDispatch, Result));
end; { GetObject }

procedure Query_AD (const sQuery: String);

var
    vUser : OleVariant;

begin
    vUser := GetObject (sQuery);  // = IADsUser
    WriteLn ('Name = ' + vUser.FullName);
end; { Query_AD }

var
    sQuery, sDomain, sUserName : String;

begin
    sDomain := GetEnvironmentVariable ('USERDNSDOMAIN');
    sUserName := GetEnvironmentVariable ('USERNAME');

    sQuery := Format ('WinNT://%s/%s,user', [sDomain, sUserName]);

    CoInitialize (NIL);

    try
        Query_AD (sQuery);

       finally
        // Causes Access Violation if AD query does not happen in subroutine
        CoUninitialize;  
    end; { try / finally }

    WriteLn;
    Write ('Press [Enter] to continue ...');
    ReadLn;
end.

实际的AD查询应该在子例程(此处为“Query_AD”)中进行,否则调用“CoUninitialize”将导致访问冲突(请参阅Why does CoUninitialize cause an error on exit?获取解释)。