How To Manipulate the ThumbNailPhoto Attribute of a User Object in the Active Directory

Article translations Article translations
Article ID: 292029
Expand all | Collapse all

On This Page

Summary

This article illustrates how to place picture data into and retrieve data from the thumbNailPhoto attribute of a user object in the Active Directory by using both the IDirectoryObject and IADsUser interfaces.

More information

The IADsUser::Picture property provides access to the thumbNailPhoto attribute of a user class object in the Active Directory (AD). The thumbNailPhoto attribute contains octet string type data. The AD interprets octet string data as an array of bytes. The user has the freedom to store any type of binary data in the thumbNailPhoto attribute. The user is responsible for both rendering the picture data and insuring that the appropriate data is stored in the attribute.

Steps to Use the Code Provided in this Article

  1. Create a new Win32 Console Application in Visual C++. Select a Hello World application type. Name the application Getpicture.
  2. Replace the entire contents of the Getpicture.cpp file with the code provided at the end of this article.
  3. Add the ADSIID.lib and ACTIVEDS.lib files to the projects link list. The steps for adding these files to the project's list of linked libraries are as follows:

    1. From the Project menu, choose Settings.
    2. Click the Link tab.
    3. In the Object/Library Modules text box, add the following two libraries: ADSIID.lib and ACTIVEDS.lib.
    4. Click OK.
  4. Disable the precompiled headers options for the project under project settings. The steps for changing the precompiled header options are:

    1. From the Project menu, click Settings.
    2. Click the C/C++ tab.
    3. In the Category: drop down box, select Precompiled Headers.
    4. Select Not using precompiled headers.
    5. Click OK
  5. Replace the predefined strings in Getpicture.cpp with their appropriate values.

    NOTE: There is a limit on the size of the image stored on the ThumbNailPhoto attribute, and this is stored in bytes on the "Picture" attributeSchema object in the Active Directory Schema. The attributes that define the size limits are "rangeLower" and "rangeUpper":
    • LDAP_USER_PATH - LDAP ADsPath for a user object
    • PICTURE_LOCAL_PATH - Local path name for a picture file (BMP/JPG/GIF)
    • OUTPUT_PATH - local path name to receive the data from the AD
  6. Compile and link the project.
  7. Execute the project.

Getpicture.cpp Source Code

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN     // Exclude rarely-used stuff from Windows headers
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>

#include <stdio.h>
#include <atlbase.h>
#include <activeds.h>
#include <adsiid.h>
// 
// Predefined strings to use for examples.
// LDAP_USER_PATH must be set to a valid DN of a user object
// PICTURE_LOCAL_PATH must be set to a pathname of a BMP/JPG/GIF or some other picture format file
// OUTPUT_PATH must be set to a filename to receive data from the AD
// 
#define LDAP_USER_PATH L"LDAP://USER_DN"
#define PICTURE_LOCAL_PATH "smile.bmp"
#define OUTPUT_PATH "directory.bmp"
// 
// BytesToVariantArry creates an ADSTYPE_OCTET_STRING variant
// from a given set of bytes
// 
HRESULT BytesToVariantArray(PBYTE pValue, //Pointer to bytes to put in a variant array.
                            ULONG cValueElements,//Size of pValue in bytes.
                            VARIANT *pVariant //Return variant containing octet string (VT_UI1|VT_ARRAY).
                            );
// 
// VariantArrayToBytes -> Assumes a single dimentioned array of bytes passed in the 
// Variant parameter as an ADSTYPE_OCTET_STRING, returns an array of bytes. the bytes parameter must be freed
// using the free() api call.
// 
HRESULT VariantArrayToBytes(
                            VARIANT Variant,
                            LPBYTE *bytes,
                            long *plcb
                            );
// 
// UseIDirectoryObjectSet-> Takes an IADs inteface of a user object, 
// QIs for an IDirectoryObject interface and places the bytes pointed to
// by pBuffer into the thumbNailPhoto attribute.
// 
HRESULT UseIDirectoryObjectSet( IADs *oIADs, 
                            LPBYTE pBuffer, 
                            DWORD cbBuffer );
