JNI crash outside JVM - EXCEPTION_ACCESS_VIOLATION - java-native-interface

I needed a Java application to manipulate the Windows registry. To do this, I wrote some native C++ code and used JNI to call it.
For the most part it works. During testing I hadn't heard of a single problem. However, recently a user experienced a crash in the C++ code that brought down the JVM. The details are below. I have almost no C++ experience, so I think that is where I need the help. I think the issue is related to permissions, since when the Java app is run as an administrator, the problem goes away. The issue there is that not all our users will have admin privileges on their machines.
hs_err_pid log info:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000076859083, pid=5316, tid=3116
#
# JRE version: 6.0_20-b02
# Java VM: Java HotSpot(TM) 64-Bit Server VM (16.3-b01 mixed mode windows-amd64 )
# Problematic frame:
# C [kernel32.dll+0x9083]
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Stack: [0x0000000013010000,0x0000000013110000], sp=0x000000001310f2d0, free space=3fc0000000000000000k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [kernel32.dll+0x9083]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j my.package.WinRegUtils.SetKeyValue(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z+0
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
Here is the native code/method in question
JNIEXPORT jboolean JNICALL Java_my_package_WinRegUtils_SetKeyValue
(JNIEnv* env, jclass clazz, jstring jRootKey, jstring jKey, jstring jValueName, jstring jValueData, jstring jDataType)
{
bool success = false;
WCHAR* key_w;
WCHAR* valueName_w;
WCHAR* valueData_w;
WCHAR* dataType_w;
HKEY hKey;
const char* rootKey = env->GetStringUTFChars(jRootKey, false);
const char* key = env->GetStringUTFChars(jKey, false);
convertString(key, &key_w);
const char* valueName = env->GetStringUTFChars(jValueName, false);
convertString(valueName, &valueName_w);
const char* valueData = env->GetStringUTFChars(jValueData, false);
convertString(valueData, &valueData_w);
const char* dataType = env->GetStringUTFChars(jDataType, false);
convertString(dataType, &dataType_w);
HKEY root = GetRootHKEY(rootKey);
if (RegOpenKeyEx(root, key_w, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
{
DWORD dwType;
BYTE* data;
DWORD length;
if (strcmp("REG_SZ", dataType) == 0)
{
dwType = REG_SZ;
data = (BYTE*)valueData_w;
length = wcslen(valueData_w) * sizeof(DWORD);
}
else if (strcmp("REG_DWORD", dataType) == 0)
{
dwType = REG_DWORD;
const DWORD intData = atoi(valueData);
data = (BYTE*)&intData;
length = sizeof(DWORD);
}
if (RegSetValueEx(hKey, valueName_w, 0, dwType, data, length) == ERROR_SUCCESS)
{
success = true;
RegCloseKey(hKey);
}
}
env->ReleaseStringUTFChars(jRootKey, rootKey);
env->ReleaseStringUTFChars(jKey, key);
env->ReleaseStringUTFChars(jValueName, valueName);
env->ReleaseStringUTFChars(jValueData, valueData);
env->ReleaseStringUTFChars(jDataType, dataType);
delete[] key_w, valueName_w, valueData_w, dataType_w;
return success;
}

To debug, add a few
printf("debug xxx");
fflush(stdout);
in
Java_my_package_WinRegUtils_SetKeyValue()
to isolate the failing line.

Related

How to get the localized return string of C++ strerror?

My Minecraft server crushed caused by insufficient spaces. About 2 hours before crushed, there were some plugins printing exception stack traces including java.io.ioexception: 设备上没有空间 (means java.io.IOException: No space left on device). After I restart it, I found that all the buildings players built in the 2 hours disappeared, and no any plugins can restore it (include CoreProtect, because of insufficient spaces, it can not submit database transactions).
In order to close server immediately when there's no enough spaces, I decided to develop a plugin. Because there's no way to listen exceptions thrown by all threads, I implemented this function by listen logs.
Any code print log like java.io.ioexception: ${message} or [...] ... [...]: java.io.ioexception: ${message} will trigger the shutdown of server.
Pay attention to the error message: In some platforms, it's 设备上没有空间; In some others platforms, it's No space left on device... So It's necessary to get the localized version of it firstly.
This error message, comes from Operating System, is strerror(28) (C++ function) in Windows or Linux. So I create a native function to invoke it:
// cn.chuanwise.nessc.errno.ErrorMessage;
public class ErrorMessage {
public native static String of(int errno, Locale locale);
}
#include "localed-error-message-native.h"
#include <locale>
#include <string.h>
JNIEXPORT jstring JNICALL Java_cn_chuanwise_nessc_errno_ErrorMessage_of(JNIEnv* env, jclass clz, jint code, jobject locale) {
// find locale class
const jclass locale_class = env->FindClass("java/util/Locale");
const jmethodID to_string_method = env->GetMethodID(locale_class, "toString", "()Ljava/lang/String;");
const jobject locale_string = env->CallObjectMethod(locale, to_string_method);
const char* locale_name = env->GetStringUTFChars(reinterpret_cast<jstring>(locale_string), nullptr);
// set locale
std::setlocale(LC_ALL, locale_name);
const char* strerr = strerror(static_cast<int>(code));
if (strerr == nullptr) {
return nullptr;
} else {
return env->NewStringUTF(strerr);
}
}
ErrorMessage.of(28, Locale.getDefault()) returns No space left on device in my device, but this code throws java.io.IOException: 设备上没有空间 in my device:
final File file = new File("test");
// ...
try (OutputStream outputStream = new FileOutputStream(file)) {
final byte[] buf = new byte[1024];
while (true) {
outputStream.write(buf);
}
}
How to get the localized return string of strerror?

Reading network computer names using mfc in windows 10

I tried to read network computer names using WNetOpenEnum. I am getting only 'Microsoft Terminal Services', 'Microsoft Windows Network' and 'Web Client Network'. Not getting the other machine connected in network.
Is there any way to read the names/IP of computers connected to network?.
if(NO_ERROR == WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, pnetrParent, &hEnum))
{
DWORD dwCount = 1;
char szBuffer[512];
char* psz = szBuffer;
DWORD dwBufferSize = sizeof(szBuffer);
while (NO_ERROR == WNetEnumResource(hEnum, &dwCount, &szBuffer, &dwBufferSize))
{
NETRESOURCE* pnetResource = (NETRESOURCE*)psz;
if (NULL != pnetResource->lpRemoteName && *pnetResource->lpRemoteName)
{
m_lstIPAddress.AddString(pnetResource->lpRemoteName);
}
dwBufferSize = sizeof(szBuffer);
}
DWORD retValue = WNetCloseEnum(hEnum);
}
Any help would be appreciated.
You need to call it recursively. Microsoft Windows Network has computers listed. So next call to WNetOpenEnum will have handle to Microsoft Windows Network you received as first parameter and so on.
I believe this would give you answer: https://learn.microsoft.com/en-us/windows/win32/wnet/enumerating-network-resources

SetupDiSetClassInstallParamsW Windows API failing

Below is the code which is calling Windows SetupDiSetClassInstallParamsW
method --
bool
Util::SvmDisableNic(HDEVINFO devInfoSet, SP_DEVINFO_DATA deviceInfo)
{
SP_PROPCHANGE_PARAMS params = {0};
SP_DEVINSTALL_PARAMS_W installParams = {0};
//
// Disable device
//
params.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
params.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
params.StateChange = DICS_DISABLE;
params.Scope = DICS_FLAG_GLOBAL;
params.HwProfile = 0;
if (!SetupDiSetClassInstallParamsW(devInfoSet, &deviceInfo,
&(params.ClassInstallHeader),
sizeof params)) {
DWORD err = GetLastError();
SYSMSG_FUNC(
Debug, _T("Failed setting device params to disable: Err: 0x%X"), err);
return false;
}
This code is running successfully, however, in a couple of VMs, the
SetupDiSetClassInstallParamsW is failing, and GetLastError is returning an error code of 0xD. From microsoft doc, looks like this corresponds to ERROR_INVALID_DATA. The VMs in which this code is running successfully and in those in which it is failing, are identical in terms of hardware/software configuration. Any idea how we can debug why this windows API is failing?

How do I access services of UPnP device?

The device: Belkin Wemo Switch
Dev environment: MS VC++ 2010 on Windows7
I'm trying to enumerate the services of a UPnP device using C++ from Windows.
I've got the IUPnPDevice pointer and can access several properties.
I've got the IUPnPServices pointer and can count the correct number of services (7).
I use QueryInterface() to get the IEnumVARIANT pointer (which appears to succeed).
However, the Next() method always fails with HRESULT of 0x80040500 - which translates as Windows error 1280 (0x500) - ERROR_ALREADY_FIBER.
This error does not make any sense to me.
(I've tried using both IEnumVARIANT and IEnumUnknown - as the docs indicate it could be either, but both produce the same result.)
I've included below the complete source file, plus the output it produces.
[Note: It's hardcoded to use the udn of my own device]
I'd be very grateful if anyone can help as I'm currently stuck.
Best regards,
Dave
Code:
// UpnpTest1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <upnp.h>
static void DumpComError(const TCHAR *api, HRESULT hr);
int _tmain(int argc, _TCHAR* argv[])
{
int retcode=-1; // assume failure
HRESULT hr = CoInitialize(0);
if (hr==S_OK)
{
IUPnPDeviceFinder *deviceFinder=0;
hr = CoCreateInstance(CLSID_UPnPDeviceFinder, 0, CLSCTX_INPROC_SERVER, IID_IUPnPDeviceFinder, (void**)&deviceFinder);
if (hr==S_OK)
{
IUPnPDevice *device=0;
hr = deviceFinder->FindByUDN(L"uuid:Socket-1_0-221239K11002F6", &device);
if (hr==S_OK)
{
if (device)
{
TCHAR *manufacturer=0, *manufacturerUrl=0;
TCHAR *description=0, *name=0, *modelUrl=0;
TCHAR *serialNumber=0, *udn=0, *upc=0, *deviceType=0;
TCHAR *presentationUrl=0;
device->get_ManufacturerName(&manufacturer);
device->get_ManufacturerURL(&manufacturerUrl);
device->get_Description(&description);
device->get_FriendlyName(&name);
device->get_ModelURL(&modelUrl);
device->get_SerialNumber(&serialNumber);
device->get_UniqueDeviceName(&udn);
device->get_UPC(&upc);
device->get_Type(&deviceType);
device->get_PresentationURL(&presentationUrl);
_tprintf(_T("MANUFACTURER: %s [%s]\n"), manufacturer, manufacturerUrl);
_tprintf(_T("MODEL: %s [%s]\n [%s]\n"), description, name, modelUrl);
_tprintf(_T("DEVICE: serial=%s\n udn=%s\n upc=%s\n type=%s\n"), serialNumber, udn, upc, deviceType);
_tprintf(_T("URL: %s\n"), presentationUrl);
IUPnPServices *services=0;
hr = device->get_Services(&services);
if (hr==S_OK)
{
if (services)
{
long numberOfServices=0;
services->get_Count(&numberOfServices);
if (numberOfServices>0)
{
IUnknown *unknown=0;
hr = services->get__NewEnum(&unknown);
if (hr==S_OK)
{
if (unknown)
{
IEnumVARIANT *enumInterface=0;
hr = unknown->QueryInterface(IID_IEnumVARIANT,(void**)&enumInterface);
if (enumInterface)
{
VARIANT var;
unsigned long fetched=0;
hr = enumInterface->Next(1, &var, &fetched);
if (hr==S_OK)
{
}
else
DumpComError(_T("IEnumVARIANT::Next"), hr);
}
else
DumpComError(_T("IUnknown::QueryInterface"), hr);
}
else
fprintf(stderr, "Failed to get enumeration interface.\n");
}
else
DumpComError(_T("IUPnPServices::get__NewEnum"), hr);
}
else
fprintf(stderr, "No services available.\n");
}
else
fprintf(stderr, "Failed to get services collection.\n");
}
else
DumpComError(_T("IUPnPDevice::get_Services"), hr);
}
else
fprintf(stderr, "Device not found.\n");
}
else
DumpComError(_T("IUPnPDeviceFinder::FindByUDN"), hr);
}
else
DumpComError(_T("CoCreateIndex"), hr);
}
else
DumpComError(_T("CoInitialize"), hr);
return retcode;
}
static void AddBoolToString(const TCHAR *name, bool value, TCHAR *buf, int &i, int max)
{
if (name && *name && value && buf && i>=0)
i += _snwprintf_s(&buf[i], max-i, (max-i-1)*sizeof(TCHAR), _T("%s%s=YES"), (i>0? _T("; "): _T("")), name);
}
static void AddIntToString(const TCHAR *name, int value, TCHAR *buf, int &i, int max)
{
if (name && *name && value && buf && i>=0)
i += _snwprintf_s(&buf[i], max-i, (max-i-1)*sizeof(TCHAR), _T("%s%s=%d"), (i>0? _T("; "): _T("")), name, value);
}
static void DumpComError(const TCHAR *api, HRESULT hr)
{
bool failure = (hr&0x80000000? true: false);
bool severe = (hr&0x40000000? true: false);
bool microsoft = (hr&0x20000000? false: true);
bool ntStatus = (hr&0x10000000? true: false);
bool xBit = (hr&0x08000000? true: false);
int facility = (hr&0x07FF0000)>>16;
int code = (hr&0x0000FFFF);
TCHAR buf[1024]={0};
int bufsize = sizeof(buf)/sizeof(TCHAR);
int i=0;
AddBoolToString(_T("failure"), failure, buf, i, bufsize);
AddBoolToString(_T("severe"), severe, buf, i, bufsize);
AddBoolToString(_T("microsoft"), microsoft, buf, i, bufsize);
AddBoolToString(_T("ntStatus"), ntStatus, buf, i, bufsize);
AddBoolToString(_T("xBit"), xBit, buf, i, bufsize);
AddIntToString(_T("facility"), facility, buf, i, bufsize);
AddIntToString(_T("code"), code, buf, i, bufsize);
_ftprintf(stderr, _T("\n%s() failed, hr=0x%08x\n[%s]\n"), api, hr, buf);
}
Output:
It produces following output:
MANUFACTURER: Belkin International Inc. [http://www.belkin.com/]
MODEL: Belkin Plugin Socket 1.0 [WeMo Switch]
[http://www.belkin.com/plugin/]
DEVICE: serial=221239K11002F6
udn=uuid:Socket-1_0-221239K11002F6
upc=123456789
type=urn:Belkin:device:controllee:1
URL: http://192.168.1.16:49153/pluginpres.html
IEnumVARIANT::Next() failed, hr=0x80040500
[failure=YES; microsoft=YES; facility=4; code=1280]
EDIT:
After a lot of dead-ends, I have managed to get this working by manually building the SOAP requests, and sending the requests via TCP using Windows sockets. Tricky bit was getting the syntax just right as I had no experience of SOAP before. [UPnP was useful to identify the IP address & port number - as these can change]. Once up and running - it's actually a lot simpler than the UPnP interface. Let me know if you're interested and I can post the code... It doesn't directly answer the question I posed here, so it wouldn't make sense to answer my question with this detail.
However, if you're interested, let me know and I can post the code.
Cheers,
Dave
HRESULT of 0x80040500 is not what you think, but UPNP_E_INVALID_DOCUMENT. For explanation of how such ambiguity is possible, see my answer in another SO question.
My guess is that your Belkin device is giving non-conformant device description or service description XML. Non-conformant doesn't necessarily mean broken format, UPnP specification has tons of secondary requirements. Try Device Spy from Intel Developer Tools (link at bottom of the other answer), if the device pops up, then run Device Validator from the same suite on it.
My experience is similar, in that UPnPDeviceFinder simply doesn't work. It never sends out the UPnP Search packet, so the devices don't respond. The only way to get it to work is if you also use windows media player or the "Cast To Device" menu (which is WMP) to initiate the search. As it is UPnPDeviceFinder will return some devices, only if they happen to be broadcasting at that moment, but even finding an XBox (another Microsoft product) doesn't work in the examples without other activity going on.

How do I read from a version resource in Visual C++

I have a version resource in my resources in a C++ project which contains version number, copyright and build details. Is there an easy way to access this at run-time to populate my help/about dialog as I am currently maintaining seperate const values of this information. Ideally, the solution should work for Windows/CE mobile and earlier versions of Visual C++ (6.0 upwards).
This is an edited version of my original answer.
bool GetProductAndVersion(CStringA & strProductName, CStringA & strProductVersion)
{
// get the filename of the executable containing the version resource
TCHAR szFilename[MAX_PATH + 1] = {0};
if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0)
{
TRACE("GetModuleFileName failed with error %d\n", GetLastError());
return false;
}
// allocate a block of memory for the version info
DWORD dummy;
DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
if (dwSize == 0)
{
TRACE("GetFileVersionInfoSize failed with error %d\n", GetLastError());
return false;
}
std::vector<BYTE> data(dwSize);
// load the version info
if (!GetFileVersionInfo(szFilename, NULL, dwSize, &data[0]))
{
TRACE("GetFileVersionInfo failed with error %d\n", GetLastError());
return false;
}
// get the name and version strings
LPVOID pvProductName = NULL;
unsigned int iProductNameLen = 0;
LPVOID pvProductVersion = NULL;
unsigned int iProductVersionLen = 0;
// replace "040904e4" with the language ID of your resources
if (!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductName"), &pvProductName, &iProductNameLen) ||
!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return false;
}
strProductName.SetString((LPCSTR)pvProductName, iProductNameLen);
strProductVersion.SetString((LPCSTR)pvProductVersion, iProductVersionLen);
return true;
}
To get a language independent result to Mark's answer change :
// replace "040904e4" with the language ID of your resources
!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return false;
}
To
UINT uiVerLen = 0;
VS_FIXEDFILEINFO* pFixedInfo = 0; // pointer to fixed file info structure
// get the fixed file info (language-independent)
if(VerQueryValue(&data[0], TEXT("\\"), (void**)&pFixedInfo, (UINT *)&uiVerLen) == 0)
{
return false;
}
strProductVersion.Format("%u.%u.%u.%u",
HIWORD (pFixedInfo->dwProductVersionMS),
LOWORD (pFixedInfo->dwProductVersionMS),
HIWORD (pFixedInfo->dwProductVersionLS),
LOWORD (pFixedInfo->dwProductVersionLS));
Something like might get you started, perhaps:
TCHAR moduleName[MAX_PATH+1];
(void)GetModuleFileName(AfxGetInstanceHandle(), moduleName, MAX_PATH);
DWORD dummyZero;
DWORD versionSize = GetFileVersionInfoSize(moduleName, &dummyZero);
if(versionSize == 0)
{
return NULL;
}
void* pVersion = malloc(versionSize);
if(pVersion == NULL)
{
return NULL;
}
if(!GetFileVersionInfo(moduleName, NULL, versionSize, pVersion))
{
free(pVersion);
return NULL;
}
UINT length;
VS_FIXEDFILEINFO* pFixInfo;
VERIFY(VerQueryValue(pVersionInfo, const_cast<LPTSTR>("\\"), (LPVOID*)&pFixInfo, &length));
Something like this will give you raw access to the resource data and get you started:
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(MY_VERSION_ID), RT_VERSION);
DWORD size = ::SizeofResource(NULL, res);
HGLOBAL mem = ::LoadResource(NULL, res);
LPVOID raw_data = ::LockResource(mem);
...
::FreeResource(mem);
Beware!
Using FindResource..LockResource is not correct. It will sometimes work (as it did in my small demo program) and sometimes cause access violations (example: the production code I was making the demo for).
The VerQueryValue() documentation states that you should call GetFileVersionInfoSize and GetFileVersionInfo instead.
Raymond Chen explains, see http://blogs.msdn.com/oldnewthing/archive/2006/12/26/1365215.aspx
Ok, a bit more googleing found the following on CodeGuru. Basically this approach uses the CFileVersionInfo object to get on any given file. It should be interesting to see if it works on the currently running .EXE file and on Windows CE.
Sometimes I receive Access Violation when use VerQueryValueA. But I never got this error when use VerQueryValueW. I think something wrong with VerQueryValueA in version.dll. Therefore I use VerQueryValueW instead of VerQueryValueA even in projects Multi-byte Character Encoding. Here is my code of ReadVersion function