Skip to content

Commit

Permalink
accommodate long path names, fixes #243
Browse files Browse the repository at this point in the history
  • Loading branch information
twall committed Jun 16, 2013
1 parent 418a538 commit 1737d6a
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Features
* [#236](https://github.com/twall/jna/issues/236): Auto-strip profiler native method prefix specified by `jna.profiler.prefix`, which defaults to $$YJP$$ - [@twall](https://github.com/twall).
* Added `jna.debug_load` property to diagnose library loading issues - [@twall](https://github.com/twall).
* Throw explicit `IllegalArgumentException` when `Structure.ByReference` is used where it shouldn't be (can result in multiply freed memory or other unexpected behavior) - [@twall](https://github.com/twall).
* [#243](https://github.com/twall/jna/issues/243): Automatically accommodate long library paths on Windows which would otherwise fail - [@twall](https://github.com/twall).

Bug Fixes
---------
Expand Down
Binary file modified lib/native/win32-x86.jar
Binary file not shown.
206 changes: 124 additions & 82 deletions native/dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include <windows.h>
#include <psapi.h>
#define STRTYPE wchar_t*
#define NAME2CSTR(ENV,JSTR) newWideCString(ENV,JSTR)
#define NAME2CSTR(ENV,JSTR) w32_short_name(ENV,JSTR)
#ifdef _WIN32_WCE
#include <tlhelp32.h>
#define DEFAULT_LOAD_OPTS 0 /* altered search path unsupported on CE */
Expand Down Expand Up @@ -98,81 +98,6 @@ static int _protect;
extern "C" {
#endif

#ifdef _WIN32
static char*
w32_format_error(int err, char* buf, int len) {
wchar_t* wbuf = NULL;
int wlen =
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, err, 0, (LPWSTR)&wbuf, 0, NULL);
if (wlen > 0) {
int result = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, len, NULL, NULL);
if (result == 0) {
fprintf(stderr, "JNA: error converting error message: %d\n", (int)GET_LAST_ERROR());
*buf = 0;
}
else {
buf[len-1] = 0;
}
}
else {
// Error retrieving message
*buf = 0;
}
if (wbuf) {
LocalFree(wbuf);
}

return buf;
}
static HANDLE
w32_find_entry(JNIEnv* env, HANDLE handle, const char* funname) {
void* func = NULL;
if (handle != GetModuleHandle(NULL)) {
func = GetProcAddress(handle, funname);
}
else {
#if defined(_WIN32_WCE)
/* CE has no EnumProcessModules, have to use an alternate API */
HANDLE snapshot;
if ((snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)) != INVALID_HANDLE_VALUE) {
MODULEENTRY32 moduleInfo;
moduleInfo.dwSize = sizeof(moduleInfo);
if (Module32First(snapshot, &moduleInfo)) {
do {
if ((func = (void *) GetProcAddress(moduleInfo.hModule, funname))) {
break;
}
} while (Module32Next(snapshot, &moduleInfo));
}
CloseToolhelp32Snapshot(snapshot);
}
#else
HANDLE cur_proc = GetCurrentProcess ();
HMODULE *modules;
DWORD needed, i;
if (!EnumProcessModules (cur_proc, NULL, 0, &needed)) {
fail:
throwByName(env, EError, "Unexpected error enumerating modules");
return 0;
}
modules = (HMODULE*) alloca (needed);
if (!EnumProcessModules (cur_proc, modules, needed, &needed)) {
goto fail;
}
for (i = 0; i < needed / sizeof (HMODULE); i++) {
if ((func = (void *) GetProcAddress (modules[i], funname))) {
break;
}
}
#endif
}
return func;
}
#endif /* _WIN32 */

#define MEMCPY(ENV,D,S,L) do { \
PSTART(); memcpy(D,S,L); PEND(ENV); \
} while(0)
Expand Down Expand Up @@ -304,6 +229,118 @@ static ffi_type* getStructureType(JNIEnv *, jobject);

typedef void (JNICALL* release_t)(JNIEnv*,jarray,void*,jint);

