tclWinReg.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:41k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclWinReg.c --
  3.  *
  4.  * This file contains the implementation of the "registry" Tcl
  5.  * built-in command.  This command is built as a dynamically
  6.  * loadable extension in a separate DLL.
  7.  *
  8.  * Copyright (c) 1997 by Sun Microsystems, Inc.
  9.  * Copyright (c) 1998-1999 by Scriptics Corporation.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tclWinReg.c,v 1.21.2.7 2007/05/15 16:08:22 dgp Exp $
  15.  */
  16. #include <tclPort.h>
  17. #include <stdlib.h>
  18. /*
  19.  * TCL_STORAGE_CLASS is set unconditionally to DLLEXPORT because the
  20.  * Registry_Init declaration is in the source file itself, which is only
  21.  * accessed when we are building a library.
  22.  */
  23. #undef TCL_STORAGE_CLASS
  24. #define TCL_STORAGE_CLASS DLLEXPORT
  25. /*
  26.  * The following macros convert between different endian ints.
  27.  */
  28. #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
  29. #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
  30. /*
  31.  * The following flag is used in OpenKeys to indicate that the specified
  32.  * key should be created if it doesn't currently exist.
  33.  */
  34. #define REG_CREATE 1
  35. /*
  36.  * The following tables contain the mapping from registry root names
  37.  * to the system predefined keys.
  38.  */
  39. static CONST char *rootKeyNames[] = {
  40.     "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
  41.     "HKEY_CURRENT_USER", "HKEY_CURRENT_CONFIG",
  42.     "HKEY_PERFORMANCE_DATA", "HKEY_DYN_DATA", NULL
  43. };
  44. static HKEY rootKeys[] = {
  45.     HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT, HKEY_CURRENT_USER,
  46.     HKEY_CURRENT_CONFIG, HKEY_PERFORMANCE_DATA, HKEY_DYN_DATA
  47. };
  48. /*
  49.  * The following table maps from registry types to strings.  Note that
  50.  * the indices for this array are the same as the constants for the
  51.  * known registry types so we don't need a separate table to hold the
  52.  * mapping.
  53.  */
  54. static CONST char *typeNames[] = {
  55.     "none", "sz", "expand_sz", "binary", "dword",
  56.     "dword_big_endian", "link", "multi_sz", "resource_list", NULL
  57. };
  58. static DWORD lastType = REG_RESOURCE_LIST;
  59. /*
  60.  * The following structures allow us to select between the Unicode and ASCII
  61.  * interfaces at run time based on whether Unicode APIs are available.  The
  62.  * Unicode APIs are preferable because they will handle characters outside
  63.  * of the current code page.
  64.  */
  65. typedef struct RegWinProcs {
  66.     int useWide;
  67.     LONG (WINAPI *regConnectRegistryProc)(CONST TCHAR *, HKEY, PHKEY);
  68.     LONG (WINAPI *regCreateKeyExProc)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
  69.     DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *, DWORD *); 
  70.     LONG (WINAPI *regDeleteKeyProc)(HKEY, CONST TCHAR *);
  71.     LONG (WINAPI *regDeleteValueProc)(HKEY, CONST TCHAR *);
  72.     LONG (WINAPI *regEnumKeyProc)(HKEY, DWORD, TCHAR *, DWORD);
  73.     LONG (WINAPI *regEnumKeyExProc)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
  74.     TCHAR *, DWORD *, FILETIME *);
  75.     LONG (WINAPI *regEnumValueProc)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
  76.     DWORD *, BYTE *, DWORD *);
  77.     LONG (WINAPI *regOpenKeyExProc)(HKEY, CONST TCHAR *, DWORD, REGSAM,
  78.     HKEY *);
  79.     LONG (WINAPI *regQueryInfoKeyProc)(HKEY, TCHAR *, DWORD *, DWORD *,
  80.     DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
  81.     FILETIME *);
  82.     LONG (WINAPI *regQueryValueExProc)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
  83.     BYTE *, DWORD *);
  84.     LONG (WINAPI *regSetValueExProc)(HKEY, CONST TCHAR *, DWORD, DWORD,
  85.     CONST BYTE*, DWORD);
  86. } RegWinProcs;
  87. static RegWinProcs *regWinProcs;
  88. static RegWinProcs asciiProcs = {
  89.     0,
  90.     (LONG (WINAPI *)(CONST TCHAR *, HKEY, PHKEY)) RegConnectRegistryA,
  91.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
  92.     DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *,
  93.     DWORD *)) RegCreateKeyExA, 
  94.     (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteKeyA,
  95.     (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteValueA,
  96.     (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD)) RegEnumKeyA,
  97.     (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
  98.     TCHAR *, DWORD *, FILETIME *)) RegEnumKeyExA,
  99.     (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
  100.     DWORD *, BYTE *, DWORD *)) RegEnumValueA,
  101.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, REGSAM,
  102.     HKEY *)) RegOpenKeyExA,
  103.     (LONG (WINAPI *)(HKEY, TCHAR *, DWORD *, DWORD *,
  104.     DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
  105.     FILETIME *)) RegQueryInfoKeyA,
  106.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
  107.     BYTE *, DWORD *)) RegQueryValueExA,
  108.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, DWORD,
  109.     CONST BYTE*, DWORD)) RegSetValueExA,
  110. };
  111. static RegWinProcs unicodeProcs = {
  112.     1,
  113.     (LONG (WINAPI *)(CONST TCHAR *, HKEY, PHKEY)) RegConnectRegistryW,
  114.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
  115.     DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *,
  116.     DWORD *)) RegCreateKeyExW, 
  117.     (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteKeyW,
  118.     (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteValueW,
  119.     (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD)) RegEnumKeyW,
  120.     (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
  121.     TCHAR *, DWORD *, FILETIME *)) RegEnumKeyExW,
  122.     (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
  123.     DWORD *, BYTE *, DWORD *)) RegEnumValueW,
  124.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, REGSAM,
  125.     HKEY *)) RegOpenKeyExW,
  126.     (LONG (WINAPI *)(HKEY, TCHAR *, DWORD *, DWORD *,
  127.     DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
  128.     FILETIME *)) RegQueryInfoKeyW,
  129.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
  130.     BYTE *, DWORD *)) RegQueryValueExW,
  131.     (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, DWORD,
  132.     CONST BYTE*, DWORD)) RegSetValueExW,
  133. };
  134. /*
  135.  * Declarations for functions defined in this file.
  136.  */
  137. static void AppendSystemError(Tcl_Interp *interp, DWORD error);
  138. static int BroadcastValue(Tcl_Interp *interp, int objc,
  139.     Tcl_Obj * CONST objv[]);
  140. static DWORD ConvertDWORD(DWORD type, DWORD value);
  141. static int DeleteKey(Tcl_Interp *interp, Tcl_Obj *keyNameObj);
  142. static int DeleteValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  143.     Tcl_Obj *valueNameObj);
  144. static int GetKeyNames(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  145.     Tcl_Obj *patternObj);
  146. static int GetType(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  147.     Tcl_Obj *valueNameObj);
  148. static int GetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  149.     Tcl_Obj *valueNameObj);
  150. static int GetValueNames(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  151.     Tcl_Obj *patternObj);
  152. static int OpenKey(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  153.     REGSAM mode, int flags, HKEY *keyPtr);
  154. static DWORD OpenSubKey(char *hostName, HKEY rootKey,
  155.     char *keyName, REGSAM mode, int flags,
  156.     HKEY *keyPtr);
  157. static int ParseKeyName(Tcl_Interp *interp, char *name,
  158.     char **hostNamePtr, HKEY *rootKeyPtr,
  159.     char **keyNamePtr);
  160. static DWORD RecursiveDeleteKey(HKEY hStartKey,
  161.     CONST TCHAR * pKeyName);
  162. static int RegistryObjCmd(ClientData clientData,
  163.     Tcl_Interp *interp, int objc,
  164.     Tcl_Obj * CONST objv[]);
  165. static int SetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
  166.     Tcl_Obj *valueNameObj, Tcl_Obj *dataObj,
  167.     Tcl_Obj *typeObj);
  168. EXTERN int Registry_Init(Tcl_Interp *interp);
  169. /*
  170.  *----------------------------------------------------------------------
  171.  *
  172.  * Registry_Init --
  173.  *
  174.  * This procedure initializes the registry command.
  175.  *
  176.  * Results:
  177.  * A standard Tcl result.
  178.  *
  179.  * Side effects:
  180.  * None.
  181.  *
  182.  *----------------------------------------------------------------------
  183.  */
  184. int
  185. Registry_Init(
  186.     Tcl_Interp *interp)
  187. {
  188.     if (!Tcl_InitStubs(interp, "8.0", 0)) {
  189. return TCL_ERROR;
  190.     }
  191.     /*
  192.      * Determine if the unicode interfaces are available and select the
  193.      * appropriate registry function table.
  194.      */
  195.     if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
  196. regWinProcs = &unicodeProcs;
  197.     } else {
  198. regWinProcs = &asciiProcs;
  199.     }
  200.     Tcl_CreateObjCommand(interp, "registry", RegistryObjCmd, NULL, NULL);
  201.     return Tcl_PkgProvide(interp, "registry", "1.1.5");
  202. }
  203. /*
  204.  *----------------------------------------------------------------------
  205.  *
  206.  * RegistryObjCmd --
  207.  *
  208.  * This function implements the Tcl "registry" command.
  209.  *
  210.  * Results:
  211.  * A standard Tcl result.
  212.  *
  213.  * Side effects:
  214.  * None.
  215.  *
  216.  *----------------------------------------------------------------------
  217.  */
  218. static int
  219. RegistryObjCmd(
  220.     ClientData clientData, /* Not used. */
  221.     Tcl_Interp *interp, /* Current interpreter. */
  222.     int objc, /* Number of arguments. */
  223.     Tcl_Obj * CONST objv[]) /* Argument values. */
  224. {
  225.     int index;
  226.     char *errString;
  227.     static CONST char *subcommands[] = {
  228. "broadcast", "delete", "get", "keys", "set", "type", "values",
  229. (char *) NULL
  230.     };
  231.     enum SubCmdIdx {
  232. BroadcastIdx, DeleteIdx, GetIdx, KeysIdx, SetIdx, TypeIdx, ValuesIdx
  233.     };
  234.     if (objc < 2) {
  235. Tcl_WrongNumArgs(interp, objc, objv, "option ?arg arg ...?");
  236. return TCL_ERROR;
  237.     }
  238.     if (Tcl_GetIndexFromObj(interp, objv[1], subcommands, "option", 0, &index)
  239.     != TCL_OK) {
  240. return TCL_ERROR;
  241.     }
  242.     switch (index) {
  243. case BroadcastIdx: /* broadcast */
  244.     return BroadcastValue(interp, objc, objv);
  245.     break;
  246. case DeleteIdx: /* delete */
  247.     if (objc == 3) {
  248. return DeleteKey(interp, objv[2]);
  249.     } else if (objc == 4) {
  250. return DeleteValue(interp, objv[2], objv[3]);
  251.     }
  252.     errString = "keyName ?valueName?";
  253.     break;
  254. case GetIdx: /* get */
  255.     if (objc == 4) {
  256. return GetValue(interp, objv[2], objv[3]);
  257.     }
  258.     errString = "keyName valueName";
  259.     break;
  260. case KeysIdx: /* keys */
  261.     if (objc == 3) {
  262. return GetKeyNames(interp, objv[2], NULL);
  263.     } else if (objc == 4) {
  264. return GetKeyNames(interp, objv[2], objv[3]);
  265.     }
  266.     errString = "keyName ?pattern?";
  267.     break;
  268. case SetIdx: /* set */
  269.     if (objc == 3) {
  270. HKEY key;
  271. /*
  272.  * Create the key and then close it immediately.
  273.  */
  274. if (OpenKey(interp, objv[2], KEY_ALL_ACCESS, 1, &key)
  275. != TCL_OK) {
  276.     return TCL_ERROR;
  277. }
  278. RegCloseKey(key);
  279. return TCL_OK;
  280.     } else if (objc == 5 || objc == 6) {
  281. Tcl_Obj *typeObj = (objc == 5) ? NULL : objv[5];
  282. return SetValue(interp, objv[2], objv[3], objv[4], typeObj);
  283.     }
  284.     errString = "keyName ?valueName data ?type??";
  285.     break;
  286. case TypeIdx: /* type */
  287.     if (objc == 4) {
  288. return GetType(interp, objv[2], objv[3]);
  289.     }
  290.     errString = "keyName valueName";
  291.     break;
  292. case ValuesIdx: /* values */
  293.     if (objc == 3) {
  294.   return GetValueNames(interp, objv[2], NULL);
  295.     } else if (objc == 4) {
  296.   return GetValueNames(interp, objv[2], objv[3]);
  297.     }
  298.     errString = "keyName ?pattern?";
  299.     break;
  300.     }
  301.     Tcl_WrongNumArgs(interp, 2, objv, errString);
  302.     return TCL_ERROR;
  303. }
  304. /*
  305.  *----------------------------------------------------------------------
  306.  *
  307.  * DeleteKey --
  308.  *
  309.  * This function deletes a registry key.
  310.  *
  311.  * Results:
  312.  * A standard Tcl result.
  313.  *
  314.  * Side effects:
  315.  * None.
  316.  *
  317.  *----------------------------------------------------------------------
  318.  */
  319. static int
  320. DeleteKey(
  321.     Tcl_Interp *interp, /* Current interpreter. */
  322.     Tcl_Obj *keyNameObj) /* Name of key to delete. */
  323. {
  324.     char *tail, *buffer, *hostName, *keyName;
  325.     CONST char *nativeTail;
  326.     HKEY rootKey, subkey;
  327.     DWORD result;
  328.     int length;
  329.     Tcl_Obj *resultPtr;
  330.     Tcl_DString buf;
  331.     /*
  332.      * Find the parent of the key being deleted and open it.
  333.      */
  334.     keyName = Tcl_GetStringFromObj(keyNameObj, &length);
  335.     buffer = ckalloc((unsigned int) length + 1);
  336.     strcpy(buffer, keyName);
  337.     if (ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName)
  338.     != TCL_OK) {
  339. ckfree(buffer);
  340. return TCL_ERROR;
  341.     }
  342.     resultPtr = Tcl_GetObjResult(interp);
  343.     if (*keyName == '') {
  344. Tcl_AppendToObj(resultPtr, "bad key: cannot delete root keys", -1);
  345. ckfree(buffer);
  346. return TCL_ERROR;
  347.     }
  348.     tail = strrchr(keyName, '\');
  349.     if (tail) {
  350. *tail++ = '';
  351.     } else {
  352. tail = keyName;
  353. keyName = NULL;
  354.     }
  355.     result = OpenSubKey(hostName, rootKey, keyName,
  356.     KEY_ENUMERATE_SUB_KEYS | DELETE, 0, &subkey);
  357.     if (result != ERROR_SUCCESS) {
  358. ckfree(buffer);
  359. if (result == ERROR_FILE_NOT_FOUND) {
  360.     return TCL_OK;
  361. } else {
  362.     Tcl_AppendToObj(resultPtr, "unable to delete key: ", -1);
  363.     AppendSystemError(interp, result);
  364.     return TCL_ERROR;
  365. }
  366.     }
  367.     /*
  368.      * Now we recursively delete the key and everything below it.
  369.      */
  370.     nativeTail = Tcl_WinUtfToTChar(tail, -1, &buf);
  371.     result = RecursiveDeleteKey(subkey, nativeTail);
  372.     Tcl_DStringFree(&buf);
  373.     if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
  374. Tcl_AppendToObj(resultPtr, "unable to delete key: ", -1);
  375. AppendSystemError(interp, result);
  376. result = TCL_ERROR;
  377.     } else {
  378. result = TCL_OK;
  379.     }
  380.     RegCloseKey(subkey);
  381.     ckfree(buffer);
  382.     return result;
  383. }
  384. /*
  385.  *----------------------------------------------------------------------
  386.  *
  387.  * DeleteValue --
  388.  *
  389.  * This function deletes a value from a registry key.
  390.  *
  391.  * Results:
  392.  * A standard Tcl result.
  393.  *
  394.  * Side effects:
  395.  * None.
  396.  *
  397.  *----------------------------------------------------------------------
  398.  */
  399. static int
  400. DeleteValue(
  401.     Tcl_Interp *interp, /* Current interpreter. */
  402.     Tcl_Obj *keyNameObj, /* Name of key. */
  403.     Tcl_Obj *valueNameObj) /* Name of value to delete. */
  404. {
  405.     HKEY key;
  406.     char *valueName;
  407.     int length;
  408.     DWORD result;
  409.     Tcl_Obj *resultPtr;
  410.     Tcl_DString ds;
  411.     /*
  412.      * Attempt to open the key for deletion.
  413.      */
  414.     if (OpenKey(interp, keyNameObj, KEY_SET_VALUE, 0, &key)
  415.     != TCL_OK) {
  416. return TCL_ERROR;
  417.     }
  418.     resultPtr = Tcl_GetObjResult(interp);
  419.     valueName = Tcl_GetStringFromObj(valueNameObj, &length);
  420.     Tcl_WinUtfToTChar(valueName, length, &ds);
  421.     result = (*regWinProcs->regDeleteValueProc)(key, Tcl_DStringValue(&ds));
  422.     Tcl_DStringFree(&ds);
  423.     if (result != ERROR_SUCCESS) {
  424. Tcl_AppendStringsToObj(resultPtr, "unable to delete value "",
  425. Tcl_GetString(valueNameObj), "" from key "",
  426. Tcl_GetString(keyNameObj), "": ", NULL);
  427. AppendSystemError(interp, result);
  428. result = TCL_ERROR;
  429.     } else {
  430. result = TCL_OK;
  431.     }
  432.     RegCloseKey(key);
  433.     return result;
  434. }
  435. /*
  436.  *----------------------------------------------------------------------
  437.  *
  438.  * GetKeyNames --
  439.  *
  440.  * This function enumerates the subkeys of a given key.  If the
  441.  * optional pattern is supplied, then only keys that match the
  442.  * pattern will be returned.
  443.  *
  444.  * Results:
  445.  * Returns the list of subkeys in the result object of the
  446.  * interpreter, or an error message on failure.
  447.  *
  448.  * Side effects:
  449.  * None.
  450.  *
  451.  *----------------------------------------------------------------------
  452.  */
  453. static int
  454. GetKeyNames(
  455.     Tcl_Interp *interp, /* Current interpreter. */
  456.     Tcl_Obj *keyNameObj, /* Key to enumerate. */
  457.     Tcl_Obj *patternObj) /* Optional match pattern. */
  458. {
  459.     char *pattern; /* Pattern being matched against subkeys */
  460.     HKEY key; /* Handle to the key being examined */
  461.     DWORD subKeyCount; /* Number of subkeys to list */
  462.     DWORD maxSubKeyLen; /* Maximum string length of any subkey */
  463.     char *buffer; /* Buffer to hold the subkey name */
  464.     DWORD bufSize; /* Size of the buffer */
  465.     DWORD index; /* Position of the current subkey */
  466.     char *name; /* Subkey name */
  467.     Tcl_Obj *resultPtr; /* List of subkeys being accumulated */
  468.     int result = TCL_OK; /* Return value from this command */
  469.     Tcl_DString ds; /* Buffer to translate subkey name to UTF-8 */
  470.     if (patternObj) {
  471. pattern = Tcl_GetString(patternObj);
  472.     } else {
  473. pattern = NULL;
  474.     }
  475.     /* Attempt to open the key for enumeration. */
  476.     if (OpenKey(interp, keyNameObj,
  477. KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  478. 0, &key) != TCL_OK) {
  479. return TCL_ERROR;
  480.     }
  481.     /* 
  482.      * Determine how big a buffer is needed for enumerating subkeys, and
  483.      * how many subkeys there are
  484.      */
  485.     result = (*regWinProcs->regQueryInfoKeyProc)
  486. (key, NULL, NULL, NULL, &subKeyCount, &maxSubKeyLen, NULL, NULL, 
  487.  NULL, NULL, NULL, NULL);
  488.     if (result != ERROR_SUCCESS) {
  489. Tcl_SetObjResult(interp, Tcl_NewObj());
  490. Tcl_AppendResult(interp, "unable to query key "", 
  491.  Tcl_GetString(keyNameObj), "": ", NULL);
  492. AppendSystemError(interp, result);
  493. RegCloseKey(key);
  494. return TCL_ERROR;
  495.     }
  496.     if (regWinProcs->useWide) {
  497. buffer = ckalloc((maxSubKeyLen+1) * sizeof(WCHAR));
  498.     } else {
  499. buffer = ckalloc(maxSubKeyLen+1);
  500.     }
  501.     /* Enumerate the subkeys */
  502.     resultPtr = Tcl_NewObj();
  503.     for (index = 0; index < subKeyCount; ++index) {
  504. bufSize = maxSubKeyLen+1;
  505. result = (*regWinProcs->regEnumKeyExProc)
  506.     (key, index, buffer, &bufSize, NULL, NULL, NULL, NULL);
  507. if (result != ERROR_SUCCESS) {
  508.     Tcl_SetObjResult(interp, Tcl_NewObj());
  509.     Tcl_AppendResult(interp,
  510.      "unable to enumerate subkeys of "",
  511.      Tcl_GetString(keyNameObj),
  512.      "": ", NULL);
  513.     AppendSystemError(interp, result);
  514.     result = TCL_ERROR;
  515.     break;
  516. }
  517. if (regWinProcs->useWide) {
  518.     Tcl_WinTCharToUtf((TCHAR *) buffer, bufSize * sizeof(WCHAR), &ds);
  519. } else {
  520.     Tcl_WinTCharToUtf((TCHAR *) buffer, bufSize, &ds);
  521. }
  522. name = Tcl_DStringValue(&ds);
  523. if (pattern && !Tcl_StringMatch(name, pattern)) {
  524.     Tcl_DStringFree(&ds);
  525.     continue;
  526. }
  527. result = Tcl_ListObjAppendElement(interp, resultPtr,
  528. Tcl_NewStringObj(name, Tcl_DStringLength(&ds)));
  529. Tcl_DStringFree(&ds);
  530. if (result != TCL_OK) {
  531.     break;
  532. }
  533.     }
  534.     if (result == TCL_OK) {
  535. Tcl_SetObjResult(interp, resultPtr);
  536.     }
  537.     ckfree(buffer);
  538.     RegCloseKey(key);
  539.     return result;
  540. }
  541. /*
  542.  *----------------------------------------------------------------------
  543.  *
  544.  * GetType --
  545.  *
  546.  * This function gets the type of a given registry value and
  547.  * places it in the interpreter result.
  548.  *
  549.  * Results:
  550.  * Returns a normal Tcl result.
  551.  *
  552.  * Side effects:
  553.  * None.
  554.  *
  555.  *----------------------------------------------------------------------
  556.  */
  557. static int
  558. GetType(
  559.     Tcl_Interp *interp, /* Current interpreter. */
  560.     Tcl_Obj *keyNameObj, /* Name of key. */
  561.     Tcl_Obj *valueNameObj) /* Name of value to get. */
  562. {
  563.     HKEY key;
  564.     Tcl_Obj *resultPtr;
  565.     DWORD result;
  566.     DWORD type;
  567.     Tcl_DString ds;
  568.     char *valueName;
  569.     CONST char *nativeValue;
  570.     int length;
  571.     /*
  572.      * Attempt to open the key for reading.
  573.      */
  574.     if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
  575.     != TCL_OK) {
  576. return TCL_ERROR;
  577.     }
  578.     /*
  579.      * Get the type of the value.
  580.      */
  581.     resultPtr = Tcl_GetObjResult(interp);
  582.     valueName = Tcl_GetStringFromObj(valueNameObj, &length);
  583.     nativeValue = Tcl_WinUtfToTChar(valueName, length, &ds);
  584.     result = (*regWinProcs->regQueryValueExProc)(key, nativeValue, NULL, &type,
  585.     NULL, NULL);
  586.     Tcl_DStringFree(&ds);
  587.     RegCloseKey(key);
  588.     if (result != ERROR_SUCCESS) {
  589. Tcl_AppendStringsToObj(resultPtr, "unable to get type of value "",
  590. Tcl_GetString(valueNameObj), "" from key "",
  591. Tcl_GetString(keyNameObj), "": ", NULL);
  592. AppendSystemError(interp, result);
  593. return TCL_ERROR;
  594.     }
  595.     /*
  596.      * Set the type into the result.  Watch out for unknown types.
  597.      * If we don't know about the type, just use the numeric value.
  598.      */
  599.     if (type > lastType || type < 0) {
  600. Tcl_SetIntObj(resultPtr, (int) type);
  601.     } else {
  602. Tcl_SetStringObj(resultPtr, typeNames[type], -1);
  603.     }
  604.     return TCL_OK;
  605. }
  606. /*
  607.  *----------------------------------------------------------------------
  608.  *
  609.  * GetValue --
  610.  *
  611.  * This function gets the contents of a registry value and places
  612.  * a list containing the data and the type in the interpreter
  613.  * result.
  614.  *
  615.  * Results:
  616.  * Returns a normal Tcl result.
  617.  *
  618.  * Side effects:
  619.  * None.
  620.  *
  621.  *----------------------------------------------------------------------
  622.  */
  623. static int
  624. GetValue(
  625.     Tcl_Interp *interp, /* Current interpreter. */
  626.     Tcl_Obj *keyNameObj, /* Name of key. */
  627.     Tcl_Obj *valueNameObj) /* Name of value to get. */
  628. {
  629.     HKEY key;
  630.     char *valueName;
  631.     CONST char *nativeValue;
  632.     DWORD result, length, type;
  633.     Tcl_Obj *resultPtr;
  634.     Tcl_DString data, buf;
  635.     int nameLen;
  636.     /*
  637.      * Attempt to open the key for reading.
  638.      */
  639.     if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
  640.     != TCL_OK) {
  641. return TCL_ERROR;
  642.     }
  643.     /*
  644.      * Initialize a Dstring to maximum statically allocated size
  645.      * we could get one more byte by avoiding Tcl_DStringSetLength()
  646.      * and just setting length to TCL_DSTRING_STATIC_SIZE, but this
  647.      * should be safer if the implementation of Dstrings changes.
  648.      *
  649.      * This allows short values to be read from the registy in one call.
  650.      * Longer values need a second call with an expanded DString.
  651.      */
  652.     Tcl_DStringInit(&data);
  653.     length = TCL_DSTRING_STATIC_SIZE - 1;
  654.     Tcl_DStringSetLength(&data, (int) length);
  655.     resultPtr = Tcl_GetObjResult(interp);
  656.     valueName = Tcl_GetStringFromObj(valueNameObj, &nameLen);
  657.     nativeValue = Tcl_WinUtfToTChar(valueName, nameLen, &buf);
  658.     result = (*regWinProcs->regQueryValueExProc)(key, nativeValue, NULL, &type,
  659.     (BYTE *) Tcl_DStringValue(&data), &length);
  660.     while (result == ERROR_MORE_DATA) {
  661. /*
  662.  * The Windows docs say that in this error case, we just need
  663.  * to expand our buffer and request more data.
  664.  * Required for HKEY_PERFORMANCE_DATA
  665.  */
  666. length *= 2;
  667.         Tcl_DStringSetLength(&data, (int) length);
  668.         result = (*regWinProcs->regQueryValueExProc)(key, (char *) nativeValue,
  669. NULL, &type, (BYTE *) Tcl_DStringValue(&data), &length);
  670.     }
  671.     Tcl_DStringFree(&buf);
  672.     RegCloseKey(key);
  673.     if (result != ERROR_SUCCESS) {
  674. Tcl_AppendStringsToObj(resultPtr, "unable to get value "",
  675. Tcl_GetString(valueNameObj), "" from key "",
  676. Tcl_GetString(keyNameObj), "": ", NULL);
  677. AppendSystemError(interp, result);
  678. Tcl_DStringFree(&data);
  679. return TCL_ERROR;
  680.     }
  681.     /*
  682.      * If the data is a 32-bit quantity, store it as an integer object.  If it
  683.      * is a multi-string, store it as a list of strings.  For null-terminated
  684.      * strings, append up the to first null.  Otherwise, store it as a binary
  685.      * string.
  686.      */
  687.     if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
  688. Tcl_SetIntObj(resultPtr, (int) ConvertDWORD(type,
  689. *((DWORD*) Tcl_DStringValue(&data))));
  690.     } else if (type == REG_MULTI_SZ) {
  691. char *p = Tcl_DStringValue(&data);
  692. char *end = Tcl_DStringValue(&data) + length;
  693. /*
  694.  * Multistrings are stored as an array of null-terminated strings,
  695.  * terminated by two null characters.  Also do a bounds check in
  696.  * case we get bogus data.
  697.  */
  698.  
  699. while (p < end  && ((regWinProcs->useWide) 
  700. ? *((Tcl_UniChar *)p) : *p) != 0) {
  701.     Tcl_WinTCharToUtf((TCHAR *) p, -1, &buf);
  702.     Tcl_ListObjAppendElement(interp, resultPtr,
  703.     Tcl_NewStringObj(Tcl_DStringValue(&buf),
  704.     Tcl_DStringLength(&buf)));
  705.     if (regWinProcs->useWide) {
  706. while (*((Tcl_UniChar *)p)++ != 0) {}
  707.     } else {
  708. while (*p++ != '') {}
  709.     }
  710.     Tcl_DStringFree(&buf);
  711. }
  712.     } else if ((type == REG_SZ) || (type == REG_EXPAND_SZ)) {
  713. Tcl_WinTCharToUtf((TCHAR *) Tcl_DStringValue(&data), -1, &buf);
  714. Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&buf),
  715. Tcl_DStringLength(&buf));
  716. Tcl_DStringFree(&buf);
  717.     } else {
  718. /*
  719.  * Save binary data as a byte array.
  720.  */
  721. Tcl_SetByteArrayObj(resultPtr, Tcl_DStringValue(&data), (int) length);
  722.     }
  723.     Tcl_DStringFree(&data);
  724.     return result;
  725. }
  726. /*
  727.  *----------------------------------------------------------------------
  728.  *
  729.  * GetValueNames --
  730.  *
  731.  * This function enumerates the values of the a given key.  If
  732.  * the optional pattern is supplied, then only value names that
  733.  * match the pattern will be returned.
  734.  *
  735.  * Results:
  736.  * Returns the list of value names in the result object of the
  737.  * interpreter, or an error message on failure.
  738.  *
  739.  * Side effects:
  740.  * None.
  741.  *
  742.  *----------------------------------------------------------------------
  743.  */
  744. static int
  745. GetValueNames(
  746.     Tcl_Interp *interp, /* Current interpreter. */
  747.     Tcl_Obj *keyNameObj, /* Key to enumerate. */
  748.     Tcl_Obj *patternObj) /* Optional match pattern. */
  749. {
  750.     HKEY key;
  751.     Tcl_Obj *resultPtr;
  752.     DWORD index, size, maxSize, result;
  753.     Tcl_DString buffer, ds;
  754.     char *pattern, *name;
  755.     /*
  756.      * Attempt to open the key for enumeration.
  757.      */
  758.     if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
  759.     != TCL_OK) {
  760. return TCL_ERROR;
  761.     }
  762.     resultPtr = Tcl_GetObjResult(interp);
  763.     /*
  764.      * Query the key to determine the appropriate buffer size to hold the
  765.      * largest value name plus the terminating null.
  766.      */
  767.     result = (*regWinProcs->regQueryInfoKeyProc)(key, NULL, NULL, NULL, NULL,
  768.     NULL, NULL, &index, &maxSize, NULL, NULL, NULL);
  769.     if (result != ERROR_SUCCESS) {
  770. Tcl_AppendStringsToObj(resultPtr, "unable to query key "",
  771. Tcl_GetString(keyNameObj), "": ", NULL);
  772. AppendSystemError(interp, result);
  773. RegCloseKey(key);
  774. result = TCL_ERROR;
  775. goto done;
  776.     }
  777.     maxSize++;
  778.     Tcl_DStringInit(&buffer);
  779.     Tcl_DStringSetLength(&buffer,
  780.     (int) ((regWinProcs->useWide) ? maxSize*2 : maxSize));
  781.     index = 0;
  782.     result = TCL_OK;
  783.     if (patternObj) {
  784. pattern = Tcl_GetString(patternObj);
  785.     } else {
  786. pattern = NULL;
  787.     }
  788.     /*
  789.      * Enumerate the values under the given subkey until we get an error,
  790.      * indicating the end of the list.  Note that we need to reset size
  791.      * after each iteration because RegEnumValue smashes the old value.
  792.      */
  793.     size = maxSize;
  794.     while ((*regWinProcs->regEnumValueProc)(key, index,
  795.     Tcl_DStringValue(&buffer), &size, NULL, NULL, NULL, NULL)
  796.     == ERROR_SUCCESS) {
  797. if (regWinProcs->useWide) {
  798.     size *= 2;
  799. }
  800. Tcl_WinTCharToUtf((TCHAR *) Tcl_DStringValue(&buffer), (int) size, &ds);
  801. name = Tcl_DStringValue(&ds);
  802. if (!pattern || Tcl_StringMatch(name, pattern)) {
  803.     result = Tcl_ListObjAppendElement(interp, resultPtr,
  804.     Tcl_NewStringObj(name, Tcl_DStringLength(&ds)));
  805.     if (result != TCL_OK) {
  806. Tcl_DStringFree(&ds);
  807. break;
  808.     }
  809. }
  810. Tcl_DStringFree(&ds);
  811. index++;
  812. size = maxSize;
  813.     }
  814.     Tcl_DStringFree(&buffer);
  815.     done:
  816.     RegCloseKey(key);
  817.     return result;
  818. }
  819. /*
  820.  *----------------------------------------------------------------------
  821.  *
  822.  * OpenKey --
  823.  *
  824.  * This function opens the specified key.  This function is a
  825.  * simple wrapper around ParseKeyName and OpenSubKey.
  826.  *
  827.  * Results:
  828.  * Returns the opened key in the keyPtr argument and a Tcl
  829.  * result code.
  830.  *
  831.  * Side effects:
  832.  * None.
  833.  *
  834.  *----------------------------------------------------------------------
  835.  */
  836. static int
  837. OpenKey(
  838.     Tcl_Interp *interp, /* Current interpreter. */
  839.     Tcl_Obj *keyNameObj, /* Key to open. */
  840.     REGSAM mode, /* Access mode. */
  841.     int flags, /* 0 or REG_CREATE. */
  842.     HKEY *keyPtr) /* Returned HKEY. */
  843. {
  844.     char *keyName, *buffer, *hostName;
  845.     int length;
  846.     HKEY rootKey;
  847.     DWORD result;
  848.     keyName = Tcl_GetStringFromObj(keyNameObj, &length);
  849.     buffer = ckalloc((unsigned int) length + 1);
  850.     strcpy(buffer, keyName);
  851.     result = ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName);
  852.     if (result == TCL_OK) {
  853. result = OpenSubKey(hostName, rootKey, keyName, mode, flags, keyPtr);
  854. if (result != ERROR_SUCCESS) {
  855.     Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
  856.     Tcl_AppendToObj(resultPtr, "unable to open key: ", -1);
  857.     AppendSystemError(interp, result);
  858.     result = TCL_ERROR;
  859. } else {
  860.     result = TCL_OK;
  861. }
  862.     }
  863.     ckfree(buffer);
  864.     return result;
  865. }
  866. /*
  867.  *----------------------------------------------------------------------
  868.  *
  869.  * OpenSubKey --
  870.  *
  871.  * This function opens a given subkey of a root key on the
  872.  * specified host.
  873.  *
  874.  * Results:
  875.  * Returns the opened key in the keyPtr and a Windows error code
  876.  * as the return value.
  877.  *
  878.  * Side effects:
  879.  * None.
  880.  *
  881.  *----------------------------------------------------------------------
  882.  */
  883. static DWORD
  884. OpenSubKey(
  885.     char *hostName, /* Host to access, or NULL for local. */
  886.     HKEY rootKey, /* Root registry key. */
  887.     char *keyName, /* Subkey name. */
  888.     REGSAM mode, /* Access mode. */
  889.     int flags, /* 0 or REG_CREATE. */
  890.     HKEY *keyPtr) /* Returned HKEY. */
  891. {
  892.     DWORD result;
  893.     Tcl_DString buf;
  894.     /*
  895.      * Attempt to open the root key on a remote host if necessary.
  896.      */
  897.     if (hostName) {
  898. hostName = (char *) Tcl_WinUtfToTChar(hostName, -1, &buf);
  899. result = (*regWinProcs->regConnectRegistryProc)(hostName, rootKey,
  900. &rootKey);
  901. Tcl_DStringFree(&buf);
  902. if (result != ERROR_SUCCESS) {
  903.     return result;
  904. }
  905.     }
  906.     /*
  907.      * Now open the specified key with the requested permissions.  Note
  908.      * that this key must be closed by the caller.
  909.      */
  910.     keyName = (char *) Tcl_WinUtfToTChar(keyName, -1, &buf);
  911.     if (flags & REG_CREATE) {
  912. DWORD create;
  913. result = (*regWinProcs->regCreateKeyExProc)(rootKey, keyName, 0, NULL,
  914. REG_OPTION_NON_VOLATILE, mode, NULL, keyPtr, &create);
  915.     } else {
  916. if (rootKey == HKEY_PERFORMANCE_DATA) {
  917.     /*
  918.      * Here we fudge it for this special root key.
  919.      * See MSDN for more info on HKEY_PERFORMANCE_DATA and
  920.      * the peculiarities surrounding it
  921.      */
  922.     *keyPtr = HKEY_PERFORMANCE_DATA;
  923.     result = ERROR_SUCCESS;
  924. } else {
  925.     result = (*regWinProcs->regOpenKeyExProc)(rootKey, keyName, 0,
  926.     mode, keyPtr);
  927. }
  928.     }
  929.     Tcl_DStringFree(&buf);
  930.     /*
  931.      * Be sure to close the root key since we are done with it now.
  932.      */
  933.     if (hostName) {
  934. RegCloseKey(rootKey);
  935.     }
  936.     return result;
  937. }
  938. /*
  939.  *----------------------------------------------------------------------
  940.  *
  941.  * ParseKeyName --
  942.  *
  943.  * This function parses a key name into the host, root, and subkey
  944.  * parts.
  945.  *
  946.  * Results:
  947.  * The pointers to the start of the host and subkey names are
  948.  * returned in the hostNamePtr and keyNamePtr variables.  The
  949.  * specified root HKEY is returned in rootKeyPtr.  Returns
  950.  * a standard Tcl result.
  951.  *
  952.  *
  953.  * Side effects:
  954.  * Modifies the name string by inserting nulls.
  955.  *
  956.  *----------------------------------------------------------------------
  957.  */
  958. static int
  959. ParseKeyName(
  960.     Tcl_Interp *interp, /* Current interpreter. */
  961.     char *name,
  962.     char **hostNamePtr,
  963.     HKEY *rootKeyPtr,
  964.     char **keyNamePtr)
  965. {
  966.     char *rootName;
  967.     int result, index;
  968.     Tcl_Obj *rootObj, *resultPtr = Tcl_GetObjResult(interp);
  969.     /*
  970.      * Split the key into host and root portions.
  971.      */
  972.     *hostNamePtr = *keyNamePtr = rootName = NULL;
  973.     if (name[0] == '\') {
  974. if (name[1] == '\') {
  975.     *hostNamePtr = name;
  976.     for (rootName = name+2; *rootName != ''; rootName++) {
  977. if (*rootName == '\') {
  978.     *rootName++ = '';
  979.     break;
  980. }
  981.     }
  982. }
  983.     } else {
  984. rootName = name;
  985.     }
  986.     if (!rootName) {
  987. Tcl_AppendStringsToObj(resultPtr, "bad key "", name,
  988. "": must start with a valid root", NULL);
  989. return TCL_ERROR;
  990.     }
  991.     /*
  992.      * Split the root into root and subkey portions.
  993.      */
  994.     for (*keyNamePtr = rootName; **keyNamePtr != ''; (*keyNamePtr)++) {
  995. if (**keyNamePtr == '\') {
  996.     **keyNamePtr = '';
  997.     (*keyNamePtr)++;
  998.     break;
  999. }
  1000.     }
  1001.     /*
  1002.      * Look for a matching root name.
  1003.      */
  1004.     rootObj = Tcl_NewStringObj(rootName, -1);
  1005.     result = Tcl_GetIndexFromObj(interp, rootObj, rootKeyNames, "root name",
  1006.     TCL_EXACT, &index);
  1007.     Tcl_DecrRefCount(rootObj);
  1008.     if (result != TCL_OK) {
  1009. return TCL_ERROR;
  1010.     }
  1011.     *rootKeyPtr = rootKeys[index];
  1012.     return TCL_OK;
  1013. }
  1014. /*
  1015.  *----------------------------------------------------------------------
  1016.  *
  1017.  * RecursiveDeleteKey --
  1018.  *
  1019.  * This function recursively deletes all the keys below a starting
  1020.  * key.  Although Windows 95 does this automatically, we still need
  1021.  * to do this for Windows NT.
  1022.  *
  1023.  * Results:
  1024.  * Returns a Windows error code.
  1025.  *
  1026.  * Side effects:
  1027.  * Deletes all of the keys and values below the given key.
  1028.  *
  1029.  *----------------------------------------------------------------------
  1030.  */
  1031. static DWORD
  1032. RecursiveDeleteKey(
  1033.     HKEY startKey, /* Parent of key to be deleted. */
  1034.     CONST char *keyName) /* Name of key to be deleted in external
  1035.  * encoding, not UTF. */
  1036. {
  1037.     DWORD result, size, maxSize;
  1038.     Tcl_DString subkey;
  1039.     HKEY hKey;
  1040.     /*
  1041.      * Do not allow NULL or empty key name.
  1042.      */
  1043.     if (!keyName || *keyName == '') {
  1044. return ERROR_BADKEY;
  1045.     }
  1046.     result = (*regWinProcs->regOpenKeyExProc)(startKey, keyName, 0,
  1047.     KEY_ENUMERATE_SUB_KEYS | DELETE | KEY_QUERY_VALUE, &hKey);
  1048.     if (result != ERROR_SUCCESS) {
  1049. return result;
  1050.     }
  1051.     result = (*regWinProcs->regQueryInfoKeyProc)(hKey, NULL, NULL, NULL, NULL,
  1052.     &maxSize, NULL, NULL, NULL, NULL, NULL, NULL);
  1053.     maxSize++;
  1054.     if (result != ERROR_SUCCESS) {
  1055. return result;
  1056.     }
  1057.     Tcl_DStringInit(&subkey);
  1058.     Tcl_DStringSetLength(&subkey,
  1059.     (int) ((regWinProcs->useWide) ? maxSize * 2 : maxSize));
  1060.     while (result == ERROR_SUCCESS) {
  1061. /*
  1062.  * Always get index 0 because key deletion changes ordering.
  1063.  */
  1064. size = maxSize;
  1065. result=(*regWinProcs->regEnumKeyExProc)(hKey, 0,
  1066. Tcl_DStringValue(&subkey), &size, NULL, NULL, NULL, NULL);
  1067. if (result == ERROR_NO_MORE_ITEMS) {
  1068.     result = (*regWinProcs->regDeleteKeyProc)(startKey, keyName);
  1069.     break;
  1070. } else if (result == ERROR_SUCCESS) {
  1071.     result = RecursiveDeleteKey(hKey, Tcl_DStringValue(&subkey));
  1072. }
  1073.     }
  1074.     Tcl_DStringFree(&subkey);
  1075.     RegCloseKey(hKey);
  1076.     return result;
  1077. }
  1078. /*
  1079.  *----------------------------------------------------------------------
  1080.  *
  1081.  * SetValue --
  1082.  *
  1083.  * This function sets the contents of a registry value.  If
  1084.  * the key or value does not exist, it will be created.  If it
  1085.  * does exist, then the data and type will be replaced.
  1086.  *
  1087.  * Results:
  1088.  * Returns a normal Tcl result.
  1089.  *
  1090.  * Side effects:
  1091.  * May create new keys or values.
  1092.  *
  1093.  *----------------------------------------------------------------------
  1094.  */
  1095. static int
  1096. SetValue(
  1097.     Tcl_Interp *interp, /* Current interpreter. */
  1098.     Tcl_Obj *keyNameObj, /* Name of key. */
  1099.     Tcl_Obj *valueNameObj, /* Name of value to set. */
  1100.     Tcl_Obj *dataObj, /* Data to be written. */
  1101.     Tcl_Obj *typeObj) /* Type of data to be written. */
  1102. {
  1103.     DWORD type, result;
  1104.     HKEY key;
  1105.     int length;
  1106.     char *valueName;
  1107.     Tcl_Obj *resultPtr;
  1108.     Tcl_DString nameBuf;
  1109.     if (typeObj == NULL) {
  1110. type = REG_SZ;
  1111.     } else if (Tcl_GetIndexFromObj(interp, typeObj, typeNames, "type",
  1112.     0, (int *) &type) != TCL_OK) {
  1113. if (Tcl_GetIntFromObj(NULL, typeObj, (int*) &type) != TCL_OK) {
  1114.     return TCL_ERROR;
  1115. }
  1116. Tcl_ResetResult(interp);
  1117.     }
  1118.     if (OpenKey(interp, keyNameObj, KEY_ALL_ACCESS, 1, &key) != TCL_OK) {
  1119. return TCL_ERROR;
  1120.     }
  1121.     valueName = Tcl_GetStringFromObj(valueNameObj, &length);
  1122.     valueName = (char *) Tcl_WinUtfToTChar(valueName, length, &nameBuf);
  1123.     resultPtr = Tcl_GetObjResult(interp);
  1124.     if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
  1125. DWORD value;
  1126. if (Tcl_GetIntFromObj(interp, dataObj, (int*) &value) != TCL_OK) {
  1127.     RegCloseKey(key);
  1128.     Tcl_DStringFree(&nameBuf);
  1129.     return TCL_ERROR;
  1130. }
  1131. value = ConvertDWORD(type, value);
  1132. result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
  1133. (BYTE*) &value, sizeof(DWORD));
  1134.     } else if (type == REG_MULTI_SZ) {
  1135. Tcl_DString data, buf;
  1136. int objc, i;
  1137. Tcl_Obj **objv;
  1138. if (Tcl_ListObjGetElements(interp, dataObj, &objc, &objv) != TCL_OK) {
  1139.     RegCloseKey(key);
  1140.     Tcl_DStringFree(&nameBuf);
  1141.     return TCL_ERROR;
  1142. }
  1143. /*
  1144.  * Append the elements as null terminated strings.  Note that
  1145.  * we must not assume the length of the string in case there are
  1146.  * embedded nulls, which aren't allowed in REG_MULTI_SZ values.
  1147.  */
  1148. Tcl_DStringInit(&data);
  1149. for (i = 0; i < objc; i++) {
  1150.     Tcl_DStringAppend(&data, Tcl_GetString(objv[i]), -1);
  1151.     /*
  1152.      * Add a null character to separate this value from the next.
  1153.      * We accomplish this by growing the string by one byte.  Since the
  1154.      * DString always tacks on an extra null byte, the new byte will
  1155.      * already be set to null.
  1156.      */
  1157.     Tcl_DStringSetLength(&data, Tcl_DStringLength(&data)+1);
  1158. }
  1159. Tcl_WinUtfToTChar(Tcl_DStringValue(&data), Tcl_DStringLength(&data)+1,
  1160. &buf);
  1161. result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
  1162. (BYTE *) Tcl_DStringValue(&buf),
  1163. (DWORD) Tcl_DStringLength(&buf));
  1164. Tcl_DStringFree(&data);
  1165. Tcl_DStringFree(&buf);
  1166.     } else if (type == REG_SZ || type == REG_EXPAND_SZ) {
  1167. Tcl_DString buf;
  1168. char *data = Tcl_GetStringFromObj(dataObj, &length);
  1169. data = (char *) Tcl_WinUtfToTChar(data, length, &buf);
  1170. /*
  1171.  * Include the null in the length, padding if needed for Unicode.
  1172.  */
  1173. if (regWinProcs->useWide) {
  1174.     Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf)+1);
  1175. }
  1176. length = Tcl_DStringLength(&buf) + 1;
  1177. result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
  1178. (BYTE*)data, (DWORD) length);
  1179. Tcl_DStringFree(&buf);
  1180.     } else {
  1181. char *data;
  1182. /*
  1183.  * Store binary data in the registry.
  1184.  */
  1185. data = Tcl_GetByteArrayFromObj(dataObj, &length);
  1186. result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
  1187. (BYTE *)data, (DWORD) length);
  1188.     }
  1189.     Tcl_DStringFree(&nameBuf);
  1190.     RegCloseKey(key);
  1191.     if (result != ERROR_SUCCESS) {
  1192. Tcl_AppendToObj(resultPtr, "unable to set value: ", -1);
  1193. AppendSystemError(interp, result);
  1194. return TCL_ERROR;
  1195.     }
  1196.     return TCL_OK;
  1197. }
  1198. /*
  1199.  *----------------------------------------------------------------------
  1200.  *
  1201.  * BroadcastValue --
  1202.  *
  1203.  * This function broadcasts a WM_SETTINGCHANGE message to indicate
  1204.  * to other programs that we have changed the contents of a registry
  1205.  * value.
  1206.  *
  1207.  * Results:
  1208.  * Returns a normal Tcl result.
  1209.  *
  1210.  * Side effects:
  1211.  * Will cause other programs to reload their system settings.
  1212.  *
  1213.  *----------------------------------------------------------------------
  1214.  */
  1215. static int
  1216. BroadcastValue(
  1217.     Tcl_Interp *interp, /* Current interpreter. */
  1218.     int objc, /* Number of arguments. */
  1219.     Tcl_Obj * CONST objv[]) /* Argument values. */
  1220. {
  1221.     LRESULT result, sendResult;
  1222.     UINT timeout = 3000;
  1223.     int len;
  1224.     char *str;
  1225.     Tcl_Obj *objPtr;
  1226.     if ((objc != 3) && (objc != 5)) {
  1227. Tcl_WrongNumArgs(interp, 2, objv, "keyName ?-timeout millisecs?");
  1228. return TCL_ERROR;
  1229.     }
  1230.     if (objc > 3) {
  1231. str = Tcl_GetStringFromObj(objv[3], &len);
  1232. if ((len < 2) || (*str != '-') || strncmp(str, "-timeout", (size_t) len)) {
  1233.     Tcl_WrongNumArgs(interp, 2, objv, "keyName ?-timeout millisecs?");
  1234.     return TCL_ERROR;
  1235. }
  1236. if (Tcl_GetIntFromObj(interp, objv[4], (int *) &timeout) != TCL_OK) {
  1237.     return TCL_ERROR;
  1238. }
  1239.     }
  1240.     str = Tcl_GetStringFromObj(objv[2], &len);
  1241.     if (len == 0) {
  1242. str = NULL;
  1243.     }
  1244.     /*
  1245.      * Use the ignore the result.
  1246.      */
  1247.     result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE,
  1248.     (WPARAM) 0, (LPARAM) str, SMTO_ABORTIFHUNG, timeout, &sendResult);
  1249.     objPtr = Tcl_NewObj();
  1250.     Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewLongObj((long) result));
  1251.     Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewLongObj((long) sendResult));
  1252.     Tcl_SetObjResult(interp, objPtr);
  1253.     return TCL_OK;
  1254. }
  1255. /*
  1256.  *----------------------------------------------------------------------
  1257.  *
  1258.  * AppendSystemError --
  1259.  *
  1260.  * This routine formats a Windows system error message and places
  1261.  * it into the interpreter result.
  1262.  *
  1263.  * Results:
  1264.  * None.
  1265.  *
  1266.  * Side effects:
  1267.  * None.
  1268.  *
  1269.  *----------------------------------------------------------------------
  1270.  */
  1271. static void
  1272. AppendSystemError(
  1273.     Tcl_Interp *interp, /* Current interpreter. */
  1274.     DWORD error) /* Result code from error. */
  1275. {
  1276.     int length;
  1277.     WCHAR *wMsgPtr;
  1278.     char *msg;
  1279.     char id[TCL_INTEGER_SPACE], msgBuf[24 + TCL_INTEGER_SPACE];
  1280.     Tcl_DString ds;
  1281.     Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
  1282.     length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
  1283.     | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error,
  1284.     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (WCHAR *) &wMsgPtr,
  1285.     0, NULL);
  1286.     if (length == 0) {
  1287. char *msgPtr;
  1288. length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
  1289. | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error,
  1290. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &msgPtr,
  1291. 0, NULL);
  1292. if (length > 0) {
  1293.     wMsgPtr = (WCHAR *) LocalAlloc(LPTR, (length + 1) * sizeof(WCHAR));
  1294.     MultiByteToWideChar(CP_ACP, 0, msgPtr, length + 1, wMsgPtr,
  1295.     length + 1);
  1296.     LocalFree(msgPtr);
  1297. }
  1298.     }
  1299.     if (length == 0) {
  1300. if (error == ERROR_CALL_NOT_IMPLEMENTED) {
  1301.     msg = "function not supported under Win32s";
  1302. } else {
  1303.     sprintf(msgBuf, "unknown error: %ld", error);
  1304.     msg = msgBuf;
  1305. }
  1306.     } else {
  1307. Tcl_Encoding encoding;
  1308. encoding = Tcl_GetEncoding(NULL, "unicode");
  1309. Tcl_ExternalToUtfDString(encoding, (char *) wMsgPtr, -1, &ds);
  1310. Tcl_FreeEncoding(encoding);
  1311. LocalFree(wMsgPtr);
  1312. msg = Tcl_DStringValue(&ds);
  1313. length = Tcl_DStringLength(&ds);
  1314. /*
  1315.  * Trim the trailing CR/LF from the system message.
  1316.  */
  1317. if (msg[length-1] == 'n') {
  1318.     msg[--length] = 0;
  1319. }
  1320. if (msg[length-1] == 'r') {
  1321.     msg[--length] = 0;
  1322. }
  1323.     }
  1324.     sprintf(id, "%ld", error);
  1325.     Tcl_SetErrorCode(interp, "WINDOWS", id, msg, (char *) NULL);
  1326.     Tcl_AppendToObj(resultPtr, msg, length);
  1327.     if (length != 0) {
  1328. Tcl_DStringFree(&ds);
  1329.     }
  1330. }
  1331. /*
  1332.  *----------------------------------------------------------------------
  1333.  *
  1334.  * ConvertDWORD --
  1335.  *
  1336.  * This function determines whether a DWORD needs to be byte
  1337.  * swapped, and returns the appropriately swapped value.
  1338.  *
  1339.  * Results:
  1340.  * Returns a converted DWORD.
  1341.  *
  1342.  * Side effects:
  1343.  * None.
  1344.  *
  1345.  *----------------------------------------------------------------------
  1346.  */
  1347. static DWORD
  1348. ConvertDWORD(
  1349.     DWORD type, /* Either REG_DWORD or REG_DWORD_BIG_ENDIAN */
  1350.     DWORD value) /* The value to be converted. */
  1351. {
  1352.     DWORD order = 1;
  1353.     DWORD localType;
  1354.     /*
  1355.      * Check to see if the low bit is in the first byte.
  1356.      */
  1357.     localType = (*((char*)(&order)) == 1) ? REG_DWORD : REG_DWORD_BIG_ENDIAN;
  1358.     return (type != localType) ? SWAPLONG(value) : value;
  1359. }