I have a Java Swing application with a couple of pieces of native functionality added via JNI. One thing this application does is show a tree view of the file system. I was trying to write a little JNI so that, on Windows systems, I could provide a “Properties…” context menu item that displayed the standard file “Properties” dialog from Windows.
I scraped the internet for the Win32 code needed to do this, and came up with this:
JNIEXPORT jboolean JNICALL Java_com_foobar_showFilePropertiesDialogImpl
(JNIEnv *env, jobject obj, jlong hwnd, jstring fileName)
{
LPITEMIDLIST pidl;
LPCITEMIDLIST pidlItem;
HRESULT hr;
IShellFolder *pFolder;
IContextMenu *pContextMenu;
CMINVOKECOMMANDINFO cmi;
const wchar_t *pszFile;
if (!coInitialized)
{
#ifdef DEBUG
MessageBoxW(NULL, L"Initializing COM (should happen just once)...", L"Initializing COM...", MB_OK);
#endif
CoInitialize(NULL);
coInitialized = true;
}
/* Get the name of the file. */
pszFile = (wchar_t *)env->GetStringChars(fileName, NULL);
if (pszFile==NULL) { /* Exception occurred */
return JNI_FALSE;
}
hr = SHGetDesktopFolder(&pFolder);
if (FAILED(hr))
{
env->ReleaseStringChars(fileName, (const jchar *)pszFile);
return JNI_FALSE;
}
hr = pFolder->ParseDisplayName(HWND_DESKTOP, NULL, (LPTSTR)pszFile, NULL, &pidl, NULL);
pFolder->Release();
if (FAILED(hr))
{
env->ReleaseStringChars(fileName, (const jchar *)pszFile);
return JNI_FALSE;
}
hr = SHBindToParent(pidl, IID_IShellFolder, (void **)&pFolder, &pidlItem);
if (FAILED(hr))
{
SHFree(pidl);
env->ReleaseStringChars(fileName, (const jchar *)pszFile);
return JNI_FALSE;
}
hr = pFolder->GetUIObjectOf(HWND_DESKTOP, 1, (LPCITEMIDLIST *)&pidlItem, IID_IContextMenu, NULL, (void **)&pContextMenu);
pFolder->Release();
if(SUCCEEDED(hr))
{
ZeroMemory(&cmi, sizeof(cmi));
cmi.cbSize = sizeof(cmi);
if (hwnd>0)
{
cmi.hwnd = (HWND)hwnd;
}
cmi.lpVerb = "properties";
cmi.nShow = SW_SHOWNORMAL;
hr = pContextMenu->InvokeCommand(&cmi);
#ifdef DEBUG
if (FAILED(hr))
{
wchar_t msg[2048];
wsprintf(msg, L"InvokeCommand failed: %d - %x", SUCCEEDED(hr), hr);
MessageBoxW(NULL, pszFile, msg, MB_OK);
}
else
{
MessageBoxW(NULL, L"InvokeCommand successful!", L"InvokeCommand Status", MB_OK);
}
#endif
}
pContextMenu->Release();
SHFree(pidl);
env->ReleaseStringChars(fileName, (const jchar *)pszFile);
return JNI_TRUE;
}
From Java, I run this method from the EDT. Unfortunately, the properties dialog doesn’t display if DEBUG is not defined. It seems that if I define DEBUG, the properties dialog will pop up after the “InvokeCommand successful!” MessageBox.
Another thing I noticed is, when DEBUG is not defined, I call this method (and the properties dialog is not displayed), it will display later on in the application if I open a different “native” window. For example, I have code that opens the native Delete dialog via SHFileOperation (which works); as soon as I display that dialog, any File properties dialogs that didn’t originally display will suddenly appear.
It seems as though I’m doing stuff on the wrong thread perhaps. Is the EDT not the thread I want to display native windows from? But I’m confused because again, the delete dialog is working fine.
Instead of digging through the Shell interfaces manually, try using
ShellExecute()instead and let the OS do the heavy work for you: