/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * * @author Mladen Turk * @version $Revision: 466585 $, $Date: 2006-10-22 00:16:34 +0200 (Sun, 22 Oct 2006) $ */ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif #include #include #include #include #include "apr.h" #include "apr_pools.h" #include "apr_arch_misc.h" /* for apr_os_level */ #include "apr_arch_atime.h" /* for FileTimeToAprTime */ #include "tcn.h" #define SAFE_CLOSE_KEY(k) \ if ((k) != NULL && (k) != INVALID_HANDLE_VALUE) { \ RegCloseKey((k)); \ (k) = NULL; \ } typedef struct { apr_pool_t *pool; HKEY root; HKEY key; } tcn_nt_registry_t; #define TCN_HKEY_CLASSES_ROOT 1 #define TCN_HKEY_CURRENT_CONFIG 2 #define TCN_HKEY_CURRENT_USER 3 #define TCN_HKEY_LOCAL_MACHINE 4 #define TCN_HKEY_USERS 5 static const struct { HKEY k; } TCN_KEYS[] = { INVALID_HANDLE_VALUE, HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, INVALID_HANDLE_VALUE }; #define TCN_KEY_ALL_ACCESS 0x0001 #define TCN_KEY_CREATE_LINK 0x0002 #define TCN_KEY_CREATE_SUB_KEY 0x0004 #define TCN_KEY_ENUMERATE_SUB_KEYS 0x0008 #define TCN_KEY_EXECUTE 0x0010 #define TCN_KEY_NOTIFY 0x0020 #define TCN_KEY_QUERY_VALUE 0x0040 #define TCN_KEY_READ 0x0080 #define TCN_KEY_SET_VALUE 0x0100 #define TCN_KEY_WOW64_64KEY 0x0200 #define TCN_KEY_WOW64_32KEY 0x0400 #define TCN_KEY_WRITE 0x0800 #define TCN_REGSAM(s, x) \ s = 0; \ if (x & TCN_KEY_ALL_ACCESS) \ s |= KEY_ALL_ACCESS; \ if (x & TCN_KEY_CREATE_LINK) \ s |= KEY_CREATE_LINK; \ if (x & TCN_KEY_CREATE_SUB_KEY) \ s |= KEY_CREATE_SUB_KEY; \ if (x & TCN_KEY_ENUMERATE_SUB_KEYS) \ s |= KEY_ENUMERATE_SUB_KEYS; \ if (x & TCN_KEY_EXECUTE) \ s |= KEY_EXECUTE; \ if (x & TCN_KEY_NOTIFY) \ s |= KEY_NOTIFY; \ if (x & TCN_KEY_READ) \ s |= KEY_READ; \ if (x & TCN_KEY_SET_VALUE) \ s |= KEY_SET_VALUE; \ if (x & TCN_KEY_WOW64_64KEY) \ s |= KEY_WOW64_64KEY; \ if (x & TCN_KEY_WOW64_32KEY) \ s |= KEY_WOW64_32KEY; \ if (x & TCN_KEY_WRITE) \ s |= KEY_WRITE #define TCN_REG_BINARY 1 #define TCN_REG_DWORD 2 #define TCN_REG_EXPAND_SZ 3 #define TCN_REG_MULTI_SZ 4 #define TCN_REG_QWORD 5 #define TCN_REG_SZ 6 static const struct { DWORD t; } TCN_REGTYPES[] = { REG_NONE, REG_BINARY, REG_DWORD, REG_EXPAND_SZ, REG_MULTI_SZ, REG_QWORD, REG_SZ, REG_NONE }; static apr_status_t registry_cleanup(void *data) { tcn_nt_registry_t *reg = (tcn_nt_registry_t *)data; if (reg) { SAFE_CLOSE_KEY(reg->key); } return APR_SUCCESS; } TCN_IMPLEMENT_CALL(jlong, Registry, create)(TCN_STDARGS, jint root, jstring name, jint sam, jlong pool) { apr_pool_t *p = J2P(pool, apr_pool_t *); tcn_nt_registry_t *reg = NULL; TCN_ALLOC_WSTRING(name); HKEY key; LONG rc; REGSAM s; UNREFERENCED(o); TCN_ASSERT(pool != 0); if (root < TCN_HKEY_CLASSES_ROOT || root > TCN_HKEY_USERS) { tcn_ThrowException(e, "Invalid Registry Root Key"); goto cleanup; } if (sam < TCN_KEY_ALL_ACCESS || root > TCN_KEY_WRITE) { tcn_ThrowException(e, "Invalid Registry Key Security"); goto cleanup; } reg = (tcn_nt_registry_t *)apr_palloc(p, sizeof(tcn_nt_registry_t)); reg->pool = p; reg->root = TCN_KEYS[root].k; reg->key = NULL; TCN_INIT_WSTRING(name); TCN_REGSAM(s, sam); rc = RegCreateKeyExW(reg->root, J2W(name), 0, NULL, REG_OPTION_NON_VOLATILE, s, NULL, &key, NULL); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } reg->key = key; apr_pool_cleanup_register(p, (const void *)reg, registry_cleanup, apr_pool_cleanup_null); cleanup: TCN_FREE_WSTRING(name); return P2J(reg); } TCN_IMPLEMENT_CALL(jlong, Registry, open)(TCN_STDARGS, jint root, jstring name, jint sam, jlong pool) { apr_pool_t *p = J2P(pool, apr_pool_t *); tcn_nt_registry_t *reg = NULL; TCN_ALLOC_WSTRING(name); HKEY key; LONG rc; REGSAM s; UNREFERENCED(o); TCN_ASSERT(pool != 0); if (root < TCN_HKEY_CLASSES_ROOT || root > TCN_HKEY_USERS) { tcn_ThrowException(e, "Invalid Registry Root Key"); goto cleanup; } if (sam < TCN_KEY_ALL_ACCESS || root > TCN_KEY_WRITE) { tcn_ThrowException(e, "Invalid Registry Key Security"); goto cleanup; } reg = (tcn_nt_registry_t *)apr_palloc(p, sizeof(tcn_nt_registry_t)); reg->pool = p; reg->root = TCN_KEYS[root].k; reg->key = NULL; TCN_INIT_WSTRING(name); TCN_REGSAM(s, sam); rc = RegOpenKeyExW(reg->root, J2W(name), 0, s, &key); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } reg->key = key; apr_pool_cleanup_register(p, (const void *)reg, registry_cleanup, apr_pool_cleanup_null); cleanup: TCN_FREE_WSTRING(name); return P2J(reg); } TCN_IMPLEMENT_CALL(jint, Registry, close)(TCN_STDARGS, jlong reg) { tcn_nt_registry_t *r = J2P(reg, tcn_nt_registry_t *); UNREFERENCED_STDARGS; TCN_ASSERT(reg != 0); registry_cleanup(r); apr_pool_cleanup_kill(r->pool, r, registry_cleanup); return APR_SUCCESS; } TCN_IMPLEMENT_CALL(jint, Registry, getType)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD v; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, &v, NULL, NULL); if (rc != ERROR_SUCCESS) v = -rc; TCN_FREE_WSTRING(name); switch (v) { case REG_BINARY: v = TCN_REG_BINARY; break; case REG_DWORD: v = TCN_REG_DWORD; break; case REG_EXPAND_SZ: v = TCN_REG_EXPAND_SZ; break; case REG_MULTI_SZ: v = TCN_REG_MULTI_SZ; break; case REG_QWORD: v = TCN_REG_QWORD; break; case REG_SZ: v = TCN_REG_SZ; break; case REG_DWORD_BIG_ENDIAN: v = 0; break; } return v; } TCN_IMPLEMENT_CALL(jint, Registry, getSize)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD v; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, NULL, &v); if (rc != ERROR_SUCCESS) v = -rc; TCN_FREE_WSTRING(name); return v; } TCN_IMPLEMENT_CALL(jint, Registry, getValueI)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD t, l; DWORD v = 0; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } if (t == REG_DWORD) { l = sizeof(DWORD); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)&v, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } } else if (t == REG_SZ || t == REG_BINARY || t == REG_MULTI_SZ || t == REG_EXPAND_SZ) v = l; else { v = 0; tcn_ThrowException(e, "Unable to convert the value to integer"); } cleanup: TCN_FREE_WSTRING(name); return v; } TCN_IMPLEMENT_CALL(jlong, Registry, getValueJ)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD t, l; UINT64 v = 0; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } if (t == REG_DWORD) { DWORD tv; l = sizeof(DWORD); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)&tv, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } v = tv; } else if (t == REG_QWORD) { l = sizeof(UINT64); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)&v, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } } else if (t == REG_SZ || t == REG_BINARY || t == REG_MULTI_SZ || t == REG_EXPAND_SZ) v = l; else { v = 0; tcn_ThrowException(e, "Unable to convert the value to long"); } cleanup: TCN_FREE_WSTRING(name); return v; } TCN_IMPLEMENT_CALL(jstring, Registry, getValueS)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD t, l; jstring v = NULL; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } if (t == REG_SZ || t == REG_EXPAND_SZ) { jchar *vw = (jchar *)malloc(l); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)vw, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); free(vw); goto cleanup; } v = (*e)->NewString((e), vw, wcslen(vw)); free(vw); } cleanup: TCN_FREE_WSTRING(name); return v; } TCN_IMPLEMENT_CALL(jbyteArray, Registry, getValueB)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD t, l; jbyteArray v = NULL; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } if (t == REG_BINARY) { BYTE *b = (BYTE *)malloc(l); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, b, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); free(b); goto cleanup; } v = tcn_new_arrayb(e, b, l); free(b); } cleanup: TCN_FREE_WSTRING(name); return v; } static jsize get_multi_sz_count(LPCWSTR str) { LPCWSTR p = str; jsize cnt = 0; for ( ; p && *p; p++) { cnt++; while (*p) p++; } return cnt; } TCN_IMPLEMENT_CALL(jobjectArray, Registry, getValueA)(TCN_STDARGS, jlong key, jstring name) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD t, l; jobjectArray v = NULL; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } if (t == REG_MULTI_SZ) { jsize cnt = 0; jchar *p; jchar *vw = (jchar *)malloc(l); rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)vw, &l); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); free(vw); goto cleanup; } cnt = get_multi_sz_count(vw); if (cnt) { jsize idx = 0; v = tcn_new_arrays(e, cnt); for (p = vw ; p && *p; p++) { jstring s; jchar *b = p; while (*p) p++; s = (*e)->NewString((e), b, (jsize)(p - b)); (*e)->SetObjectArrayElement((e), v, idx++, s); } } free(vw); } cleanup: TCN_FREE_WSTRING(name); return v; } TCN_IMPLEMENT_CALL(jint, Registry, setValueI)(TCN_STDARGS, jlong key, jstring name, jint val) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; DWORD v = (DWORD)val; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegSetValueExW(k->key, J2W(name), 0, REG_DWORD, (CONST BYTE *)&v, sizeof(DWORD)); TCN_FREE_WSTRING(name); return v; } TCN_IMPLEMENT_CALL(jint, Registry, setValueJ)(TCN_STDARGS, jlong key, jstring name, jlong val) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; UINT64 v = (UINT64)val; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); rc = RegSetValueExW(k->key, J2W(name), 0, REG_QWORD, (CONST BYTE *)&v, sizeof(UINT64)); TCN_FREE_WSTRING(name); return rc; } TCN_IMPLEMENT_CALL(jint, Registry, setValueS)(TCN_STDARGS, jlong key, jstring name, jstring val) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); TCN_ALLOC_WSTRING(val); LONG rc; DWORD len; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); TCN_INIT_WSTRING(val); len = wcslen(J2W(val)); rc = RegSetValueExW(k->key, J2W(name), 0, REG_SZ, (CONST BYTE *)J2W(val), (len + 1) * 2); TCN_FREE_WSTRING(name); TCN_FREE_WSTRING(val); return rc; } TCN_IMPLEMENT_CALL(jint, Registry, setValueE)(TCN_STDARGS, jlong key, jstring name, jstring val) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); TCN_ALLOC_WSTRING(val); LONG rc; DWORD len; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); TCN_INIT_WSTRING(val); len = wcslen(J2W(val)); rc = RegSetValueExW(k->key, J2W(name), 0, REG_EXPAND_SZ, (CONST BYTE *)J2W(val), (len + 1) * 2); TCN_FREE_WSTRING(name); TCN_FREE_WSTRING(val); return rc; } TCN_IMPLEMENT_CALL(jint, Registry, setValueA)(TCN_STDARGS, jlong key, jstring name, jobjectArray vals) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); LONG rc; jsize i, len; jsize sl = 0; jchar *msz, *p; UNREFERENCED(o); TCN_ASSERT(key != 0); TCN_INIT_WSTRING(name); len = (*e)->GetArrayLength((e), vals); for (i = 0; i < len; i++) { jstring s = (jstring)(*e)->GetObjectArrayElement((e), vals, i); sl += (*e)->GetStringLength((e), s) + 1; } sl = (sl + 1) * 2; p = msz = (jchar *)calloc(1, sl); for (i = 0; i < len; i++) { jsize l; jstring s = (jstring)(*e)->GetObjectArrayElement((e), vals, i); l = (*e)->GetStringLength((e), s); wcsncpy(p, (*e)->GetStringChars(e, s, 0), l); p += l + 1; } rc = RegSetValueExW(k->key, J2W(name), 0, REG_MULTI_SZ, (CONST BYTE *)msz, sl); TCN_FREE_WSTRING(name); free(msz); return rc; } TCN_IMPLEMENT_CALL(jint, Registry, setValueB)(TCN_STDARGS, jlong key, jstring name, jbyteArray val) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); TCN_ALLOC_WSTRING(name); jsize nbytes = (*e)->GetArrayLength(e, val); jbyte *bytes = (*e)->GetByteArrayElements(e, val, NULL); LONG rc; rc = RegSetValueExW(k->key, J2W(name), 0, REG_BINARY, bytes, (DWORD)nbytes); (*e)->ReleaseByteArrayElements(e, val, bytes, JNI_ABORT); TCN_FREE_WSTRING(name); return rc; } #define MAX_VALUE_NAME 4096 TCN_IMPLEMENT_CALL(jobjectArray, Registry, enumKeys)(TCN_STDARGS, jlong key) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); LONG rc; jobjectArray v = NULL; jsize cnt = 0; WCHAR achKey[MAX_PATH]; WCHAR achClass[MAX_PATH] = L""; DWORD cchClassName = MAX_PATH; DWORD cSubKeys; DWORD cbMaxSubKey; DWORD cchMaxClass; DWORD cValues; DWORD cchMaxValue; DWORD cbMaxValueData; DWORD cbSecurityDescriptor; FILETIME ftLastWriteTime; DWORD cchValue = MAX_VALUE_NAME; UNREFERENCED(o); TCN_ASSERT(key != 0); rc = RegQueryInfoKeyW(k->key, achClass, &cchClassName, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValue, &cbMaxValueData, &cbSecurityDescriptor, &ftLastWriteTime); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } cnt = cSubKeys; if (cnt) { jsize idx = 0; v = tcn_new_arrays(e, cnt); for (idx = 0; idx < cnt; idx++) { jstring s; DWORD achKeyLen = MAX_PATH; rc = RegEnumKeyExW(k->key, idx, achKey, &achKeyLen, NULL, NULL, NULL, &ftLastWriteTime); if (rc == (DWORD)ERROR_SUCCESS) { s = (*e)->NewString((e), achKey, wcslen(achKey)); (*e)->SetObjectArrayElement((e), v, idx, s); } } } cleanup: return v; } TCN_IMPLEMENT_CALL(jobjectArray, Registry, enumValues)(TCN_STDARGS, jlong key) { tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); LONG rc; jobjectArray v = NULL; jsize cnt = 0; WCHAR achClass[MAX_PATH] = L""; DWORD cchClassName = MAX_PATH; DWORD cSubKeys; DWORD cbMaxSubKey; DWORD cchMaxClass; DWORD cValues; DWORD cchMaxValue; DWORD cbMaxValueData; DWORD cbSecurityDescriptor; FILETIME ftLastWriteTime; WCHAR achValue[MAX_VALUE_NAME]; DWORD cchValue = MAX_VALUE_NAME; UNREFERENCED(o); TCN_ASSERT(key != 0); /* Get the class name and the value count. */ rc = RegQueryInfoKeyW(k->key, achClass, &cchClassName, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValue, &cbMaxValueData, &cbSecurityDescriptor, &ftLastWriteTime); if (rc != ERROR_SUCCESS) { tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc)); goto cleanup; } cnt = cValues; if (cnt) { jsize idx = 0; v = tcn_new_arrays(e, cnt); for (idx = 0; idx < cnt; idx++) { jstring s; cchValue = MAX_VALUE_NAME; achValue[0] = '\0'; rc = RegEnumValueW(k->key, idx, achValue, &cchValue, NULL, NULL, // &dwType, NULL, // &bData, NULL); // &bcData if (rc == (DWORD)ERROR_SUCCESS) { s = (*e)->NewString((e), achValue, wcslen(achValue)); (*e)->SetObjectArrayElement((e), v, idx, s); } } } cleanup: return v; } TCN_IMPLEMENT_CALL(jint, Registry, deleteKey)(TCN_STDARGS, jint root, jstring name, jboolean only_if_empty) { DWORD rv; TCN_ALLOC_WSTRING(name); UNREFERENCED(o); if (root < TCN_HKEY_CLASSES_ROOT || root > TCN_HKEY_USERS) { rv = EBADF; goto cleanup; } if (only_if_empty) rv = SHDeleteEmptyKeyW(TCN_KEYS[root].k, J2W(name)); else rv = SHDeleteKeyW(TCN_KEYS[root].k, J2W(name)); cleanup: TCN_FREE_WSTRING(name); return rv; } TCN_IMPLEMENT_CALL(jint, Registry, deleteValue)(TCN_STDARGS, jlong key, jstring name) { LONG rv; TCN_ALLOC_WSTRING(name); tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *); UNREFERENCED(o); rv = RegDeleteValueW(k->key, J2W(name)); TCN_FREE_WSTRING(name); return (jint)rv; }