使用 JNA 访问 COM 接口

Har*_*rry 3 java com jna

我正在尝试使用 JNA访问IDesktopWallpaper界面,但我遇到了障碍

我通过ShOljIdl_core.idl(从Windows 10 SDK)并发现界面的GUID如下

// IDesktopWallpaper
[
    uuid(B92B56A9-8B55-4E14-9A89-0199BBB6F93B),
    object
]
Run Code Online (Sandbox Code Playgroud)

和实现接口的具体类的 GUID

// CLSID_DesktopWallpaper
[uuid(C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD)] coclass DesktopWallpaper { interface IDesktopWallpaper; }
Run Code Online (Sandbox Code Playgroud)

于是我按照JDA github中的官方例子,写了如下

@ComObject(clsId="{C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD}")
public interface DesktopWallpaper extends IUnknown{


}
Run Code Online (Sandbox Code Playgroud)

并在 Main

Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
try {
    Factory factory = new Factory();
    try {
        DesktopWallpaper dw = factory.createObject(DesktopWallpaper.class);
    } finally {
        factory.disposeAll();
        factory.getComThread().terminate(1 * 1000);
    }

} finally {
    Ole32.INSTANCE.CoUninitialize();
}
Run Code Online (Sandbox Code Playgroud)

但是factory.createObject(DesktopWallpaper.class)抛出No such interface supported(HRESULT: 80004002) (puArgErr=),我不知道如何解决这个问题或为什么会发生这种情况。

任何专家都可以启发我了解正在发生的事情吗?(我是一个完整的菜鸟)我会提供任何必要的进一步信息。JNA 可以实现我想要的还是我必须使用 Com4j 之类的其他东西?

Har*_*rry 6

TL; 博士

经过大量的谷歌搜索,我终于让它工作了。问题(至少就我目前的理解而言)是当前的 JNA 助手仅适用于从IDispatch. 因此,如果有问题的接口如IDesktopWallpaper不继承自IDispatch,则应使用 vtable 进行函数调用。我从这篇很棒的 Google 论坛帖子中获得了这些信息,其中的海报还提供了让我入门的代码示例。

这是该SetWallpaper()函数的一些工作代码:

public class DesktopWallpaperHandler extends Unknown{
    private static final GUID CLSID_DesktopWallpaper = new GUID("{C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD}");
    private static final GUID IID_IDesktopWallpaper = new GUID("{B92B56A9-8B55-4E14-9A89-0199BBB6F93B}");

    private DesktopWallpaperHandler(Pointer pvInstance) {
        super(pvInstance);
    }

    public static DesktopWallpaperHandler create(){
        PointerByReference p = new PointerByReference();

        WinNT.HRESULT hr = Ole32.INSTANCE.CoCreateInstance(CLSID_DesktopWallpaper, null, WTypes.CLSCTX_SERVER, IID_IDesktopWallpaper, p);
        COMUtils.checkRC(hr);

        DesktopWallpaperHandler handler = new DesktopWallpaperHandler(p.getValue());

        return handler;
    }

    public void SetWallpaper(WTypes.LPWSTR monitor, WTypes.LPWSTR wallpaper){
        int result = this._invokeNativeInt(3, new Object[]{this.getPointer(), monitor, wallpaper});
        COMUtils.checkRC(new HRESULT(result));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在Main

Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
try {
    WTypes.LPWSTR path = new LPWSTR("C:\\Users\\Harry\\Desktop\\1.jpg");
    DesktopWallpaperHandler handler = DesktopWallpaperHandler.create();
    handler.SetWallpaper(null, path);
} finally {
    Ole32.INSTANCE.CoUninitialize();
}
Run Code Online (Sandbox Code Playgroud)

最初使用的动机IDesktopWallpaper是访问淡入淡出过渡效果,现在可以通过添加以下内容来完成:

User32.INSTANCE.SendMessageTimeout(User32.INSTANCE.FindWindow("Progman", null), 0x52c, 0, 0, 0, 500, null);
Run Code Online (Sandbox Code Playgroud)