Using BatchExport Function

One of very interesting functions in DAPI is the BatchExport function. With it you can export properties of multiple directory objects into a file. It has a counterpart - BatchImport, to do the same operation in reverse. When I was just starting investigating MS Exchange directory - I thought about a way to export the whole directory into a file for examination. In fact, it is very useful to dump the contents of the whole tree (or a sub-tree) for closer look.
 

Batch Export of All Addr-Type Objects in Organization

The following DELPHI 5 sample demonstrates how to dump 4 specific attributes for all Addr-Type objects in organization.

procedure TfrmMAIN.btOKClick(Sender: TObject);
const
   Classes : array [0..1] of PChar = ('Addr-Type',nil);
var
  bep:BEXPORT_PARMS;
  deAttributes:PDAPI_ENTRY;
  avAttrName:PATT_VALUE;
  CountAtt:Integer;
  dwResult:DWORD;
begin

if SaveDialog1.Execute then
    begin
        if Assigned(SiteInfo) then FreeAndNil(SiteInfo);
        SiteInfo:=TSiteInfo.Create(Trim(ebESA.Text));
             if SiteInfo.GetInfo then
                begin
                    ZeroMemory(@bep, SizeOf( BEXPORT_PARMS ));
                   
// Initialize BEXPORT_PARMS struct
                    bep.dwDAPISignature := DAPI_SIGNATURE;
                    bep.pszDSAName:=PCHAR(SiteInfo.SrvName);
                    bep.dwFlags :=  DAPI_EXPORT_ALL_CLASSES or
                                            DAPI_EXPORT_HIDDEN or
                                            DAPI_EXPORT_SUBTREE or
                                            DAPI_RAW_MODE;

                    bep.pszExportFile := PCHAR(SaveDialog1.FileName);
   
                 // Base point is current organization object
                    bep.pszBasePoint := PCHAR(SiteInfo.OrganizationDNString); 


                    // Specify which objects to export. This overrides DAPI_EXPORT_ALL_CLASSES
                    // in dwFlags.
                    bep.rgpszClasses := @Classes;

                    CountAtt:=4;

                    avAttrName:=DAPIAllocBuffer(SizeOf(ATT_VALUE)*CountAtt,nil);
                    ZeroMemory(avAttrName,SizeOf(ATT_VALUE)*CountAtt);

                    PATT_VALUE(ULONG(avAttrName)+ULONG(SizeOf(ATT_VALUE)*(CountAtt-4)))^:=
                            PATT_VALUE(ToAttValue('Object-Class',DAPI_TEXT))^;
                    PATT_VALUE(ULONG(avAttrName)+ULONG(SizeOf(ATT_VALUE)*(CountAtt-3)))^:=
                            PATT_VALUE(ToAttValue('Directory Name',DAPI_TEXT))^;
                    PATT_VALUE(ULONG(avAttrName)+ULONG(SizeOf(ATT_VALUE)*(CountAtt-2)))^:=
                            PATT_VALUE(ToAttValue('Proxy-Generator-DLL',DAPI_TEXT))^;
                    PATT_VALUE(ULONG(avAttrName)+ULONG(SizeOf(ATT_VALUE)*(CountAtt-1)))^:=
                            PATT_VALUE(ToAttValue('File-Version',DAPI_TEXT))^;

                    deAttributes:=DAPIAllocBuffer(SizeOf(DAPI_ENTRY), nil);
                    ZeroMemory(deAttributes, SizeOf(DAPI_ENTRY));

                    deAttributes.unAttributes := CountAtt;
                    deAttributes.ulEvalTag := TEXT_VALUE_ARRAY;
               deAttributes.rgEntryValues:=DAPIAllocBuffer(SizeOf(ATT_VALUE)*(CountAtt),deAttributes);
                    ZeroMemory(deAttributes.rgEntryValues, SizeOf(ATT_VALUE)*(CountAtt));
               
                    deAttributes.rgEntryValues:=avAttrName;
                    
                    bep.pAttributes := deAttributes;

                    dwResult:=BatchExport(@bep);

                    if Assigned(avAttrName) then
                            DAPIFreeMemory(avAttrName);

                    if Assigned(deAttributes) then
                            DAPIFreeMemory(deAttributes);


                end;
        end;
   if Assigned(SiteInfo) then FreeAndNil(SiteInfo);
