Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

794 lines
25 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: UserPict.cpp
  3. //
  4. // Copyright (c) 2000, Microsoft Corporation
  5. //
  6. // Functions that implement user picture manipulation.
  7. //
  8. // History: 2000-03-24 vtan created
  9. // 2000-05-03 jeffreys reworked using DIB sections
  10. // 2000-10-26 jeffreys switched from %ALLUSERSPROFILE%\Pictures
  11. // to CSIDL_COMMON_APPDATA\User Account Pictures
  12. // --------------------------------------------------------------------------
  13. #include "shellprv.h"
  14. #include <lmcons.h>
  15. #include <shimgdata.h>
  16. #include <aclapi.h> // for SetNamedSecurityInfo
  17. #include <shgina.h> // for ILogonUser
  18. #pragma warning(push,4)
  19. // --------------------------------------------------------------------------
  20. // SaveDIBSectionToFile
  21. //
  22. // Arguments: hbm = Source image (DIB section) to save
  23. // hdc = Device Context containing hbm. May be NULL
  24. // if hbm is not selected in any DC or hbm
  25. // is known to have no color table.
  26. // pszFile = Target image file.
  27. //
  28. // Returns: BOOL
  29. //
  30. // Purpose: Write a DIB to disk in the proper format
  31. //
  32. // History: 2000-05-03 jeffreys created
  33. // --------------------------------------------------------------------------
  34. #define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B')
  35. BOOL SaveDIBSectionToFile(HBITMAP hbm, HDC hdc, LPCTSTR pszFile)
  36. {
  37. BOOL bResult;
  38. DIBSECTION ds;
  39. HANDLE hFile;
  40. BITMAPFILEHEADER bf;
  41. DWORD cbWritten;
  42. bResult = FALSE;
  43. // Get the details about the bitmap. This also validates hbm.
  44. if (GetObject(hbm, sizeof(ds), &ds) == 0)
  45. return FALSE;
  46. // Fill in a couple of optional fields if necessary
  47. if (ds.dsBmih.biSizeImage == 0)
  48. ds.dsBmih.biSizeImage = ds.dsBmih.biHeight * ds.dsBm.bmWidthBytes;
  49. if (ds.dsBmih.biBitCount <= 8 && ds.dsBmih.biClrUsed == 0)
  50. ds.dsBmih.biClrUsed = 1 << ds.dsBmih.biBitCount;
  51. // Open the target file. This also validates pszFile.
  52. hFile = CreateFile(pszFile,
  53. GENERIC_WRITE,
  54. 0,
  55. NULL,
  56. CREATE_ALWAYS,
  57. FILE_ATTRIBUTE_NORMAL,
  58. NULL);
  59. if (INVALID_HANDLE_VALUE == hFile)
  60. return FALSE;
  61. // Prepare the BITMAPFILEHEADER for writing
  62. bf.bfType = DIB_HEADER_MARKER;
  63. bf.bfReserved1 = 0;
  64. bf.bfReserved2 = 0;
  65. // The bit offset is the cumulative size of all of the header stuff
  66. bf.bfOffBits = sizeof(bf) + sizeof(ds.dsBmih) + (ds.dsBmih.biClrUsed*sizeof(RGBQUAD));
  67. if (ds.dsBmih.biCompression == BI_BITFIELDS)
  68. bf.bfOffBits += sizeof(ds.dsBitfields);
  69. // Round up to the next 16-byte boundary. This isn't strictly necessary,
  70. // but it makes the file layout cleaner. (You can create a file mapping
  71. // and pass it to CreateDIBSection this way.)
  72. bf.bfOffBits = ((bf.bfOffBits + 15) & ~15);
  73. // The file size is the bit offset + the size of the bits
  74. bf.bfSize = bf.bfOffBits + ds.dsBmih.biSizeImage;
  75. // Write the BITMAPFILEHEADER first
  76. bResult = WriteFile(hFile, &bf, sizeof(bf), &cbWritten, NULL);
  77. if (bResult)
  78. {
  79. // Next is the BITMAPINFOHEADER
  80. bResult = WriteFile(hFile, &ds.dsBmih, sizeof(ds.dsBmih), &cbWritten, NULL);
  81. if (bResult)
  82. {
  83. // Then the 3 bitfields, if necessary
  84. if (ds.dsBmih.biCompression == BI_BITFIELDS)
  85. {
  86. bResult = WriteFile(hFile, &ds.dsBitfields, sizeof(ds.dsBitfields), &cbWritten, NULL);
  87. }
  88. if (bResult)
  89. {
  90. // Now the color table, if any
  91. if (ds.dsBmih.biClrUsed != 0)
  92. {
  93. RGBQUAD argb[256];
  94. HDC hdcDelete;
  95. HBITMAP hbmOld;
  96. // Assume failure here
  97. bResult = FALSE;
  98. hdcDelete = NULL;
  99. if (!hdc)
  100. {
  101. hdcDelete = CreateCompatibleDC(NULL);
  102. if (hdcDelete)
  103. {
  104. hbmOld = (HBITMAP)SelectObject(hdcDelete, hbm);
  105. hdc = hdcDelete;
  106. }
  107. }
  108. if (hdc &&
  109. GetDIBColorTable(hdc, 0, ARRAYSIZE(argb), argb) == ds.dsBmih.biClrUsed)
  110. {
  111. bResult = WriteFile(hFile, argb, ds.dsBmih.biClrUsed*sizeof(RGBQUAD), &cbWritten, NULL);
  112. }
  113. if (hdcDelete)
  114. {
  115. SelectObject(hdcDelete, hbmOld);
  116. DeleteDC(hdcDelete);
  117. }
  118. }
  119. // Finally, write the bits
  120. if (bResult)
  121. {
  122. SetFilePointer(hFile, bf.bfOffBits, NULL, FILE_BEGIN);
  123. bResult = WriteFile(hFile, ds.dsBm.bmBits, ds.dsBmih.biSizeImage, &cbWritten, NULL);
  124. SetEndOfFile(hFile);
  125. }
  126. }
  127. }
  128. }
  129. CloseHandle(hFile);
  130. if (!bResult)
  131. {
  132. // Something failed, clean up
  133. DeleteFile(pszFile);
  134. }
  135. return bResult;
  136. }
  137. // --------------------------------------------------------------------------
  138. // MakeDIBSection
  139. //
  140. // Arguments: pImage = Source image
  141. //
  142. // Returns: HBITMAP
  143. //
  144. // Purpose: Create a DIB section containing the given image
  145. // on a white background
  146. //
  147. // History: 2000-05-03 jeffreys created
  148. // --------------------------------------------------------------------------
  149. HBITMAP MakeDIBSection(IShellImageData *pImage, ULONG cx, ULONG cy)
  150. {
  151. HBITMAP hbm;
  152. HDC hdc;
  153. BITMAPINFO dib;
  154. hdc = CreateCompatibleDC(NULL);
  155. if (hdc == NULL)
  156. return NULL;
  157. dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  158. dib.bmiHeader.biWidth = cx;
  159. dib.bmiHeader.biHeight = cy;
  160. dib.bmiHeader.biPlanes = 1;
  161. dib.bmiHeader.biBitCount = 24;
  162. dib.bmiHeader.biCompression = BI_RGB;
  163. dib.bmiHeader.biSizeImage = 0;
  164. dib.bmiHeader.biXPelsPerMeter = 0;
  165. dib.bmiHeader.biYPelsPerMeter = 0;
  166. dib.bmiHeader.biClrUsed = 0;
  167. dib.bmiHeader.biClrImportant = 0;
  168. hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
  169. if (hbm)
  170. {
  171. HBITMAP hbmOld;
  172. RECT rc;
  173. hbmOld = (HBITMAP)SelectObject(hdc, hbm);
  174. // Initialize the entire image with white
  175. PatBlt(hdc, 0, 0, cx, cy, WHITENESS);
  176. rc.left = 0;
  177. rc.top = 0;
  178. rc.right = cx;
  179. rc.bottom = cy;
  180. // Draw the source image into the DIB section
  181. HRESULT hr = pImage->Draw(hdc, &rc, NULL);
  182. SelectObject(hdc, hbmOld);
  183. if (FAILED(hr))
  184. {
  185. DeleteObject(hbm);
  186. hbm = NULL;
  187. SetLastError(hr);
  188. }
  189. }
  190. DeleteDC(hdc);
  191. return hbm;
  192. }
  193. // --------------------------------------------------------------------------
  194. // ConvertAndResizeImage
  195. //
  196. // Arguments: pszFileSource = Source image file.
  197. // pszFileTarget = Target image file (resized).
  198. //
  199. // Returns: HRESULT
  200. //
  201. // Purpose: Uses GDI+ via COM interfaces to convert the given image file
  202. // to a bmp sized at 96x96.
  203. //
  204. // History: 2000-03-24 vtan created
  205. // 2000-05-03 jeffreys reworked using DIB sections
  206. // --------------------------------------------------------------------------
  207. HRESULT ConvertAndResizeImage (LPCTSTR pszFileSource, LPCTSTR pszFileTarget)
  208. {
  209. HRESULT hr;
  210. IShellImageDataFactory *pImagingFactory;
  211. hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellImageDataFactory, &pImagingFactory));
  212. if (SUCCEEDED(hr))
  213. {
  214. IShellImageData *pImage;
  215. hr = pImagingFactory->CreateImageFromFile(pszFileSource, &pImage);
  216. if (SUCCEEDED(hr))
  217. {
  218. hr = pImage->Decode(SHIMGDEC_DEFAULT, 0, 0);
  219. if (SUCCEEDED(hr))
  220. {
  221. SIZE sizeImg;
  222. ULONG cxDest, cyDest;
  223. HDC hdc;
  224. HBITMAP hbm;
  225. DWORD dwErr;
  226. // The default dimensions are based on the screen resolution
  227. hdc = GetDC(NULL);
  228. if (hdc != NULL)
  229. {
  230. // Make it 1/2 inch square by default
  231. cxDest = GetDeviceCaps(hdc, LOGPIXELSX) / 2;
  232. cyDest = GetDeviceCaps(hdc, LOGPIXELSY) / 2;
  233. ReleaseDC(NULL, hdc);
  234. }
  235. else
  236. {
  237. // Most common display modes run at 96dpi ("small fonts")
  238. cxDest = cyDest = 48;
  239. }
  240. // Get the current image dimensions so we can maintain aspect ratio
  241. if ( SUCCEEDED(pImage->GetSize(&sizeImg)) )
  242. {
  243. // Don't want to make small images bigger
  244. cxDest = min(cxDest, (ULONG)sizeImg.cx);
  245. cyDest = min(cyDest, (ULONG)sizeImg.cy);
  246. // If it's not square, scale the smaller dimension
  247. // to maintain the aspect ratio.
  248. if (sizeImg.cx > sizeImg.cy)
  249. {
  250. cyDest = MulDiv(cxDest, sizeImg.cy, sizeImg.cx);
  251. }
  252. else if (sizeImg.cx < sizeImg.cy)
  253. {
  254. cxDest = MulDiv(cyDest, sizeImg.cx, sizeImg.cy);
  255. }
  256. }
  257. // Resize the image
  258. // Note that this gives better results than scaling while drawing
  259. // into the DIB section (see MakeDIBSection).
  260. //
  261. // However, it doesn't always work. For example, animated images
  262. // result in E_NOTVALIDFORANIMATEDIMAGE. So ignore the return
  263. // value and the scaling will be done in MakeDIBSection if necessary.
  264. pImage->Scale(cxDest, cyDest, 0);
  265. hbm = MakeDIBSection(pImage, cxDest, cyDest);
  266. if (hbm)
  267. {
  268. // Save the DIB section to disk
  269. if (!SaveDIBSectionToFile(hbm, NULL, pszFileTarget))
  270. {
  271. dwErr = GetLastError();
  272. hr = HRESULT_FROM_WIN32(dwErr);
  273. }
  274. DeleteObject(hbm);
  275. }
  276. else
  277. {
  278. dwErr = GetLastError();
  279. hr = HRESULT_FROM_WIN32(dwErr);
  280. }
  281. }
  282. pImage->Release();
  283. }
  284. pImagingFactory->Release();
  285. }
  286. return(hr);
  287. }
  288. // --------------------------------------------------------------------------
  289. // SetExplicitAccessToObject
  290. //
  291. // Arguments: pszTarget = Target object
  292. // seType = Type of object
  293. // pszUser = User to grant access to
  294. // dwMask = Permissions granted
  295. // dwFlags = Inheritance flags
  296. //
  297. // Returns: BOOL
  298. //
  299. // Purpose: Grants Read/Write/Execute/Delete access to the
  300. // specified user on the specified file.
  301. //
  302. // Note that this stomps existing explicit entries in the DACL.
  303. // Multiple calls are not cumulative.
  304. //
  305. // History: 2000-05-19 jeffreys created
  306. // --------------------------------------------------------------------------
  307. DWORD SetExplicitAccessToObject(LPTSTR pszTarget, SE_OBJECT_TYPE seType, LPCTSTR pszUser, DWORD dwMask, DWORD dwFlags)
  308. {
  309. BOOL bResult;
  310. // 84 bytes
  311. BYTE rgAclBuffer[sizeof(ACL)
  312. + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))
  313. + (sizeof(SID) + (SID_MAX_SUB_AUTHORITIES-1)*sizeof(ULONG))];
  314. PACL pDacl = (PACL)rgAclBuffer;
  315. if (!InitializeAcl(pDacl, sizeof(rgAclBuffer), ACL_REVISION)) return FALSE;
  316. pDacl->AceCount = 1;
  317. PACCESS_ALLOWED_ACE pAce = (PACCESS_ALLOWED_ACE)(pDacl+1);
  318. pAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
  319. pAce->Header.AceFlags = (UCHAR)(dwFlags & 0xFF);
  320. pAce->Mask = dwMask;
  321. SID_NAME_USE snu;
  322. TCHAR szDomainName[MAX_PATH];
  323. DWORD cbDomainName = ARRAYSIZE(szDomainName);
  324. DWORD cbSid = sizeof(SID) + (SID_MAX_SUB_AUTHORITIES-1)*sizeof(ULONG);
  325. bResult = LookupAccountName(
  326. NULL,
  327. pszUser,
  328. (PSID)&(pAce->SidStart),
  329. &cbSid,
  330. szDomainName,
  331. &cbDomainName,
  332. &snu);
  333. if (bResult)
  334. {
  335. DWORD dwErr;
  336. // LookupAccountName doesn't return the SID length on success
  337. cbSid = GetLengthSid((PSID)&(pAce->SidStart));
  338. // Update the ACE size
  339. pAce->Header.AceSize = (USHORT)(sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + cbSid);
  340. dwErr = SetNamedSecurityInfo(
  341. pszTarget,
  342. seType,
  343. DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION,
  344. NULL,
  345. NULL,
  346. pDacl,
  347. NULL);
  348. if (ERROR_SUCCESS != dwErr)
  349. {
  350. SetLastError(dwErr);
  351. bResult = FALSE;
  352. }
  353. }
  354. return bResult;
  355. }
  356. // --------------------------------------------------------------------------
  357. // SetDefaultUserPicture
  358. //
  359. // Arguments: pszUsername = Desired user (NULL for current user).
  360. //
  361. // Returns: HRESULT
  362. //
  363. // Purpose: Picks one of the default user pictures at random and
  364. // assigns it to the specified user.
  365. //
  366. // History: 2001-03-27 reinerf created
  367. // --------------------------------------------------------------------------
  368. HRESULT SetDefaultUserPicture(LPCTSTR pszUsername)
  369. {
  370. HRESULT hr;
  371. TCHAR szPath[MAX_PATH];
  372. hr = SHGetUserPicturePath(NULL, SHGUPP_FLAG_DEFAULTPICSPATH, szPath);
  373. if (SUCCEEDED(hr))
  374. {
  375. BOOL bFound = FALSE; // assume we won't find a picture
  376. // Assume everything in the dir is a vaild image file
  377. if (PathAppend(szPath, TEXT("*.*")))
  378. {
  379. static DWORD dwSeed = 0;
  380. WIN32_FIND_DATA fd;
  381. HANDLE hFind = FindFirstFile(szPath, &fd);
  382. if (dwSeed == 0)
  383. {
  384. dwSeed = GetTickCount();
  385. }
  386. if (hFind != INVALID_HANDLE_VALUE)
  387. {
  388. DWORD dwCount = 0;
  389. // use a probability collector algorithim (with a limit of 100 files)
  390. do
  391. {
  392. if (!PathIsDotOrDotDot(fd.cFileName))
  393. {
  394. dwCount++;
  395. // although RtlRandom returns a ULONG it is distributed from 0...MAXLONG
  396. if (RtlRandomEx(&dwSeed) <= (MAXLONG / dwCount))
  397. {
  398. bFound = TRUE;
  399. PathRemoveFileSpec(szPath);
  400. PathAppend(szPath, fd.cFileName);
  401. }
  402. }
  403. } while (FindNextFile(hFind, &fd) && (dwCount < 100));
  404. FindClose(hFind);
  405. }
  406. }
  407. if (bFound)
  408. {
  409. hr = SHSetUserPicturePath(pszUsername, 0, szPath);
  410. }
  411. else
  412. {
  413. hr = E_FAIL;
  414. }
  415. }
  416. return hr;
  417. }
  418. // --------------------------------------------------------------------------
  419. // ::SHGetUserPicturePath
  420. //
  421. // Arguments: pszUsername = Desired user (NULL for current user).
  422. // dwFlags = Flags.
  423. // pszPath = Path to user picture.
  424. //
  425. // Returns: HRESULT
  426. //
  427. // Purpose: Returns the user's picture path (absolute). Does parameter
  428. // validation as well. This function only supports .bmp files.
  429. //
  430. // Use SHGUPP_FLAG_BASEPATH to return the base to the pictures
  431. // directory.
  432. //
  433. // Use SHGUPP_FLAG_DEFAULTPICSPATH to return the path to the
  434. // default pictures directory.
  435. //
  436. // Use SHGUPP_FLAG_CREATE to create the user picture directory.
  437. //
  438. // If neither SHGUPP_FLAG_BASEPATH or SHGUPP_FLAG_DEFAULTPICSPATH
  439. // is specified, and the user has no picture, SHGUPP_FLAG_CREATE
  440. // will select one of the default pictures at random.
  441. //
  442. // History: 2000-02-22 vtan created
  443. // 2000-03-24 vtan moved from folder.cpp
  444. // --------------------------------------------------------------------------
  445. #define UASTR_PATH_PICTURES TEXT("Microsoft\\User Account Pictures")
  446. #define UASTR_PATH_DEFPICS UASTR_PATH_PICTURES TEXT("\\Default Pictures")
  447. STDAPI SHGetUserPicturePath (LPCTSTR pszUsername, DWORD dwFlags, LPTSTR pszPath)
  448. {
  449. HRESULT hr;
  450. TCHAR szPath[MAX_PATH];
  451. // Validate dwFlags.
  452. if ((dwFlags & SHGUPP_FLAG_INVALID_MASK) != 0)
  453. {
  454. return(E_INVALIDARG);
  455. }
  456. // Validate pszPath. This must not be NULL.
  457. if ((pszPath == NULL) || IsBadWritePtr(pszPath, MAX_PATH * sizeof(TCHAR)))
  458. {
  459. return(E_INVALIDARG);
  460. }
  461. // Start by getting the base picture path
  462. hr = SHGetFolderPathAndSubDir(NULL,
  463. (dwFlags & SHGUPP_FLAG_CREATE) ? (CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE) : CSIDL_COMMON_APPDATA,
  464. NULL,
  465. SHGFP_TYPE_CURRENT,
  466. (dwFlags & SHGUPP_FLAG_DEFAULTPICSPATH) ? UASTR_PATH_DEFPICS : UASTR_PATH_PICTURES,
  467. szPath);
  468. // If the base path is requested this function is done.
  469. if (S_OK == hr && 0 == (dwFlags & (SHGUPP_FLAG_BASEPATH | SHGUPP_FLAG_DEFAULTPICSPATH)))
  470. {
  471. TCHAR szUsername[UNLEN + sizeof('\0')];
  472. if (pszUsername == NULL)
  473. {
  474. DWORD dwUsernameSize;
  475. dwUsernameSize = ARRAYSIZE(szUsername);
  476. if (GetUserName(szUsername, &dwUsernameSize) != FALSE)
  477. {
  478. pszUsername = szUsername;
  479. }
  480. else
  481. {
  482. hr = HRESULT_FROM_WIN32(GetLastError());
  483. }
  484. }
  485. if (pszUsername != NULL)
  486. {
  487. // Append the user name to the picture path. Then look for
  488. // <username>.bmp. This function only supports bmp.
  489. PathAppend(szPath, pszUsername);
  490. lstrcatn(szPath, TEXT(".bmp"), ARRAYSIZE(szPath));
  491. if (PathFileExistsAndAttributes(szPath, NULL) != FALSE)
  492. {
  493. hr = S_OK;
  494. }
  495. else if (dwFlags & SHGUPP_FLAG_CREATE)
  496. {
  497. // No picture has been set for this user. Select one
  498. // of the default pictures at random.
  499. hr = SetDefaultUserPicture(pszUsername);
  500. ASSERT(FAILED(hr) || PathFileExistsAndAttributes(szPath, NULL));
  501. }
  502. else
  503. {
  504. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  505. }
  506. }
  507. }
  508. if (S_OK == hr)
  509. {
  510. lstrcpyn(pszPath, szPath, MAX_PATH);
  511. }
  512. return(hr);
  513. }
  514. // --------------------------------------------------------------------------
  515. // ::SHSetUserPicturePath
  516. //
  517. // Arguments: pszUsername = Desired user (NULL for current user).
  518. // dwFlags = Flags.
  519. // pszPath = Path to NEW user picture.
  520. //
  521. // Returns: HRESULT
  522. //
  523. // Purpose: Sets the specified user's picture as a copy of the given
  524. // image file. The image file may be any supported standard image
  525. // file (.gif / .jpg / .bmp). The file is converted to a 96x96
  526. // .bmp file in the user picture directory.
  527. //
  528. // History: 2000-02-22 vtan created
  529. // 2000-03-24 vtan moved from folder.cpp
  530. // 2000-04-27 jeffreys restore old image on conversion failure
  531. // --------------------------------------------------------------------------
  532. STDAPI SHSetUserPicturePath (LPCTSTR pszUsername, DWORD dwFlags, LPCTSTR pszPath)
  533. {
  534. HRESULT hr;
  535. TCHAR szPath[MAX_PATH];
  536. TCHAR szUsername[UNLEN + sizeof('\0')];
  537. DWORD dwUsernameSize;
  538. hr = E_FAIL;
  539. // Validate dwFlags. Currently no valid flags so this must be 0x00000000.
  540. if ((dwFlags & SHSUPP_FLAG_INVALID_MASK) != 0)
  541. {
  542. return(E_INVALIDARG);
  543. }
  544. dwUsernameSize = ARRAYSIZE(szUsername);
  545. if (GetUserName(szUsername, &dwUsernameSize) == FALSE)
  546. {
  547. return(HRESULT_FROM_WIN32(GetLastError()));
  548. }
  549. if (pszUsername != NULL)
  550. {
  551. // Privilege check. Must be an administrator to use this function when
  552. // pszUsername is not NULL (i.e. for somebody else).
  553. if ((lstrcmpi(pszUsername, szUsername) != 0) &&
  554. (SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS) == FALSE))
  555. {
  556. static const SID c_SystemSid = {SID_REVISION,1,SECURITY_NT_AUTHORITY,{SECURITY_LOCAL_SYSTEM_RID}};
  557. BOOL bSystem = FALSE;
  558. // One more check. Allow local system through since we may
  559. // get called from the logon screen.
  560. if (!CheckTokenMembership(NULL, (PSID)&c_SystemSid, &bSystem) || !bSystem)
  561. {
  562. return(E_ACCESSDENIED);
  563. }
  564. }
  565. }
  566. else
  567. {
  568. pszUsername = szUsername;
  569. }
  570. // Start by getting the base picture path
  571. hr = SHGetFolderPathAndSubDir(NULL,
  572. CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE,
  573. NULL,
  574. SHGFP_TYPE_CURRENT,
  575. UASTR_PATH_PICTURES,
  576. szPath);
  577. if (S_OK == hr)
  578. {
  579. // Before attempt to delete what's there try to access the
  580. // new file. If this fails deleting what's currently installed
  581. // could leave the user without a picture. Fail the API before
  582. // anything is lost.
  583. if ((pszPath == NULL) || (PathFileExistsAndAttributes(pszPath, NULL) != FALSE))
  584. {
  585. TCHAR szTemp[MAX_PATH];
  586. PathAppend(szPath, pszUsername);
  587. lstrcpyn(szTemp, szPath, ARRAYSIZE(szTemp));
  588. lstrcatn(szPath, TEXT(".bmp"), ARRAYSIZE(szPath));
  589. lstrcatn(szTemp, TEXT(".tmp"), ARRAYSIZE(szTemp));
  590. if ((pszPath == NULL) || lstrcmpi(pszPath, szPath) != 0)
  591. {
  592. // If present, rename <username>.Bmp to <username>.Tmp.
  593. // First reset the attributes so file ops work. Don't use
  594. // trace macros because failure is expected.
  595. (BOOL)SetFileAttributes(szPath, 0);
  596. (BOOL)SetFileAttributes(szTemp, 0);
  597. (BOOL)MoveFileEx(szPath, szTemp, MOVEFILE_REPLACE_EXISTING);
  598. // Convert the given image to a bmp and resize it
  599. // using the helper function which does all the goo.
  600. if (pszPath != NULL)
  601. {
  602. hr = ConvertAndResizeImage(pszPath, szPath);
  603. if (SUCCEEDED(hr))
  604. {
  605. // Since this may be an admin setting someone else's
  606. // picture, we need to grant that person access to
  607. // modify/delete the file so they can change it
  608. // themselves later.
  609. (BOOL)SetExplicitAccessToObject(szPath,
  610. SE_FILE_OBJECT,
  611. pszUsername,
  612. GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE | DELETE,
  613. 0);
  614. }
  615. }
  616. else
  617. {
  618. hr = S_OK;
  619. }
  620. if (SUCCEEDED(hr))
  621. {
  622. // Delete the old picture
  623. (BOOL)DeleteFile(szTemp);
  624. }
  625. else
  626. {
  627. // Restore the old picture
  628. (BOOL)MoveFileEx(szTemp, szPath, MOVEFILE_REPLACE_EXISTING);
  629. }
  630. // Notify everyone that a user picture has changed
  631. SHChangeDWORDAsIDList dwidl;
  632. dwidl.cb = SIZEOF(dwidl) - SIZEOF(dwidl.cbZero);
  633. dwidl.dwItem1 = SHCNEE_USERINFOCHANGED;
  634. dwidl.dwItem2 = 0;
  635. dwidl.cbZero = 0;
  636. SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_FLUSH, (LPCITEMIDLIST)&dwidl, NULL);
  637. }
  638. else
  639. {
  640. // Source and destination are the same, nothing to do.
  641. hr = S_FALSE;
  642. }
  643. }
  644. else
  645. {
  646. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  647. }
  648. }
  649. return(hr);
  650. }
  651. #pragma warning(pop)