Files
jlibloader/src/main/cpp/win.cpp
2014-03-15 16:13:53 +11:00

599 lines
24 KiB
C++
Executable File

/*
* Copyright 2012 Adam Murdoch
*
* Licensed 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.
*/
#ifdef _WIN32
#include "native.h"
#include "generic.h"
#include <windows.h>
#include <Shlwapi.h>
#include <wchar.h>
/*
* Marks the given result as failed, using the current value of GetLastError()
*/
void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
mark_failed_with_code(env, message, GetLastError(), NULL, result);
}
jstring wchar_to_java(JNIEnv* env, const wchar_t* chars, size_t len, jobject result) {
if (sizeof(wchar_t) != 2) {
mark_failed_with_message(env, "unexpected size of wchar_t", result);
return NULL;
}
return env->NewString((jchar*)chars, len);
}
wchar_t* java_to_wchar(JNIEnv *env, jstring string, jobject result) {
jsize len = env->GetStringLength(string);
wchar_t* str = (wchar_t*)malloc(sizeof(wchar_t) * (len+1));
env->GetStringRegion(string, 0, len, (jchar*)str);
str[len] = L'\0';
return str;
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) {
jclass infoClass = env->GetObjectClass(info);
OSVERSIONINFOEX versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((OSVERSIONINFO*)&versionInfo) == 0) {
mark_failed_with_errno(env, "could not get version info", result);
return;
}
SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo);
jstring arch = NULL;
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
arch = env->NewStringUTF("amd64");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
arch = env->NewStringUTF("x86");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
arch = env->NewStringUTF("ia64");
} else {
arch = env->NewStringUTF("unknown");
}
jmethodID method = env->GetMethodID(infoClass, "windows", "(IIIZLjava/lang/String;)V");
env->CallVoidMethod(info, method, versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
versionInfo.dwBuildNumber, versionInfo.wProductType == VER_NT_WORKSTATION,
arch);
}
/*
* Process functions
*/
JNIEXPORT jint JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) {
return GetCurrentProcessId();
}
JNIEXPORT jstring JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getWorkingDirectory(JNIEnv *env, jclass target, jobject result) {
DWORD size = GetCurrentDirectoryW(0, NULL);
if (size == 0) {
mark_failed_with_errno(env, "could not determine length of working directory path", result);
return NULL;
}
size = size+1; // Needs to include null character
wchar_t* path = (wchar_t*)malloc(sizeof(wchar_t) * size);
DWORD copied = GetCurrentDirectoryW(size, path);
if (copied == 0) {
free(path);
mark_failed_with_errno(env, "could get working directory path", result);
return NULL;
}
jstring dirName = wchar_to_java(env, path, copied, result);
free(path);
return dirName;
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_setWorkingDirectory(JNIEnv *env, jclass target, jstring dir, jobject result) {
wchar_t* dirPath = java_to_wchar(env, dir, result);
if (dirPath == NULL) {
return;
}
BOOL ok = SetCurrentDirectoryW(dirPath);
free(dirPath);
if (!ok) {
mark_failed_with_errno(env, "could not set current directory", result);
return;
}
}
JNIEXPORT jstring JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getEnvironmentVariable(JNIEnv *env, jclass target, jstring var, jobject result) {
wchar_t* varStr = java_to_wchar(env, var, result);
DWORD len = GetEnvironmentVariableW(varStr, NULL, 0);
if (len == 0) {
if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
mark_failed_with_errno(env, "could not determine length of environment variable", result);
}
free(varStr);
return NULL;
}
wchar_t* valueStr = (wchar_t*)malloc(sizeof(wchar_t) * len);
DWORD copied = GetEnvironmentVariableW(varStr, valueStr, len);
if (copied == 0) {
if (len > 1) {
// If the value is empty, then copied will be 0
mark_failed_with_errno(env, "could not get environment variable", result);
}
free(varStr);
free(valueStr);
return NULL;
}
free(varStr);
jstring value = wchar_to_java(env, valueStr, copied, result);
free(valueStr);
return value;
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_setEnvironmentVariable(JNIEnv *env, jclass target, jstring var, jstring value, jobject result) {
wchar_t* varStr = java_to_wchar(env, var, result);
wchar_t* valueStr = value == NULL ? NULL : java_to_wchar(env, value, result);
BOOL ok = SetEnvironmentVariableW(varStr, valueStr);
free(varStr);
if (valueStr != NULL) {
free(valueStr);
}
if (!ok && GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
mark_failed_with_errno(env, "could not set environment var", result);
return;
}
}
/*
* File system functions
*/
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) {
wchar_t* volumeName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
jclass info_class = env->GetObjectClass(info);
jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V");
HANDLE handle = FindFirstVolumeW(volumeName, MAX_PATH+1);
if (handle == INVALID_HANDLE_VALUE) {
free(volumeName);
mark_failed_with_errno(env, "could not find first volume", result);
return;
}
wchar_t* deviceName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
wchar_t* pathNames = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
wchar_t* fsName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
while(true) {
// Chop off the trailing '\'
size_t len = wcslen(volumeName);
if (len < 5) {
mark_failed_with_message(env, "volume name is too short", result);
break;
}
volumeName[len-1] = L'\0';
if (QueryDosDeviceW(&volumeName[4], deviceName, MAX_PATH+1) == 0) {
mark_failed_with_errno(env, "could not query dos device", result);
break;
}
volumeName[len-1] = L'\\';
DWORD used;
if (GetVolumePathNamesForVolumeNameW(volumeName, pathNames, MAX_PATH+1, &used) == 0) {
// TODO - try again if the buffer is too small
mark_failed_with_errno(env, "could not query volume paths", result);
break;
}
wchar_t* cur = pathNames;
if (cur[0] != L'\0') {
// TODO - use GetDriveTypeW() to determine if removable, remote, etc
if(GetVolumeInformationW(cur, NULL, 0, NULL, NULL, NULL, fsName, MAX_PATH+1) == 0) {
if (GetLastError() != ERROR_NOT_READY) {
mark_failed_with_errno(env, "could not query volume information", result);
break;
}
wcscpy(fsName, L"unknown");
}
for (;cur[0] != L'\0'; cur += wcslen(cur) + 1) {
env->CallVoidMethod(info, method,
wchar_to_java(env, cur, wcslen(cur), result),
wchar_to_java(env, fsName, wcslen(fsName), result),
wchar_to_java(env, deviceName, wcslen(deviceName), result),
JNI_FALSE);
}
}
if (FindNextVolumeW(handle, volumeName, MAX_PATH) == 0) {
if (GetLastError() != ERROR_NO_MORE_FILES) {
mark_failed_with_errno(env, "could find next volume", result);
}
break;
}
}
free(volumeName);
free(deviceName);
free(pathNames);
free(fsName);
FindVolumeClose(handle);
}
/*
* Console functions
*/
HANDLE getHandle(JNIEnv *env, int output, jobject result) {
HANDLE handle = output == 0 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE);
if (handle == INVALID_HANDLE_VALUE) {
mark_failed_with_errno(env, "could not get console handle", result);
return NULL;
}
return handle;
}
JNIEXPORT jboolean JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
HANDLE handle = getHandle(env, output, result);
if (handle == NULL) {
return JNI_FALSE;
}
if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
if (GetLastError() == ERROR_INVALID_HANDLE) {
return JNI_FALSE;
}
mark_failed_with_errno(env, "could not get console buffer", result);
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
HANDLE handle = getHandle(env, output, result);
if (handle == NULL) {
mark_failed_with_message(env, "not a console", result);
return;
}
if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
jclass dimensionClass = env->GetObjectClass(dimension);
jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I");
env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1);
jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I");
env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1);
}
HANDLE current_console = NULL;
WORD original_attributes = 0;
WORD current_attributes = 0;
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_initConsole(JNIEnv *env, jclass target, jint output, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
HANDLE handle = getHandle(env, output, result);
if (handle == NULL) {
mark_failed_with_message(env, "not a terminal", result);
return;
}
if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
if (GetLastError() == ERROR_INVALID_HANDLE) {
mark_failed_with_message(env, "not a console", result);
} else {
mark_failed_with_errno(env, "could not get console buffer", result);
}
return;
}
current_console = handle;
original_attributes = console_info.wAttributes;
current_attributes = original_attributes;
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result);
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) {
current_attributes |= FOREGROUND_INTENSITY;
if (!SetConsoleTextAttribute(current_console, current_attributes)) {
mark_failed_with_errno(env, "could not set text attributes", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) {
current_attributes &= ~FOREGROUND_INTENSITY;
if (!SetConsoleTextAttribute(current_console, current_attributes)) {
mark_failed_with_errno(env, "could not set text attributes", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) {
current_attributes = original_attributes;
if (!SetConsoleTextAttribute(current_console, current_attributes)) {
mark_failed_with_errno(env, "could not set text attributes", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) {
current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN);
switch (color) {
case 0:
break;
case 1:
current_attributes |= FOREGROUND_RED;
break;
case 2:
current_attributes |= FOREGROUND_GREEN;
break;
case 3:
current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN;
break;
case 4:
current_attributes |= FOREGROUND_BLUE;
break;
case 5:
current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE;
break;
case 6:
current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE;
break;
default:
current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
break;
}
if (!SetConsoleTextAttribute(current_console, current_attributes)) {
mark_failed_with_errno(env, "could not set text attributes", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
console_info.dwCursorPosition.X -= count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
console_info.dwCursorPosition.X += count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
console_info.dwCursorPosition.Y -= count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
console_info.dwCursorPosition.Y += count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
console_info.dwCursorPosition.X = 0;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result);
return;
}
DWORD count;
if (!FillConsoleOutputCharacterW(current_console, L' ', console_info.dwSize.X - console_info.dwCursorPosition.X, console_info.dwCursorPosition, &count)) {
mark_failed_with_errno(env, "could not clear console", result);
}
}
void uninheritStream(JNIEnv *env, DWORD stdInputHandle, jobject result) {
HANDLE streamHandle = GetStdHandle(stdInputHandle);
if (streamHandle == NULL) {
// We're not attached to a stdio (eg Desktop application). Ignore.
return;
}
if (streamHandle == INVALID_HANDLE_VALUE) {
mark_failed_with_errno(env, "could not get std handle", result);
return;
}
boolean ok = SetHandleInformation(streamHandle, HANDLE_FLAG_INHERIT, 0);
if (!ok) {
if (GetLastError() != ERROR_INVALID_PARAMETER && GetLastError() != ERROR_INVALID_HANDLE) {
mark_failed_with_errno(env, "could not change std handle", result);
}
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsHandleFunctions_markStandardHandlesUninheritable(JNIEnv *env, jclass target, jobject result) {
uninheritStream(env, STD_INPUT_HANDLE, result);
uninheritStream(env, STD_OUTPUT_HANDLE, result);
uninheritStream(env, STD_ERROR_HANDLE, result);
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsHandleFunctions_restoreStandardHandles(JNIEnv *env, jclass target, jobject result) {
}
HKEY get_key_from_ordinal(jint keyNum) {
return keyNum == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
}
JNIEXPORT jstring JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions_getStringValue(JNIEnv *env, jclass target, jint keyNum, jstring subkey, jstring valueName, jobject result) {
HKEY key = get_key_from_ordinal(keyNum);
wchar_t* subkeyStr = java_to_wchar(env, subkey, result);
wchar_t* valueNameStr = java_to_wchar(env, valueName, result);
DWORD size = 0;
LONG retval = SHRegGetValueW(key, subkeyStr, valueNameStr, SRRF_RT_REG_SZ, NULL, NULL, &size);
if (retval != ERROR_SUCCESS) {
free(subkeyStr);
free(valueNameStr);
if (retval != ERROR_FILE_NOT_FOUND) {
mark_failed_with_code(env, "could not determine size of registry value", retval, NULL, result);
}
return NULL;
}
wchar_t* value = (wchar_t*)malloc(sizeof(wchar_t) * (size+1));
retval = SHRegGetValueW(key, subkeyStr, valueNameStr, SRRF_RT_REG_SZ, NULL, value, &size);
free(subkeyStr);
free(valueNameStr);
if (retval != ERROR_SUCCESS) {
free(value);
mark_failed_with_code(env, "could not get registry value", retval, NULL, result);
return NULL;
}
jstring jvalue = wchar_to_java(env, value, wcslen(value), result);
free(value);
return jvalue;
}
JNIEXPORT jboolean JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions_getSubkeys(JNIEnv *env, jclass target, jint keyNum, jstring subkey, jobject subkeys, jobject result) {
wchar_t* subkeyStr = java_to_wchar(env, subkey, result);
jclass subkeys_class = env->GetObjectClass(subkeys);
jmethodID method = env->GetMethodID(subkeys_class, "add", "(Ljava/lang/Object;)Z");
HKEY key;
LONG retval = RegOpenKeyExW(get_key_from_ordinal(keyNum), subkeyStr, 0, KEY_READ, &key);
if (retval != ERROR_SUCCESS) {
free(subkeyStr);
if (retval != ERROR_FILE_NOT_FOUND) {
mark_failed_with_code(env, "could open registry key", retval, NULL, result);
}
return false;
}
DWORD subkeyCount;
DWORD maxSubkeyLen;
retval = RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkeyCount, &maxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
if (retval != ERROR_SUCCESS) {
mark_failed_with_code(env, "could query registry key", retval, NULL, result);
} else {
wchar_t* keyNameStr = (wchar_t*)malloc(sizeof(wchar_t) * (maxSubkeyLen+1));
for (int i = 0; i < subkeyCount; i++) {
DWORD keyNameLen = maxSubkeyLen + 1;
retval = RegEnumKeyExW(key, i, keyNameStr, &keyNameLen, NULL, NULL, NULL, NULL);
if (retval != ERROR_SUCCESS) {
mark_failed_with_code(env, "could enumerate registry subkey", retval, NULL, result);
break;
}
env->CallVoidMethod(subkeys, method, wchar_to_java(env, keyNameStr, wcslen(keyNameStr), result));
}
free(keyNameStr);
}
RegCloseKey(key);
free(subkeyStr);
return true;
}
JNIEXPORT jboolean JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions_getValueNames(JNIEnv *env, jclass target, jint keyNum, jstring subkey, jobject names, jobject result) {
wchar_t* subkeyStr = java_to_wchar(env, subkey, result);
jclass names_class = env->GetObjectClass(names);
jmethodID method = env->GetMethodID(names_class, "add", "(Ljava/lang/Object;)Z");
HKEY key;
LONG retval = RegOpenKeyExW(get_key_from_ordinal(keyNum), subkeyStr, 0, KEY_READ, &key);
if (retval != ERROR_SUCCESS) {
free(subkeyStr);
if (retval != ERROR_FILE_NOT_FOUND) {
mark_failed_with_code(env, "could open registry key", retval, NULL, result);
}
return false;
}
DWORD valueCount;
DWORD maxValueNameLen;
retval = RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, &maxValueNameLen, NULL, NULL, NULL);
if (retval != ERROR_SUCCESS) {
mark_failed_with_code(env, "could query registry key", retval, NULL, result);
} else {
wchar_t* valueNameStr = (wchar_t*)malloc(sizeof(wchar_t) * (maxValueNameLen+1));
for (int i = 0; i < valueCount; i++) {
DWORD valueNameLen = maxValueNameLen + 1;
retval = RegEnumValueW(key, i, valueNameStr, &valueNameLen, NULL, NULL, NULL, NULL);
if (retval != ERROR_SUCCESS) {
mark_failed_with_code(env, "could enumerate registry value name", retval, NULL, result);
break;
}
env->CallVoidMethod(names, method, wchar_to_java(env, valueNameStr, wcslen(valueNameStr), result));
}
free(valueNameStr);
}
RegCloseKey(key);
free(subkeyStr);
return true;
}
#endif