使用JNA访问COM接口

5
我正在尝试使用JNA访问IDesktopWallpaper接口,但我遇到了障碍。
我查阅了Windows 10 SDK中的ShOljIdl_core.idl,并发现该接口的GUID如下。
// IDesktopWallpaper
[
    uuid(B92B56A9-8B55-4E14-9A89-0199BBB6F93B),
    object
]

以及实现接口的具体类的GUID

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

所以我按照JDA Github上的官方示例编写了以下内容。

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


}

并且在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();
}

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

有没有专家能够为我解释一下发生了什么?(我完全不懂)如果需要任何进一步的信息,我会提供。JNA能否实现我想要的功能,还是我必须使用像Com4j这样的其他工具?


如果您使用GUID而不是类来使用IDesktopWallpaper,它是否有效?只是猜测。 - cbr
顺便提一下,这里有一个相同的C++示例:https://github.com/mvaneerde/blog/blob/master/desktopwallpaper/desktopwallpaper/main.cpp - cbr
1
@cubrr 不,先生,它不行,我已经尝试过了,并且从概念上讲,工厂需要一个实际的COM接口的具体实现来实例化。 - Harry
1个回答

7

简述

经过大量的谷歌搜索,我终于让它工作了。问题(至少根据我的理解)是当前的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));
    }
}

然后在 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();
}

使用 IDesktopWallpaper 的原始动机是访问淡入转换效果,现在可以通过添加以下内容来实现:

User32.INSTANCE.SendMessageTimeout(User32.INSTANCE.FindWindow("Progman", null), 0x52c, 0, 0, 0, 500, null);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接