Leaked source code of windows server 2003
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.

612 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996-2002 Microsoft Corp. & Ricoh Co., Ltd. All rights reserved.
  3. FILE: COMMON.C
  4. Abstract: Implementation of common functions for rendering & UI
  5. plugin module.
  6. Functions: OEMGetInfo
  7. OEMDevMode
  8. RWFileData
  9. safe_sprintfA
  10. safe_sprintfW
  11. Environment: Windows NT Unidrv5 driver
  12. Revision History:
  13. 04/07/97 -zhanw-
  14. Created it.
  15. 02/11/99 -Masatoshi Kubokura-
  16. Last modified for Windows2000.
  17. 08/30/99 -Masatoshi Kubokura-
  18. Began to modify for NT4SP6(Unidrv5.4).
  19. 09/27/99 -Masatoshi Kubokura-
  20. Last modified for NT4SP6.
  21. 03/17/2000 -Masatoshi Kubokura-
  22. Eliminate "\\" from temp file name.
  23. 05/22/2000 -Masatoshi Kubokura-
  24. V.1.03 for NT4
  25. 09/22/2000 -Masatoshi Kubokura-
  26. Last modified for XP inbox.
  27. 03/01/2002 -Masatoshi Kubokura-
  28. Include strsafe.h.
  29. Add FileNameBufSize as arg3 at RWFileData().
  30. Inplement safe_sprintfA/W().
  31. 04/01/2002 -Masatoshi Kubokura-
  32. Use SecureZeroMemory() instead of memset(,0,)
  33. --*/
  34. #include "pdev.h"
  35. #include "resource.h"
  36. #ifndef WINNT_40
  37. #include "strsafe.h" // @Mar/01/2002
  38. #endif // !WINNT_40
  39. // shared data file between rendering and UI plugin
  40. #ifndef WINNT_40
  41. #define SHAREDFILENAME L"RIMD5.BIN" // eliminate "\\" @Mar/15/2000
  42. #else // WINNT_40
  43. #define SHAREDFILENAME L"\\2\\RI%.4ls%02x.BIN" // %02x<-%02d @Sep/21/99
  44. DWORD gdwDrvMemPoolTag = 'meoD'; // minidrv.h requires this global var
  45. #endif // WINNT_40
  46. #if DBG && !defined(KM_DRIVER)
  47. INT giDebugLevel = DBG_ERROR;
  48. #endif
  49. ////////////////////////////////////////////////////////
  50. // INTERNAL PROTOTYPES
  51. ////////////////////////////////////////////////////////
  52. static BOOL BInitOEMExtraData(POEMUD_EXTRADATA pOEMExtra);
  53. static BOOL BMergeOEMExtraData(POEMUD_EXTRADATA pdmIn, POEMUD_EXTRADATA pdmOut);
  54. static BOOL BIsValidOEMDevModeParam(DWORD dwMode, POEMDMPARAM pOEMDevModeParam);
  55. #if DBG
  56. static void VDumpOEMDevModeParam(POEMDMPARAM pOEMDevModeParam);
  57. #endif // DBG
  58. //////////////////////////////////////////////////////////////////////////
  59. // Function: OEMGetInfo
  60. //////////////////////////////////////////////////////////////////////////
  61. BOOL APIENTRY OEMGetInfo(DWORD dwInfo, PVOID pBuffer, DWORD cbSize, PDWORD pcbNeeded)
  62. {
  63. #if DBG
  64. LPCSTR OEM_INFO[] = { "Bad Index",
  65. "OEMGI_GETSIGNATURE",
  66. "OEMGI_GETINTERFACEVERSION",
  67. "OEMGI_GETVERSION",
  68. };
  69. VERBOSE((DLLTEXT("OEMGetInfo(%s) entry.\n"), OEM_INFO[dwInfo]));
  70. #endif // DBG
  71. // Validate parameters.
  72. if( ( (OEMGI_GETSIGNATURE != dwInfo) &&
  73. (OEMGI_GETINTERFACEVERSION != dwInfo) &&
  74. (OEMGI_GETVERSION != dwInfo) ) ||
  75. (NULL == pcbNeeded)
  76. )
  77. {
  78. ERR(("OEMGetInfo() ERROR_INVALID_PARAMETER.\n"));
  79. // Did not write any bytes.
  80. if(NULL != pcbNeeded)
  81. *pcbNeeded = 0;
  82. return FALSE;
  83. }
  84. // Need/wrote 4 bytes.
  85. *pcbNeeded = 4;
  86. // Validate buffer size. Minimum size is four bytes.
  87. if( (NULL == pBuffer) || (4 > cbSize) )
  88. {
  89. ERR(("OEMGetInfo() ERROR_INSUFFICIENT_BUFFER.\n"));
  90. return FALSE;
  91. }
  92. // Write information to buffer.
  93. switch(dwInfo)
  94. {
  95. case OEMGI_GETSIGNATURE:
  96. *(LPDWORD)pBuffer = OEM_SIGNATURE;
  97. break;
  98. case OEMGI_GETINTERFACEVERSION:
  99. *(LPDWORD)pBuffer = PRINTER_OEMINTF_VERSION;
  100. break;
  101. case OEMGI_GETVERSION:
  102. *(LPDWORD)pBuffer = OEM_VERSION;
  103. break;
  104. }
  105. return TRUE;
  106. } //*** OEMGetInfo
  107. //////////////////////////////////////////////////////////////////////////
  108. // Function: OEMDevMode
  109. //////////////////////////////////////////////////////////////////////////
  110. BOOL APIENTRY OEMDevMode(DWORD dwMode, POEMDMPARAM pOEMDevModeParam)
  111. {
  112. #if DBG
  113. LPCSTR OEMDevMode_fMode[] = { "NULL",
  114. "OEMDM_SIZE",
  115. "OEMDM_DEFAULT",
  116. "OEMDM_CONVERT",
  117. "OEMDM_MERGE",
  118. };
  119. VERBOSE((DLLTEXT("OEMDevMode(%s) entry.\n"), OEMDevMode_fMode[dwMode]));
  120. #endif // DBG
  121. // Validate parameters.
  122. if(!BIsValidOEMDevModeParam(dwMode, pOEMDevModeParam))
  123. {
  124. #if DBG
  125. ERR(("OEMDevMode() ERROR_INVALID_PARAMETER.\n"));
  126. VDumpOEMDevModeParam(pOEMDevModeParam);
  127. #endif // DBG
  128. return FALSE;
  129. }
  130. // Verify OEM extra data size.
  131. if( (dwMode != OEMDM_SIZE) &&
  132. sizeof(OEMUD_EXTRADATA) > pOEMDevModeParam->cbBufSize )
  133. {
  134. ERR(("OEMDevMode() ERROR_INSUFFICIENT_BUFFER.\n"));
  135. return FALSE;
  136. }
  137. // Handle dwMode.
  138. switch(dwMode)
  139. {
  140. case OEMDM_SIZE:
  141. pOEMDevModeParam->cbBufSize = sizeof(OEMUD_EXTRADATA);
  142. break;
  143. case OEMDM_DEFAULT:
  144. #ifdef WINNT_40 // @Sep/20/99
  145. // Because NT4 spooler doesn't support collate, we clear dmCollate.
  146. // Later at OEMUICallBack, if printer collate is available, we set
  147. // dmCollate.
  148. pOEMDevModeParam->pPublicDMIn->dmCollate = DMCOLLATE_FALSE;
  149. pOEMDevModeParam->pPublicDMIn->dmFields &= ~DM_COLLATE;
  150. #endif // WINNT_40
  151. return BInitOEMExtraData((POEMUD_EXTRADATA)pOEMDevModeParam->pOEMDMOut);
  152. case OEMDM_CONVERT:
  153. // @Jul/08/98 ->
  154. // // nothing to convert for this private devmode. So just initialize it.
  155. // return BInitOEMExtraData((POEMUD_EXTRADATA)pOEMDevModeParam->pOEMDMOut);
  156. // @Jul/08/98 <-
  157. case OEMDM_MERGE:
  158. if(!BMergeOEMExtraData((POEMUD_EXTRADATA)pOEMDevModeParam->pOEMDMIn,
  159. (POEMUD_EXTRADATA)pOEMDevModeParam->pOEMDMOut) )
  160. {
  161. ERR(("OEMUD OEMDevMode(): not valid OEM Extra Data.\n"));
  162. return FALSE;
  163. }
  164. break;
  165. }
  166. return TRUE;
  167. } //*** OEMDevMode
  168. //////////////////////////////////////////////////////////////////////////
  169. // Function: BInitOEMExtraData
  170. //
  171. // Description: Initializes OEM Extra data.
  172. //
  173. // Parameters:
  174. // pOEMExtra Pointer to a OEM Extra data.
  175. // dwSize Size of OEM extra data.
  176. //
  177. // Returns: TRUE if successful; FALSE otherwise.
  178. //
  179. // Comments:
  180. //
  181. // History:
  182. // 02/11/97 APresley Created.
  183. // 08/11/97 Masatoshi Kubokura Modified for RPDL
  184. //
  185. //////////////////////////////////////////////////////////////////////////
  186. BOOL BInitOEMExtraData(POEMUD_EXTRADATA pOEMExtra)
  187. {
  188. INT num;
  189. // Initialize OEM Extra data.
  190. pOEMExtra->dmExtraHdr.dwSize = sizeof(OEMUD_EXTRADATA);
  191. pOEMExtra->dmExtraHdr.dwSignature = OEM_SIGNATURE;
  192. pOEMExtra->dmExtraHdr.dwVersion = OEM_VERSION;
  193. pOEMExtra->fUiOption = 0;
  194. pOEMExtra->UiScale = VAR_SCALING_DEFAULT;
  195. pOEMExtra->UiBarHeight = BAR_H_DEFAULT;
  196. pOEMExtra->UiBindMargin = DEFAULT_0;
  197. pOEMExtra->nUiTomboAdjX = pOEMExtra->nUiTomboAdjY = DEFAULT_0; // add @Sep/14/98
  198. // Use SecureZeroMemory @Mar/29/2002 ->
  199. #if defined(WINNT_40) || defined(RICOH_RELEASE)
  200. memset(pOEMExtra->SharedFileName, 0, sizeof(pOEMExtra->SharedFileName)); // @Aug/31/99
  201. #else
  202. SecureZeroMemory(pOEMExtra->SharedFileName, sizeof(pOEMExtra->SharedFileName));
  203. #endif
  204. // Mar/29/2002 <-
  205. #ifdef JOBLOGSUPPORT_DM // @Oct/05/2000
  206. pOEMExtra->JobType = IDC_RADIO_JOB_NORMAL;
  207. pOEMExtra->LogDisabled = IDC_RADIO_LOG_DISABLED;
  208. #endif // JOBLOGSUPPORT_DM
  209. return TRUE;
  210. } //*** BInitOEMExtraData
  211. //////////////////////////////////////////////////////////////////////////
  212. // Function: BMergeOEMExtraData
  213. //
  214. // Description: Validates and merges OEM Extra data.
  215. //
  216. // Parameters:
  217. // pdmIn pointer to an input OEM private devmode containing the settings
  218. // to be validated and merged. Its size is current.
  219. // pdmOut pointer to the output OEM private devmode containing the
  220. // default settings.
  221. //
  222. // Returns: TRUE if valid; FALSE otherwise.
  223. //
  224. // Comments:
  225. //
  226. // History:
  227. // 02/11/97 APresley Created.
  228. // 04/08/97 ZhanW Modified the interface
  229. // 08/11/97 Masatoshi Kubokura Modified for RPDL
  230. //
  231. //////////////////////////////////////////////////////////////////////////
  232. BOOL BMergeOEMExtraData(POEMUD_EXTRADATA pdmIn, POEMUD_EXTRADATA pdmOut)
  233. {
  234. if(pdmIn) {
  235. LPBYTE pDst = (LPBYTE)&(pdmOut->fUiOption);
  236. LPBYTE pSrc = (LPBYTE)&(pdmIn->fUiOption);
  237. DWORD dwCount = sizeof(OEMUD_EXTRADATA) - sizeof(OEM_DMEXTRAHEADER);
  238. //
  239. // copy over the private fields, if they are valid
  240. //
  241. while (dwCount-- > 0)
  242. *pDst++ = *pSrc++;
  243. }
  244. return TRUE;
  245. } //*** BMergeOEMExtraData
  246. //////////////////////////////////////////////////////////////////////////
  247. // Function: BIsValidOEMDevModeParam
  248. //
  249. // Description: Validates OEM_DEVMODEPARAM structure.
  250. //
  251. // Parameters:
  252. // dwMode calling mode
  253. // pOEMDevModeParam Pointer to a OEMDEVMODEPARAM structure.
  254. //
  255. // Returns: TRUE if valid; FALSE otherwise.
  256. //
  257. // Comments:
  258. //
  259. // History:
  260. // 02/11/97 APresley Created.
  261. //
  262. //////////////////////////////////////////////////////////////////////////
  263. static BOOL BIsValidOEMDevModeParam(DWORD dwMode, POEMDMPARAM pOEMDevModeParam)
  264. {
  265. BOOL bValid = TRUE;
  266. if(NULL == pOEMDevModeParam)
  267. {
  268. ERR(("OEMUD IsValidOEMDevModeParam(): pOEMDevModeParam is NULL.\n"));
  269. return FALSE;
  270. }
  271. if(sizeof(OEMDMPARAM) > pOEMDevModeParam->cbSize)
  272. {
  273. ERR(("OEMUD IsValidOEMDevModeParam(): cbSize is smaller than sizeof(OEM_DEVMODEPARAM).\n"));
  274. bValid = FALSE;
  275. }
  276. if(NULL == pOEMDevModeParam->hPrinter)
  277. {
  278. ERR(("OEMUD IsValidOEMDevModeParam(): hPrinter is NULL.\n"));
  279. bValid = FALSE;
  280. }
  281. if(NULL == pOEMDevModeParam->hModule)
  282. {
  283. ERR(("OEMUD IsValidOEMDevModeParam(): hModule is NULL.\n"));
  284. bValid = FALSE;
  285. }
  286. if( (0 != pOEMDevModeParam->cbBufSize) &&
  287. (NULL == pOEMDevModeParam->pOEMDMOut)
  288. )
  289. {
  290. ERR(("OEMUD IsValidOEMDevModeParam(): pOEMDMOut is NULL when it should not be.\n"));
  291. bValid = FALSE;
  292. }
  293. if( (OEMDM_MERGE == dwMode) && (NULL == pOEMDevModeParam->pOEMDMIn) )
  294. {
  295. ERR(("OEMUD IsValidOEMDevModeParam(): pOEMDMIn is NULL when it should not be.\n"));
  296. bValid = FALSE;
  297. }
  298. return bValid;
  299. } //*** BIsValidOEMDevModeParam
  300. #if DBG
  301. //////////////////////////////////////////////////////////////////////////
  302. // Function: VDumpOEMDevModeParam
  303. //
  304. // Description: Debug dump of OEM_DEVMODEPARAM structure.
  305. //
  306. // Parameters:
  307. // pOEMDevModeParam Pointer to an OEM DevMode param structure.
  308. //
  309. // Returns: N/A.
  310. //
  311. // Comments:
  312. //
  313. // History:
  314. // 02/18/97 APresley Created.
  315. //
  316. //////////////////////////////////////////////////////////////////////////
  317. static void VDumpOEMDevModeParam(POEMDMPARAM pOEMDevModeParam)
  318. {
  319. // Can't dump if pOEMDevModeParam NULL.
  320. if(NULL != pOEMDevModeParam)
  321. {
  322. VERBOSE(("\n\tOEM_DEVMODEPARAM dump:\n\n"));
  323. VERBOSE(("\tcbSize = %d.\n", pOEMDevModeParam->cbSize));
  324. VERBOSE(("\thPrinter = %#lx.\n", pOEMDevModeParam->hPrinter));
  325. VERBOSE(("\thModule = %#lx.\n", pOEMDevModeParam->hModule));
  326. VERBOSE(("\tpPublicDMIn = %#lx.\n", pOEMDevModeParam->pPublicDMIn));
  327. VERBOSE(("\tpPublicDMOut = %#lx.\n", pOEMDevModeParam->pPublicDMOut));
  328. VERBOSE(("\tpOEMDMIn = %#lx.\n", pOEMDevModeParam->pOEMDMIn));
  329. VERBOSE(("\tpOEMDMOut = %#lx.\n", pOEMDevModeParam->pOEMDMOut));
  330. VERBOSE(("\tcbBufSize = %d.\n", pOEMDevModeParam->cbBufSize));
  331. }
  332. } //*** VDumpOEMDevModeParam
  333. #endif // DBG
  334. //////////////////////////////////////////////////////////////////////////
  335. // Function: RWFileData
  336. //
  337. // Description: Read/Write common file between UI plugin and rendering
  338. // plugin
  339. //
  340. // Parameters:
  341. // pFileData pointer to file data structure
  342. // pwszFileName pointer to file name of private devmode
  343. // FileNameBufSize file name buffer size (add 02/26/2002)
  344. // type GENERIC_READ/GENERIC_WRITE
  345. //
  346. // Returns: TRUE if valid; FALSE otherwise.
  347. //
  348. // Comments: Rendering plugin records printing-done flag to the file.
  349. // Both rendering plugin and UI plugin can know that status.
  350. //
  351. // History:
  352. // 09/30/1998 Masatoshi Kubokura Created.
  353. // 08/16/1999 takashim modified for Unidrv5.4 on NT4.
  354. // 09/01/1999 Kubokura modified for Unidrv5.4 on NT4.
  355. // 02/26/2002 Kubokura added FileNameBufSize param.
  356. //
  357. //////////////////////////////////////////////////////////////////////////
  358. BOOL RWFileData(PFILEDATA pFileData, LPWSTR pwszFileName, LONG FileNameBufSize, LONG type)
  359. {
  360. HANDLE hFile;
  361. DWORD dwSize;
  362. BOOL bRet = FALSE;
  363. #ifndef KM_DRIVER
  364. WCHAR szFileName[MY_MAX_PATH]; // MY_MAX_PATH=80
  365. #endif // KM_DRIVER
  366. VERBOSE(("** Filename[0]=%d (%ls) **\n", pwszFileName[0], pwszFileName));
  367. #ifndef KM_DRIVER
  368. #ifndef WINNT_40
  369. //
  370. // CAUTION:
  371. // TempPath is different whether EMF spool is enable(system) or not(user).
  372. // We need to store the file name to private devmode.
  373. //
  374. // Set shared file name to private devmode at first time
  375. if (0 == pwszFileName[0])
  376. {
  377. if (0 == (dwSize = GetTempPath(MY_MAX_PATH, szFileName)))
  378. {
  379. ERR(("Could not get temp directory."));
  380. return bRet;
  381. }
  382. // @Feb/26/2002 ->
  383. // wcscpy(&szFileName[dwSize], SHAREDFILENAME);
  384. StringCbCopyW(&szFileName[dwSize], sizeof(szFileName) - dwSize, SHAREDFILENAME);
  385. // @Feb/26/2002 <-
  386. VERBOSE(("** Set Filename: %ls **\n", szFileName));
  387. // copy file name to private devmode
  388. // @Feb/26/2002 ->
  389. // wcscpy(pwszFileName, szFileName);
  390. StringCbCopyW(pwszFileName, FileNameBufSize, szFileName);
  391. // @Feb/26/2002 <-
  392. }
  393. #else // WINNT_40
  394. //
  395. // CAUTION:
  396. // The file path differs on each PC in printer sharing.
  397. // We always update the file name (with path) of private devmode.
  398. // (@Sep/03/99)
  399. //
  400. // Kernel-mode driver (NT4 RPDLRES.DLL) can access files under
  401. // %systemroot%\system32. Driver directory will be OK.
  402. if (GetPrinterDriverDirectory(NULL, NULL, 1, (PBYTE)szFileName,
  403. sizeof(szFileName), &dwSize))
  404. {
  405. WCHAR szValue[MY_MAX_PATH] = L"XXXX";
  406. DWORD dwSize2;
  407. DWORD dwNum = 0; // @Sep/21/99
  408. PWCHAR pwszTmp; // @Sep/21/99
  409. // Make unique filename "RIXXXXNN.BIN". "XXXX" is filled with top 4char of username.
  410. dwSize2 = GetEnvironmentVariable(L"USERNAME", szValue, MY_MAX_PATH);
  411. // @Sep/21/99 ->
  412. // wsprintf(&szFileName[dwSize/sizeof(WCHAR)-1], SHAREDFILENAME, szValue, dwSize2);
  413. pwszTmp = szValue;
  414. while (dwSize2-- > 0)
  415. dwNum += (DWORD)*pwszTmp++;
  416. wsprintf(&szFileName[dwSize/sizeof(WCHAR)-1], SHAREDFILENAME, szValue, (BYTE)dwNum);
  417. // @Sep/21/99 <-
  418. VERBOSE(("** Set Filename: %ls **\n", szFileName));
  419. // copy file name to private devmode
  420. wcscpy(pwszFileName, szFileName);
  421. }
  422. else
  423. {
  424. ERR(("Could not get printer driver directory.(dwSize=%d)", dwSize));
  425. return bRet;
  426. }
  427. #endif // WINNT_40
  428. hFile = CreateFile((LPTSTR) pwszFileName, // filename
  429. type, // open for read/write
  430. FILE_SHARE_READ, // share to read
  431. NULL, // no security
  432. OPEN_ALWAYS, // open existing file,or open new if not exist
  433. FILE_ATTRIBUTE_NORMAL, // normal file
  434. NULL); // no attr. template
  435. if (INVALID_HANDLE_VALUE == hFile)
  436. {
  437. ERR(("Could not create shared file."));
  438. return bRet;
  439. }
  440. if (GENERIC_WRITE == type)
  441. bRet = WriteFile(hFile, (PBYTE)pFileData, sizeof(FILEDATA), &dwSize, NULL);
  442. else if (GENERIC_READ == type)
  443. bRet = ReadFile(hFile, (PBYTE)pFileData, sizeof(FILEDATA), &dwSize, NULL);
  444. VERBOSE(("** RWFileData: bRet=%d, dwSize=%d**\n", bRet, dwSize));
  445. // Close files.
  446. CloseHandle(hFile);
  447. #else // KM_DRIVER
  448. if (0 != pwszFileName[0])
  449. {
  450. PBYTE pTemp;
  451. if (GENERIC_WRITE == type)
  452. {
  453. hFile = DrvMapFileForWrite(pwszFileName, sizeof (FILEDATA),
  454. &pTemp, &dwSize);
  455. if (NULL != hFile)
  456. {
  457. memcpy(pTemp, pFileData, sizeof (FILEDATA));
  458. DrvUnMapFile(hFile);
  459. bRet = TRUE;
  460. }
  461. }
  462. else
  463. {
  464. hFile = DrvMapFileForRead(pwszFileName, &pTemp, &dwSize);
  465. if (NULL != hFile)
  466. {
  467. memcpy(pFileData, pTemp, sizeof (FILEDATA));
  468. DrvUnMapFile(hFile);
  469. bRet = TRUE;
  470. }
  471. }
  472. }
  473. #endif // KM_DRIVER
  474. return bRet;
  475. } //*** RWFileData
  476. //////////////////////////////////////////////////////////////////////////
  477. // Function: safe_sprintfA / safe_sprintfW
  478. //
  479. // Description: safer sprintf replacement.
  480. //
  481. // History:
  482. // 03/01/2002 Masatoshi Kubokura Created.
  483. //
  484. //////////////////////////////////////////////////////////////////////////
  485. INT safe_sprintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...)
  486. {
  487. #ifndef WINNT_40
  488. HRESULT hr;
  489. char* pszDestEnd;
  490. size_t cchRemaining;
  491. #endif // !WINNT_40
  492. va_list argList;
  493. INT retSize = 0;
  494. va_start(argList, pszFormat);
  495. #ifndef WINNT_40
  496. hr = StringCchVPrintfExA(pszDest, cchDest, &pszDestEnd, &cchRemaining,
  497. STRSAFE_NO_TRUNCATION, pszFormat, argList);
  498. if (SUCCEEDED(hr))
  499. retSize = cchDest - cchRemaining;
  500. #else // WINNT_40
  501. if ((retSize = vsprintf(pszDest, pszFormat, argList)) < 0)
  502. retSize = 0;
  503. #endif // WINNT_40
  504. va_end(argList);
  505. return retSize;
  506. } //*** safe_sprintfA
  507. INT safe_sprintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, ...)
  508. {
  509. #ifndef WINNT_40
  510. HRESULT hr;
  511. wchar_t* pszDestEnd;
  512. size_t cchRemaining;
  513. #endif // !WINNT_40
  514. va_list argList;
  515. INT retSize = 0;
  516. va_start(argList, pszFormat);
  517. #ifndef WINNT_40
  518. hr = StringCchVPrintfExW(pszDest, cchDest, &pszDestEnd, &cchRemaining,
  519. STRSAFE_NO_TRUNCATION, pszFormat, argList);
  520. if (SUCCEEDED(hr))
  521. retSize = cchDest - cchRemaining;
  522. #else // WINNT_40
  523. if ((retSize = vswprintf(pszDest, pszFormat, argList)) < 0)
  524. retSize = 0;
  525. #endif // WINNT_40
  526. va_end(argList);
  527. return retSize;
  528. } //*** safe_sprintfW