Alex
Alex

Reputation: 31

Mapping COM interface method in JNA Invalid Memory Access exception

I am using JNA to access the following COM API. I have created this class which maps the " IMGApplication" interface of the API and some of the methods

public class IMGApplication extends Dispatch {
    private static final GUID IID_IMGApplication = new GUID("5FD5D92B-A4B6-4B32-AC3D-A6FF7AE83CD8");
    
    public IMGApplication() {

    }

    private IMGApplication(Pointer pvInstance) {
        super(pvInstance);
    }
    
    public static IMGApplication create(CLSID.ByReference clsIdByRef) {
        try {
            PointerByReference pointerByRef = new PointerByReference();
            
            HRESULT hres = Ole32.INSTANCE.CoCreateInstance(clsIdByRef, null, WTypes.CLSCTX_INPROC_SERVER, IID_IMGApplication, pointerByRef);
            if (COMUtils.SUCCEEDED(hres)) {
                return new IMGApplication(pointerByRef.getValue());
            } else {
                return null;
            }
        } catch(Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    public void minimize() {
        try {
            HRESULT hres = (HRESULT)this._invokeNativeObject(8, new Object[]{this.getPointer()}, HRESULT.class);
            COMUtils.checkRC(hres);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public void maximize() {
        try {
            HRESULT hres = (HRESULT)this._invokeNativeObject(9, new Object[]{this.getPointer()}, HRESULT.class);
            COMUtils.checkRC(hres);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public void setPosition(long x, long y, long width, long height) {
        try {
            HRESULT hres = (HRESULT)this._invokeNativeObject(11, new Object[]{this.getPointer(), new NativeLong(x), new NativeLong(y), new NativeLong(width), new NativeLong(height)}, HRESULT.class);
            COMUtils.checkRC(hres);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public void shutdownMG() {
        try {
            HRESULT hres = (HRESULT)this._invokeNativeObject(12, new Object[]{this.getPointer()}, HRESULT.class);
            COMUtils.checkRC(hres);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public void startMG(int startMode) {
        try {
            HRESULT hres = (HRESULT)this._invokeNativeObject(20, new Object[]{this.getPointer(), startMode}, HRESULT.class);
            COMUtils.checkRC(hres);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

And the Main (it is a Lotus Notes Java Agent)

public class JavaAgent extends lotus.domino.AgentBase {

    public void NotesMain() {
        private boolean comWasInitialized = false;

        try {
            if(!COMUtils.comIsInitialized()) {
                Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_APARTMENTTHREADED);
            } else {
                comWasInitialized = true;
            }

            CLSID.ByReference clsIdRef = new CLSID.ByReference();
            HRESULT hRes = Ole32.INSTANCE.CLSIDFromProgID("mgAPI.mg_API", clsIdRef);
            COMUtils.checkRC(hRes);

            IMGApplication ComIMGApplication = IMGApplication.create(clsIdRef);
            
            ComIMGApplication.startMG(0); //<--- Works
                        
            ComIMGApplication.maximize(); //<--- Invalid Memory Access exception
            
            ComIMGApplication.minimize(); //<--- Invalid Memory Access exception
            
            ComIMGApplication.shutdownMG(); //<--- Invalid Memory Access exception

            ComIMGApplication.Release();
            
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(comWasInitialized == false) {
                if(COMUtils.comIsInitialized()) {
                    Ole32.INSTANCE.CoUninitialize();
                }
            }
        }
    }
}

I have checked the COM API with the OLE/COM Object Viewer. The "IMGApplication" interface extends IDispatch and IUnknown.

So it has the 3 methods QueryInterface, AddRef, Release from IUnknown and the 4 methods GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, Invoke from IDispatch.

OLE/COM Object Viewer

The vtblId of the first method (BringToFront()) in the "IMGApplication" interface has to be at index 7 and the last at index 21.

Unfortunately the only method that is working is startMG() (vtblId 20). On all the other methods i get the "Invalid Memory Access exception".

Upvotes: 1

Views: 190

Answers (1)

Alex
Alex

Reputation: 31

I think I just found the cause of the problem (thanks to the hint from Daniel with initializing). As soon as a Lotus Notes Agent is done running and is terminated, all instances are lost of course. Every when I start the Agent again it is creating a new instance of the "mgAPI.mg_API".

        CLSID.ByReference clsIdRef = new CLSID.ByReference();
        HRESULT hRes = Ole32.INSTANCE.CLSIDFromProgID("mgAPI.mg_API", clsIdRef);
        COMUtils.checkRC(hRes);

        HRESULT hres = Ole32.INSTANCE.CoCreateInstance(clsIdByRef, null, WTypes.CLSCTX_INPROC_SERVER, IID_IMGApplication, pointerByRefDispatch);
        if (COMUtils.SUCCEEDED(hres)) {
            return new IMGApplication(pointerByRefDispatch.getValue());
        } else {
            return null;
        }

And of course i can not call any method other than startMG(0), because in this instance the program is not running yet. That's why i got the "Invalid Memory Access exception"....

I tried this and it works, exactly as it should.

        if(!COMUtils.comIsInitialized()) {
            Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
        } else {
            comWasInitialized = true;
        }

        CLSID.ByReference clsIdRef = new CLSID.ByReference();
        HRESULT hRes = Ole32.INSTANCE.CLSIDFromProgID("mgAPI.mg_API", clsIdRef);
        COMUtils.checkRC(hRes);
        
        PointerByReference pointerByRefUnknown = new PointerByReference();
        HRESULT hres = OleAuto.INSTANCE.GetActiveObject(clsIdRef, null, pointerByRefUnknown);
        
        IMGApplication ComIMGApplication = IMGApplication.create(clsIdRef);
        ComIMGApplication.startMG(0);
        
        TimeUnit.SECONDS.sleep(30);
        
        ComIMGApplication.minimize();
        ComIMGApplication.shutdownMG();

Now I have to find out how to get an instance of the program if it is already running.

I tried it with OleAuto.INSTANCE.GetActiveObject but it did not work.

        if(!COM.COMUtils.comIsInitialized()) {
            Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
        }

        CLSID.ByReference clsIdRef = new CLSID.ByReference();
        HRESULT hRes = Ole32.INSTANCE.CLSIDFromProgID("mgAPI.mg_API", clsIdRef);
        COMUtils.checkRC(hRes);
            
        PointerByReference pointerByRefUnknown = new PointerByReference();
        HRESULT hres = OleAuto.INSTANCE.GetActiveObject(clsIdRef, null, pointerByRefUnknown);

Upvotes: 1

Related Questions