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.

1689 lines
43 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This module provides all the utility functions for the Routing Layer and
  7. the local Print Providor
  8. Author:
  9. Dave Snipp (DaveSn) 15-Mar-1991
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. #include "local.h"
  15. #include <winddiui.h>
  16. #include <winsock2.h>
  17. #include <wininet.h>
  18. LPWSTR *ppszOtherNames = NULL; // Contains szMachineName, DNS name, and all other machine name forms
  19. DWORD cOtherNames = 0; // Number of other names
  20. WCHAR *gszDrvConvert = L",DrvConvert";
  21. //
  22. // Lowercase, just like win31 for WM_WININICHANGE
  23. //
  24. WCHAR *szDevices=L"devices";
  25. WCHAR *szWindows=L"windows";
  26. #define NUM_INTERACTIVE_RIDS 1
  27. extern DWORD RouterCacheSize;
  28. extern PROUTERCACHE RouterCacheTable;
  29. typedef struct _DEVMODECHG_INFO {
  30. DWORD signature;
  31. HANDLE hDrvModule;
  32. FARPROC pfnConvertDevMode;
  33. } DEVMODECHG_INFO, *PDEVMODECHG_INFO;
  34. #define DMC_SIGNATURE 'DMC' /* 'DMC' is the signature value */
  35. DWORD
  36. RouterIsOlderThan(
  37. DWORD i,
  38. DWORD j
  39. );
  40. VOID
  41. FreeOtherNames(LPWSTR **ppszMyOtherNames, DWORD *cOtherNames);
  42. LPWSTR
  43. AnsiToUnicodeStringWithAlloc(LPSTR pAnsi);
  44. BOOL
  45. DeleteSubKeyTree(
  46. HKEY ParentHandle,
  47. WCHAR SubKeyName[]
  48. )
  49. {
  50. LONG Error;
  51. DWORD Index;
  52. HKEY KeyHandle;
  53. BOOL RetValue = TRUE;
  54. PWSTR pszChildKeyName = NULL;
  55. if(pszChildKeyName = AllocSplMem( sizeof(WCHAR)*MAX_PATH ))
  56. {
  57. Error = RegOpenKeyEx(ParentHandle,
  58. SubKeyName,
  59. 0,
  60. KEY_READ | KEY_WRITE,
  61. &KeyHandle);
  62. if (Error != ERROR_SUCCESS)
  63. {
  64. SetLastError(Error);
  65. RetValue = FALSE;
  66. }
  67. else
  68. {
  69. DWORD ChildKeyNameLength = MAX_PATH;
  70. //
  71. // Don't increment this Index
  72. //
  73. Index = 0;
  74. while ((Error = RegEnumKeyEx(KeyHandle,
  75. Index,
  76. pszChildKeyName,
  77. &ChildKeyNameLength,
  78. NULL,
  79. NULL,
  80. NULL,
  81. NULL
  82. )) == ERROR_SUCCESS)
  83. {
  84. RetValue = DeleteSubKeyTree( KeyHandle, pszChildKeyName );
  85. if (RetValue == FALSE)
  86. {
  87. //
  88. // Error -- couldn't delete the sub key
  89. //
  90. break;
  91. }
  92. ChildKeyNameLength = MAX_PATH;
  93. }
  94. Error = RegCloseKey(KeyHandle);
  95. if(RetValue)
  96. {
  97. if (Error != ERROR_SUCCESS)
  98. {
  99. RetValue = FALSE;
  100. }
  101. else
  102. {
  103. Error = RegDeleteKey(ParentHandle,
  104. SubKeyName);
  105. if (Error != ERROR_SUCCESS)
  106. {
  107. RetValue = FALSE;
  108. }
  109. }
  110. }
  111. }
  112. FreeSplMem(pszChildKeyName);
  113. }
  114. else
  115. {
  116. RetValue = FALSE;
  117. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  118. }
  119. return(RetValue);
  120. }
  121. LPWSTR RemoveOrderEntry(
  122. LPWSTR szOrderString,
  123. DWORD cbStringSize,
  124. LPWSTR szOrderEntry,
  125. LPDWORD pcbBytesReturned
  126. )
  127. {
  128. LPWSTR lpMem, psz, temp;
  129. if (szOrderString == NULL) {
  130. *pcbBytesReturned = 0;
  131. return(NULL);
  132. }
  133. if (lpMem = AllocSplMem( cbStringSize)) {
  134. DWORD cchStringLen = cbStringSize/sizeof(WCHAR);
  135. temp = szOrderString;
  136. psz = lpMem;
  137. while (*temp)
  138. {
  139. DWORD cchTempStrLen = 0;
  140. if (!lstrcmpi(temp, szOrderEntry))
  141. // we need to remove
  142. // this entry in Order
  143. {
  144. temp += lstrlen(temp)+1;
  145. continue;
  146. }
  147. if((cchTempStrLen = lstrlen(temp)+1) > cchStringLen)
  148. {
  149. break;
  150. }
  151. else
  152. {
  153. StringCchCopy(psz,cchStringLen,temp);
  154. cchStringLen -= cchTempStrLen;
  155. psz += cchTempStrLen;
  156. temp += cchTempStrLen;
  157. }
  158. }
  159. *psz = L'\0';
  160. *pcbBytesReturned = (DWORD) ((psz - lpMem)+1)*sizeof(WCHAR);
  161. return(lpMem);
  162. }
  163. *pcbBytesReturned = 0;
  164. return(lpMem);
  165. }
  166. LPWSTR AppendOrderEntry(
  167. LPWSTR szOrderString,
  168. DWORD cbStringSize,
  169. LPWSTR szOrderEntry,
  170. LPDWORD pcbBytesReturned
  171. )
  172. {
  173. LPWSTR lpMem,
  174. temp,
  175. psz;
  176. DWORD cb = 0;
  177. DWORD cchStrLen = 0;
  178. BOOL bExists = FALSE;
  179. if ((szOrderString == NULL) && (szOrderEntry == NULL)) {
  180. *pcbBytesReturned = 0;
  181. return(NULL);
  182. }
  183. if (szOrderString == NULL) {
  184. cb = wcslen(szOrderEntry)*sizeof(WCHAR)+ sizeof(WCHAR) + sizeof(WCHAR);
  185. if (lpMem = AllocSplMem(cb)){
  186. StringCbCopy(lpMem, cb, szOrderEntry);
  187. *pcbBytesReturned = cb;
  188. } else {
  189. *pcbBytesReturned = 0;
  190. }
  191. return lpMem;
  192. }
  193. cchStrLen = cbStringSize + wcslen(szOrderEntry) + 1;
  194. if (lpMem = AllocSplMem(cchStrLen * sizeof(WCHAR))){
  195. temp = szOrderString;
  196. psz = lpMem;
  197. while (*temp)
  198. {
  199. DWORD cchTempStrLen = 0;
  200. if (!lstrcmpi(temp, szOrderEntry))
  201. {
  202. //
  203. // Make sure we don't
  204. // duplicate entries
  205. //
  206. bExists = TRUE;
  207. }
  208. if((cchTempStrLen = lstrlen(temp)+1) > cchStrLen)
  209. {
  210. break;
  211. }
  212. else
  213. {
  214. StringCchCopy(psz,cchStrLen,temp);
  215. cchStrLen -= cchTempStrLen;
  216. psz += cchTempStrLen;
  217. temp += cchTempStrLen;
  218. }
  219. }
  220. if (!bExists)
  221. {
  222. //
  223. // if it doesn't exist
  224. // add the entry
  225. //
  226. StringCchCopy(psz, cchStrLen, szOrderEntry);
  227. psz += min(cchStrLen,(DWORD)(lstrlen(szOrderEntry)+1));
  228. }
  229. *psz = L'\0'; // the second null character
  230. *pcbBytesReturned = (DWORD) ((psz - lpMem) + 1)* sizeof(WCHAR);
  231. }
  232. return(lpMem);
  233. }
  234. typedef struct {
  235. DWORD dwType;
  236. DWORD dwMessage;
  237. WPARAM wParam;
  238. LPARAM lParam;
  239. } MESSAGE, *PMESSAGE;
  240. VOID
  241. SendMessageThread(
  242. PMESSAGE pMessage);
  243. BOOL
  244. BroadcastMessage(
  245. DWORD dwType,
  246. DWORD dwMessage,
  247. WPARAM wParam,
  248. LPARAM lParam)
  249. {
  250. HANDLE hThread;
  251. DWORD ThreadId;
  252. PMESSAGE pMessage;
  253. BOOL bReturn = FALSE;
  254. pMessage = AllocSplMem(sizeof(MESSAGE));
  255. if (pMessage) {
  256. pMessage->dwType = dwType;
  257. pMessage->dwMessage = dwMessage;
  258. pMessage->wParam = wParam;
  259. pMessage->lParam = lParam;
  260. //
  261. // We should have a queue of events to broadcast and then have a
  262. // single thread pulling them off the queue until there is nothing
  263. // left and then that thread could go away.
  264. //
  265. // The current design can lead to a huge number of threads being
  266. // created and torn down in both this and csrss process.
  267. //
  268. hThread = CreateThread(NULL, 0,
  269. (LPTHREAD_START_ROUTINE)SendMessageThread,
  270. (LPVOID)pMessage,
  271. 0,
  272. &ThreadId);
  273. if (hThread) {
  274. CloseHandle(hThread);
  275. bReturn = TRUE;
  276. } else {
  277. FreeSplMem(pMessage);
  278. }
  279. }
  280. return bReturn;
  281. }
  282. // The Broadcasts are done on a separate thread, the reason it CSRSS
  283. // will create a server side thread when we call user and we don't want
  284. // that to be pared up with the RPC thread which is in the spooss server.
  285. // We want it to go away the moment we have completed the SendMessage.
  286. // We also call SendNotifyMessage since we don't care if the broadcasts
  287. // are syncronous this uses less resources since usually we don't have more
  288. // than one broadcast.
  289. VOID
  290. SendMessageThread(
  291. PMESSAGE pMessage)
  292. {
  293. switch (pMessage->dwType) {
  294. case BROADCAST_TYPE_MESSAGE:
  295. SendNotifyMessage(HWND_BROADCAST,
  296. pMessage->dwMessage,
  297. pMessage->wParam,
  298. pMessage->lParam);
  299. break;
  300. case BROADCAST_TYPE_CHANGEDEFAULT:
  301. //
  302. // Same order and strings as win31.
  303. //
  304. SendNotifyMessage(HWND_BROADCAST,
  305. WM_WININICHANGE,
  306. 0,
  307. (LPARAM)szDevices);
  308. SendNotifyMessage(HWND_BROADCAST,
  309. WM_WININICHANGE,
  310. 0,
  311. (LPARAM)szWindows);
  312. break;
  313. }
  314. FreeSplMem(pMessage);
  315. ExitThread(0);
  316. }
  317. BOOL
  318. IsNamedPipeRpcCall(
  319. VOID
  320. )
  321. {
  322. unsigned int uType;
  323. //
  324. //
  325. //
  326. return ERROR_SUCCESS == I_RpcBindingInqTransportType(NULL, &uType) &&
  327. uType != TRANSPORT_TYPE_LPC;
  328. }
  329. /*++
  330. Name:
  331. CheckLocalCall
  332. Description:
  333. This function checks whether the current thread is handling a local
  334. or a remote call. Definiton of a remote call:
  335. -the call came via a transport different from LPC or
  336. -the came came via LPC, but the thread's security token includes the SECURITY_NETWORK_RID
  337. Arguments:
  338. None.
  339. Return Value:
  340. S_OK - local call
  341. S_FALSE - remote call
  342. other HR - an error occurred and the call type could not be determined
  343. --*/
  344. HRESULT
  345. CheckLocalCall(
  346. VOID
  347. )
  348. {
  349. HRESULT hResult = S_OK;
  350. DWORD SaveLastError = GetLastError();
  351. unsigned int uType;
  352. DWORD Error = I_RpcBindingInqTransportType(NULL, &uType);
  353. switch (Error)
  354. {
  355. case RPC_S_NO_CALL_ACTIVE:
  356. {
  357. //
  358. // KM call
  359. //
  360. hResult = S_OK;
  361. break;
  362. }
  363. case ERROR_SUCCESS:
  364. {
  365. if (uType != TRANSPORT_TYPE_LPC)
  366. {
  367. //
  368. // Not LRPC so call is remote
  369. //
  370. hResult = S_FALSE;
  371. }
  372. else
  373. {
  374. HANDLE hToken;
  375. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
  376. {
  377. SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
  378. PSID pTestSid = NULL;
  379. BOOL bMember;
  380. if (AllocateAndInitializeSid(&sia,
  381. 1,
  382. SECURITY_NETWORK_RID,
  383. 0, 0, 0, 0, 0, 0, 0,
  384. &pTestSid))
  385. {
  386. if (CheckTokenMembership(hToken, pTestSid, &bMember))
  387. {
  388. hResult = bMember ? S_FALSE : S_OK;
  389. }
  390. else
  391. {
  392. hResult = GetLastErrorAsHResult();
  393. }
  394. FreeSid(pTestSid);
  395. }
  396. else
  397. {
  398. hResult = GetLastErrorAsHResult();
  399. }
  400. CloseHandle(hToken);
  401. }
  402. else
  403. {
  404. hResult = GetLastErrorAsHResult();
  405. //
  406. // The following call originates from the print processor. There are port monitors which
  407. // will revert to spooler self before calling open printer. In this case, OpenThreadToken
  408. // fails. We need to treat ERROR_NO_TOKEN as call coming from the local machine.
  409. //
  410. // localspl!SplOpenPrinter
  411. // localspl!LocalOpenPrinterEx+0x7b
  412. // SPOOLSS!RouterOpenPrinterW+0x13f
  413. // SPOOLSS!OpenPrinterW+0x17
  414. // SOMEPORTMON!pfnStartDocPort
  415. // localspl!PrintingDirectlyToPort+0x199
  416. // localspl!LocalStartDocPrinter+0x4e
  417. // SPOOLSS!StartDocPrinterW+0x21
  418. // spoolsv!YStartDocPrinter+0xaf
  419. // spoolsv!RpcStartDocPrinter+0x13
  420. // RPCRT4!something
  421. //
  422. if (hResult == HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
  423. {
  424. hResult = S_OK;
  425. }
  426. }
  427. }
  428. break;
  429. }
  430. default:
  431. {
  432. hResult = HRESULT_FROM_WIN32(Error);
  433. }
  434. }
  435. SetLastError(SaveLastError);
  436. return hResult;
  437. }
  438. LPPROVIDOR
  439. FindEntryinRouterCache(
  440. LPWSTR pPrinterName
  441. )
  442. {
  443. DWORD i;
  444. if (!pPrinterName)
  445. return NULL;
  446. DBGMSG(DBG_TRACE, ("FindEntryinRouterCache with %ws\n", pPrinterName));
  447. if (RouterCacheSize == 0 ) {
  448. DBGMSG(DBG_TRACE, ("FindEntryInRouterCache with %ws returning -1 (zero cache)\n", pPrinterName));
  449. return NULL;
  450. }
  451. for (i = 0; i < RouterCacheSize; i++ ) {
  452. if (RouterCacheTable[i].bAvailable) {
  453. if (!_wcsicmp(RouterCacheTable[i].pPrinterName, pPrinterName)) {
  454. //
  455. // update the time stamp so that it is current and not old
  456. //
  457. GetSystemTime(&RouterCacheTable[i].st);
  458. //
  459. //
  460. //
  461. DBGMSG(DBG_TRACE, ("FindEntryinRouterCache returning with %d\n", i));
  462. return RouterCacheTable[i].pProvidor;
  463. }
  464. }
  465. }
  466. DBGMSG(DBG_TRACE, ("FindEntryinRouterCache returning with -1\n"));
  467. return NULL;
  468. }
  469. DWORD
  470. AddEntrytoRouterCache(
  471. LPWSTR pPrinterName,
  472. LPPROVIDOR pProvidor
  473. )
  474. {
  475. DWORD LRUEntry = (DWORD)-1;
  476. DWORD i;
  477. DBGMSG(DBG_TRACE, ("AddEntrytoRouterCache with %ws\n", pPrinterName));
  478. if (RouterCacheSize == 0 ) {
  479. DBGMSG(DBG_TRACE, ("AddEntrytoRouterCache with %ws returning -1 (zero cache)\n", pPrinterName));
  480. return (DWORD)-1;
  481. }
  482. for (i = 0; i < RouterCacheSize; i++ ) {
  483. if (!RouterCacheTable[i].bAvailable) {
  484. //
  485. // Found an available entry; use it
  486. // fill in the name of the printer and the providor
  487. // that supports this printer.
  488. //
  489. break;
  490. } else {
  491. if ((LRUEntry == -1) || (i == RouterIsOlderThan(i, LRUEntry))){
  492. LRUEntry = i;
  493. }
  494. }
  495. }
  496. if (i == RouterCacheSize) {
  497. //
  498. // We have no available entries so we need to use
  499. // the LRUEntry which is busy
  500. //
  501. FreeSplStr(RouterCacheTable[LRUEntry].pPrinterName);
  502. RouterCacheTable[LRUEntry].bAvailable = FALSE;
  503. i = LRUEntry;
  504. }
  505. if ((RouterCacheTable[i].pPrinterName = AllocSplStr(pPrinterName)) == NULL){
  506. //
  507. // Alloc failed so we're kinda hosed so return -1
  508. //
  509. return (DWORD)-1;
  510. }
  511. RouterCacheTable[i].bAvailable = TRUE;
  512. RouterCacheTable[i].pProvidor = pProvidor;
  513. //
  514. // update the time stamp so that we know when this entry was made
  515. //
  516. GetSystemTime(&RouterCacheTable[i].st);
  517. DBGMSG(DBG_TRACE, ("AddEntrytoRouterCache returning with %d\n", i));
  518. return i;
  519. }
  520. VOID
  521. DeleteEntryfromRouterCache(
  522. LPWSTR pPrinterName
  523. )
  524. {
  525. DWORD i;
  526. if (RouterCacheSize == 0) {
  527. DBGMSG(DBG_TRACE, ("DeleteEntryfromRouterCache with %ws returning -1 (zero cache)\n", pPrinterName));
  528. return;
  529. }
  530. DBGMSG(DBG_TRACE, ("DeleteEntryFromRouterCache with %ws\n", pPrinterName));
  531. for (i = 0; i < RouterCacheSize; i++ ) {
  532. if (RouterCacheTable[i].bAvailable) {
  533. if (!_wcsicmp(RouterCacheTable[i].pPrinterName, pPrinterName)) {
  534. //
  535. // reset the available flag on this node
  536. //
  537. FreeSplStr(RouterCacheTable[i].pPrinterName);
  538. RouterCacheTable[i].pProvidor = NULL;
  539. RouterCacheTable[i].bAvailable = FALSE;
  540. DBGMSG(DBG_TRACE, ("DeleteEntryFromRouterCache returning after deleting the %d th entry\n", i));
  541. return;
  542. }
  543. }
  544. }
  545. DBGMSG(DBG_TRACE, ("DeleteEntryFromRouterCache returning after not finding an entry to delete\n"));
  546. }
  547. DWORD
  548. RouterIsOlderThan(
  549. DWORD i,
  550. DWORD j
  551. )
  552. {
  553. SYSTEMTIME *pi, *pj;
  554. DWORD iMs, jMs;
  555. DBGMSG(DBG_TRACE, ("RouterIsOlderThan entering with i %d j %d\n", i, j));
  556. pi = &(RouterCacheTable[i].st);
  557. pj = &(RouterCacheTable[j].st);
  558. DBGMSG(DBG_TRACE, ("Index i %d - %d:%d:%d:%d:%d:%d:%d\n",
  559. i, pi->wYear, pi->wMonth, pi->wDay, pi->wHour, pi->wMinute, pi->wSecond, pi->wMilliseconds));
  560. DBGMSG(DBG_TRACE,("Index j %d - %d:%d:%d:%d:%d:%d:%d\n",
  561. j, pj->wYear, pj->wMonth, pj->wDay, pj->wHour, pj->wMinute, pj->wSecond, pj->wMilliseconds));
  562. if (pi->wYear < pj->wYear) {
  563. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i));
  564. return(i);
  565. } else if (pi->wYear > pj->wYear) {
  566. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j));
  567. return(j);
  568. } else if (pi->wMonth < pj->wMonth) {
  569. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i));
  570. return(i);
  571. } else if (pi->wMonth > pj->wMonth) {
  572. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j));
  573. return(j);
  574. } else if (pi->wDay < pj->wDay) {
  575. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i));
  576. return(i);
  577. } else if (pi->wDay > pj->wDay) {
  578. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j));
  579. return(j);
  580. } else {
  581. iMs = ((((pi->wHour * 60) + pi->wMinute)*60) + pi->wSecond)* 1000 + pi->wMilliseconds;
  582. jMs = ((((pj->wHour * 60) + pj->wMinute)*60) + pj->wSecond)* 1000 + pj->wMilliseconds;
  583. if (iMs <= jMs) {
  584. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i));
  585. return(i);
  586. } else {
  587. DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j));
  588. return(j);
  589. }
  590. }
  591. }
  592. /*++
  593. Routine Name
  594. ImpersonationToken
  595. Routine Description:
  596. This routine checks if a token is a primary token or an impersonation
  597. token.
  598. Arguments:
  599. hToken - impersonation token or primary token of the process
  600. Return Value:
  601. TRUE, if the token is an impersonation token
  602. FALSE, otherwise.
  603. --*/
  604. BOOL
  605. ImpersonationToken(
  606. IN HANDLE hToken
  607. )
  608. {
  609. BOOL bRet = TRUE;
  610. TOKEN_TYPE eTokenType;
  611. DWORD cbNeeded;
  612. DWORD LastError;
  613. //
  614. // Preserve the last error. Some callers of ImpersonatePrinterClient (which
  615. // calls ImpersonationToken) rely on the fact that ImpersonatePrinterClient
  616. // does not alter the last error.
  617. //
  618. LastError = GetLastError();
  619. //
  620. // Get the token type from the thread token. The token comes
  621. // from RevertToPrinterSelf. An impersonation token cannot be
  622. // queried, because RevertToPRinterSelf doesn't open it with
  623. // TOKEN_QUERY access. That's why we assume that hToken is
  624. // an impersonation token by default
  625. //
  626. if (GetTokenInformation(hToken,
  627. TokenType,
  628. &eTokenType,
  629. sizeof(eTokenType),
  630. &cbNeeded))
  631. {
  632. bRet = eTokenType == TokenImpersonation;
  633. }
  634. SetLastError(LastError);
  635. return bRet;
  636. }
  637. /*++
  638. Routine Name
  639. RevertToPrinterSelf
  640. Routine Description:
  641. This routine will revert to the local system. It returns the token that
  642. ImpersonatePrinterClient then uses to imersonate the client again. If the
  643. current thread doesn't impersonate, then the function merely returns the
  644. primary token of the process. (instead of returning NULL) Thus we honor
  645. a request for reverting to printer self, even if the thread is not impersonating.
  646. Arguments:
  647. None.
  648. Return Value:
  649. NULL, if the function failed
  650. HANDLE to token, otherwise.
  651. --*/
  652. HANDLE
  653. RevertToPrinterSelf(
  654. VOID
  655. )
  656. {
  657. HANDLE NewToken, OldToken;
  658. NTSTATUS Status;
  659. NewToken = NULL;
  660. Status = NtOpenThreadToken(NtCurrentThread(),
  661. TOKEN_IMPERSONATE,
  662. TRUE,
  663. &OldToken);
  664. if (NT_SUCCESS(Status))
  665. {
  666. //
  667. // We are currently impersonating
  668. //
  669. Status = NtSetInformationThread(NtCurrentThread(),
  670. ThreadImpersonationToken,
  671. (PVOID)&NewToken,
  672. (ULONG)sizeof(HANDLE));
  673. }
  674. else if (Status == STATUS_NO_TOKEN)
  675. {
  676. //
  677. // We are not impersonating
  678. //
  679. Status = NtOpenProcessToken(NtCurrentProcess(),
  680. TOKEN_QUERY,
  681. &OldToken);
  682. }
  683. if (!NT_SUCCESS(Status))
  684. {
  685. SetLastError(Status);
  686. return NULL;
  687. }
  688. return OldToken;
  689. }
  690. /*++
  691. Routine Name
  692. ImpersonatePrinterClient
  693. Routine Description:
  694. This routine attempts to set the passed in hToken as the token for the
  695. current thread. If hToken is not an impersonation token, then the routine
  696. will simply close the token.
  697. Arguments:
  698. hToken - impersonation token or primary token of the process
  699. Return Value:
  700. TRUE, if the function succeeds in setting hToken
  701. FALSE, otherwise.
  702. --*/
  703. BOOL
  704. ImpersonatePrinterClient(
  705. HANDLE hToken)
  706. {
  707. NTSTATUS Status;
  708. //
  709. // Check if we have an impersonation token
  710. //
  711. if (ImpersonationToken(hToken))
  712. {
  713. Status = NtSetInformationThread(NtCurrentThread(),
  714. ThreadImpersonationToken,
  715. (PVOID)&hToken,
  716. (ULONG)sizeof(HANDLE));
  717. if (!NT_SUCCESS(Status))
  718. {
  719. SetLastError(Status);
  720. return FALSE;
  721. }
  722. }
  723. NtClose(hToken);
  724. return TRUE;
  725. }
  726. HANDLE
  727. LoadDriver(
  728. LPWSTR pDriverFile)
  729. {
  730. UINT uOldErrorMode;
  731. fnWinSpoolDrv fnList;
  732. HANDLE hReturn = NULL;
  733. if (!pDriverFile || !*pDriverFile) {
  734. // Nothing to load
  735. return hReturn;
  736. }
  737. uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
  738. if (SplInitializeWinSpoolDrv(&fnList)) {
  739. hReturn = (*(fnList.pfnRefCntLoadDriver))(pDriverFile,
  740. LOAD_WITH_ALTERED_SEARCH_PATH,
  741. 0, FALSE);
  742. }
  743. (VOID)SetErrorMode(uOldErrorMode);
  744. return hReturn;
  745. }
  746. VOID
  747. UnloadDriver(
  748. HANDLE hModule
  749. )
  750. {
  751. fnWinSpoolDrv fnList;
  752. if (SplInitializeWinSpoolDrv(&fnList)) {
  753. (* (fnList.pfnRefCntUnloadDriver))(hModule, TRUE);
  754. }
  755. }
  756. VOID
  757. UnloadDriverFile(
  758. IN OUT HANDLE hDevModeChgInfo
  759. )
  760. /*++
  761. Description:
  762. Does a FreeLibrary on the driver file and frees memory
  763. Arguments:
  764. hDevModeChgInfo - A handle returned by LoadDriverFiletoConvertDevmode
  765. Return Value:
  766. None
  767. --*/
  768. {
  769. PDEVMODECHG_INFO pDevModeChgInfo = (PDEVMODECHG_INFO) hDevModeChgInfo;
  770. SPLASSERT(pDevModeChgInfo &&
  771. pDevModeChgInfo->signature == DMC_SIGNATURE);
  772. if ( pDevModeChgInfo && pDevModeChgInfo->signature == DMC_SIGNATURE ) {
  773. if ( pDevModeChgInfo->hDrvModule ) {
  774. UnloadDriver(pDevModeChgInfo->hDrvModule);
  775. }
  776. FreeSplMem((LPVOID)pDevModeChgInfo);
  777. }
  778. }
  779. HANDLE
  780. LoadDriverFiletoConvertDevmode(
  781. IN LPWSTR pDriverFile
  782. )
  783. /*++
  784. Description:
  785. Does a LoadLibrary on the driver file given. This will give a handle
  786. which can be used to do devmode conversion later using
  787. CallDrvDevModeConversion.
  788. Caller should call UnloadDriverFile to do a FreeLibrary and free memory
  789. Note: Driver will call OpenPrinter to spooler
  790. Arguments:
  791. pDriverFile - Full path of the driver file to do a LoadLibrary
  792. Return Value:
  793. A handle value to be used to make calls to CallDrvDevModeConversion,
  794. NULL on error
  795. --*/
  796. {
  797. PDEVMODECHG_INFO pDevModeChgInfo = NULL;
  798. BOOL bFail = TRUE;
  799. DWORD dwNeeded;
  800. SPLASSERT(pDriverFile != NULL);
  801. pDevModeChgInfo = (PDEVMODECHG_INFO) AllocSplMem(sizeof(*pDevModeChgInfo));
  802. if ( !pDevModeChgInfo ) {
  803. DBGMSG(DBG_WARNING, ("printer.c: Memory allocation failed for DEVMODECHG_INFO\n"));
  804. goto Cleanup;
  805. }
  806. pDevModeChgInfo->signature = DMC_SIGNATURE;
  807. pDevModeChgInfo->hDrvModule = LoadDriver(pDriverFile);
  808. if ( !pDevModeChgInfo->hDrvModule ) {
  809. DBGMSG(DBG_WARNING,("LoadDriverFiletoConvertDevmode: Can't load driver file %ws\n", pDriverFile));
  810. goto Cleanup;
  811. }
  812. //
  813. // Some third party driver may not be providing DrvConvertDevMode
  814. //
  815. pDevModeChgInfo->pfnConvertDevMode = GetProcAddress(pDevModeChgInfo->hDrvModule,
  816. "DrvConvertDevMode");
  817. if ( !pDevModeChgInfo->pfnConvertDevMode )
  818. goto Cleanup;
  819. bFail = FALSE;
  820. Cleanup:
  821. if ( bFail ) {
  822. if ( pDevModeChgInfo ) {
  823. UnloadDriverFile((HANDLE)pDevModeChgInfo);
  824. }
  825. return (HANDLE) NULL;
  826. } else {
  827. return (HANDLE) pDevModeChgInfo;
  828. }
  829. }
  830. DWORD
  831. CallDrvDevModeConversion(
  832. IN HANDLE hDevModeChgInfo,
  833. IN LPWSTR pszPrinterName,
  834. IN LPBYTE pDevMode1,
  835. IN OUT LPBYTE *ppDevMode2,
  836. IN OUT LPDWORD pdwOutDevModeSize,
  837. IN DWORD dwConvertMode,
  838. IN BOOL bAlloc
  839. )
  840. /*++
  841. Description:
  842. Does deve mode conversion by calling driver
  843. If bAlloc is TRUE routine will do the allocation using AllocSplMem
  844. Note: Driver is going to call OpenPrinter.
  845. Arguments:
  846. hDevModeChgInfo - Points to DEVMODECHG_INFO
  847. pszPrinterName - Printer name
  848. pInDevMode - Input devmode (will be NULL for CDM_DRIVER_DEFAULT)
  849. *pOutDevMode - Points to output devmode
  850. pdwOutDevModeSize - Output devmode size on succesful return
  851. if !bAlloc this will give input buffer size
  852. dwConvertMode - Devmode conversion mode to give to driver
  853. bAllocate - Tells the routine to do allocation to *pOutPrinter
  854. If bAllocate is TRUE and no devmode conversion is required
  855. call will fail.
  856. Return Value:
  857. Returns last error
  858. --*/
  859. {
  860. DWORD dwBufSize, dwSize, dwLastError = ERROR_SUCCESS;
  861. LPDEVMODE pInDevMode = (LPDEVMODE)pDevMode1,
  862. *ppOutDevMode = (LPDEVMODE *) ppDevMode2;
  863. PDEVMODECHG_INFO pDevModeChgInfo = (PDEVMODECHG_INFO) hDevModeChgInfo;
  864. PWSTR pszDrvConvert = pszPrinterName;
  865. if ( !pDevModeChgInfo ||
  866. pDevModeChgInfo->signature != DMC_SIGNATURE ||
  867. !pDevModeChgInfo->pfnConvertDevMode ) {
  868. SPLASSERT(pDevModeChgInfo &&
  869. pDevModeChgInfo->signature == DMC_SIGNATURE &&
  870. pDevModeChgInfo->pfnConvertDevMode);
  871. return ERROR_INVALID_PARAMETER;
  872. }
  873. //
  874. // We decorate the pszPrinterName with ",DrvConvert" to prevent drivers from
  875. // infinitely recursing by calling GetPrinter inside ConvertDevMode
  876. //
  877. if (wcsstr(pszPrinterName, gszDrvConvert)) {
  878. return ERROR_INVALID_PARAMETER;
  879. }
  880. if (!(pszDrvConvert = AutoCat(pszPrinterName, gszDrvConvert))) {
  881. return GetLastError();
  882. }
  883. DBGMSG(DBG_INFO,("Convert DevMode %d\n", dwConvertMode));
  884. #if DBG
  885. #else
  886. try {
  887. #endif
  888. if ( bAlloc ) {
  889. //
  890. // If we have to do allocation first find size neeeded
  891. //
  892. *pdwOutDevModeSize = 0;
  893. *ppOutDevMode = NULL;
  894. (*pDevModeChgInfo->pfnConvertDevMode)(pszDrvConvert,
  895. pInDevMode,
  896. NULL,
  897. pdwOutDevModeSize,
  898. dwConvertMode);
  899. dwLastError = GetLastError();
  900. if ( dwLastError != ERROR_INSUFFICIENT_BUFFER ) {
  901. DBGMSG(DBG_WARNING,
  902. ("CallDrvDevModeConversion: Unexpected error %d\n",
  903. GetLastError()));
  904. if (dwLastError == ERROR_SUCCESS) {
  905. SPLASSERT(dwLastError != ERROR_SUCCESS);
  906. // if driver doesn't fail the above call, it is a broken driver and probably
  907. // failed a HeapAlloc, which doesn't SetLastError()
  908. SetLastError(dwLastError = ERROR_NOT_ENOUGH_MEMORY);
  909. }
  910. #if DBG
  911. goto Cleanup;
  912. #else
  913. leave;
  914. #endif
  915. }
  916. *ppOutDevMode = AllocSplMem(*pdwOutDevModeSize);
  917. if ( !*ppOutDevMode ) {
  918. dwLastError = GetLastError();
  919. #if DBG
  920. goto Cleanup;
  921. #else
  922. leave;
  923. #endif
  924. }
  925. }
  926. dwBufSize = *pdwOutDevModeSize;
  927. if ( !(*pDevModeChgInfo->pfnConvertDevMode)(
  928. pszDrvConvert,
  929. pInDevMode,
  930. ppOutDevMode ? *ppOutDevMode
  931. : NULL,
  932. pdwOutDevModeSize,
  933. dwConvertMode) ) {
  934. dwLastError = GetLastError();
  935. if (dwLastError == ERROR_SUCCESS) {
  936. SPLASSERT(dwLastError != ERROR_SUCCESS);
  937. // if driver doesn't fail the above call, it is a broken driver and probably
  938. // failed a HeapAlloc, which doesn't SetLastError()
  939. SetLastError(dwLastError = ERROR_NOT_ENOUGH_MEMORY);
  940. }
  941. } else {
  942. dwLastError = StatusFromHResult(SplIsValidDevmodeW(*ppOutDevMode, *pdwOutDevModeSize));
  943. }
  944. #if DBG
  945. Cleanup:
  946. #else
  947. } except(1) {
  948. DBGMSG(DBG_ERROR,
  949. ("CallDrvDevModeConversion: Exception from driver\n"));
  950. dwLastError = GetExceptionCode();
  951. SetLastError(dwLastError);
  952. }
  953. #endif
  954. //
  955. // If we allocated mmeory free it and zero the pointer
  956. //
  957. if ( dwLastError != ERROR_SUCCESS && bAlloc && *ppOutDevMode ) {
  958. FreeSplMem(*ppOutDevMode);
  959. *ppOutDevMode = 0;
  960. *pdwOutDevModeSize = 0;
  961. }
  962. if ( dwLastError != ERROR_SUCCESS &&
  963. dwLastError != ERROR_INSUFFICIENT_BUFFER ) {
  964. DBGMSG(DBG_WARNING, ("DevmodeConvert unexpected error %d\n", dwLastError));
  965. }
  966. if ( dwLastError == ERROR_SUCCESS ) {
  967. dwSize = (*ppOutDevMode)->dmSize + (*ppOutDevMode)->dmDriverExtra;
  968. //
  969. // Did the driver return correct size as per the devmode?
  970. //
  971. if ( *pdwOutDevModeSize != dwSize ) {
  972. DBGMSG(DBG_ERROR,
  973. ("Driver says outsize is %d, really %d\n",
  974. *pdwOutDevModeSize, dwSize));
  975. *pdwOutDevModeSize = dwSize;
  976. }
  977. //
  978. // Is it a valid devmode which did not overwrite the buffer?
  979. //
  980. if ( *pdwOutDevModeSize < MIN_DEVMODE_SIZEW ||
  981. *pdwOutDevModeSize > dwBufSize ) {
  982. DBGMSG(DBG_ERROR,
  983. ("Bad devmode from the driver size %d, buffer %d",
  984. *pdwOutDevModeSize, dwBufSize));
  985. dwLastError = ERROR_INVALID_PARAMETER;
  986. if ( bAlloc ) {
  987. FreeSplMem(*ppOutDevMode);
  988. *ppOutDevMode = NULL;
  989. }
  990. *pdwOutDevModeSize = 0;
  991. }
  992. }
  993. FreeSplMem(pszDrvConvert);
  994. return dwLastError;
  995. }
  996. BOOL
  997. BuildOtherNamesFromMachineName(
  998. LPWSTR **ppszMyOtherNames,
  999. DWORD *cOtherNames
  1000. )
  1001. /*++
  1002. Routine Description:
  1003. This routine builds list of names other than the machine name that
  1004. can be used to call spooler APIs.
  1005. --*/
  1006. {
  1007. HANDLE hModule;
  1008. struct hostent *HostEnt, *(*Fngethostbyname)(LPTSTR);
  1009. struct in_addr *ptr;
  1010. INT (*FnWSAStartup)(WORD, LPWSADATA);
  1011. DWORD Index, Count;
  1012. WSADATA WSAData;
  1013. VOID (*FnWSACleanup)();
  1014. LPSTR (*Fninet_ntoa)(struct in_addr);
  1015. WORD wVersion;
  1016. BOOL bRet = FALSE;
  1017. DWORD dwRet;
  1018. SPLASSERT(cOtherNames && ppszMyOtherNames);
  1019. *cOtherNames = 0;
  1020. wVersion = MAKEWORD(1, 1);
  1021. dwRet = WSAStartup(wVersion, &WSAData);
  1022. if (dwRet) {
  1023. DBGMSG(DBG_WARNING, ("BuildOtherNamesFromMachineName: WSAStartup failed\n"));
  1024. SetLastError(dwRet);
  1025. return FALSE;
  1026. }
  1027. HostEnt = gethostbyname(NULL);
  1028. if (HostEnt) {
  1029. for (*cOtherNames = 0 ; HostEnt->h_addr_list[*cOtherNames] ; ++(*cOtherNames))
  1030. ;
  1031. *cOtherNames += 2; // Add one for DNS and one for machine name
  1032. *ppszMyOtherNames = (LPWSTR *) AllocSplMem(*cOtherNames*sizeof *ppszMyOtherNames);
  1033. if ( !*ppszMyOtherNames ) {
  1034. *cOtherNames = 0;
  1035. goto Cleanup;
  1036. }
  1037. (*ppszMyOtherNames)[0] = AllocSplStr(szMachineName + 2); // Exclude the leading double-backslash
  1038. (*ppszMyOtherNames)[1] = AnsiToUnicodeStringWithAlloc(HostEnt->h_name);
  1039. for (Index = 0 ; HostEnt->h_addr_list[Index] ; ++Index) {
  1040. ptr = (struct in_addr *) HostEnt->h_addr_list[Index];
  1041. (*ppszMyOtherNames)[Index+2] = AnsiToUnicodeStringWithAlloc(inet_ntoa(*ptr));
  1042. }
  1043. // check for allocation failures
  1044. for (Index = 0 ; Index < *cOtherNames ; ++Index) {
  1045. if ( !(*ppszMyOtherNames)[Index] ) {
  1046. FreeOtherNames(ppszMyOtherNames, cOtherNames);
  1047. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1048. goto Cleanup;
  1049. }
  1050. }
  1051. bRet = TRUE;
  1052. } else {
  1053. DBGMSG(DBG_WARNING, ("BuildOtherNamesFromMachineName: gethostbyname failed for with %d\n", GetLastError()));
  1054. }
  1055. Cleanup:
  1056. WSACleanup();
  1057. return bRet;
  1058. }
  1059. VOID
  1060. FreeOtherNames(LPWSTR **ppszMyOtherNames, DWORD *cOtherNames)
  1061. {
  1062. DWORD i;
  1063. for( i = 0 ; i < *cOtherNames ; ++i)
  1064. FreeSplMem((*ppszMyOtherNames)[i]);
  1065. FreeSplMem(*ppszMyOtherNames);
  1066. }
  1067. LPWSTR
  1068. AnsiToUnicodeStringWithAlloc(
  1069. LPSTR pAnsi
  1070. )
  1071. /*++
  1072. Description:
  1073. Convert ANSI string to UNICODE. Routine allocates memory from the heap
  1074. which should be freed by the caller.
  1075. Arguments:
  1076. pAnsi - Points to the ANSI string
  1077. Return Vlaue:
  1078. Pointer to UNICODE string
  1079. --*/
  1080. {
  1081. LPWSTR pUnicode;
  1082. DWORD rc;
  1083. rc = MultiByteToWideChar(CP_ACP,
  1084. MB_PRECOMPOSED,
  1085. pAnsi,
  1086. -1,
  1087. NULL,
  1088. 0);
  1089. rc *= sizeof(WCHAR);
  1090. if ( !rc || !(pUnicode = (LPWSTR) AllocSplMem(rc)) )
  1091. return NULL;
  1092. rc = MultiByteToWideChar(CP_ACP,
  1093. MB_PRECOMPOSED,
  1094. pAnsi,
  1095. -1,
  1096. pUnicode,
  1097. rc);
  1098. if ( rc )
  1099. return pUnicode;
  1100. else {
  1101. FreeSplMem(pUnicode);
  1102. return NULL;
  1103. }
  1104. }
  1105. /*++
  1106. Routine Description
  1107. Determines whether or not a machine name contains the local machine name.
  1108. Localspl enum calls fail if pName != local machine name (\\Machine).
  1109. Remote enum provider is then called. The remote enum provider must check
  1110. if the UNC name refers to the local machine, and fail if it does to avoid
  1111. endless recursion.
  1112. Arguments:
  1113. LPWSTR pName - UNC name.
  1114. Return Value:
  1115. TRUE: pName == \\szMachineName\...
  1116. - or -
  1117. pName == \\szMachineName
  1118. FALSE: anything else
  1119. Author: swilson
  1120. --*/
  1121. BOOL
  1122. MyUNCName(
  1123. LPWSTR pNameStart
  1124. )
  1125. {
  1126. PWCHAR pMachine = szMachineName;
  1127. LPWSTR pName;
  1128. DWORD i;
  1129. extern LPWSTR *ppszOtherNames; // Contains szMachineName, DNS name, and all other machine name forms
  1130. extern DWORD cOtherNames;
  1131. if (!pNameStart || !*pNameStart) // This differs from MyName(), which returns TRUE
  1132. return FALSE;
  1133. if (*pNameStart == L'\\' && *(pNameStart + 1) == L'\\') {
  1134. for (i = 0 , pName = pNameStart + 2 ; i < cOtherNames ; ++i , pName = pNameStart + 2) {
  1135. for(pMachine = ppszOtherNames[i] ;
  1136. *pName && towupper(*pName) == towupper(*pMachine) ;
  1137. ++pName, ++pMachine)
  1138. ;
  1139. if(!*pMachine && (!*pName || *pName == L'\\'))
  1140. return TRUE;
  1141. }
  1142. }
  1143. return FALSE;
  1144. }
  1145. BOOL
  1146. SplIsUpgrade(
  1147. VOID
  1148. )
  1149. {
  1150. return dwUpgradeFlag;
  1151. }
  1152. PWSTR
  1153. AutoCat(
  1154. PCWSTR pszInput,
  1155. PCWSTR pszCat
  1156. )
  1157. {
  1158. PWSTR pszOut;
  1159. if (!pszCat) {
  1160. pszOut = AllocSplStr(pszInput);
  1161. } else if (pszInput) {
  1162. DWORD cchOutStr = wcslen(pszInput) + wcslen(pszCat) + 1;
  1163. pszOut = AllocSplMem(cchOutStr * sizeof(WCHAR));
  1164. if (pszOut)
  1165. {
  1166. StrNCatBuff(pszOut,
  1167. cchOutStr,
  1168. pszInput,
  1169. pszCat,
  1170. NULL);
  1171. }
  1172. } else {
  1173. pszOut = AllocSplStr(pszCat);
  1174. }
  1175. return pszOut;
  1176. }
  1177. PBIDI_RESPONSE_CONTAINER
  1178. RouterAllocBidiResponseContainer(
  1179. DWORD Count
  1180. )
  1181. {
  1182. DWORD MemSize = 0;
  1183. //
  1184. // Add the size of the container - the size of the first data element
  1185. //
  1186. MemSize += (sizeof(BIDI_RESPONSE_CONTAINER) - sizeof(BIDI_RESPONSE_DATA));
  1187. //
  1188. // Add the size of all the returned RESPONSE_DATA elements
  1189. //
  1190. MemSize += (Count * sizeof(BIDI_RESPONSE_DATA));
  1191. return((PBIDI_RESPONSE_CONTAINER) MIDL_user_allocate(MemSize));
  1192. }
  1193. /*++
  1194. Routine Name
  1195. GetAPDPolicy
  1196. Routine Description:
  1197. This function reads a DWORD value from the location
  1198. HKEY\pszRelPath\pszValueName. We use this function for
  1199. preserving the AddPrinterDrivers policy value when the
  1200. LanMan Print Services print provider is deleted from
  1201. the system.
  1202. Arguments:
  1203. hKey - key tree
  1204. pszRelPath - relative path of the value to be get
  1205. pszValueName - value name
  1206. pValue - pointer to memory to store a dword value
  1207. Return Value:
  1208. ERROR_SUCCESS - the value was retrieved
  1209. Win32 error code - an error occured
  1210. --*/
  1211. DWORD
  1212. GetAPDPolicy(
  1213. IN HKEY hKey,
  1214. IN LPCWSTR pszRelPath,
  1215. IN LPCWSTR pszValueName,
  1216. IN LPDWORD pValue
  1217. )
  1218. {
  1219. DWORD Error = ERROR_INVALID_PARAMETER;
  1220. if (hKey && pszRelPath && pszValueName && pValue)
  1221. {
  1222. HKEY hRelKey = NULL;
  1223. *pValue = 0;
  1224. //
  1225. // Check if we have a value in the new location already
  1226. //
  1227. if ((Error = RegOpenKeyEx(hKey,
  1228. pszRelPath,
  1229. 0,
  1230. KEY_READ,
  1231. &hRelKey)) == ERROR_SUCCESS)
  1232. {
  1233. DWORD cbData = sizeof(DWORD);
  1234. Error = RegQueryValueEx(hRelKey,
  1235. pszValueName,
  1236. NULL,
  1237. NULL,
  1238. (LPBYTE)pValue,
  1239. &cbData);
  1240. RegCloseKey(hRelKey);
  1241. }
  1242. }
  1243. return Error;
  1244. }
  1245. /*++
  1246. Routine Name
  1247. SetAPDPolicy
  1248. Routine Description:
  1249. This function writes a DWORD value to the location
  1250. HKEY\pszRelPath\pszValueName. We use this function for
  1251. preserving the AddPrinterDrivers policy value when the
  1252. LanMan Print Services print provider is deleted from
  1253. the system.
  1254. Arguments:
  1255. hKey - key tree
  1256. pszRelPath - relative path of the value to be set
  1257. pszValueName - value name to be set
  1258. Value - dword value to be set
  1259. Return Value:
  1260. ERROR_SUCCESS - the value was set sucessfully
  1261. Win32 error code - an error occured and the value was not set
  1262. --*/
  1263. DWORD
  1264. SetAPDPolicy(
  1265. IN HKEY hKey,
  1266. IN LPCWSTR pszRelPath,
  1267. IN LPCWSTR pszValueName,
  1268. IN DWORD Value
  1269. )
  1270. {
  1271. DWORD Error = ERROR_INVALID_PARAMETER;
  1272. if (hKey && pszRelPath && pszValueName)
  1273. {
  1274. HKEY hRelKey = NULL;
  1275. //
  1276. // Check if we have a value in the new location already
  1277. //
  1278. if ((Error = RegCreateKeyEx(hKey,
  1279. pszRelPath,
  1280. 0,
  1281. NULL,
  1282. 0,
  1283. KEY_SET_VALUE,
  1284. NULL,
  1285. &hRelKey,
  1286. NULL)) == ERROR_SUCCESS)
  1287. {
  1288. Error = RegSetValueEx(hRelKey,
  1289. pszValueName,
  1290. 0,
  1291. REG_DWORD,
  1292. (LPBYTE)&Value,
  1293. sizeof(DWORD));
  1294. RegCloseKey(hRelKey);
  1295. }
  1296. }
  1297. return Error;
  1298. }