All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.openfin.desktop.win32.DesktopPortHandler Maven / Gradle / Ivy

There is a newer version: 11.0.2
Show newest version
package com.openfin.desktop.win32;


import com.openfin.desktop.ActionEvent;
import com.openfin.desktop.EventListener;
import com.openfin.desktop.PortDiscoveryHandler;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.*;
import com.sun.jna.win32.StdCallLibrary;
import org.json.JSONObject;

import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Create native Windows and listens to WM_COPYDATA from Runtime for Runtime port discovery.
 *
 * @author wche
 * @since 12/11/14
 *
 */
public class DesktopPortHandler implements WinUser.WindowProc, PortDiscoveryHandler {
    private final static Logger logger = LoggerFactory.getLogger(DesktopPortHandler.class.getName());

    private static final String windowClassName = "OPENFIN_ADAPTER_WINDOW";
    private Map callbacks = Collections.synchronizedMap(new HashMap());
    private WinDef.HMODULE hInst;
    private static DesktopPortHandler instance;  // singleton to process all messages of the Window class
    private static int WM_COPYDATA = 74;
    private static long MSGFLT_ALLOW = 1;

    private DesktopPortHandler() {
        this.registerWindowClass();
        logger.debug("Created");
    }

    public static synchronized DesktopPortHandler getInstance() {
        if (instance == null) {
            instance = new DesktopPortHandler();
        }
        return instance;
    }

    public void registerEventListener(EventListener listener, int timeout) {
        this.createMesssageThread(listener, timeout);
    }

