static int GetKeyNames( Tcl_Interp *interp, /* Current interpreter. */ Tcl_Obj *keyNameObj, /* Key to enumerate. */ Tcl_Obj *patternObj, /* Optional match pattern. */ REGSAM mode) /* Mode flags to pass. */ { const char *pattern; /* Pattern being matched against subkeys */ HKEY key; /* Handle to the key being examined */ DWORD index; /* Position of the current subkey */ Tcl_Obj *resultPtr; /* List of subkeys being accumulated */ int result; if (patternObj) { pattern = Tcl_GetString(patternObj); } else { pattern = NULL; } /* * Attempt to open the key for enumeration. */ mode |= KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS; if (OpenKey(interp, keyNameObj, mode, 0, &key) != TCL_OK) { return TCL_ERROR; } /* * NOTE: the original code used RegQueryInfoKey to get the key count * and max subkey len. There are several issues with this, including * a race condition (others might modify counts and/or lengths), * as well as a mysterious bug (seemingly in RegQueryInfoKey) where * a subkey is included in the return count but its length is not * accounted for - observed in a running system through a debugger * * The modified code below makes use of the fact that a key is * at most 256 bytes long and buffer is simply allocated on stack. It then * iterates until "eof" (ERROR_NO_MORE_ITEMS). So we do not need * RegQueryInfoKey or memory allocs/frees. */ /* resultPtr is ALWAYS used, both on success, as well as failure */ resultPtr = Tcl_NewObj(); /* Loop until we break out on eof or error */ for (result = ERROR_SUCCESS, index = 0; ; ++index) { /* * Microsoft specifies the max key length as 255. Plus 1 for * terminating \0. However, it turns out you can actually write 256 * chars to a key (even though you are not supposed to) and get it * back so we make the buffer 256+1 bytes */ TCHAR subkeyName[257]; DWORD bufSize; /* Size of the buffer */ Tcl_DString ds; /* Must init bufSize on each iteration */ bufSize = sizeof(subkeyName)/sizeof(subkeyName[0]); result = RegEnumKeyEx(key, index, subkeyName, &bufSize, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) break; /* Got subkey, store it after pattern match check (if there is one) */ Tcl_WinTCharToUtf(subkeyName, bufSize * sizeof(TCHAR), &ds); if (pattern == NULL || Tcl_StringMatch(Tcl_DStringValue(&ds), pattern)) { Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); } Tcl_DStringFree(&ds); } RegCloseKey(key); /* * On loop termination, result contains a Windows error code. * ERROR_NO_MORE_ITEMS means eof - i.e. success retrieving all * anything else is a genuine error */ if (result == ERROR_NO_MORE_ITEMS) { /* SUCCESS RETURN - All done with subkeys */ Tcl_SetObjResult(interp, resultPtr); result = TCL_OK; } else { /* ERROR RETURN */ Tcl_SetObjLength(resultPtr, 0); /* Discard collected data */ Tcl_AppendStringsToObj(resultPtr, "unable to enumerate subkeys of \"", Tcl_GetString(keyNameObj), "\": ", NULL); Tcl_SetObjResult(interp, resultPtr); AppendSystemError(interp, result); result = TCL_ERROR; } return result; }