#ifdef _WIN32
static char*
w32_format_error(int err, char* buf, int len) {
wchar_t* wbuf = NULL;
int wlen =
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, err, 0, (LPWSTR)&wbuf, 0, NULL);
if (wlen > 0) {
int result = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, len, NULL, NULL);
if (result == 0) {
fprintf(stderr, "JNA: error converting error message: %d\n", (int)GET_LAST_ERROR());
*buf = 0;
}
else {
buf[len-1] = 0;
}
}
else {
// Error retrieving message
*buf = 0;
}
if (wbuf) {
LocalFree(wbuf);
}

return buf;
}
static wchar_t*
w32_short_name(JNIEnv* env, jstring str) {
wchar_t* wstr = newWideCString(env, str);
if (wstr && *wstr) {
DWORD required;
size_t size = wcslen(wstr) + 5;
wchar_t* prefixed = (wchar_t*)alloca(sizeof(wchar_t) * size);

#ifdef _MSC_VER
swprintf(prefixed, size, L"\\\\?\\%ls", wstr);
#else
swprintf(prefixed, L"\\\\?\\%ls", wstr);
#endif
if ((required = GetShortPathNameW(prefixed, NULL, 0)) != 0) {
wchar_t* wshort = (wchar_t*)malloc(sizeof(wchar_t) * required);
if (GetShortPathNameW(prefixed, wshort, required)) {
free((void *)wstr);
wstr = wshort;
}
else {
char buf[MSG_SIZE];
throwByName(env, EError, LOAD_ERROR(buf, sizeof(buf)));
free((void *)wstr);
free((void *)wshort);
wstr = NULL;
}
}
else if (GET_LAST_ERROR() != ERROR_FILE_NOT_FOUND) {
char buf[MSG_SIZE];
throwByName(env, EError, LOAD_ERROR(buf, sizeof(buf)));
free((void *)wstr);
wstr = NULL;
}
}
return wstr;
}

static HANDLE
w32_find_entry(JNIEnv* env, HANDLE handle, const char* funname) {
void* func = NULL;
if (handle != GetModuleHandle(NULL)) {
func = GetProcAddress(handle, funname);
}
else {
#if defined(_WIN32_WCE)
/* CE has no EnumProcessModules, have to use an alternate API */
HANDLE snapshot;
if ((snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)) != INVALID_HANDLE_VALUE) {
MODULEENTRY32 moduleInfo;
moduleInfo.dwSize = sizeof(moduleInfo);
if (Module32First(snapshot, &moduleInfo)) {
do {
if ((func = (void *) GetProcAddress(moduleInfo.hModule, funname))) {
break;
}
} while (Module32Next(snapshot, &moduleInfo));
}
CloseToolhelp32Snapshot(snapshot);
}
#else
HANDLE cur_proc = GetCurrentProcess ();
HMODULE *modules;
DWORD needed, i;
if (!EnumProcessModules (cur_proc, NULL, 0, &needed)) {
fail:
throwByName(env, EError, "Unexpected error enumerating modules");
return 0;
}
modules = (HMODULE*) alloca (needed);
if (!EnumProcessModules (cur_proc, modules, needed, &needed)) {
goto fail;
}
for (i = 0; i < needed / sizeof (HMODULE); i++) {
if ((func = (void *) GetProcAddress (modules[i], funname))) {
break;
}
}
#endif
}
return func;
}
#endif /* _WIN32 */

