Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing COM Interface with JNA

Tags:

java

com

jna

I'm trying to access the IDesktopWallpaper interface with JNA, but I've hit a wall.

I went through ShOljIdl_core.idl (from Windows 10 SDK) and discovered the GUID of the interface as follows

// IDesktopWallpaper
[
    uuid(B92B56A9-8B55-4E14-9A89-0199BBB6F93B),
    object
]

and the GUID of the concrete class that implements the interface

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

So I followed the official example in the JDA github and wrote the following

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


}

and in 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();
}

But the factory.createObject(DesktopWallpaper.class) throws No such interface supported(HRESULT: 80004002) (puArgErr=)and I don't know how to get around this or why it is happening.

Can any experts enlighten me on what's happening? (I am a complete noob) I will provide any further info that's necessary. Can JNA achieve what I want or do I have to use something else like Com4j?

like image 776
Harry Avatar asked Mar 09 '23 02:03

Harry


1 Answers

TL;DR

After a lot of googling, I finally got it to work. The problem (at least to my current understanding) is that the current JNA helpers only work with interfaces that inherit from IDispatch. So if the interface in question such as IDesktopWallpaper does not inherit from IDispatch, then one should use vtable for function calls. I got this information from this amazing Google forum post in which the poster also provided a code sample that got me started.

Here is some working code for the SetWallpaper() function:

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

And then in 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();
}

The original motive to use IDesktopWallpaper was to access the fade in transition effect, and now that can be done by adding the following:

User32.INSTANCE.SendMessageTimeout(User32.INSTANCE.FindWindow("Progman", null), 0x52c, 0, 0, 0, 500, null);
like image 149
Harry Avatar answered Mar 19 '23 08:03

Harry