// 
// UseIDirectoryObjectGet-> Takes an IADs interface of a user object,
// QIs for an IDirectorObject interface and copies the buffer returned 
// from the thumbNailPhoto attribute into the bytes parameter.  The bytes
// parameter is allocated by the function and must be free using 
// the free() API.  The length of the buffer is returned in the plcb parameter.
// 
HRESULT UseIDirectoryObjectGet( IADs *pIADs, LPBYTE *bytes, long *plcb );
// 
// Main Function
// 
int main(int argc, char* argv[])
{

    int hFile;
    CoInitialize( NULL );
    // 
    // Read in the test file
    // and determine how many bytes are in the file
    // using _lseek
    // 
    hFile = _open( PICTURE_LOCAL_PATH, _O_BINARY | _O_RDONLY );
    long retval = _lseek( hFile, 0l, SEEK_END);
    // 
    // Allocate memory and copy the file contents into memory
    // 
    LPBYTE lpbPicture = (LPBYTE)malloc( retval );
    _lseek( hFile, 0l, SEEK_SET);
    long bRead = _read( hFile, lpbPicture, retval);
    // 
    // Put the memory into a safe array in order to place it
    // into the directory
    // 
    VARIANT vPicture;
    HRESULT hr = BytesToVariantArray( lpbPicture, retval, &vPicture);
    // 
    // Retrieve an IADsUser object interface
    // 
    CComPtr <IADsUser> pOusr;
    CComPtr <IADs> pIADs;
    // 
    hr = ADsGetObject(LDAP_USER_PATH, IID_IADsUser, (VOID **)&pOusr);
    // 
    // Example of how to call the UseIDirectoryObjectSet function:
    //hr = UseIDirectoryObjectSet( pIADs, lpbPicture, retval);
    // 
    // Put the variant data into the directory
    // 
    hr = pOusr->put_Picture( vPicture );
    hr = pOusr->SetInfo();  // flush the cache back to the server...
    pOusr.Release();        // Release the interface and rebind....
    // 
    // Bind to the directory again, and read the property
    // 
    hr = ADsGetObject(LDAP_USER_PATH, IID_IADsUser, (VOID **)&pOusr);
    // 
    // To retrieve the buffer using UseIDirectoryObjectGet, here are the declarations
    // and the call:
    // 
    // CComQIPtr<IADs, &IID_IADs> pds( pOusr);
    // LPBYTE bBuffer;
    // long cbBuffer;
    // hr = UseIDirectoryObjectGet( pds, &bBuffer, &cbBuffer);
    // pds.Release();
    // .
    // .
    // .
    // free( bBuffer );
    // 
    VariantClear( &vPicture ); // Release the old variant, **very important** to free the 
                               // the safe array before reusing the variable vPicture
    hr = pOusr->get_Picture( &vPicture );
    pOusr.Release();
    // 
    // Write it back to a file
    // 
    PBYTE lpbADPicture;
    long cb;
    hr = VariantArrayToBytes( vPicture, &lpbADPicture, &cb);
    // 
    // Copy the data back out to the disk file
    // 
    int hFile2 = _open(OUTPUT_PATH, _O_BINARY | _O_CREAT | _O_RDWR);
    retval = _lseek( hFile2, 0l, SEEK_SET );
    retval = _write( hFile2, lpbADPicture, cb );
    // 
    // Time to clean up..
    // 
    _close( hFile2 );
    _close(hFile );
    free( (void *)lpbADPicture );
    free( (void *)lpbPicture );
    VariantClear( &vPicture );
    pOusr.Release();
    CoUninitialize( );
    printf("Done\n");
    
    return 0;
}
// 
// This function can be found in the Platform SDK documention
// It allocates the buffer from heap. The buffer must be freed
// when the caller is finished using it.
// 
HRESULT VariantArrayToBytes(
                            VARIANT Variant,
                            LPBYTE *bytes,
                            long *plcb
                            )
{
    HRESULT hr = E_FAIL;
    SAFEARRAY *pArrayVal = NULL;
    CHAR HUGEP *pArray = NULL;
    
    // 
    //Retrieve the safe array....
    // 
    pArrayVal = Variant.parray;
    
    if (!(pArrayVal == NULL) )
    {
        long cSize = pArrayVal->rgsabound[0].cElements;
        *bytes = (LPBYTE)malloc( cSize );
        if( *bytes == NULL ) return E_FAIL;
        hr = SafeArrayAccessData(pArrayVal, (void HUGEP * FAR *) &pArray );
        if (SUCCEEDED(hr))
        {
            //Copy the bytes to the safe array.
            memcpy( *bytes, pArray, cSize );
            SafeArrayUnaccessData( pArrayVal );
            *plcb = cSize;
            hr = S_OK;
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    
    return hr;
}
// 
// This function can be found in the Platform SDK documention
// 
HRESULT BytesToVariantArray(
                            PBYTE pValue, //Pointer to bytes to put in a variant array.
                            ULONG cValueElements,//Size of pValue in bytes.
                            VARIANT *pVariant //Return variant containing octet string (VT_UI1|VT_ARRAY).
                            )
{
    HRESULT hr = E_FAIL;
    SAFEARRAY *pArrayVal = NULL;
    SAFEARRAYBOUND arrayBound;
    CHAR HUGEP *pArray = NULL;
    
    //Set bound for array
    arrayBound.lLbound = 0;
    arrayBound.cElements = cValueElements;
    
    //Create the safe array for the octet string. unsigned char elements;single dimension;aBound size.
    pArrayVal = SafeArrayCreate( VT_UI1, 1, &arrayBound );
    
    if (!(pArrayVal == NULL) )
    {
        hr = SafeArrayAccessData(pArrayVal, (void HUGEP * FAR *) &pArray );
        if (SUCCEEDED(hr))
        {
            //Copy the bytes to the safe array.
            memcpy( pArray, pValue, arrayBound.cElements );
            SafeArrayUnaccessData( pArrayVal );
            //Set type to array of unsigned char
            V_VT(pVariant) = VT_ARRAY | VT_UI1;
            //Assign the safe array to the array member.
            V_ARRAY(pVariant) = pArrayVal;
            hr = S_OK;
        }
        else
        {
            //Clean up if array can't be accessed.
            if ( pArrayVal )
                SafeArrayDestroy( pArrayVal );
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    
    return hr;
}
// 
// Take an IADs interface obtain an IDirectoryObject interface, then
// put the buffer pointed to by pBuffer into the thumbNailPhoto attribute
// of the user object
// 
// the IADs interface must have been obtained using the LDAP provider.  WinNT
// provided IADs interfaces do not support the IDirectoryObject interface.
// 
// IDirectoryObject is supported by the LDAP provider.
// 
HRESULT UseIDirectoryObjectSet( IADs *oIADs, LPBYTE pBuffer, DWORD cbBuffer )
{
    CComQIPtr<IDirectoryObject, &IID_IDirectoryObject> poDirObj(oIADs);
    HRESULT    hr;
    DWORD  dwReturn;
    ADSVALUE  snValue;
    ADS_OCTET_STRING octString = { cbBuffer, pBuffer };
    ADS_ATTR_INFO  attrInfo[] = {   {L"thumbNailPhoto",ADS_ATTR_UPDATE,ADSTYPE_OCTET_STRING,&snValue,1} };
    DWORD dwAttrs = sizeof(attrInfo)/sizeof(ADS_ATTR_INFO); 
    
    snValue.dwType=ADSTYPE_OCTET_STRING;
    snValue.OctetString = octString;
    
    hr=poDirObj->SetObjectAttributes(attrInfo, dwAttrs, &dwReturn);
    return hr;
}
// 
// Illustration of how to use the IDirectoryObject to retrieve 
// the thumbNailPhoto attribute
// 
// The method takes an IADs interface pointer obtained from the LDAP
// provider.
// See comments above for information on WinNT and IDirectoryObject.
// 
// The buffer pointer returned in the bytes parameter must be freed using
// the free() api.
// 
HRESULT UseIDirectoryObjectGet( IADs *pIADs, LPBYTE *bytes, long *plcb )
{
    CComQIPtr<IDirectoryObject, &IID_IDirectoryObject> poDirObj( pIADs );
    HRESULT   hr;
    ADS_ATTR_INFO   *pAttrInfo=NULL;
    DWORD   dwReturn;
    LPWSTR   pAttrNames[]={L"thumbNailPhoto" };
    DWORD   dwNumAttr=sizeof(pAttrNames)/sizeof(LPWSTR);
 
    ///////////////////////////////////////// 
    // Get attribute values requested
    // Note: The order is not necessarily the 
    // same as requested using pAttrNames.
    /////////////////////////////////////////// 
    hr = poDirObj->GetObjectAttributes( pAttrNames,
                                        dwNumAttr, 
                                        &pAttrInfo, 
                                        &dwReturn );
 
    if ( SUCCEEDED(hr) )
    {
       for(DWORD idx=0; idx < dwReturn;idx++, pAttrInfo++ )
       {
           if ( _wcsicmp(pAttrInfo->pszAttrName,L"thumbNailPhoto") == 0 )
           {
               if (pAttrInfo->dwADsType == ADSTYPE_OCTET_STRING)
               {
                   *bytes = (LPBYTE)malloc( pAttrInfo->pADsValues->OctetString.dwLength);
                   *plcb = pAttrInfo->pADsValues->OctetString.dwLength;
                   memcpy( *bytes, pAttrInfo->pADsValues->OctetString.lpValue, *plcb);
               }
           }

       }
    }
 
   ///////////////////////////////////////////////////////////// 
   // Use FreeADsMem for all memory obtained from the ADSI call. 
   ///////////////////////////////////////////////////////////// 
   FreeADsMem( pAttrInfo );


    return hr;
}
				

Properties

Article ID: 292029 - Last Review: June 19, 2014 - Revision: 3.0
Keywords: 
kbdswadsi2003swept kbhowto KB292029
Retired KB Content Disclaimer
This article was written about products for which Microsoft no longer offers support. Therefore, this article is offered "as is" and will no longer be updated.

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com