end;

The file named "Dump.txt" may be found in example with the result of this export operation on my Exchange server. In my particular case it contains 10 exported objects.

Batch Export of the Mailbox and Distribution List 

If you like to export Mailboxes and DList, change classes to this:

procedure TfrmMAIN.btOKClick(Sender: TObject);
const
Classes : array [0..2] of PChar = ('Mailbox','Distribution-List',nil);
var
....

begin
......
end;

DELPHI 5 compiled example

Batch Export of the Whole Directory Tree in an Organization

If you'd like to export the contents of the whole tree, you may do it by using the following fragment (corresponding sample is BatchExportAll):

procedure TfrmMAIN.btOKClick(Sender: TObject);
var
    bep:BEXPORT_PARMS;
    deAttributes:PDAPI_ENTRY;
    avAttrName:PATT_VALUE;
    CountAtt:Integer;
    dwResult:DWORD;
begin

if SaveDialog1.Execute then
    begin
        if Assigned(SiteInfo) then FreeAndNil(SiteInfo);
        SiteInfo:=TSiteInfo.Create(Trim(ebESA.Text));
        if SiteInfo.GetInfo then
            begin
                ZeroMemory(@bep, SizeOf( BEXPORT_PARMS ));
                // Initialize BEXPORT_PARMS struct
                bep.dwDAPISignature := DAPI_SIGNATURE;
                bep.pszDSAName:=PCHAR(SiteInfo.SrvName);
                bep.dwFlags := DAPI_EXPORT_ALL_CLASSES or
                                        DAPI_EXPORT_HIDDEN or
                                        DAPI_EXPORT_SUBTREE or
                                        DAPI_RAW_MODE;

                bep.pszExportFile := PCHAR(SaveDialog1.FileName);
                bep.pszBasePoint := PCHAR(SiteInfo.OrganizationDNString);

                bep.rgpszClasses := nil;

                CountAtt:=2;

                avAttrName:=DAPIAllocBuffer(SizeOf(ATT_VALUE)*CountAtt,nil);
                ZeroMemory(avAttrName,SizeOf(ATT_VALUE)*CountAtt);

                PATT_VALUE(ULONG(avAttrName)+ULONG(SizeOf(ATT_VALUE)*(CountAtt-2)))^:=
                        PATT_VALUE(ToAttValue('Object-Class',DAPI_TEXT))^;
                PATT_VALUE(ULONG(avAttrName)+ULONG(SizeOf(ATT_VALUE)*(CountAtt-1)))^:=
                        PATT_VALUE(ToAttValue('Obj-Dist-Name',DAPI_TEXT))^;

                deAttributes:=DAPIAllocBuffer(SizeOf(DAPI_ENTRY), nil);
                ZeroMemory(deAttributes, SizeOf(DAPI_ENTRY));

                deAttributes.unAttributes := CountAtt;
                deAttributes.ulEvalTag := TEXT_VALUE_ARRAY;

             deAttributes.rgEntryValues:=DAPIAllocBuffer(SizeOf(ATT_VALUE)*(CountAtt), deAttributes);
             ZeroMemory(deAttributes.rgEntryValues, SizeOf(ATT_VALUE)*(CountAtt));

             deAttributes.rgEntryValues:=avAttrName;

            bep.pAttributes := deAttributes;

            dwResult:=BatchExport(@bep);

            if Assigned(avAttrName) then
                    DAPIFreeMemory(avAttrName);

            if Assigned(deAttributes) then
                    DAPIFreeMemory(deAttributes);

        end;
     end;
    if Assigned(SiteInfo) then FreeAndNil(SiteInfo);
end;

This code exports only class names and distinguished names for all objects in the organization. Again, the result of the dump may be found together with source files. If you look into Dump.txt file in BatchExportAll example, you'll notice that most of its contents is occupied by Class-Schema and Attribute-Schema objects, and only a small fraction in the end - by real physical "instances" of them. This is because my test installation of MS Exchange is very small - it contains only a few mailboxes.

The fragment above is different from the first one because I don't specify class names here. In the first sample I have initialized rgpszClasses member of BEXPORT_PARMS, thus telling that I specifically request Addr-Type objects only. Also, the number of exported attributes is different - I have decided for the sake of simplicity only export distinguished names (and class names). If you want to export other attributes - you need to do extra work initializing its pAttributes member.