#if 0
/** Invokes System.err.println (for debugging only). */
void
Expand Down Expand Up @@ -621,6 +658,8 @@ getChars(JNIEnv* env, wchar_t* volatile dst, jcharArray chars, volatile jint off
int i;
(*env)->GetCharArrayRegion(env, chars, off, count, buf);
for (i=0;i < count;i++) {
// TODO: ensure proper encoding conversion from jchar to native
// wchar_t
dst[i] = (wchar_t)buf[i];
}
dst += count;
Expand Down Expand Up @@ -725,13 +764,16 @@ newCStringEncoding(JNIEnv *env, jstring jstr, const char* encoding)
/* Translates a Java string to a wide C string using the String.toCharArray
* method.
*/
// TODO: are any encoding changes required?
static wchar_t *
newWideCString(JNIEnv *env, jstring str)
{
jcharArray chars = 0;
wchar_t *result = NULL;

if ((*env)->IsSameObject(env, str, NULL)) {
return result;
}

chars = (*env)->CallObjectMethod(env, str, MID_String_toCharArray);
if (!(*env)->ExceptionCheck(env)) {
jint len = (*env)->GetArrayLength(env, chars);
Expand All @@ -741,7 +783,6 @@ newWideCString(JNIEnv *env, jstring str)
throwByName(env, EOutOfMemory, "Can't allocate wide C string");
return NULL;
}
// TODO: ensure proper encoding conversion from jchar to native wchar_t
getChars(env, result, chars, 0, len);
if ((*env)->ExceptionCheck(env)) {
free((void *)result);
Expand Down Expand Up @@ -1816,8 +1857,9 @@ method_handler(ffi_cif* cif, void* volatile resp, void** argp, void *cdata) {
case CVT_ARRAY_LONG:
case CVT_ARRAY_FLOAT:
case CVT_ARRAY_DOUBLE:
if (*(void **)args[i] && release[i])
if (*(void **)args[i] && release[i] != NULL) {
release[i](env, objects[i], elems[i], 0);
}
break;
}
}
Expand Down Expand Up @@ -2036,7 +2078,7 @@ Java_com_sun_jna_Native_open(JNIEnv *env, jclass UNUSED(cls), jstring lib, jint

handle = (void *)LOAD_LIBRARY(libname, flags != -1 ? flags : DEFAULT_LOAD_OPTS);
if (!handle) {
char buf[1024];
char buf[MSG_SIZE];
throwByName(env, EUnsatisfiedLink, LOAD_ERROR(buf, sizeof(buf)));
}
if (libname != NULL) {
Expand All @@ -2054,7 +2096,7 @@ JNIEXPORT void JNICALL
Java_com_sun_jna_Native_close(JNIEnv *env, jclass UNUSED(cls), jlong handle)
{
if (FREE_LIBRARY(L2A(handle))) {
char buf[1024];
char buf[MSG_SIZE];
throwByName(env, EError, LOAD_ERROR(buf, sizeof(buf)));
}
}
Expand All @@ -2075,7 +2117,7 @@ Java_com_sun_jna_Native_findSymbol(JNIEnv *env, jclass UNUSED(cls),
if (funname != NULL) {
func = (void *)FIND_ENTRY(handle, funname);
if (!func) {
char buf[1024];
char buf[MSG_SIZE];
throwByName(env, EUnsatisfiedLink, LOAD_ERROR(buf, sizeof(buf)));
}
free((void *)funname);
Expand Down
25 changes: 25 additions & 0 deletions test/com/sun/jna/LibraryLoadTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,31 @@ public void testLoadLibraryWithUnicodeName() throws Exception {
}
}

public void testLoadLibraryWithLongName() throws Exception {
File tmpdir = Native.getTempDir();
String libName = NativeLibrary.mapSharedLibraryName("testlib");
File src = new File(TESTPATH, libName);
assertTrue("Expected JNA native library at " + src + " is missing", src.exists());

for (int i=0;i < 16;i++) {
tmpdir = new File(tmpdir, "subdir0123456789");
tmpdir.deleteOnExit();
}

final String NAME = getName();
String newLibName = libName.replace("testlib", NAME);
tmpdir.mkdirs();
File dst = new File(tmpdir, newLibName);
copy(src, dst);
try {
NativeLibrary.getInstance(NAME, new TestLoader(tmpdir));
dst.deleteOnExit();
}
catch(UnsatisfiedLinkError e) {
fail("Library '" + newLibName + "' at " + dst + " could not be loaded: " + e);
}
}

public void testLoadFrameworkLibrary() {
if (Platform.isMac()) {
final String PATH = "/System/Library/Frameworks/CoreServices.framework";
Expand Down

0 comments on commit 1737d6a

Please sign in to comment.