1

我有一个 Java Swing 应用程序,其中包含一些通过 JNI 添加的本机功能。此应用程序所做的一件事是显示文件系统的树形视图。我试图编写一个小 JNI,以便在 Windows 系统上,我可以提供一个“属性...”上下文菜单项,该菜单项显示来自 Windows 的标准文件“属性”对话框。

我在互联网上搜索了执行此操作所需的 Win32 代码,并想出了这个:

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;
}

在 Java 中,我从 EDT 运行此方法。不幸的是,如果未定义 DEBUG,则不会显示属性对话框。好像我定义DEBUG的话,在“InvokeCommand成功!”之后会弹出属性对话框 消息框。

我注意到的另一件事是,当未定义 DEBUG 时,我调用此方法(并且不显示属性对话框),如果我打开不同的“本机”窗口,它将稍后显示在应用程序中。例如,我有通过 SHFileOperation 打开本机删除对话框的代码(有效);一旦我显示该对话框,任何最初未显示的文件属性对话框都会突然出现。

好像我在错误的线程上做事。EDT 不是我想从中显示本机窗口的线程吗?但我很困惑,因为删除对话框再次正常工作。

4

1 回答 1

0

与其手动挖掘 Shell 接口,不如尝试使用ShellExecute()并让操作系统为您完成繁重的工作:

JNIEXPORT jboolean JNICALL Java_com_foobar_showFilePropertiesDialogImpl 
  (JNIEnv *env, jobject obj, jlong hwnd, jstring fileName) 
{ 
    /* Get the name of the file. */ 
    const wchar_t *pszFile = (wchar_t *)env->GetStringChars(fileName, NULL); 
    if (pszFile==NULL) { /* Exception occurred */ 
        return JNI_FALSE; 
    } 

    int iErr = (int) ShellExecuteW((HWND)hwnd, L"properties", pszFile, NULL, NULL, SW_SHOWNORMAL);
    env->ReleaseStringChars(fileName, (const jchar *)pszFile); 

    /*
    wchar_t msg[36];
    if (iErr > 32)
        wsprintfW(msg, L"ShellExecute successful!"); 
    else 
        wsprintfW(msg, L"ShellExecute failed: %d", iErr); 
    MessageBoxW((HWND)hwnd, pszFile, msg, MB_OK); 
    */

    return JNI_TRUE; 
} 
于 2012-09-21T19:45:47.260 回答