winapiactive-directorycommon-dialog

Is there a common dialog for selecting Active Directory users?


Is the Windows Select Users, Service Accounts, or Groups dialog:

enter image description here

available through an API to 3rd party developers?

Is there a "AD Browser" common dialog?


Solution

  • Directory Object Picker

    Sample pseudo-code:

    String SelectUsers(HWND hwndParent, IList<String> usersLdapPaths)
    {
       IDsObjectPicker objPicker;
       IDataObject objData;
       PDSOP_INIT_INFO pInfo;
       LPWSTR[0..2] attr;
       HRESULT hr;
    
       /*
          Returns the LDAP path to the selected user, e.g.:
             LDAP://stackoverflow.com/CN=Ian Boyd,OU=Stack Users,DC=stackoverflow,DC=com
    
          usersLdapPaths can be null. 
          If not null then the user can mutli-select users, 
          and the selected user's LDAP paths will be returned in usersLdapPaths 
          (with the function result containing just the first user)
    
          If the user cancels the dialog, then the result (and usersLdapPaths ) will be empty
       */
       Result := '';
    
       objPicker = CreateComObject(CLSID_DsObjectPicker) as IDsObjectPicker;
    
       System.New(pInfo);
       try
       {
          ZeroMemory(pInfo, SizeOf(DSOP_INIT_INFO));
          pInfo.cbSize = SizeOf(DSOP_INIT_INFO);
          pInfo.pwzTargetComputer = nil; //local computer
    
          pInfo.cDsScopeInfos := 1;
          System.New(pInfo.aDsScopeInfos);
          try
          {
             ZeroMemory(pInfo.aDsScopeInfos, SizeOf(DSOP_SCOPE_INIT_INFO));
             pInfo.aDsScopeInfos.cbSize = SizeOf(pInfo.aDsScopeInfos);
             pInfo.aDsScopeInfos.flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;  //or DSOP_SCOPE_TYPE_TARGET_COMPUTER;
             pInfo.aDsScopeInfos.flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP;
             pInfo.aDsScopeInfos.FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS;
             pInfo.aDsScopeInfos.FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS;
    
             if (UsersLdapPaths != null)
                pInfo.flOptions = DSOP_FLAG_MULTISELECT;
    
             pInfo.cAttributesToFetch := 3;
             attr[0] = "description";
             attr[1] = "name";
             attr[2] = "fullName";
             pInfo.apwzAttributeNames = @attr;
    
             hr = objPicker.Initialize(pInfo);
             OleCheck(hr);
             hr = objPicker.InvokeDialog(hwndParent, objData);
             OleCheck(hr);
    
             //the result is false if the user cancelled the dialog
             if hr = S_FALSE then
                return '';
    
             return ReadAttributes(objData, UsersLdapPaths);
          }
          finally
          {
             System.Dispose(pInfo.aDsScopeInfos);
          }      
       }
       finally
       {
          Dispose(pInfo);
       }
    }
    

    And the helper function (that i won't bother to transcode from one pseudocode language to another pseudocode language):

    function TActiveDirectory.ReadAttributes(ADataObject: IDataObject; AValues: TStrings): string;
    var
        fmtIn: TFormatEtc;
        stgOut: TStgMedium;
        pSelList: PDS_SELECTION_LIST;
        i: Integer;
        path: string;
    //  x: LongWord;
    //  pVar: POleVariant;
        items: PDsSelectionArray;
    begin
        Result := '';
    
        if Assigned(AValues) then
            AValues.Clear;
    
        if not Assigned(ADataObject) then
            Exit;
    
        stgOut.tymed := TYMED_HGLOBAL;
        fmtIn.tymed := TYMED_HGLOBAL;
        fmtIn.cfFormat := RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
        fmtIn.dwAspect := DVASPECT_CONTENT;
        fmtIn.lindex := -1;
    
        if (ADataObject.GetData(fmtIn, stgOut) <> S_OK) then
            Exit;
    
        pSelList := GlobalLock(stgOut.hGlobal);
        try
            if pSelList.cItems > 0 then
                items := PDsSelectionArray(@pSellist.aDsSelection)
            else
                items := nil;
    
            for i := 0 to pSelList^.cItems-1 do
            begin
    //          path := TDsSelectionArray(pSellist.aDsSelection)[i].pwzADsPath;
                path := items[i].pwzADsPath;
    
                if Assigned(AValues) then
                    AValues.Add(path);
    
                if Result = '' then
                    Result := path;
    
    {           Result := pSelList^.aDsSelection[i].pwzName+' ('+pSelList.aDsSelection[i].pwzADsPath+')';
                AValues.Add(Result);
                AValues.Add('   Class: '+pSelList^.aDsSelection[i].pwzClass); //"user"
                AValues.Add('   UPN: '+pSelList^.aDsSelection[i].pwzUPN );    //e.g. "ian@stackoverflow.com"
                pVar := pSelList^.aDsSelection[i].pvarFetchedAttributes;
                for x := 0 to pSelList^.cFetchedAttributes-1 do
                begin
                    AValues.Add('   '+VarToStr(pVar^));
                    if x < pSelList^.cFetchedAttributes then
                        Inc(pVar);
                end;}
            end;
        finally
            GlobalUnlock(stgOut.hGlobal);
        end;
    end;