Microsoft KB Archive/292029

{|
 * width="100%"|

HOWTO: Manipulate the ThumbNailPhoto Attribute of a User Object in the Active Directory

 * }

Q292029

-

The information in this article applies to:


 * Microsoft Active Directory Services Interface, System Component, included with:
 * Microsoft Windows 2000 Professional
 * Microsoft Windows 2000 Server
 * Microsoft Windows 2000 Advanced Server

-

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
Create a new Win32 Console Application in Visual C++. Select a Hello World application type. Name the application Getpicture.

Replace the entire contents of the Getpicture.cpp file with the code provided at the end of this article.

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:

 From the Project menu, choose Settings. Click the Link tab. In the Object/Library Modules text box, add the following two libraries: ADSIID.lib and ACTIVEDS.lib. Click OK.

Disable the precompiled headers options for the project under project settings.

The steps for changing the precompiled header options are:

 From the Project menu, click Settings. Click the C/C++ tab. In the Category: drop down box, select Precompiled Headers.</li> Select Not using precompiled headers.</li> Click OK</li></ol>

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 &quot;Picture&quot; attributeSchema object in the Active Directory Schema. The attributes that define the size limits are &quot;rangeLower&quot; and &quot;rangeUpper&quot;:


 * 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

Compile and link the project.

Execute the project.

Getpicture.cpp Source Code
<pre class="CODESAMP">#if _MSC_VER > 1000
 * 1) pragma once
 * 2) endif // _MSC_VER > 1000


 * 1) define WIN32_LEAN_AND_MEAN    // Exclude rarely-used stuff from Windows headers
 * 2) include <fcntl.h>
 * 3) include <sys/types.h>
 * 4) include <sys/stat.h>
 * 5) include <io.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 // // // 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[]) {
 * 1) include <stdio.h>
 * 2) include <atlbase.h>
 * 3) include <activeds.h>
 * 4) include <adsiid.h>
 * 1) define LDAP_USER_PATH L&quot;LDAP://USER_DN&quot;
 * 2) define PICTURE_LOCAL_PATH &quot;smile.bmp&quot;
 * 3) define OUTPUT_PATH &quot;directory.bmp&quot;

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(&quot;Done\n&quot;); 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&quot;thumbNailPhoto&quot;,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&quot;thumbNailPhoto&quot; }; 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&quot;thumbNailPhoto&quot;) == 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; } Additional query words:

Keywords : kbADSI kbDSupport

Issue type : kbhowto

Technology : kbAudDeveloper kbADSISearch kbADSISysComp