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.

624 lines
19 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This module:
  7. 1) Finds the device name of a port
  8. 2) Initializes NTLog
  9. 3) Starts the log file
  10. 4) Closes the log file
  11. 5) Gets the current time
  12. 6) Writes a string to the log and text files
  13. 7) Checks if an edit control string is composed of only ASCII characters
  14. 8) Encodes a TSID
  15. 9) Decodes a TSID
  16. Author:
  17. Steven Kehrli (steveke) 11/15/1997
  18. --*/
  19. #ifndef _UTIL_C
  20. #define _UTIL_C
  21. VOID
  22. fnFindDeviceName(
  23. PFAX_PORT_INFO pFaxPortsConfig,
  24. DWORD dwNumPorts,
  25. DWORD dwDeviceId,
  26. LPWSTR *pszDeviceName
  27. )
  28. /*++
  29. Routine Description:
  30. Finds the device name of a port
  31. Arguments:
  32. pFaxPortsConfig - pointer to the fax ports configuration
  33. dwNumFaxPorts - number of ports
  34. dwDeviceId - port id
  35. pszDeviceName - device name
  36. Return Value:
  37. None
  38. --*/
  39. {
  40. // dwIndex is a counter to enumerate each port
  41. DWORD dwIndex;
  42. // Set szDeviceName to NULL
  43. *pszDeviceName = NULL;
  44. for (dwIndex = 0; dwIndex < dwNumPorts; dwIndex++) {
  45. // Search, by priority, each port for the appropriate port
  46. if (pFaxPortsConfig[dwIndex].DeviceId == dwDeviceId) {
  47. if (pFaxPortsConfig[dwIndex].DeviceName) {
  48. *pszDeviceName = (LPWSTR) pFaxPortsConfig[dwIndex].DeviceName;
  49. }
  50. return;
  51. }
  52. }
  53. }
  54. BOOL
  55. fnInitializeNTLog(
  56. )
  57. /*++
  58. Routine Description:
  59. Initializes NTLog
  60. Return Value:
  61. TRUE on success
  62. --*/
  63. {
  64. // hInstance is the handle to the NTLog dll
  65. HINSTANCE hInstance;
  66. // Get the handle to the NTLog dll
  67. hInstance = LoadLibrary((LPCWSTR) NTLOG_DLL);
  68. if (!hInstance) {
  69. DebugMacro(L"LoadLibrary(%s) failed, ec = 0x%08x\n", NTLOG_DLL, GetLastError());
  70. return FALSE;
  71. }
  72. // Map all needed functions
  73. g_NTLogApi.hInstance = hInstance;
  74. // tlCreateLog
  75. g_NTLogApi.ptlCreateLog = (PTLCREATELOG) GetProcAddress(hInstance, "tlCreateLog_W");
  76. if (!g_NTLogApi.ptlCreateLog) {
  77. DebugMacro(L"GetProcAddress(tlCreateLog) failed, ec = 0x%08x\n", GetLastError());
  78. FreeLibrary(hInstance);
  79. return FALSE;
  80. }
  81. // tlDestroyLog
  82. g_NTLogApi.ptlDestroyLog = (PTLDESTROYLOG) GetProcAddress(hInstance, "tlDestroyLog");
  83. if (!g_NTLogApi.ptlDestroyLog) {
  84. DebugMacro(L"GetProcAddress(tlDestroyLog) failed, ec = 0x%08x\n", GetLastError());
  85. FreeLibrary(hInstance);
  86. return FALSE;
  87. }
  88. // tlAddParticipant
  89. g_NTLogApi.ptlAddParticipant = (PTLADDPARTICIPANT) GetProcAddress(hInstance, "tlAddParticipant");
  90. if (!g_NTLogApi.ptlAddParticipant) {
  91. DebugMacro(L"GetProcAddress(tlAddParticipant) failed, ec = 0x%08x\n", GetLastError());
  92. FreeLibrary(hInstance);
  93. return FALSE;
  94. }
  95. // tlRemoveParticipant
  96. g_NTLogApi.ptlRemoveParticipant = (PTLREMOVEPARTICIPANT) GetProcAddress(hInstance, "tlRemoveParticipant");
  97. if (!g_NTLogApi.ptlRemoveParticipant) {
  98. DebugMacro(L"GetProcAddress(tlRemoveParticipant) failed, ec = 0x%08x\n", GetLastError());
  99. FreeLibrary(hInstance);
  100. return FALSE;
  101. }
  102. // tlLog
  103. g_NTLogApi.ptlLog = (PTLLOG) GetProcAddress(hInstance, "tlLog_W");
  104. if (!g_NTLogApi.ptlLog) {
  105. DebugMacro(L"GetProcAddress(tlLog) failed, ec = 0x%08x\n", GetLastError());
  106. FreeLibrary(hInstance);
  107. return FALSE;
  108. }
  109. return TRUE;
  110. }
  111. VOID
  112. fnStartLogFile(
  113. )
  114. /*++
  115. Routine Description:
  116. Starts the log file
  117. Return Value:
  118. None
  119. --*/
  120. {
  121. // cUnicodeBOM is the Unicode BOM
  122. WCHAR cUnicodeBOM = 0xFEFF;
  123. DWORD cb;
  124. INT iInt;
  125. // Create the new text log file
  126. g_hTxtFile = CreateFile(FAXVRFY_TXT, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  127. WriteFile(g_hTxtFile, &cUnicodeBOM, sizeof(WCHAR), &cb, NULL);
  128. // Create the new NTLog log file
  129. if (g_bNTLogAvailable) {
  130. g_hLogFile = (HANDLE) g_NTLogApi.ptlCreateLog(FAXVRFY_LOG, TLS_INFO | TLS_SEV2 | TLS_WARN | TLS_PASS | TLS_TEST | TLS_VARIATION | TLS_REFRESH);
  131. g_NTLogApi.ptlAddParticipant(g_hLogFile, 0, 0);
  132. }
  133. }
  134. VOID
  135. fnCloseLogFile(
  136. )
  137. /*++
  138. Routine Description:
  139. Closes the log file
  140. Return Value:
  141. None
  142. --*/
  143. {
  144. CloseHandle(g_hTxtFile);
  145. if (g_bNTLogAvailable) {
  146. g_NTLogApi.ptlRemoveParticipant(g_hLogFile);
  147. g_NTLogApi.ptlDestroyLog(g_hLogFile);
  148. FreeLibrary(g_NTLogApi.hInstance);
  149. }
  150. }
  151. VOID
  152. fnGetCurrentTime(
  153. LPWSTR *szCurrentTime
  154. )
  155. /*++
  156. Routine Description:
  157. Gets the current time
  158. Arguments:
  159. szFaxTime - string representation of the current time
  160. Return Value:
  161. None
  162. --*/
  163. {
  164. // CurrentTime is the current time
  165. SYSTEMTIME CurrentTime;
  166. // lcid is the default locale
  167. LCID lcid;
  168. // szDate is the date
  169. LPWSTR szDate;
  170. // szTime is the time
  171. LPWSTR szTime;
  172. DWORD cb;
  173. // Set szCurrentTime to NULL
  174. *szCurrentTime = NULL;
  175. // Get the current time
  176. GetLocalTime(&CurrentTime);
  177. // Get the default locale
  178. lcid = GetUserDefaultLCID();
  179. // Determine the memory required by the date
  180. cb = GetDateFormat(lcid, DATE_SHORTDATE, &CurrentTime, NULL, NULL, 0);
  181. // Allocate the memory for the date
  182. szDate = MemAllocMacro(cb * sizeof(WCHAR));
  183. // Get the date
  184. GetDateFormat(lcid, DATE_SHORTDATE, &CurrentTime, NULL, szDate, cb);
  185. // Determine the memory required by the time
  186. cb = GetTimeFormat(lcid, TIME_FORCE24HOURFORMAT, &CurrentTime, NULL, NULL, 0);
  187. // Allocate the memory for the time
  188. szTime = MemAllocMacro(cb * sizeof(WCHAR));
  189. // Get the time
  190. GetTimeFormat(lcid, 0, &CurrentTime, NULL, szTime, cb);
  191. // Allocate the memory for the time of the fax
  192. *szCurrentTime = MemAllocMacro((lstrlen(szDate) + lstrlen(szTime) + 4) * sizeof(WCHAR));
  193. // Copy the date
  194. lstrcpy(*szCurrentTime, szDate);
  195. // Append a space
  196. lstrcat(*szCurrentTime, L" ");
  197. // Append the time
  198. lstrcat(*szCurrentTime, szTime);
  199. lstrcat(*szCurrentTime, L"\r\n");
  200. }
  201. VOID
  202. fnWriteLogFile(
  203. BOOL bTime,
  204. LPWSTR szFormatString,
  205. ...
  206. )
  207. /*++
  208. Routine Description:
  209. Writes a string to the log and text files
  210. Arguments:
  211. bTime - indicates current time should be logged
  212. szFormatString - pointer to the string
  213. Return Value:
  214. None
  215. --*/
  216. {
  217. va_list varg_ptr;
  218. // szOutputString is the output string
  219. WCHAR szOutputString[1024];
  220. // szCurrentTime is the current time
  221. LPWSTR szCurrentTime;
  222. DWORD cb;
  223. if (bTime) {
  224. // Get the current time
  225. fnGetCurrentTime(&szCurrentTime);
  226. WriteFile(g_hTxtFile, szCurrentTime, lstrlen(szCurrentTime) * sizeof(WCHAR), &cb, NULL);
  227. // Free the current time
  228. MemFreeMacro(szCurrentTime);
  229. }
  230. va_start(varg_ptr, szFormatString);
  231. _vsnwprintf(szOutputString, sizeof(szOutputString), szFormatString, varg_ptr);
  232. WriteFile(g_hTxtFile, szOutputString, lstrlen(szOutputString) * sizeof(WCHAR), &cb, NULL);
  233. }
  234. BOOL
  235. fnIsStringASCII(
  236. LPWSTR szString
  237. )
  238. /*++
  239. Routine Description:
  240. Checks if a string is composed of only ASCII characters
  241. Arguments:
  242. szString - string
  243. Return Value:
  244. TRUE on success
  245. --*/
  246. {
  247. // szSearchString is the string used to search for non-ASCII characters
  248. LPWSTR szSearchString;
  249. BOOL bRslt;
  250. szSearchString = szString;
  251. bRslt = TRUE;
  252. while ((*szSearchString) && (bRslt)) {
  253. if ((*szSearchString < (WCHAR) 0x0020) || (*szSearchString >= (WCHAR) MAXCHAR)) {
  254. // Found a non-ASCII character
  255. bRslt = FALSE;
  256. }
  257. szSearchString = CharNext(szSearchString);
  258. }
  259. return bRslt;
  260. }
  261. BOOL
  262. fnIsEditControlASCII(
  263. HWND hWnd,
  264. UINT uResource,
  265. DWORD dwResourceLen
  266. )
  267. /*++
  268. Routine Description:
  269. Checks if an edit control string is composed of only ASCII characters
  270. Arguments:
  271. hWnd - handle to the window
  272. uResource - edit control resource id
  273. dwResourceLen - length of the edit control
  274. Return Value:
  275. TRUE on success
  276. --*/
  277. {
  278. // szResourceString is the edit control string
  279. LPWSTR szResourceString;
  280. BOOL bRslt;
  281. bRslt = TRUE;
  282. // Allocate the memory for the edit control string
  283. szResourceString = MemAllocMacro(dwResourceLen * sizeof(WCHAR));
  284. // Get the edit control string
  285. GetDlgItemText(hWnd, uResource, szResourceString, dwResourceLen);
  286. bRslt = fnIsStringASCII(szResourceString);
  287. MemFreeMacro(szResourceString);
  288. return bRslt;
  289. }
  290. VOID
  291. fnEncodeTsid(
  292. LPWSTR szTsid,
  293. LPWSTR szControlChars,
  294. LPWSTR szEncodedTsid
  295. )
  296. /*++
  297. Routine Description:
  298. Encodes a TSID
  299. Arguments:
  300. szTsid - original TSID
  301. szControlChars - control characters to prepend TSID
  302. szEncodedTsid - encoded TSID
  303. Encoding a TSID is difficult. Each ASCII value of the TSID must fall in the range 32 (0x0020) and 126 (0x009E), a range of 95.
  304. The TSID is encoded as follows:
  305. 1) Prepend TSID with the control characters. The control characters are used to verify FaxVrfy sent the fax.
  306. 2) Create a random number based on wMilliseconds:
  307. 1) wMilliseconds MOD 48 (0x0030): This gives a range of 0 - 48, half the ASCII value range of TSID
  308. 2) Add 24 (0x0018): This moves the range to 24 - 72, guaranteeing the ASCII values of the TSID will be changed.
  309. 3) Create a shift number based on the random number:
  310. 1) wRandomNumber MOD (TSID length / 2): This gives a range of 0 - (TSID length / 2), half the TSID length
  311. 2) Add (TSID length / 4): This moves the range to (TSID length / 4) - 3 * (TSID length / 4), guaranteeing the ASCII values of the TSID will be shifted.
  312. 4) Create an intermediate string where each ASCII value of the intermediate string is
  313. 1) Subtract 32 (0x0020) from each ASCII value of the TSID: This gives a range of 0 - 94.
  314. 2) Add the random number
  315. 3) MOD 95 (0x005F): This ensures each ASCII value of the intermediate string is in the range 0 - 94.
  316. 4) Add 32 (0x0020): This moves the range back to 32 - 126.
  317. 5) Create an array of pointers to each ASCII value of the encoded TSID
  318. 6) Determine the appropriate offset in the encoded TSID
  319. 1) Add the shift number to the current offset
  320. 2) MOD (TSID length - wIndex): This ensures the offset is in the range 0 - (number of remaining offsets).
  321. 7) Place the next ASCII value of the intermediate string into the appropriate offset in the encoded TSID
  322. 8) Compact the remaining offsets of the encoded TSID
  323. 9) Repeat steps 6 - 8 until all offsets have been used
  324. 10) Create the final encoded string
  325. 1) Add 32 (0x0020) to the random number: This ensure the random number is in the range 56 - 104.
  326. 2) Set the first character of the encoded string to the ASCII value of the random number.
  327. 3) Concatenate the encoded string with the encoded TSID
  328. Return Value:
  329. None
  330. --*/
  331. {
  332. // szControlString is the TSID prepended by the control characters
  333. WCHAR szControlString[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  334. // szEncodeChar is the encoding character
  335. WCHAR szEncodeChar[ENCODE_CHAR_LEN + 1] = {'\0'};
  336. // szIntermediateString is the intermediate encoded string
  337. WCHAR szIntermediateString[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  338. // szFinalString is the final encoded string
  339. WCHAR szFinalString[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  340. // szShiftChars is an array of pointers to each ASCII value of the encoded TSID
  341. LPWSTR szShiftChars[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  342. // SystemTime is the current system time where wMilliseconds is used to generate wRandomNumber and wShiftNumber
  343. SYSTEMTIME SystemTime;
  344. // wRandomNumber is a number to increment each ASCII value of the TSID
  345. WORD wRandomNumber;
  346. // wShiftNumber is a number to shift the order of each ASCII value of the TSID
  347. WORD wShiftNumber;
  348. // wIndex is a counter to enumerate each ASCII value of the TSID
  349. WORD wIndex;
  350. // wOffset is a counter to identify the appropriate offset of the encoded TSID when shifting each ASCII value of the TSID
  351. WORD wOffset;
  352. // wCompact is a counter to compact the remaining offsets of the encoded TSID
  353. WORD wCompact;
  354. // Set szControlString
  355. lstrcpy(szControlString, szControlChars);
  356. lstrcat(szControlString, szTsid);
  357. // Get the current system time
  358. GetSystemTime(&SystemTime);
  359. // Create the random number: (wMilliseconds MOD 48) + 24
  360. wRandomNumber = (SystemTime.wMilliseconds % (WORD) 0x0030) + (WORD) 0x0018;
  361. // Create the shift number: (wRandomNumber MOD (lstrlen(szControlString) / 2)) + (lstrlen(szControlString) / 4)
  362. wShiftNumber = ((wRandomNumber) % ((WORD) lstrlen(szControlString) / 2)) + ((WORD) lstrlen(szControlString) / 4);
  363. // Set szEncodeChar
  364. szEncodeChar[0] = (WCHAR) wRandomNumber + (WCHAR) 0x0020;
  365. for (wIndex = 0; wIndex < (WORD) lstrlen(szControlString); wIndex++) {
  366. // Create the intermediate string: ((ASCII value of the TSID - 32 + wRandomNumber) MOD 95) + 32
  367. szIntermediateString[wIndex] = ((szControlString[wIndex] - (WCHAR) 0x0020 + (WCHAR) wRandomNumber) % (WCHAR) 0x005F) + (WCHAR) 0x0020;
  368. }
  369. for (wIndex = 0; wIndex < (WORD) lstrlen(szIntermediateString); wIndex++) {
  370. // Create the list of remaining offsets of the encoded TSID
  371. szShiftChars[wIndex] = (LPWSTR) ((UINT_PTR) szFinalString + (sizeof(WCHAR) * wIndex));
  372. }
  373. for (wIndex = 0, wOffset = 0; wIndex < (WORD) lstrlen(szIntermediateString); wIndex++) {
  374. // Determine the appropriate offset of the encoded TSID
  375. wOffset = (wOffset + wShiftNumber) % (lstrlen(szIntermediateString) - wIndex);
  376. // Set the appropriate offset of the encoded TSID
  377. *((LPWSTR) szShiftChars[wOffset]) = szIntermediateString[wIndex];
  378. // Compact the remaining offsets of the encoded TSID
  379. for (wCompact = wOffset; wCompact < (lstrlen(szIntermediateString) - wIndex); wCompact++) {
  380. szShiftChars[wCompact] = szShiftChars[wCompact + 1];
  381. }
  382. }
  383. // Set szEncodedTsid
  384. lstrcpy(szEncodedTsid, szEncodeChar);
  385. lstrcat(szEncodedTsid, szFinalString);
  386. }
  387. BOOL
  388. fnDecodeTsid(
  389. LPWSTR szTsid,
  390. LPWSTR szControlChars,
  391. LPWSTR szDecodedTsid
  392. )
  393. /*++
  394. Routine Description:
  395. Decodes a TSID
  396. Arguments:
  397. szTsid - encoded TSID
  398. szControlChars - control characters to prepend TSID
  399. szDecodedTsid - decoded TSID
  400. The TSID is decoded as follows:
  401. 1) Isolate the encoded TSID from the random number.
  402. 2) Get the random number:
  403. 1) The first character in the TSID is the ASCII value of the random number
  404. 2) Subtract 32 (0x0020) from the ASCII value to get the random number.
  405. 3) Get the shift number based on the random number:
  406. 1) wRandomNumber MOD (TSID length / 2): This gives a range of 0 - (TSID length / 2), half the TSID length
  407. 2) Add (TSID length / 4): This moves the range to (TSID length / 4) - 3 * (TSID length / 4), guaranteeing the ASCII values of the TSID will be shifted.
  408. 4) Create an array of pointers to each ASCII value of the intermediate string
  409. 5) Determine the appropriate offset in the intermediate string
  410. 1) Add the shift number to the current offset
  411. 2) MOD (TSID length - wIndex): This ensures the offset is in the range 0 - (number of remaining offsets).
  412. 6) Place the next ASCII value of the encoded TSID into the appropriate offset in the intermediate string
  413. 7) Compact the remaining offsets of the intermediate string
  414. 8) Repeat steps 6 - 8 until all offsets have been used
  415. 9) Create the final string where each ASCII value of the final string is
  416. 1) Subtract 32 (0x0020) from each ASCII value of the intermediate string: This gives a range of 0 - 94.
  417. 2) Add 95 (0x005F): This ensures each ASCII value will not underflow when subtracting the random number.
  418. 3) Subtract the random number
  419. 3) MOD 95 (0x005F): This ensures each ASCII value of the intermediate string is in the range 0 - 94.
  420. 4) Add 32 (0x0020): This moves the range back to 32 - 126.
  421. 10) Create the final decoded string
  422. 11) Isolate and compare the control characters
  423. Return Value:
  424. TRUE if control characters match
  425. --*/
  426. {
  427. // szControlString is the TSID prepended by the control characters
  428. WCHAR szControlString[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  429. // szEncodeChar is the encoding character
  430. WCHAR szEncodeChar[ENCODE_CHAR_LEN + 1] = {'\0'};
  431. // szIntermediateString is the intermediate encoded string
  432. WCHAR szIntermediateString[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  433. // szFinalString is the final encoded string
  434. WCHAR szFinalString[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  435. // szShiftChars is an array of pointers to each ASCII value of the encoded TSID
  436. LPWSTR szShiftChars[CONTROL_CHAR_LEN + PHONE_NUM_LEN + 1] = {'\0'};
  437. // wRandomNumber is a number to decrement each ASCII value of the encoded TSID
  438. WORD wRandomNumber;
  439. // wShiftNumber is a number to shift the order of each ASCII value of the encoded TSID
  440. WORD wShiftNumber;
  441. // wIndex is a counter to enumerate each ASCII value of the TSID
  442. WORD wIndex;
  443. // wOffset is a counter to identify the appropriate offset of the decoded TSID when shifting each ASCII value of the encoded TSID
  444. WORD wOffset;
  445. // wCompact is a counter to compact the remaining offsets of the decoded TSID
  446. WORD wCompact;
  447. // Get szControlString
  448. lstrcpy(szControlString, (LPWSTR) ((UINT_PTR) szTsid + sizeof(WCHAR)));
  449. // Get szEncodeChar
  450. szEncodeChar[0] = *szTsid;
  451. // Get the random number
  452. wRandomNumber = (WORD) ((WCHAR) szEncodeChar[0] - (WCHAR) 0x0020);
  453. // Create the shift number: (wRandomNumber MOD (lstrlen(szControlString) / 2)) + (lstrlen(szControlString) / 4)
  454. wShiftNumber = ((wRandomNumber) % ((WORD) lstrlen(szControlString) / 2)) + ((WORD) lstrlen(szControlString) / 4);
  455. for (wIndex = 0; wIndex < (WORD) lstrlen(szControlString); wIndex++) {
  456. // Create the list of remaining offsets of the decoded TSID
  457. szShiftChars[wIndex] = (LPWSTR) ((UINT_PTR) szControlString + (sizeof(WCHAR) * wIndex));
  458. }
  459. for (wIndex = 0, wOffset = 0; wIndex < (WORD) lstrlen(szControlString); wIndex++) {
  460. // Determine the appropriate offset of the decoded TSID
  461. wOffset = (wOffset + wShiftNumber) % (lstrlen(szControlString) - wIndex);
  462. // Set the appropriate offset of the decoded TSID
  463. szIntermediateString[wIndex] = *((LPWSTR) szShiftChars[wOffset]);
  464. // Compact the remaining offsets of the decoded TSID
  465. for (wCompact = wOffset; wCompact < (lstrlen(szControlString) - wIndex); wCompact++) {
  466. szShiftChars[wCompact] = szShiftChars[wCompact + 1];
  467. }
  468. }
  469. for (wIndex = 0; wIndex < (WORD) lstrlen(szIntermediateString); wIndex++) {
  470. // Create the final string: ((ASCII value of the TSID - 32 + 95 - wRandomNumber) MOD 95) + 32
  471. szFinalString[wIndex] = ((szIntermediateString[wIndex] - (WCHAR) 0x0020 + (WCHAR) 0x005F - (WCHAR) wRandomNumber) % (WCHAR) 0x005F) + (WCHAR) 0x0020;
  472. }
  473. // Set szDecodedTsid
  474. lstrcpy(szDecodedTsid, (LPWSTR) ((UINT_PTR) szFinalString + lstrlen(szControlChars) * sizeof(WCHAR)));
  475. // Isolate the control characters
  476. lstrcpy((LPWSTR) ((UINT_PTR) szFinalString + lstrlen(szControlChars) * sizeof(WCHAR)), L"\0");
  477. // Compare the control characters
  478. return lstrcmp(szControlChars, szFinalString) ? FALSE : TRUE;
  479. }
  480. #endif