使用 JNA 访问 COM 接口
Accessing COM Interface with JNA
我正在尝试使用 JNA 访问 IDesktopWallpaper 接口,但我遇到了困难。
我通过ShOljIdl_core.idl
(来自Windows 10 SDK)发现界面的GUID如下
// IDesktopWallpaper
[
uuid(B92B56A9-8B55-4E14-9A89-0199BBB6F93B),
object
]
和实现接口
的具体class的GUID
// CLSID_DesktopWallpaper
[uuid(C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD)] coclass DesktopWallpaper { interface IDesktopWallpaper; }
所以我按照JDAgithub中的官方例子,写了下面的
@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 这样的其他东西?
经过大量谷歌搜索,我终于让它工作了。问题(至少就我目前的理解而言)是当前的 JNA 助手只能使用从 IDispatch
继承的接口。因此,如果 IDesktopWallpaper
等有问题的接口不是从 IDispatch
继承的,那么应该使用 vtable 进行函数调用。我从 this 令人惊叹的 Google 论坛 post 获得了这些信息,其中 poster 还提供了一个代码示例,让我开始了。
这里是 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);
我正在尝试使用 JNA 访问 IDesktopWallpaper 接口,但我遇到了困难。
我通过ShOljIdl_core.idl
(来自Windows 10 SDK)发现界面的GUID如下
// IDesktopWallpaper
[
uuid(B92B56A9-8B55-4E14-9A89-0199BBB6F93B),
object
]
和实现接口
的具体class的GUID// CLSID_DesktopWallpaper
[uuid(C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD)] coclass DesktopWallpaper { interface IDesktopWallpaper; }
所以我按照JDAgithub中的官方例子,写了下面的
@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 这样的其他东西?
经过大量谷歌搜索,我终于让它工作了。问题(至少就我目前的理解而言)是当前的 JNA 助手只能使用从 IDispatch
继承的接口。因此,如果 IDesktopWallpaper
等有问题的接口不是从 IDispatch
继承的,那么应该使用 vtable 进行函数调用。我从 this 令人惊叹的 Google 论坛 post 获得了这些信息,其中 poster 还提供了一个代码示例,让我开始了。
这里是 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);