    public void removeEventListener(EventListener listener) {
        Iterator iterator = this.callbacks.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            WinMessageThread wthread = this.callbacks.get(key);
            if (listener == wthread.getEventListener() && wthread.gethWnd() != null) {
                logger.debug("destroy message window " + wthread.gethWnd().getPointer().toString());
                User32.INSTANCE.PostMessage(wthread.gethWnd(), User32.WM_CLOSE, null, null);
                this.getLastError();
                wthread.timeoutThread.interrupt();
                this.callbacks.remove(key);
                break;
            }
        }
    }

    @Override
    public String getEffectivePipeName() {
        return null;
    }

    private void registerWindowClass() {
        logger.debug("Registering window class " + windowClassName);
        this.hInst = Kernel32.INSTANCE.GetModuleHandle("");
        WinUser.WNDCLASSEX wClass = new WinUser.WNDCLASSEX();
        wClass.hInstance = this.hInst;
        wClass.lpfnWndProc = this;
        wClass.lpszClassName = windowClassName;
        // register window class
        User32.INSTANCE.RegisterClassEx(wClass);
        getLastError();
    }

    @Override
    public WinDef.LRESULT callback(WinDef.HWND hwnd, int uMsg, WinDef.WPARAM wparam, WinDef.LPARAM lparam) {
        logger.debug("uMsg " + uMsg);
        switch (uMsg) {
            case 74: // WM_COPYDATA
                try {
                    COPYDATASTRUCT struct = new COPYDATASTRUCT(lparam.longValue());
                    String runtimeMsg = new String(struct.lpData.getByteArray(0, struct.cbData), "UTF-16LE");
                    logger.debug("COPYDATA: " + runtimeMsg + " HWND " + hwnd.getPointer().toString());
                    JSONObject jsonObject = new JSONObject(runtimeMsg);
                    ActionEvent actionEvent = new ActionEvent(windowClassName, jsonObject, this);
                    fireEvent(hwnd, actionEvent);
                } catch (Exception e) {
                    logger.error("Error processing WM_COPYDATA", e);
                }
                return User32.INSTANCE.DefWindowProc(hwnd, uMsg, wparam, lparam);
      		default:
      			return User32.INSTANCE.DefWindowProc(hwnd, uMsg, wparam, lparam);
      		}
    }

    public int getLastError() {
   		int rc = Kernel32.INSTANCE.GetLastError();
   		if (rc != 0) {
            logger.debug("GetLastError: " + rc);
        }
   		return rc;
   	}

    private void createMesssageThread(EventListener listener, int timeout) {
        WinMessageThread winMessageThread = new WinMessageThread(listener, timeout);
        winMessageThread.start();
    }

    private void addEvenListener(WinDef.HWND hWnd, WinMessageThread winMessageThread) {
        logger.debug("addEvenListener HWND " + hWnd.getPointer().toString());
        this.callbacks.put(hWnd.getPointer().toString(), winMessageThread);
    }

    public void fireEvent(WinDef.HWND hwnd, ActionEvent actionEvent) {
        if (this.callbacks.get(hwnd.getPointer().toString()) != null) {
            this.callbacks.get(hwnd.getPointer().toString()).getEventListener().eventReceived(actionEvent);
        } else {
            logger.debug("HWND missing for fireEvent " + hwnd.getPointer().toString());
        }
    }

    protected class TimeoutThread extends Thread {
        private EventListener eventListener;
        private int timeout;
        private volatile boolean interrupted = false;
        public TimeoutThread(EventListener eventListener, int timeout) {
            this.eventListener = eventListener;
            this.timeout = timeout;
            this.setName(DesktopPortHandler.class.getName() + ".TimeoutThread");
        }
        @Override
        public void run() {
            logger.debug("Starting timeout thread");
            synchronized (eventListener) {
                try {
                    eventListener.wait(timeout * 1000);
                } catch (InterruptedException e) {
                }
            }
            if (!interrupted) {
                JSONObject jsonObject = new JSONObject();
                ActionEvent actionEvent = new ActionEvent("TIMEOUT", jsonObject, this);
                this.eventListener.eventReceived(actionEvent);
            }
            logger.debug("exiting");
        }
        public void interrupt() {
            logger.debug("Interrupting timeout thread");
            this.interrupted = true;
            synchronized (this.eventListener) {
                this.eventListener.notifyAll();
            }
        }
    }

    protected class WinMessageThread extends Thread {
        private WinDef.HWND hWnd; // HWND for this thread
        private EventListener eventListener;
        private TimeoutThread timeoutThread;

        public WinMessageThread(EventListener listener, int timeout) {
            super();
            this.setDaemon(true);
            this.setName(DesktopPortHandler.class.getName() + ".WinMessageThread");
            this.eventListener = listener;
            this.timeoutThread = new TimeoutThread(listener, timeout);
            this.timeoutThread.start();
        }

        @Override
        public void run() {
            // CreateWindowEx and GetMessage have to be called from the same thread
            // create new window
            this.hWnd = User32.INSTANCE.CreateWindowEx(0, windowClassName,
                    "OpenFin Java hidden window used to catch the windows events ",
                    0, 0, 0, 0, 0,
                    null, // WM_DEVICECHANGE contradicts parent=WinUser.HWND_MESSAGE
                    null, DesktopPortHandler.this.hInst, null);
            if (getLastError() == 0) {
                changeWindowFilter();
                logger.debug(Thread.currentThread().getName() + " started User32.INSTANCE.GetMessage " + this.hWnd.getPointer().toString());
                addEvenListener(this.hWnd, this);
                WinUser.MSG msg = new WinUser.MSG();
                int returnCode;
                while ( (returnCode = User32.INSTANCE.GetMessage(msg, this.hWnd, 0, 0)) != 0) {
                    if (returnCode > 0) {
                        User32.INSTANCE.TranslateMessage(msg);
                        User32.INSTANCE.DispatchMessage(msg);
                    } else {
                        logger.debug(Thread.currentThread().getName() + " GetMessage return " + returnCode + ", stop GetMessage loop " + getLastError());
                        break;
                    }
                }
            } else {
                logger.warn("Error creating message window");
            }
            logger.debug(Thread.currentThread().getName() + " exiting ");
        }

        public WinDef.HWND gethWnd() {
            return this.hWnd;
        }
        public EventListener getEventListener() {
            return this.eventListener;
        }

        private void changeWindowFilter() {
            try {
                // allow processes with lower integrity to send messages
                boolean filterResult = WinMessageHelper.customUser32.ChangeWindowMessageFilterEx(this.hWnd, WM_COPYDATA, new WinDef.DWORD(MSGFLT_ALLOW), null);
                if (filterResult) {
                    logger.debug("ChangeWindowMessageFilterEx returns " + filterResult);
                } else {
                    logger.warn("ChangeWindowMessageFilterEx returns " + getLastError());
                }
            } catch (UnsatisfiedLinkError linkError) {
                // ChangeWindowMessageFilterEx is only supported in W7+
                logger.warn("Error calling ChangeWindowMessageFilterEx", linkError);
            } catch (Exception ex) {
                logger.warn("Error calling ChangeWindowMessageFilterEx", ex);
            }
        }
    }

    public static class COPYDATASTRUCT extends Structure {
        /**
         * The by-reference version of this structure.
         */
        public static class ByReference
                extends COPYDATASTRUCT
                implements Structure.ByReference { }

        /**
         * Instantiates a new COPYDATASTRUCT.
         */
        public COPYDATASTRUCT() { }

        /**
         * Instantiates a new COPYDATASTRUCT with existing data given
         * the address of that data.
         *
         * @param pointer Address of the existing structure.
         */
        public COPYDATASTRUCT(final long pointer) {
            this(new Pointer(pointer));
        }

        /**
         * Instantiates a new COPYDATASTRUCT with existing data given
         * a pointer to that data.
         *
         * @param memory Pointer to the existing structure.
         */
        public COPYDATASTRUCT(final Pointer memory) {
            super(memory);
            read();
        }

        /** The data to be passed to the receiving application. */
        public BaseTSD.ULONG_PTR dwData;

        /** The size, in bytes, of the data pointed to by the lpData
         * member. */
        public int cbData;

        /** The data to be passed to the receiving application. This
         * member can be null. */
        public Pointer lpData;

        /**
         * Returns the serialized order of this structure's fields.
         *
         * @return The serialized order of this structure's fields.
         * @see com.sun.jna.Structure#getFieldOrder()
         */
        @Override
        protected final List getFieldOrder() {
            return Arrays.asList(new String[]{"dwData", "cbData", "lpData"});
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy