/*
 * The contents of this file is dual-licensed under 2
 * alternative Open Source/Free licenses: LGPL 2.1 or later and
 * Apache License 2.0. (starting with JNA version 4.0.0).
 *
 * You can freely decide which license you want to apply to
 * the project.
 *
 * You may obtain a copy of the LGPL License at:
 *
 * http://www.gnu.org/licenses/licenses.html
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "LGPL2.1".
 *
 * You may obtain a copy of the Apache License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "AL2.0".
 */
package com.sun.jna.platform.win32;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.BaseTSD.DWORD_PTR;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.Ddeml.CONVCONTEXT;
import com.sun.jna.platform.win32.Ddeml.CONVINFO;
import com.sun.jna.platform.win32.Ddeml.HCONV;
import com.sun.jna.platform.win32.Ddeml.HCONVLIST;
import com.sun.jna.platform.win32.Ddeml.HDDEDATA;
import com.sun.jna.platform.win32.Ddeml.HSZ;
import com.sun.jna.platform.win32.Ddeml.HSZPAIR;
import com.sun.jna.platform.win32.User32Util.MessageLoopThread;
import com.sun.jna.platform.win32.User32Util.MessageLoopThread.Handler;
import com.sun.jna.win32.W32APIOptions;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * DdemlUtil defines helper classes, that help with manageing DDE communications.
 */
public abstract class DdemlUtil {
    /**
     * StandaloneDdeClient is a convenience class, that wraps a DdeClient and
     * a {@link User32Util.MessageLoopThread}. The DdeClient needs a running
     * windows message loop, which is supplied by the MessageLoopThread.
     */
    public static class StandaloneDdeClient implements IDdeClient, Closeable {

        private final MessageLoopThread messageLoop = new MessageLoopThread();
        private final IDdeClient ddeClient;
        private final IDdeClient clientDelegate;

        public StandaloneDdeClient() {
            ddeClient = new DdeClient();
            IDdeClient messageLoopHandler = (IDdeClient) Proxy.newProxyInstance(StandaloneDdeClient.class.getClassLoader(),
                    new Class[]{IDdeClient.class},
                    messageLoop.new Handler(ddeClient));
            clientDelegate = (IDdeClient) Proxy.newProxyInstance(StandaloneDdeClient.class.getClassLoader(),
                    new Class[]{IDdeClient.class},
                    new MessageLoopWrapper(messageLoop, messageLoopHandler));
            messageLoop.setDaemon(true);
            messageLoop.start();
        }

        public Integer getInstanceIdentitifier() {
            return ddeClient.getInstanceIdentitifier();
        }

        public void initialize(int afCmd) throws DdemlException {
            clientDelegate.initialize(afCmd);
        }

        public Ddeml.HSZ createStringHandle(String value) throws DdemlException {
            return clientDelegate.createStringHandle(value);
        }

        public void nameService(Ddeml.HSZ name, int afCmd) throws DdemlException {
            clientDelegate.nameService(name, afCmd);
        }

        public int getLastError() {
            return clientDelegate.getLastError();
        }

        public IDdeConnection connect(Ddeml.HSZ service, Ddeml.HSZ topic, Ddeml.CONVCONTEXT convcontext) {
            return clientDelegate.connect(service, topic, convcontext);
        }

        public String queryString(Ddeml.HSZ value) throws DdemlException {
            return clientDelegate.queryString(value);
        }

        @Override
        public Ddeml.HDDEDATA createDataHandle(Pointer pSrc, int cb, int cbOff, Ddeml.HSZ hszItem, int wFmt, int afCmd) {
            return clientDelegate.createDataHandle(pSrc, cb, cbOff, hszItem, wFmt, afCmd);
        }

        @Override
        public void freeDataHandle(Ddeml.HDDEDATA hData) {
            clientDelegate.freeDataHandle(hData);
        }

        @Override
        public Ddeml.HDDEDATA addData(Ddeml.HDDEDATA hData, Pointer pSrc, int cb, int cbOff) {
            return clientDelegate.addData(hData, pSrc, cb, cbOff);
        }

        @Override
        public int getData(Ddeml.HDDEDATA hData, Pointer pDst, int cbMax, int cbOff) {
            return clientDelegate.getData(hData, pDst, cbMax, cbOff);
        }

        @Override
        public Pointer accessData(Ddeml.HDDEDATA hData, WinDef.DWORDByReference pcbDataSize) {
            return clientDelegate.accessData(hData, pcbDataSize);
        }

        @Override
        public void unaccessData(Ddeml.HDDEDATA hData) {
            clientDelegate.unaccessData(hData);
        }

        @Override
        public void postAdvise(Ddeml.HSZ hszTopic, Ddeml.HSZ hszItem) {
            clientDelegate.postAdvise(hszTopic, hszItem);
        }

        public void close() throws IOException {
            clientDelegate.uninitialize();
            messageLoop.exit();
        }

        @Override
        public boolean freeStringHandle(Ddeml.HSZ value) {
            return clientDelegate.freeStringHandle(value);
        }

        @Override
        public boolean keepStringHandle(Ddeml.HSZ value) {
            return clientDelegate.keepStringHandle(value);
        }

        @Override
        public void abandonTransactions() {
            clientDelegate.abandonTransactions();
        }

        @Override
        public IDdeConnectionList connectList(Ddeml.HSZ service, Ddeml.HSZ topic, IDdeConnectionList existingList, Ddeml.CONVCONTEXT ctx) {
            return clientDelegate.connectList(service, topic, existingList, ctx);
        }

        @Override
        public boolean enableCallback(int wCmd) {
            return clientDelegate.enableCallback(wCmd);
        }

        @Override
        public IDdeConnection wrap(HCONV conv) {
            return clientDelegate.wrap(conv);
        }

        @Override
        public IDdeConnection connect(String service, String topic, Ddeml.CONVCONTEXT convcontext) {
            return clientDelegate.connect(service, topic, convcontext);
        }

        @Override
        public boolean uninitialize() {
            return clientDelegate.uninitialize();
        }

        @Override
        public void postAdvise(String hszTopic, String hszItem) {
            clientDelegate.postAdvise(hszTopic, hszItem);
        }

        @Override
        public IDdeConnectionList connectList(String service, String topic, IDdeConnectionList existingList, Ddeml.CONVCONTEXT ctx) {
            return clientDelegate.connectList(service, topic, existingList, ctx);
        }

        @Override
        public void nameService(String name, int afCmd) throws DdemlException {
            clientDelegate.nameService(name, afCmd);
        }

        @Override
        public void registerAdvstartHandler(AdvstartHandler handler) {
            clientDelegate.registerAdvstartHandler(handler);
        }

        @Override
        public void unregisterAdvstartHandler(AdvstartHandler handler) {
            clientDelegate.unregisterAdvstartHandler(handler);
        }

        @Override
        public void registerAdvstopHandler(AdvstopHandler handler) {
            clientDelegate.registerAdvstopHandler(handler);
        }

        @Override
        public void unregisterAdvstopHandler(AdvstopHandler handler) {
            clientDelegate.unregisterAdvstopHandler(handler);
        }

        @Override
        public void registerConnectHandler(ConnectHandler handler) {
            clientDelegate.registerConnectHandler(handler);
        }

        @Override
        public void unregisterConnectHandler(ConnectHandler handler) {
            clientDelegate.unregisterConnectHandler(handler);
        }

        @Override
        public void registerAdvReqHandler(AdvreqHandler handler) {
            clientDelegate.registerAdvReqHandler(handler);
        }

        @Override
        public void unregisterAdvReqHandler(AdvreqHandler handler) {
            clientDelegate.unregisterAdvReqHandler(handler);
        }

        @Override
        public void registerRequestHandler(RequestHandler handler) {
            clientDelegate.registerRequestHandler(handler);
        }

        @Override
        public void unregisterRequestHandler(RequestHandler handler) {
            clientDelegate.unregisterRequestHandler(handler);
        }

        @Override
        public void registerWildconnectHandler(WildconnectHandler handler) {
            clientDelegate.registerWildconnectHandler(handler);
        }

        @Override
        public void unregisterWildconnectHandler(WildconnectHandler handler) {
            clientDelegate.unregisterWildconnectHandler(handler);
        }

        @Override
        public void registerAdvdataHandler(AdvdataHandler handler) {
            clientDelegate.registerAdvdataHandler(handler);
        }

        @Override
        public void unregisterAdvdataHandler(AdvdataHandler handler) {
            clientDelegate.unregisterAdvdataHandler(handler);
        }

        @Override
        public void registerExecuteHandler(ExecuteHandler handler) {
            clientDelegate.registerExecuteHandler(handler);
        }

        @Override
        public void unregisterExecuteHandler(ExecuteHandler handler) {
            clientDelegate.unregisterExecuteHandler(handler);
        }

        @Override
        public void registerPokeHandler(PokeHandler handler) {
            clientDelegate.registerPokeHandler(handler);
        }

        @Override
        public void unregisterPokeHandler(PokeHandler handler) {
            clientDelegate.unregisterPokeHandler(handler);
        }

        @Override
        public void registerConnectConfirmHandler(ConnectConfirmHandler handler) {
            clientDelegate.registerConnectConfirmHandler(handler);
        }

        @Override
        public void unregisterConnectConfirmHandler(ConnectConfirmHandler handler) {
            clientDelegate.unregisterConnectConfirmHandler(handler);
        }

        @Override
        public void registerDisconnectHandler(DisconnectHandler handler) {
            clientDelegate.registerDisconnectHandler(handler);
        }

        @Override
        public void unregisterDisconnectHandler(DisconnectHandler handler) {
            clientDelegate.unregisterDisconnectHandler(handler);
        }

        @Override
        public void registerErrorHandler(ErrorHandler handler) {
            clientDelegate.registerErrorHandler(handler);
        }

        @Override
        public void unregisterErrorHandler(ErrorHandler handler) {
            clientDelegate.unregisterErrorHandler(handler);
        }

        @Override
        public void registerRegisterHandler(RegisterHandler handler) {
            clientDelegate.registerRegisterHandler(handler);
        }

        @Override
        public void unregisterRegisterHandler(RegisterHandler handler) {
            clientDelegate.unregisterRegisterHandler(handler);
        }

        @Override
        public void registerXactCompleteHandler(XactCompleteHandler handler) {
            clientDelegate.registerXactCompleteHandler(handler);
        }

        @Override
        public void unregisterXactCompleteHandler(XactCompleteHandler handler) {
            clientDelegate.unregisterXactCompleteHandler(handler);
        }

        @Override
        public void registerUnregisterHandler(UnregisterHandler handler) {
            clientDelegate.registerUnregisterHandler(handler);
        }

        @Override
        public void unregisterUnregisterHandler(UnregisterHandler handler) {
            clientDelegate.unregisterUnregisterHandler(handler);
        }

        @Override
        public void registerMonitorHandler(MonitorHandler handler) {
            clientDelegate.registerMonitorHandler(handler);
        }

        @Override
        public void unregisterMonitorHandler(MonitorHandler handler) {
            clientDelegate.unregisterMonitorHandler(handler);
        }
    }

    private static class MessageLoopWrapper implements InvocationHandler {
        private final Object delegate;
        private final MessageLoopThread loopThread;

        public MessageLoopWrapper(MessageLoopThread thread, Object delegate) {
            this.loopThread = thread;
            this.delegate = delegate;
        }

        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
            try {
                Object result = method.invoke(delegate, args);
                Class<?> wrapClass = null;
                if ( result instanceof IDdeConnection ) {
                    wrapClass = IDdeConnection.class;
                } else if (result instanceof IDdeConnectionList) {
                    wrapClass = IDdeConnectionList.class;
                } else if (result instanceof IDdeClient) {
                    wrapClass = IDdeClient.class;
                }
                if(wrapClass != null && method.getReturnType().isAssignableFrom(wrapClass)) {
                    result = wrap(result, wrapClass);
                }
                return result;
            } catch (InvocationTargetException ex) {
                Throwable cause = ex.getCause();
                if (cause instanceof Exception) {
                    throw (Exception) cause;
                } else {
                    throw ex;
                }
            }
        }

        private <V> V wrap(V delegate, Class clazz) {
            V messageLoopHandler = (V) Proxy.newProxyInstance(StandaloneDdeClient.class.getClassLoader(),
                    new Class[]{clazz},
                    loopThread.new Handler(delegate));
            V clientDelegate = (V) Proxy.newProxyInstance(StandaloneDdeClient.class.getClassLoader(),
                    new Class[]{clazz},
                    new MessageLoopWrapper(loopThread, messageLoopHandler));
            return clientDelegate;
        }
    }

    public static class DdeConnection implements IDdeConnection {
        private HCONV conv;
        private final IDdeClient client;

        public DdeConnection(IDdeClient client, HCONV conv) {
            this.conv = conv;
            this.client = client;
        }

        public Ddeml.HCONV getConv() {
            return conv;
        }

        @Override
        public void abandonTransaction(int transactionId) {
            boolean result = Ddeml.INSTANCE.DdeAbandonTransaction(client.getInstanceIdentitifier(), conv, transactionId);
            if(! result) {
                throw DdemlException.create(client.getLastError());
            }
        }

        public void abandonTransactions() {
            boolean result = Ddeml.INSTANCE.DdeAbandonTransaction(client.getInstanceIdentitifier(), conv, 0);
            if(! result) {
                throw DdemlException.create(client.getLastError());
            }
        }

        @Override
        public Ddeml.HDDEDATA clientTransaction(Pointer data, int dataLength, Ddeml.HSZ item, int wFmt, int transaction, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            if(timeout == Ddeml.TIMEOUT_ASYNC && result == null) {
                result = new WinDef.DWORDByReference();
            }
            Ddeml.HDDEDATA returnData = Ddeml.INSTANCE.DdeClientTransaction(data, dataLength, conv, item, wFmt, transaction, timeout, result);
            if(returnData == null) {
                throw DdemlException.create(client.getLastError());
            }
            if (userHandle != null) {
                if (timeout != Ddeml.TIMEOUT_ASYNC) {
                    setUserHandle(Ddeml.QID_SYNC, userHandle);
                } else {
                    setUserHandle(result.getValue().intValue(), userHandle);
                }
            }
            return returnData;
        }

        public Ddeml.HDDEDATA clientTransaction(Pointer data, int dataLength, String item, int wFmt, int transaction, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            HSZ itemHSZ = null;
            try {
                itemHSZ = client.createStringHandle(item);
                return clientTransaction(data, dataLength, itemHSZ, wFmt, transaction, timeout, result, userHandle);
            } finally {
                client.freeStringHandle(itemHSZ);
            }
        }

        @Override
        public void poke(Pointer data, int dataLength, Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            clientTransaction(data, dataLength, item, wFmt, Ddeml.XTYP_POKE, timeout, result, userHandle);
        }

        public void poke(Pointer data, int dataLength, String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            HSZ itemHSZ = null;
            try {
                itemHSZ = client.createStringHandle(item);
                poke(data, dataLength, itemHSZ, wFmt, timeout, result, userHandle);
            } finally {
                client.freeStringHandle(itemHSZ);
            }
        }

        @Override
        public Ddeml.HDDEDATA request(Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            return clientTransaction(Pointer.NULL, 0, item, wFmt, Ddeml.XTYP_REQUEST, timeout, result, userHandle);
        }

        public Ddeml.HDDEDATA request(String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            HSZ itemHSZ = null;
            try {
                itemHSZ = client.createStringHandle(item);
                return request(itemHSZ, wFmt, timeout, result, userHandle);
            } finally {
                client.freeStringHandle(itemHSZ);
            }
        }

        @Override
        public void execute(String executeString, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            Memory mem = new Memory(executeString.length() * 2 + 2);
            mem.setWideString(0, executeString);
            clientTransaction(mem, (int) mem.size(), (HSZ) null, 0, Ddeml.XTYP_EXECUTE, timeout, result, userHandle);
        }

        @Override
        public void advstart(Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            clientTransaction(Pointer.NULL, 0, item, wFmt, Ddeml.XTYP_ADVSTART, timeout, result, userHandle);
        }

        public void advstart(String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            HSZ itemHSZ = null;
            try {
                itemHSZ = client.createStringHandle(item);
                advstart(itemHSZ, wFmt, timeout, result, userHandle);
            } finally {
                client.freeStringHandle(itemHSZ);
            }
        }

        @Override
        public void advstop(Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            clientTransaction(Pointer.NULL, 0, item, wFmt, Ddeml.XTYP_ADVSTOP, timeout, result, userHandle);
        }

        public void advstop(String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle) {
            HSZ itemHSZ = null;
            try {
                itemHSZ = client.createStringHandle(item);
                advstop(itemHSZ, wFmt, timeout, result, userHandle);
            } finally {
                client.freeStringHandle(itemHSZ);
            }
        }

        public void impersonateClient() {
            boolean result = Ddeml.INSTANCE.DdeImpersonateClient(conv);
            if (!result) {
                throw DdemlException.create(client.getLastError());
            }
        }

        public void close() {
            boolean result = Ddeml.INSTANCE.DdeDisconnect(conv);
            if(! result) {
                throw DdemlException.create(client.getLastError());
            }
        }

        public void reconnect() {
            Ddeml.HCONV newConv = Ddeml.INSTANCE.DdeReconnect(conv);
            if(newConv != null) {
                conv = newConv;
            } else {
                throw DdemlException.create(client.getLastError());
            }
        }

        public boolean enableCallback(int wCmd) {
            boolean result = Ddeml.INSTANCE.DdeEnableCallback(client.getInstanceIdentitifier(), conv, wCmd);
            if ((!result) && wCmd == Ddeml.EC_QUERYWAITING) {
                throw DdemlException.create(client.getLastError());
            }
            return result;
        }

        public void setUserHandle(int id, DWORD_PTR hUser) throws DdemlException {
            boolean result = Ddeml.INSTANCE.DdeSetUserHandle(conv, id, hUser);
            if (!result) {
                throw DdemlException.create(client.getLastError());
            }
        }

        public CONVINFO queryConvInfo(int idTransaction) throws DdemlException {
            CONVINFO convInfo = new Ddeml.CONVINFO();
            convInfo.cb = convInfo.size();
            convInfo.ConvCtxt.cb = convInfo.ConvCtxt.size();
            convInfo.write();
            int result = Ddeml.INSTANCE.DdeQueryConvInfo(conv, idTransaction, convInfo);
            if (result == 0) {
                throw DdemlException.create(client.getLastError());
            }
            return convInfo;
        }

    }

    public static class DdeConnectionList implements IDdeConnectionList {
        private final IDdeClient client;
        private final HCONVLIST convList;

        public DdeConnectionList(IDdeClient client, Ddeml.HCONVLIST convList) {
            this.convList = convList;
            this.client = client;
        }

        @Override
        public Ddeml.HCONVLIST getHandle() {
            return this.convList;
        }

        @Override
        public IDdeConnection queryNextServer(IDdeConnection prevConnection) {
            Ddeml.HCONV conv = Ddeml.INSTANCE.DdeQueryNextServer(
                    convList,
                    prevConnection != null ? prevConnection.getConv() : null);
            if(conv != null) {
                return new DdeConnection(client, conv);
            } else {
                return null;
            }
        }

        @Override
        public void close() {
            boolean result = Ddeml.INSTANCE.DdeDisconnectList(convList);
            if(! result){
                throw DdemlException.create(client.getLastError());
            }
        }
    }

    public static class DdeClient implements IDdeClient {
        private Integer idInst;
        private final DdeAdapter ddeAdapter = new DdeAdapter();

        public Integer getInstanceIdentitifier() {
            return idInst;
        }

        public void initialize(int afCmd) throws DdemlException {
            WinDef.DWORDByReference pidInst = new WinDef.DWORDByReference();
            Integer result = Ddeml.INSTANCE.DdeInitialize(pidInst, ddeAdapter, afCmd, 0);
            if(result != Ddeml.DMLERR_NO_ERROR) {
                throw DdemlException.create(result);
            }
            idInst = pidInst.getValue().intValue();
            if(ddeAdapter instanceof DdeAdapter) {
                ddeAdapter.setInstanceIdentifier(idInst);
            }
        }

        public Ddeml.HSZ createStringHandle(String value) throws DdemlException {
            if(value == null) {
                return null;
            }
            int codePage;
            if(W32APIOptions.DEFAULT_OPTIONS == W32APIOptions.UNICODE_OPTIONS) {
                codePage = Ddeml.CP_WINUNICODE;
            } else {
                codePage = Ddeml.CP_WINANSI;
            }
            Ddeml.HSZ handle = Ddeml.INSTANCE.DdeCreateStringHandle(idInst, value, codePage);
            if(handle == null) {
                throw DdemlException.create(getLastError());
            }
            return handle;
        }

        public void nameService(Ddeml.HSZ name, int afCmd) throws DdemlException {
            Ddeml.HDDEDATA handle = Ddeml.INSTANCE.DdeNameService(idInst, name, new Ddeml.HSZ(), afCmd);
            if (handle == null) {
                throw DdemlException.create(getLastError());
            }
        }

        public void nameService(String name, int afCmd) throws DdemlException {
            HSZ nameHSZ = null;
            try {
                nameHSZ = createStringHandle(name);
                nameService(nameHSZ, afCmd);
            } finally {
                freeStringHandle(nameHSZ);
            }
        }

        public int getLastError() {
            return Ddeml.INSTANCE.DdeGetLastError(idInst);
        }

        public IDdeConnection connect(Ddeml.HSZ service, Ddeml.HSZ topic, Ddeml.CONVCONTEXT convcontext) {
            Ddeml.HCONV hconv = Ddeml.INSTANCE.DdeConnect(idInst, service, topic, convcontext);
            if(hconv == null) {
                throw DdemlException.create(getLastError());
            }
            return new DdeConnection(this, hconv);
        }

        public IDdeConnection connect(String service, String topic, Ddeml.CONVCONTEXT convcontext) {
            HSZ serviceHSZ = null;
            HSZ topicHSZ = null;
            try {
                serviceHSZ = createStringHandle(service);
                topicHSZ = createStringHandle(topic);
                return connect(serviceHSZ, topicHSZ, convcontext);
            } finally {
                freeStringHandle(topicHSZ);
                freeStringHandle(serviceHSZ);
            }
        }

        public String queryString(Ddeml.HSZ value) throws DdemlException {
            int codePage;
            int byteWidth;
            if(W32APIOptions.DEFAULT_OPTIONS == W32APIOptions.UNICODE_OPTIONS) {
                codePage = Ddeml.CP_WINUNICODE;
                byteWidth = 2;
            } else {
                codePage = Ddeml.CP_WINANSI;
                byteWidth = 1;
            }
            Memory buffer = new Memory((256 + 1) * byteWidth);
            try {
                int length = Ddeml.INSTANCE.DdeQueryString(idInst, value, buffer, 256, codePage);
                if (W32APIOptions.DEFAULT_OPTIONS == W32APIOptions.UNICODE_OPTIONS) {
                    return buffer.getWideString(0);
                } else {
                    return buffer.getString(0);
                }
            } finally {
                buffer.valid();
            }
        }


        public Ddeml.HDDEDATA createDataHandle(Pointer pSrc, int cb, int cbOff, Ddeml.HSZ hszItem, int wFmt, int afCmd) {
            Ddeml.HDDEDATA returnData = Ddeml.INSTANCE.DdeCreateDataHandle(idInst, pSrc, cb, cbOff, hszItem, wFmt, afCmd);
            if(returnData == null) {
                throw DdemlException.create(getLastError());
            }
            return returnData;
        }

        public void freeDataHandle(Ddeml.HDDEDATA hData) {
            boolean result = Ddeml.INSTANCE.DdeFreeDataHandle(hData);
            if(! result) {
                throw DdemlException.create(getLastError());
            }
        }

        public Ddeml.HDDEDATA addData(Ddeml.HDDEDATA hData, Pointer pSrc, int cb, int cbOff) {
            Ddeml.HDDEDATA newHandle = Ddeml.INSTANCE.DdeAddData(hData, pSrc, cb, cbOff);
            if(newHandle == null) {
                throw DdemlException.create(getLastError());
            }
            return newHandle;
        }

        public int getData(Ddeml.HDDEDATA hData, Pointer pDst, int cbMax, int cbOff) {
            int result = Ddeml.INSTANCE.DdeGetData(hData, pDst, cbMax, cbOff);
            int errorCode = getLastError();
            if(errorCode != Ddeml.DMLERR_NO_ERROR) {
                throw DdemlException.create(errorCode);
            }
            return result;
        }

        public Pointer accessData(Ddeml.HDDEDATA hData, WinDef.DWORDByReference pcbDataSize) {
            Pointer result = Ddeml.INSTANCE.DdeAccessData(hData, pcbDataSize);
            if(result == null) {
                throw DdemlException.create(getLastError());
            }
            return result;
        }

        public void unaccessData(Ddeml.HDDEDATA hData) {
            boolean result = Ddeml.INSTANCE.DdeUnaccessData(hData);
            if (!result) {
                throw DdemlException.create(getLastError());
            }
        }

        public void postAdvise(Ddeml.HSZ hszTopic, Ddeml.HSZ hszItem) {
            boolean result = Ddeml.INSTANCE.DdePostAdvise(idInst, hszTopic, hszItem);
            if (!result) {
                throw DdemlException.create(getLastError());
            }
        }

        public void postAdvise(String topic, String item) {
            HSZ itemHSZ = null;
            HSZ topicHSZ = null;
            try {
                topicHSZ = createStringHandle(topic);
                itemHSZ = createStringHandle(item);
                postAdvise(topicHSZ, itemHSZ);
            } finally {
                freeStringHandle(topicHSZ);
                freeStringHandle(itemHSZ);
            }
        }

        public boolean freeStringHandle(Ddeml.HSZ value) {
            if(value == null) {
                return true;
            }
            return Ddeml.INSTANCE.DdeFreeStringHandle(idInst, value);
        }

        public boolean keepStringHandle(Ddeml.HSZ value) {
            return Ddeml.INSTANCE.DdeKeepStringHandle(idInst, value);
        }

        public void abandonTransactions() {
            boolean result = Ddeml.INSTANCE.DdeAbandonTransaction(idInst, null, 0);
            if(! result) {
                throw DdemlException.create(getLastError());
            }
        }

        public IDdeConnectionList connectList(Ddeml.HSZ service, Ddeml.HSZ topic, IDdeConnectionList existingList, Ddeml.CONVCONTEXT ctx) {
            Ddeml.HCONVLIST convlist = Ddeml.INSTANCE.DdeConnectList(idInst, service, topic, existingList != null ? existingList.getHandle() : null, ctx);
            if(convlist == null) {
                throw DdemlException.create(getLastError());
            } else {
                return new DdeConnectionList(this, convlist);
            }
        }

        public IDdeConnectionList connectList(String service, String topic, IDdeConnectionList existingList, Ddeml.CONVCONTEXT ctx) {
            HSZ serviceHSZ = null;
            HSZ topicHSZ = null;
            try {
                serviceHSZ = createStringHandle(service);
                topicHSZ = createStringHandle(topic);
                return connectList(serviceHSZ, topicHSZ, existingList, ctx);
            } finally {
                freeStringHandle(topicHSZ);
                freeStringHandle(serviceHSZ);
            }
        }

        public boolean enableCallback(int wCmd) {
            boolean result = Ddeml.INSTANCE.DdeEnableCallback(idInst, null, wCmd);
            if ((!result) && wCmd != Ddeml.EC_QUERYWAITING) {
                int errorCode = getLastError();
                if(errorCode != Ddeml.DMLERR_NO_ERROR) {
                    throw DdemlException.create(getLastError());
                }
            }
            return result;
        }

        public boolean uninitialize() {
            return Ddeml.INSTANCE.DdeUninitialize(idInst);
        }

        public void close() {
            uninitialize();
        }

        public IDdeConnection wrap(HCONV hconv) {
            return new DdeConnection(this, hconv);
        }


        public void unregisterDisconnectHandler(DisconnectHandler handler) {
            ddeAdapter.unregisterDisconnectHandler(handler);
        }

        public void registerAdvstartHandler(AdvstartHandler handler) {
            ddeAdapter.registerAdvstartHandler(handler);
        }

        public void unregisterAdvstartHandler(AdvstartHandler handler) {
            ddeAdapter.unregisterAdvstartHandler(handler);
        }

        public void registerAdvstopHandler(AdvstopHandler handler) {
            ddeAdapter.registerAdvstopHandler(handler);
        }

        public void unregisterAdvstopHandler(AdvstopHandler handler) {
            ddeAdapter.unregisterAdvstopHandler(handler);
        }

        public void registerConnectHandler(ConnectHandler handler) {
            ddeAdapter.registerConnectHandler(handler);
        }

        public void unregisterConnectHandler(ConnectHandler handler) {
            ddeAdapter.unregisterConnectHandler(handler);
        }

        public void registerAdvReqHandler(AdvreqHandler handler) {
            ddeAdapter.registerAdvReqHandler(handler);
        }

        public void unregisterAdvReqHandler(AdvreqHandler handler) {
            ddeAdapter.unregisterAdvReqHandler(handler);
        }

        public void registerRequestHandler(RequestHandler handler) {
            ddeAdapter.registerRequestHandler(handler);
        }

        public void unregisterRequestHandler(RequestHandler handler) {
            ddeAdapter.unregisterRequestHandler(handler);
        }

        public void registerWildconnectHandler(WildconnectHandler handler) {
            ddeAdapter.registerWildconnectHandler(handler);
        }

        public void unregisterWildconnectHandler(WildconnectHandler handler) {
            ddeAdapter.unregisterWildconnectHandler(handler);
        }

        public void registerAdvdataHandler(AdvdataHandler handler) {
            ddeAdapter.registerAdvdataHandler(handler);
        }

        public void unregisterAdvdataHandler(AdvdataHandler handler) {
            ddeAdapter.unregisterAdvdataHandler(handler);
        }

        public void registerExecuteHandler(ExecuteHandler handler) {
            ddeAdapter.registerExecuteHandler(handler);
        }

        public void unregisterExecuteHandler(ExecuteHandler handler) {
            ddeAdapter.unregisterExecuteHandler(handler);
        }

        public void registerPokeHandler(PokeHandler handler) {
            ddeAdapter.registerPokeHandler(handler);
        }

        public void unregisterPokeHandler(PokeHandler handler) {
            ddeAdapter.unregisterPokeHandler(handler);
        }

        public void registerConnectConfirmHandler(ConnectConfirmHandler handler) {
            ddeAdapter.registerConnectConfirmHandler(handler);
        }

        public void unregisterConnectConfirmHandler(ConnectConfirmHandler handler) {
            ddeAdapter.unregisterConnectConfirmHandler(handler);
        }

        public void registerDisconnectHandler(DisconnectHandler handler) {
            ddeAdapter.registerDisconnectHandler(handler);
        }

        public void registerErrorHandler(ErrorHandler handler) {
            ddeAdapter.registerErrorHandler(handler);
        }

        public void unregisterErrorHandler(ErrorHandler handler) {
            ddeAdapter.unregisterErrorHandler(handler);
        }

        public void registerRegisterHandler(RegisterHandler handler) {
            ddeAdapter.registerRegisterHandler(handler);
        }

        public void unregisterRegisterHandler(RegisterHandler handler) {
            ddeAdapter.unregisterRegisterHandler(handler);
        }

        public void registerXactCompleteHandler(XactCompleteHandler handler) {
            ddeAdapter.registerXactCompleteHandler(handler);
        }

        public void unregisterXactCompleteHandler(XactCompleteHandler handler) {
            ddeAdapter.xactCompleteXactCompleteHandler(handler);
        }

        public void registerUnregisterHandler(UnregisterHandler handler) {
            ddeAdapter.registerUnregisterHandler(handler);
        }

        public void unregisterUnregisterHandler(UnregisterHandler handler) {
            ddeAdapter.unregisterUnregisterHandler(handler);
        }

        public void registerMonitorHandler(MonitorHandler handler) {
            ddeAdapter.registerMonitorHandler(handler);
        }

        public void unregisterMonitorHandler(MonitorHandler handler) {
            ddeAdapter.unregisterMonitorHandler(handler);
        }
    }

    public interface AdvstartHandler {
        /**
         * A server callback function should return TRUE to allow an advise loop
         * on the specified topic name and item name pair, or FALSE to deny the
         * advise loop. If the callback function returns TRUE, any subsequent
         * calls to the DdePostAdvise function by the server on the same topic
         * name and item name pair causes the system to send XTYP_ADVREQ
         * transactions to the server.
         *
         * <p>
         * <strong>Remarks</strong></p>
         * <p>If a client requests an advise loop on a topic name, item name,
         * and data format for an advise loop that is already established, the
         * Dynamic Data Exchange Management Library (DDEML) does not create a
         * duplicate advise loop but instead alters the advise loop flags
         * (XTYPF_ACKREQ and XTYPF_NODATA) to match the latest request.</p>
         *
         * <p>
         * This transaction is filtered if the server application specified the
         * CBF_FAIL_ADVISES flag in the DdeInitialize function.</p>
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The data format requested by the client.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param item hsz2 - A handle to the item name.
         * @return true if advise loop can be started
         */
        boolean onAdvstart(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item);
    }

    public interface AdvstopHandler {

        /**
         * A client uses the XTYP_ADVSTOP transaction to end an advise loop with
         * a server. A Dynamic Data Exchange (DDE) server callback function,
         * DdeCallback, receives this transaction when a client specifies
         * XTYP_ADVSTOP in the DdeClientTransaction function.
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The data format associated with the advise
         * loop being ended.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param item hsz2 - A handle to the item name.
         */
        void onAdvstop(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item);
    }

    public interface ConnectHandler {
        /**
         * A client uses the XTYP_CONNECT transaction to establish a
         * conversation. A Dynamic Data Exchange (DDE) server callback function,
         * DdeCallback, receives this transaction when a client specifies a
         * service name that the server supports (and a topic name that is not
         * NULL) in a call to the DdeConnect function.
         *
         * @param transactionType uType - The transaction type.
         * @param topic hsz1 - A handle to the topic name.
         * @param service hsz2 - A handle to the service name.
         * @param convcontext dwData1 - CONVCONTEXT structure that contains
         * context information for the conversation. If the client is not a
         * DDEML application, this parameter is NULL.
         * @param sameInstance dwData2 - Specifies whether the client is the
         * same application instance as the server.
         * @return true is connect can continue
         */
        boolean onConnect(int transactionType, HSZ topic, HSZ service, CONVCONTEXT convcontext, boolean sameInstance);
    }

    public interface AdvreqHandler {

        /**
         * The XTYP_ADVREQ transaction informs the server that an advise
         * transaction is outstanding on the specified topic name and item name
         * pair and that data corresponding to the topic name and item name pair
         * has changed. The system sends this transaction to the Dynamic Data
         * Exchange (DDE) callback function, DdeCallback, after the server calls
         * the DdePostAdvise function.
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The format in which the data should be
         * submitted to the client.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param item hsz2 - A handle to the item name that has changed.
         * @param count The count of XTYP_ADVREQ transactions that remain to be
         * processed on the same topic, item, and format name set within the
         * context of the current call to the DdePostAdvise function. The count
         * is zero if the current XTYP_ADVREQ transaction is the last one. A
         * server can use this count to determine whether to create an
         * HDATA_APPOWNED data handle to the advise data.
         *
         * <p>
         * This is set to CADV_LATEACK if the DDEML issued the XTYP_ADVREQ
         * transaction because of a late-arriving DDE_ACK message from a client
         * being outrun by the server. </p>
         *
         * @return Data for the changed item or NULL if transaction can't be
         * completed.
         */
        HDDEDATA onAdvreq(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item, int count);
    }

    public interface RequestHandler {
        /**
         * A client uses the XTYP_REQUEST transaction to request data from a
         * server. A Dynamic Data Exchange (DDE) server callback function,
         * DdeCallback, receives this transaction when a client specifies
         * XTYP_REQUEST in the DdeClientTransaction function.
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The format in which the data should be
         * submitted to the client.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param item hsz2 - A handle to the item name.
         *
         * @return Data for the changed item or NULL if transaction can't be
         * completed. If the server returns NULL, the client will receive a
         * DDE_FNOTPROCESSED flag.
         */
        HDDEDATA onRequest(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item);
    }

    public interface WildconnectHandler {

        /**
         * Enables a client to establish a conversation on each of the server's
         * service name and topic name pairs that match the specified service
         * name and topic name. A Dynamic Data Exchange (DDE) server callback
         * function, DdeCallback, receives this transaction when a client
         * specifies a NULL service name, a NULL topic name, or both in a call
         * to the DdeConnect or DdeConnectList function.
         *
         * @param transactionType uType - The transaction type.
         * @param topic A handle to the topic name. If this parameter is NULL,
         * the client is requesting a conversation on all topic names that the
         * server supports.
         * @param service A handle to the service name. If this parameter is
         * NULL, the client is requesting a conversation on all service names
         * that the server supports.
         * @param convcontext dwData1 - CONVCONTEXT structure that contains
         * context information for the conversation. If the client is not a
         * DDEML application, this parameter is NULL.
         * @param sameInstance dwData2 - Specifies whether the client is the
         * same application instance as the server.
         * @return the supported HSZPAIRs (do not include the terminating pair
         * needed be the DdeCallback!)
         */
        List<HSZPAIR> onWildconnect(int transactionType, HSZ topic, HSZ service, CONVCONTEXT convcontext, boolean sameInstance);
    }

    public interface AdvdataHandler {
        /**
         * Informs the client that the value of the data item has changed. The
         * Dynamic Data Exchange (DDE) client callback function, DdeCallback,
         * receives this transaction after establishing an advise loop with a
         * server.
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The format atom of the data sent from the
         * server.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param item hsz2 - A handle to the item name.
         * @param hdata A handle to the data associated with the topic name and
         * item name pair. This parameter is NULL if the client specified the
         * XTYPF_NODATA flag when it requested the advise loop.
         * @return A DDE callback function should return DDE_FACK if it
         * processes this transaction, DDE_FBUSY if it is too busy to process
         * this transaction, or DDE_FNOTPROCESSED if it rejects this
         * transaction.
         */
        int onAdvdata(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item, HDDEDATA hdata);
    }

    public interface ConnectConfirmHandler {

        /**
         * A Dynamic Data Exchange (DDE) server callback function, DdeCallback,
         * receives the XTYP_CONNECT_CONFIRM transaction to confirm that a
         * conversation has been established with a client and to provide the
         * server with the conversation handle. The system sends this
         * transaction as a result of a previous XTYP_CONNECT or
         * XTYP_WILDCONNECT transaction.
         *
         * @param transactionType uType - The transaction type.
         * @param hconv A handle to the new conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param service hsz2 - A handle to the service name on which the
         * conversation has been established.
         * @param sameInstance dwData2 - Specifies whether the client is the
         * same application instance as the server.
         */
        void onConnectConfirm(int transactionType, Ddeml.HCONV hconv, Ddeml.HSZ topic, Ddeml.HSZ service, boolean sameInstance);
    }

    public interface DisconnectHandler {

        /**
         * An application's Dynamic Data Exchange (DDE) callback function,
         * DdeCallback, receives the XTYP_DISCONNECT transaction when the
         * application's partner in a conversation uses the DdeDisconnect
         * function to terminate the conversation.
         *
         * <p>
         * <strong>Remarks</strong></p>
         *
         * <p>
         * This transaction is filtered if the application specified the
         * CBF_SKIP_DISCONNECTS flag in the DdeInitialize function.</p>
         * <p>
         * The application can obtain the status of the terminated conversation
         * by calling the DdeQueryConvInfo function while processing this
         * transaction. The conversation handle becomes invalid after the
         * callback function returns.</p>
         * <p>
         * An application cannot block this transaction type; the CBR_BLOCK
         * return code is ignored.</p>
         *
         * @param transactionType uType - The transaction type.
         * @param hconv A handle to that the conversation was terminated.
         * @param sameInstance dwData2 - Specifies whether the client is the
         * same application instance as the server.
         */
        void onDisconnect(int transactionType, HCONV hconv, boolean sameInstance);
    }

    public interface ErrorHandler {

        /**
         * A Dynamic Data Exchange (DDE) callback function, DdeCallback,
         * receives the XTYP_ERROR transaction when a critical error occurs.
         *
         * <p>
         * <strong>Remarks</strong></p>
         *
         * <p>
         * An application cannot block this transaction type; the CBR_BLOCK
         * return code is ignored. The Dynamic Data Exchange Management Library
         * (DDEML) attempts to free memory by removing noncritical resources. An
         * application that has blocked conversations should unblock them. </p>
         *
         * @param transactionType uType - The transaction type.
         * @param hconv A handle to the conversation associated with the error.
         * This parameter is NULL if the error is not associated with a
         * conversation.
         * @param errorCode dwData1 - The error code in the low-order word.
         * Currently, only the following error code is supported.
         * <p>
         * Only Ddeml.DMLERR_LOW_MEMRORY is known: Memory is low; advise, poke,
         * or execute data may be lost, or the system may fail.</p>
         */
        void onError(int transactionType, HCONV hconv, int errorCode);
    }

    public interface RegisterHandler {
        /**
         * A Dynamic Data Exchange (DDE) callback function, DdeCallback, receives the XTYP_REGISTER transaction type whenever a Dynamic Data Exchange Management Library (DDEML) server application uses the DdeNameService function to register a service name, or whenever a non-DDEML application that supports the System topic is started.
         *
         * <p>
         * <strong>Remarks</strong></p>
         *<p>
         * This transaction is filtered if the application specified the
         * CBF_SKIP_REGISTRATIONS flag in the DdeInitialize function.</p>
         *<p>
         * A application cannot block this transaction type; the CBR_BLOCK
         * return code is ignored.</p>
         *<p>
         * An application should use the hsz1 parameter to add the service name
         * to the list of servers available to the user. An application should
         * use the hsz2 parameter to identify which application instance has
         * started. </p>
         *
         * @param transactionType uType - The transaction type.
         * @param baseServiceName hsz1 - A handle to the base service name being
         * registered.
         * @param instanceSpecificServiceName hsz2 - A handle to the
         * instance-specific service name being registered.
         */
        void onRegister(int transactionType, HSZ baseServiceName, HSZ instanceSpecificServiceName);
    }

    public interface XactCompleteHandler {
        /**
         * A Dynamic Data Exchange (DDE) client callback function, DdeCallback,
         * receives the XTYP_XACT_COMPLETE transaction when an asynchronous
         * transaction, initiated by a call to the DdeClientTransaction
         * function, has completed.
         *
         * <p>
         * <strong>Remarks</strong></p>
         * <p>
         * An application must not free the data handle obtained during this
         * transaction. An application must, however, copy the data associated
         * with the data handle if the application must process the data after
         * the callback function returns. An application can use the DdeGetData
         * function to copy the data.</p>
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The format of the data associated with the
         * completed transaction (if applicable) or NULL if no data was
         * exchanged during the transaction.
         * @param hConv - A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name involved in the
         * completed transaction.
         * @param item hsz2 - A handle to the item name involved in the
         * completed transaction.
         * @param hdata A handle to the data involved in the completed
         * transaction, if applicable. If the transaction was successful but
         * involved no data, this parameter is TRUE. This parameter is NULL if
         * the transaction was unsuccessful.
         * @param transactionIdentifier dwData1 - The transaction identifier of
         * the completed transaction.
         * @param statusFlag dwData2 - Any applicable DDE_ status flags in the low word. This parameter provides support for applications dependent on DDE_APPSTATUS bits. It is recommended that applications no longer use these bits � they may not be supported in future versions of the DDEML.
         */
        void onXactComplete(int transactionType, int dataFormat, HCONV hConv, HSZ topic, HSZ item, HDDEDATA hdata, ULONG_PTR transactionIdentifier, ULONG_PTR statusFlag);
    }

    public interface UnregisterHandler {
        /**
         * A Dynamic Data Exchange (DDE) callback function, DdeCallback, receives the XTYP_REGISTER transaction type whenever a Dynamic Data Exchange Management Library (DDEML) server application uses the DdeNameService function to register a service name, or whenever a non-DDEML application that supports the System topic is started.
         *
         * <p>
         * <strong>Remarks</strong></p>
         * <p>
         * This transaction is filtered if the application specified the
         * CBF_SKIP_REGISTRATIONS flag in the DdeInitialize function.</p>
         *<p>
         * A application cannot block this transaction type; the CBR_BLOCK
         * return code is ignored.</p>
         *<p>
         * An application should use the hsz1 parameter to remove the service
         * name from the list of servers available to the user. An application
         * should use the hsz2 parameter to identify which application instance
         * has terminated. </p>
         *
         * @param transactionType uType - The transaction type.
         * @param baseServiceName hsz1 - A handle to the base service name being
         * registered.
         * @param instanceSpecificServiceName hsz2 - A handle to the
         * instance-specific service name being registered.
         */
        void onUnregister(int transactionType, HSZ baseServiceName, HSZ instanceSpecificServiceName);
    }

    public interface ExecuteHandler {
            /**
         * A client uses the XTYP_EXECUTE transaction to send a command string
         * to the server. A Dynamic Data Exchange (DDE) server callback
         * function, DdeCallback, receives this transaction when a client
         * specifies XTYP_EXECUTE in the DdeClientTransaction function.
         *
         * <p>
         * <strong>Remarks</strong></p>
         * <p>
         * This transaction is filtered if the server application specified the
         * CBF_FAIL_EXECUTES flag in the DdeInitialize function.</p>
         *<p>
         * Because most client applications expect a server application to
         * perform an XTYP_EXECUTE transaction synchronously, a server should
         * attempt to perform all processing of the XTYP_EXECUTE transaction
         * either from within the DDE callback function or by returning the
         * CBR_BLOCK return code. If the hdata parameter is a command that
         * instructs the server to terminate, the server should do so after
         * processing the XTYP_EXECUTE transaction. </p>
         *
         * @param transactionType uType - The transaction type.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param commandString A handle to the command string.
         * @return A server callback function should return DDE_FACK if it
         * processes this transaction, DDE_FBUSY if it is too busy to process
         * this transaction, or DDE_FNOTPROCESSED if it rejects this
         * transaction.
         *
         */
        int onExecute(int transactionType, Ddeml.HCONV hconv, Ddeml.HSZ topic, Ddeml.HDDEDATA commandString);
    }

    public interface PokeHandler {
        /**
         * A client uses the XTYP_POKE transaction to send unsolicited data to
         * the server. A Dynamic Data Exchange (DDE) server callback function,
         * DdeCallback, receives this transaction when a client specifies
         * XTYP_POKE in the DdeClientTransaction function.
         *
         * @param transactionType uType - The transaction type.
         * @param dataFormat uFmt - The format of the data sent from the server.
         * @param hconv A handle to the conversation.
         * @param topic hsz1 - A handle to the topic name.
         * @param item hsz2 - A handle to the item name.
         * @param hdata - A handle to the data that the client is sending to the
         * server.
         * @return A server callback function should return the DDE_FACK flag if
         * it processes this transaction, the DDE_FBUSY flag if it is too busy
         * to process this transaction, or the DDE_FNOTPROCESSED flag if it
         * rejects this transaction.
         *
         */
        int onPoke(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item, HDDEDATA hdata);
    }

    public interface MonitorHandler {

        /**
         * A Dynamic Data Exchange (DDE) debugger's DDE callback function,
         * DdeCallback, receives the XTYP_MONITOR transaction whenever a DDE
         * event occurs in the system. To receive this transaction, an
         * application must specify the APPCLASS_MONITOR value when it calls the
         * DdeInitialize function.
         *
         * @param transactionType uType - The transaction type.
         * @param hdata A handle to a DDE object that contains information about
         * the DDE event. The application should use the DdeAccessData function
         * to obtain a pointer to the object.
         * @param eventCode dwData2 - The DDE event. This parameter can be one
         * of the following values:
         *
         * <table>
         * <tr>
         * <th>Value</th><th>Meaning</th>
         * </tr>
         * <tr><td>
         * MF_CALLBACKS<br>
         * 0x08000000
         * </td>
         * <td>
         * The system sent a transaction to a DDE callback function. The DDE
         * object contains a MONCBSTRUCT structure that provides information
         * about the transaction.
         * </td>
         * </tr>
         * <tr><td>
         * MF_CONV<br>
         * 0x40000000
         * </td>
         * <td>
         * A DDE conversation was established or terminated. The DDE object
         * contains a MONCONVSTRUCT structure that provides information about
         * the conversation.
         * </td>
         * </tr>
         * <tr><td>
         * MF_ERRORS<br>
         * 0x10000000
         * </td>
         * <td>
         * A DDE error occurred. The DDE object contains a MONERRSTRUCT
         * structure that provides information about the error.
         * </td>
         * </tr>
         * <tr><td>
         * MF_HSZ_INFO<br>
         * 0x01000000
         * </td>
         * <td>
         * A DDE application created, freed, or incremented the usage count of a
         * string handle, or a string handle was freed as a result of a call to
         * the DdeUninitialize function. The DDE object contains a MONHSZSTRUCT
         * structure that provides information about the string handle.
         * </td>
         * </tr>
         * <tr><td>
         * MF_LINKS<br>
         * 0x20000000
         * </td>
         * <td>
         * A DDE application started or stopped an advise loop. The DDE object
         * contains a MONLINKSTRUCT structure that provides information about
         * the advise loop.
         * </td>
         * </tr>
         * <tr><td>
         * MF_POSTMSGS<br>
         * 0x04000000
         * </td>
         * <td>
         * The system or an application posted a DDE message. The DDE object
         * contains a MONMSGSTRUCT structure that provides information about the
         * message.
         * </td>
         * </tr>
         * <tr><td>
         * MF_SENDMSGS<br>
         * 0x02000000
         * </td>
         * <td>
         * The system or an application sent a DDE message. The DDE object
         * contains a MONMSGSTRUCT structure that provides information about the
         * message.
         * </td>
         * </tr>
         * </table>
         */
        void onMonitor(int transactionType, HDDEDATA hdata, int eventCode);
    }

    /**
     * DdeAdapter implements DdeCallback and allow dynamic registration for
     * mulitple handlers, that can be registered and unregistered at runtime.
     *
     * <dl>
     * <dt>AdvstartHandler</dt>
     * <dd>All registered AdvstartHandler are evaluated. If at least one returns
     * true, the whole evaluation is considered true.</dd>
     * <dt>AdvstopHandler</dt>
     * <dd>All registered AdvstopHandler are invoked.</dd>
     * <dt>ConnectHandler</dt>
     * <dd>All registered ConnectHandler are evaluated. If at least one returns
     * true, the whole evaluation is considered true.</dd>
     * <dt>WildconnectHandler</dt>
     * <dd>All registered WildconnectHandler are evaluated. The result is the
     * union of all HSZPAIRs.</dd>
     * <dt>ConnectConfirmHandler</dt>
     * <dd>All registered ConnectConfirmHandler are evaluated.</dd>
     * <dt>DisconnectHandler</dt>
     * <dd>All registered DisconnectHandler are evaluated.</dd>
     * <dt>ErrorHandler</dt>
     * <dd>All registered ErrorHandler are evaluated.</dd>
     * <dt>RegisterHandler</dt>
     * <dd>All registered RegisterHandler are evaluated.</dd>
     * <dt>XactCompleteHandler</dt>
     * <dd>All registered XactCompleteHandler are evaluated.</dd>
     * <dt>UnregisterHandler</dt>
     * <dd>All registered UnregisterHandlers are evaluated.</dd>
     * <dt>MonitorHandler</dt>
     * <dd>All registered AdvstopHandler are invoked.</dd>
     * <dt>AdvdataHandler</dt>
     * <dd>The AdvdataHandlers are evaluated in registration order - evaluation
     * stops after the first handler not returning Ddeml.FNOTPROCESSED.</dd>
     * <dt>ExecuteHandler</dt>
     * <dd>The ExecuteHandler are evaluated in registration order - evaluation
     * stops after the first handler not returning Ddeml.FNOTPROCESSED.</dd>
     * <dt>PokeHandler</dt>
     * <dd>The PokeHandler are evaluated in registration order - evaluation
     * stops after the first handler not returning Ddeml.FNOTPROCESSED.</dd>
     * <dt>AdvreqHandler</dt>
     * <dd>The AdvreqHandlers are evaluated in registration order - evaluation
     * stops after the first handler returning a non null value.</dd>
     * <dt>RequestHandler</dt>
     * <dd>The RequestHandlers are evaluated in registration order - evaluation
     * stops after the first handler returning a non null value.</dd>
     * </dl>
     */
    public static class DdeAdapter implements Ddeml.DdeCallback {

        public static class BlockException extends RuntimeException{};

        private static final Logger LOG = Logger.getLogger(DdeAdapter.class.getName());

        private int idInst;

        public void setInstanceIdentifier(int idInst) {
            this.idInst = idInst;
        }

        public WinDef.PVOID ddeCallback(int wType, int wFmt, Ddeml.HCONV hConv, Ddeml.HSZ hsz1, Ddeml.HSZ hsz2, Ddeml.HDDEDATA hData, BaseTSD.ULONG_PTR lData1, BaseTSD.ULONG_PTR lData2) {
            boolean booleanResult;
            Ddeml.HDDEDATA data;
            Ddeml.CONVCONTEXT convcontext;
            int intResult;
            String transactionTypeName = null;
            try {
                switch (wType) {
                    case Ddeml.XTYP_ADVSTART:
                        booleanResult = onAdvstart(wType, wFmt, hConv, hsz1, hsz2);
                        return new WinDef.PVOID(Pointer.createConstant(new WinDef.BOOL(booleanResult).intValue()));
                    case Ddeml.XTYP_CONNECT:
                        convcontext = null;
                        if (lData1.toPointer() != null) {
                            convcontext = new Ddeml.CONVCONTEXT(new Pointer(lData1.longValue()));
                        }
                        booleanResult = onConnect(wType, hsz1, hsz2, convcontext, lData2 != null && lData2.intValue() != 0);
                        return new WinDef.PVOID(Pointer.createConstant(new WinDef.BOOL(booleanResult).intValue()));
                    case Ddeml.XTYP_ADVREQ:
                        int count = lData1.intValue() & 0xFFFF;
                        data = onAdvreq(wType, wFmt, hConv, hsz1, hsz2, count);
                        if (data == null) {
                            return new WinDef.PVOID();
                        } else {
                            return new WinDef.PVOID(data.getPointer());
                        }
                    case Ddeml.XTYP_REQUEST:
                        data = onRequest(wType, wFmt, hConv, hsz1, hsz2);
                        if (data == null) {
                            return new WinDef.PVOID();
                        } else {
                            return new WinDef.PVOID(data.getPointer());
                        }
                    case Ddeml.XTYP_WILDCONNECT:
                        convcontext = null;
                        if (lData1.toPointer() != null) {
                            convcontext = new Ddeml.CONVCONTEXT(new Pointer(lData1.longValue()));
                        }
                        Ddeml.HSZPAIR[] hszPairs = onWildconnect(wType, hsz1, hsz2, convcontext, lData2 != null && lData2.intValue() != 0);
                        if (hszPairs == null || hszPairs.length == 0) {
                            return new WinDef.PVOID();
                        }
                        int size = 0;
                        for (Ddeml.HSZPAIR hp : hszPairs) {
                            hp.write();
                            size += hp.size();
                        }
                        data = Ddeml.INSTANCE.DdeCreateDataHandle(idInst,
                                hszPairs[0].getPointer(),
                                size,
                                0,
                                null,
                                wFmt,
                                0);
                        return new WinDef.PVOID(data.getPointer());
                    case Ddeml.XTYP_ADVDATA:
                        intResult = onAdvdata(wType, wFmt, hConv, hsz1, hsz2, hData);
                        return new WinDef.PVOID(Pointer.createConstant(intResult));
                    case Ddeml.XTYP_EXECUTE:
                        intResult = onExecute(wType, hConv, hsz1, hData);
                        Ddeml.INSTANCE.DdeFreeDataHandle(hData);
                        return new WinDef.PVOID(Pointer.createConstant(intResult));
                    case Ddeml.XTYP_POKE:
                        intResult = onPoke(wType, wFmt, hConv, hsz1, hsz2, hData);
                        return new WinDef.PVOID(Pointer.createConstant(intResult));
                    case Ddeml.XTYP_ADVSTOP:
                        onAdvstop(wType, wFmt, hConv, hsz1, hsz2);
                        break;
                    case Ddeml.XTYP_CONNECT_CONFIRM:
                        onConnectConfirm(wType, hConv, hsz1, hsz2, lData2 != null && lData2.intValue() != 0);
                        break;
                    case Ddeml.XTYP_DISCONNECT:
                        onDisconnect(wType, hConv, lData2 != null && lData2.intValue() != 0);
                        break;
                    case Ddeml.XTYP_ERROR:
                        onError(wType, hConv, (int) (lData2.longValue() & 0xFFFF));
                        break;
                    case Ddeml.XTYP_REGISTER:
                        onRegister(wType, hsz1, hsz2);
                        break;
                    case Ddeml.XTYP_XACT_COMPLETE:
                        onXactComplete(wType, wFmt, hConv, hsz1, hsz2, hData, lData1, lData2);
                        break;
                    case Ddeml.XTYP_UNREGISTER:
                        onUnregister(wType, hsz1, hsz2);
                        break;
                    case Ddeml.XTYP_MONITOR:
                        onMonitor(wType, hData, lData2.intValue());
                        break;
                    default:
                        LOG.log(Level.FINE, String.format("Not implemented Operation - Transaction type: 0x%X (%s)", wType, transactionTypeName));
                }
            } catch (BlockException ex) {
                return new WinDef.PVOID(Pointer.createConstant(-1));
            } catch (Throwable ex) {
                LOG.log(Level.WARNING, "Exception in DDECallback", ex);
            }
            return new WinDef.PVOID();
        };

        private final List<AdvstartHandler> advstartHandler = new CopyOnWriteArrayList<>();

        public void registerAdvstartHandler(AdvstartHandler handler) {
            advstartHandler.add(handler);
        }

        public void unregisterAdvstartHandler(AdvstartHandler handler) {
            advstartHandler.remove(handler);
        }

        private boolean onAdvstart(int transactionType, int dataFormat, Ddeml.HCONV hconv, Ddeml.HSZ topic, Ddeml.HSZ item) {
            boolean oneHandlerTrue = false;
            for (AdvstartHandler handler : advstartHandler) {
                if (handler.onAdvstart(transactionType, dataFormat, hconv, topic, item)) {
                    oneHandlerTrue = true;
                }
            }
            return oneHandlerTrue;
        }

        private final List<AdvstopHandler> advstopHandler = new CopyOnWriteArrayList<>();

        public void registerAdvstopHandler(AdvstopHandler handler) {
            advstopHandler.add(handler);
        }

        public void unregisterAdvstopHandler(AdvstopHandler handler) {
            advstopHandler.remove(handler);
        }

        private void onAdvstop(int transactionType, int dataFormat, Ddeml.HCONV hconv, Ddeml.HSZ topic, Ddeml.HSZ item) {
            for (AdvstopHandler handler : advstopHandler) {
                handler.onAdvstop(transactionType, dataFormat, hconv, topic, item);
            }
        }

        private final List<ConnectHandler> connectHandler = new CopyOnWriteArrayList<>();

        public void registerConnectHandler(ConnectHandler handler) {
            connectHandler.add(handler);
        }

        public void unregisterConnectHandler(ConnectHandler handler) {
            connectHandler.remove(handler);
        }

        private boolean onConnect(int transactionType, Ddeml.HSZ topic, Ddeml.HSZ service, Ddeml.CONVCONTEXT convcontext, boolean sameInstance) {
            boolean oneHandlerTrue = false;
            for (ConnectHandler handler : connectHandler) {
                if (handler.onConnect( transactionType, topic, service, convcontext, sameInstance)) {
                    oneHandlerTrue = true;
                }
            }
            return oneHandlerTrue;
        }

        private final List<AdvreqHandler> advReqHandler = new CopyOnWriteArrayList<>();

        public void registerAdvReqHandler(AdvreqHandler handler) {
            advReqHandler.add(handler);
        }

        public void unregisterAdvReqHandler(AdvreqHandler handler) {
            advReqHandler.remove(handler);
        }

        private Ddeml.HDDEDATA onAdvreq(int transactionType, int dataFormat, Ddeml.HCONV hconv, Ddeml.HSZ topic, Ddeml.HSZ item, int count) {
            for (AdvreqHandler handler : advReqHandler) {
                HDDEDATA result = handler.onAdvreq(transactionType, dataFormat, hconv, topic, item, count);
                if(result != null) {
                    return result;
                }
            }
            return null;
        }

        private final List<RequestHandler> requestHandler = new CopyOnWriteArrayList<>();

        public void registerRequestHandler(RequestHandler handler) {
            requestHandler.add(handler);
        }

        public void unregisterRequestHandler(RequestHandler handler) {
            requestHandler.remove(handler);
        }

        private Ddeml.HDDEDATA onRequest(int transactionType, int dataFormat, Ddeml.HCONV hconv, Ddeml.HSZ topic, Ddeml.HSZ item) {
            for (RequestHandler handler : requestHandler) {
                HDDEDATA result = handler.onRequest(transactionType, dataFormat, hconv, topic, item);
                if(result != null) {
                    return result;
                }
            }
            return null;
        }

        private final List<WildconnectHandler> wildconnectHandler = new CopyOnWriteArrayList<>();

        public void registerWildconnectHandler(WildconnectHandler handler) {
            wildconnectHandler.add(handler);
        }

        public void unregisterWildconnectHandler(WildconnectHandler handler) {
            wildconnectHandler.remove(handler);
        }

        private Ddeml.HSZPAIR[] onWildconnect(int transactionType, HSZ topic, HSZ service, CONVCONTEXT convcontext, boolean sameInstance) {
            List<HSZPAIR> hszpairs = new ArrayList<>(1);
            for(WildconnectHandler handler: wildconnectHandler) {
                hszpairs.addAll(handler.onWildconnect(transactionType, topic, service, convcontext, sameInstance));
            }
            return hszpairs.toArray(new HSZPAIR[0]);
        }


        private final List<AdvdataHandler> advdataHandler = new CopyOnWriteArrayList<>();

        public void registerAdvdataHandler(AdvdataHandler handler) {
            advdataHandler.add(handler);
        }

        public void unregisterAdvdataHandler(AdvdataHandler handler) {
            advdataHandler.remove(handler);
        }

        private int onAdvdata(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item, HDDEDATA hdata) {
            for (AdvdataHandler handler : advdataHandler) {
                int result = handler.onAdvdata(transactionType, dataFormat, hconv, topic, item, hdata);
                if(result != Ddeml.DDE_FNOTPROCESSED) {
                    return result;
                }
            }
            return Ddeml.DDE_FNOTPROCESSED;
        }

        private final List<ExecuteHandler> executeHandler = new CopyOnWriteArrayList<>();

        public void registerExecuteHandler(ExecuteHandler handler) {
            executeHandler.add(handler);
        }

        public void unregisterExecuteHandler(ExecuteHandler handler) {
            executeHandler.remove(handler);
        }

        private int onExecute(int transactionType, HCONV hconv, HSZ topic, HDDEDATA commandString) {
            for (ExecuteHandler handler : executeHandler) {
                int result = handler.onExecute(transactionType, hconv, topic, commandString);
                if(result != Ddeml.DDE_FNOTPROCESSED) {
                    return result;
                }
            }
            return Ddeml.DDE_FNOTPROCESSED;
        }

        private final List<PokeHandler> pokeHandler = new CopyOnWriteArrayList<>();

        public void registerPokeHandler(PokeHandler handler) {
            pokeHandler.add(handler);
        }

        public void unregisterPokeHandler(PokeHandler handler) {
            pokeHandler.remove(handler);
        }

        private int onPoke(int transactionType, int dataFormat, HCONV hconv, HSZ topic, HSZ item, HDDEDATA hdata) {
            for (PokeHandler handler : pokeHandler) {
                int result = handler.onPoke(transactionType, dataFormat, hconv, topic, item, hdata);
                if(result != Ddeml.DDE_FNOTPROCESSED) {
                    return result;
                }
            }
            return Ddeml.DDE_FNOTPROCESSED;
        }

        private final List<ConnectConfirmHandler> connectConfirmHandler = new CopyOnWriteArrayList<>();

        public void registerConnectConfirmHandler(ConnectConfirmHandler handler) {
            connectConfirmHandler.add(handler);
        }

        public void unregisterConnectConfirmHandler(ConnectConfirmHandler handler) {
            connectConfirmHandler.remove(handler);
        }

        private void onConnectConfirm(int transactionType, HCONV hconv, HSZ topic, HSZ service, boolean sameInstance) {
            for(ConnectConfirmHandler handler: connectConfirmHandler) {
                handler.onConnectConfirm(transactionType, hconv, topic, service, sameInstance);
            }
        }

        private final List<DisconnectHandler> disconnectHandler = new CopyOnWriteArrayList<>();

        public void registerDisconnectHandler(DisconnectHandler handler) {
            disconnectHandler.add(handler);
        }

        public void unregisterDisconnectHandler(DisconnectHandler handler) {
            disconnectHandler.remove(handler);
        }

        private void onDisconnect(int transactionType, Ddeml.HCONV hconv, boolean sameInstance) {
            for(DisconnectHandler handler: disconnectHandler) {
                handler.onDisconnect(transactionType, hconv, sameInstance);
            }
        }

        private final List<ErrorHandler> errorHandler = new CopyOnWriteArrayList<>();

        public void registerErrorHandler(ErrorHandler handler) {
            errorHandler.add(handler);
        }

        public void unregisterErrorHandler(ErrorHandler handler) {
            errorHandler.remove(handler);
        }

        private void onError(int transactionType, Ddeml.HCONV hconv, int errorCode) {
            for(ErrorHandler handler: errorHandler) {
                handler.onError(transactionType, hconv, errorCode);
            }
        }

        private final List<RegisterHandler> registerHandler = new CopyOnWriteArrayList<>();

        public void registerRegisterHandler(RegisterHandler handler) {
            registerHandler.add(handler);
        }

        public void unregisterRegisterHandler(RegisterHandler handler) {
            registerHandler.remove(handler);
        }

        private void onRegister(int transactionType, Ddeml.HSZ baseServiceName, Ddeml.HSZ instanceSpecificServiceName) {
            for(RegisterHandler handler: registerHandler) {
                handler.onRegister(transactionType, baseServiceName, instanceSpecificServiceName);
            }
        }

        private final List<XactCompleteHandler> xactCompleteHandler = new CopyOnWriteArrayList<>();

        public void registerXactCompleteHandler(XactCompleteHandler handler) {
            xactCompleteHandler.add(handler);
        }

        public void xactCompleteXactCompleteHandler(XactCompleteHandler handler) {
            xactCompleteHandler.remove(handler);
        }

        private void onXactComplete(int transactionType, int dataFormat, HCONV hConv, HSZ topic, HSZ item, HDDEDATA hdata, ULONG_PTR transactionIdentifier, ULONG_PTR statusFlag) {
            for(XactCompleteHandler handler: xactCompleteHandler) {
                handler.onXactComplete(transactionType, dataFormat, hConv, topic, item, hdata, transactionIdentifier, statusFlag);
            }
        }

        private final List<UnregisterHandler> unregisterHandler = new CopyOnWriteArrayList<>();

        public void registerUnregisterHandler(UnregisterHandler handler) {
            unregisterHandler.add(handler);
        }

        public void unregisterUnregisterHandler(UnregisterHandler handler) {
            unregisterHandler.remove(handler);
        }

        private void onUnregister(int transactionType, HSZ baseServiceName, HSZ instanceSpecificServiceName) {
            for(UnregisterHandler handler: unregisterHandler) {
                handler.onUnregister(transactionType, baseServiceName, instanceSpecificServiceName);
            }
        }

        private final List<MonitorHandler> monitorHandler = new CopyOnWriteArrayList<>();

        public void registerMonitorHandler(MonitorHandler handler) {
            monitorHandler.add(handler);
        }

        public void unregisterMonitorHandler(MonitorHandler handler) {
            monitorHandler.remove(handler);
        }

        private void onMonitor(int transactionType, HDDEDATA hdata, int dwData2) {
            for(MonitorHandler handler: monitorHandler) {
                handler.onMonitor(transactionType, hdata, dwData2);
            }
        }
    }

    /**
     * DdemlException wraps error codes reported by the DDEML functions as an
     * exception.
     */
    public static class DdemlException extends RuntimeException {
        private static final Map<Integer,String> ERROR_CODE_MAP;

        static {
            Map<Integer, String> errorCodeMapBuilder = new HashMap<>();
            for (Field f : Ddeml.class.getFields()) {
                String name = f.getName();
                if (name.startsWith("DMLERR_") && (!name.equals("DMLERR_FIRST")) && (!name.equals("DMLERR_LAST"))) {
                    try {
                        errorCodeMapBuilder.put(f.getInt(null), name);
                    } catch (IllegalArgumentException | IllegalAccessException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
            ERROR_CODE_MAP = Collections.unmodifiableMap(errorCodeMapBuilder);
        }

        private final int errorCode;

        public static DdemlException create(int errorCode) {
            String errorName = ERROR_CODE_MAP.get(errorCode);
            return new DdemlException(errorCode, String.format("%s (Code: 0x%X)",
                    errorName != null ? errorName : "",
                    errorCode));
        }

        public DdemlException(int errorCode, String message) {
            super(message);
            this.errorCode = errorCode;
        }

        public int getErrorCode() {
            return errorCode;
        }
    }

    /**
     * The IDdeConnection defines the functions, that work an a concrete
     * connection/conversation.
     */
    public interface IDdeConnection extends Closeable {
        public Ddeml.HCONV getConv();

        /**
         * Run an XTYP_EXECUTE client transaction.
         *
         * @param executeString The string passed to the server for execution
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         * @param userHandle data to associate with the transaction
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_BUSY</li>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void execute(String executeString, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Run an XTYP_POKE client transaction
         *
         * @param data The beginning of the data the client must pass to the
         * server.
         *
         * <p>
         * Optionally, an application can specify the data handle (HDDEDATA) to
         * pass to the server and in that case the cbData parameter should be
         * set to -1. This parameter is required only if the wType parameter is
         * XTYP_EXECUTE or XTYP_POKE. Otherwise, this parameter should be
         * NULL.</p>
         *
         * <p>
         * For the optional usage of this parameter, XTYP_POKE transactions
         * where pData is a data handle, the handle must have been created by a
         * previous call to the DdeCreateDataHandle function, employing the same
         * data format specified in the wFmt parameter.</p>
         *
         * @param dataLength The length, in bytes, of the data pointed to by the
         * pData parameter, including the terminating NULL, if the data is a
         * string. A value of -1 indicates that pData is a data handle that
         * identifies the data being sent.
         *
         * @param item A handle to the data item for which data is being
         * exchanged during the transaction. This handle must have been created
         * by a previous call to the DdeCreateStringHandle function. This
         * parameter is ignored (and should be set to 0L) if the wType parameter
         * is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * <p>
         * If the transaction specified by the wType parameter does not pass
         * data or is XTYP_EXECUTE, this parameter should be zero.</p>
         *
         * <p>
         * If the transaction specified by the wType parameter references
         * non-execute DDE data ( XTYP_POKE, XTYP_ADVSTART, XTYP_ADVSTOP,
         * XTYP_REQUEST), the wFmt value must be either a valid predefined (CF_)
         * DDE format or a valid registered clipboard format.</p>
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param userHandle data to associate with the transaction
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_BUSY</li>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void poke(Pointer data, int dataLength, Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Run an XTYP_POKE client transaction
         *
         * @param data The beginning of the data the client must pass to the
         * server.
         *
         * <p>
         * Optionally, an application can specify the data handle (HDDEDATA) to
         * pass to the server and in that case the cbData parameter should be
         * set to -1. This parameter is required only if the wType parameter is
         * XTYP_EXECUTE or XTYP_POKE. Otherwise, this parameter should be
         * NULL.</p>
         *
         * <p>
         * For the optional usage of this parameter, XTYP_POKE transactions
         * where pData is a data handle, the handle must have been created by a
         * previous call to the DdeCreateDataHandle function, employing the same
         * data format specified in the wFmt parameter.</p>
         *
         * @param dataLength The length, in bytes, of the data pointed to by the
         * pData parameter, including the terminating NULL, if the data is a
         * string. A value of -1 indicates that pData is a data handle that
         * identifies the data being sent.
         *
         * @param item The data item for which data is being exchanged during
         * the transaction. This parameter is ignored (and should be set to NULL)
         * if the wType parameter is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * <p>
         * If the transaction specified by the wType parameter does not pass
         * data or is XTYP_EXECUTE, this parameter should be zero.</p>
         *
         * <p>
         * If the transaction specified by the wType parameter references
         * non-execute DDE data ( XTYP_POKE, XTYP_ADVSTART, XTYP_ADVSTOP,
         * XTYP_REQUEST), the wFmt value must be either a valid predefined (CF_)
         * DDE format or a valid registered clipboard format.</p>
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param userHandle data to associate with the transaction
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_BUSY</li>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void poke(Pointer data, int dataLength, String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Begins a data transaction between a client and a server. Only a
         * Dynamic Data Exchange (DDE) client application can call this
         * function, and the application can use it only after establishing a
         * conversation with the server.
         *
         * @param item A handle to the data item for which data is being
         * exchanged during the transaction. This handle must have been created
         * by a previous call to the DdeCreateStringHandle function. This
         * parameter is ignored (and should be set to 0L) if the wType parameter
         * is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * <p>
         * If the transaction specified by the wType parameter references
         * non-execute DDE data ( XTYP_POKE, XTYP_ADVSTART, XTYP_ADVSTOP,
         * XTYP_REQUEST), the wFmt value must be either a valid predefined (CF_)
         * DDE format or a valid registered clipboard format.</p>
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * @return If the function succeeds, the return value is a data handle
         * that identifies the data for successful synchronous transactions in
         * which the client expects data from the server. The return value is
         * nonzero for successful asynchronous transactions and for synchronous
         * transactions in which the client does not expect data. The return
         * value is zero for all unsuccessful transactions.
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public Ddeml.HDDEDATA request(Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Begins a data transaction between a client and a server. Only a
         * Dynamic Data Exchange (DDE) client application can call this
         * function, and the application can use it only after establishing a
         * conversation with the server.
         *
         * @param item The data item for which data is being exchanged during
         * the transaction. This parameter is ignored (and should be set to NULL)
         * if the wType parameter is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * <p>
         * If the transaction specified by the wType parameter references
         * non-execute DDE data ( XTYP_POKE, XTYP_ADVSTART, XTYP_ADVSTOP,
         * XTYP_REQUEST), the wFmt value must be either a valid predefined (CF_)
         * DDE format or a valid registered clipboard format.</p>
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * @return If the function succeeds, the return value is a data handle
         * that identifies the data for successful synchronous transactions in
         * which the client expects data from the server. The return value is
         * nonzero for successful asynchronous transactions and for synchronous
         * transactions in which the client does not expect data. The return
         * value is zero for all unsuccessful transactions.
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public Ddeml.HDDEDATA request(String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Begins a data transaction between a client and a server. Only a
         * Dynamic Data Exchange (DDE) client application can call this
         * function, and the application can use it only after establishing a
         * conversation with the server.
         *
         * @param data The beginning of the data the client must pass to the
         * server.
         *
         * <p>
         * Optionally, an application can specify the data handle (HDDEDATA) to
         * pass to the server and in that case the cbData parameter should be
         * set to -1. This parameter is required only if the wType parameter is
         * XTYP_EXECUTE or XTYP_POKE. Otherwise, this parameter should be
         * NULL.</p>
         *
         * <p>
         * For the optional usage of this parameter, XTYP_POKE transactions
         * where pData is a data handle, the handle must have been created by a
         * previous call to the DdeCreateDataHandle function, employing the same
         * data format specified in the wFmt parameter.</p>
         *
         * @param dataLength The length, in bytes, of the data pointed to by the
         * pData parameter, including the terminating NULL, if the data is a
         * string. A value of -1 indicates that pData is a data handle that
         * identifies the data being sent.
         *
         * @param item A handle to the data item for which data is being
         * exchanged during the transaction. This handle must have been created
         * by a previous call to the DdeCreateStringHandle function. This
         * parameter is ignored (and should be set to 0L) if the wType parameter
         * is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * <p>
         * If the transaction specified by the wType parameter does not pass
         * data or is XTYP_EXECUTE, this parameter should be zero.</p>
         *
         * <p>
         * If the transaction specified by the wType parameter references
         * non-execute DDE data ( XTYP_POKE, XTYP_ADVSTART, XTYP_ADVSTOP,
         * XTYP_REQUEST), the wFmt value must be either a valid predefined (CF_)
         * DDE format or a valid registered clipboard format.</p>
         *
         * @param transaction The transaction type. This parameter can be one of the
         * following values.
         *
         * <table>
         * <tr><th>Value</th><th>Meaning</th></tr>
         * <tr><td>XTYP_ADVSTART</td><td>Begins an advise loop. Any number of
         * distinct advise loops can exist within a conversation. An application
         * can alter the advise loop type by combining the XTYP_ADVSTART
         * transaction type with one or more of the following flags:
         * <dl>
         * <dt>XTYPF_NODATA.</dt><dd>Instructs the server to notify the client
         * of any data changes without actually sending the data. This flag
         * gives the client the option of ignoring the notification or
         * requesting the changed data from the server.</dd>
         * <dt>XTYPF_ACKREQ.</dt><dd>Instructs the server to wait until the
         * client acknowledges that it received the previous data item before
         * sending the next data item. This flag prevents a fast server from
         * sending data faster than the client can process it.</dd>
         * </dl>
         * </td></tr>
         * <tr><td>XTYP_ADVSTOP</td><td>Ends an advise loop.</td></tr>
         * <tr><td>XTYP_EXECUTE</td><td>Begins an execute transaction.</td></tr>
         * <tr><td>XTYP_POKE</td><td>Begins a poke transaction.</td></tr>
         * <tr><td>XTYP_REQUEST</td><td>Begins a request transaction.</td></tr>
         * </table>
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * @return If the function succeeds, the return value is a data handle
         * that identifies the data for successful synchronous transactions in
         * which the client expects data from the server. The return value is
         * nonzero for successful asynchronous transactions and for synchronous
         * transactions in which the client does not expect data. The return
         * value is zero for all unsuccessful transactions.
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_ADVACKTIMEOUT</li>
         * <li>DMLERR_BUSY</li>
         * <li>DMLERR_DATAACKTIMEOUT</li>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_EXECACKTIMEOUT</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_MEMORY_ERROR</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_NOTPROCESSED</li>
         * <li>DMLERR_POKEACKTIMEOUT</li>
         * <li>DMLERR_POSTMSG_FAILED</li>
         * <li>DMLERR_REENTRANCY</li>
         * <li>DMLERR_SERVER_DIED</li>
         * <li>DMLERR_UNADVACKTIMEOUT</li>
         * </ul>
         */
        public Ddeml.HDDEDATA clientTransaction(Pointer data, int dataLength, Ddeml.HSZ item, int wFmt, int transaction, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Begins a data transaction between a client and a server. Only a
         * Dynamic Data Exchange (DDE) client application can call this
         * function, and the application can use it only after establishing a
         * conversation with the server.
         *
         * @param data The beginning of the data the client must pass to the
         * server.
         *
         * <p>
         * Optionally, an application can specify the data handle (HDDEDATA) to
         * pass to the server and in that case the cbData parameter should be
         * set to -1. This parameter is required only if the wType parameter is
         * XTYP_EXECUTE or XTYP_POKE. Otherwise, this parameter should be
         * NULL.</p>
         *
         * <p>
         * For the optional usage of this parameter, XTYP_POKE transactions
         * where pData is a data handle, the handle must have been created by a
         * previous call to the DdeCreateDataHandle function, employing the same
         * data format specified in the wFmt parameter.</p>
         *
         * @param dataLength The length, in bytes, of the data pointed to by the
         * pData parameter, including the terminating NULL, if the data is a
         * string. A value of -1 indicates that pData is a data handle that
         * identifies the data being sent.
         *
         * @param item The data item for which data is being exchanged during
         * the transaction. This parameter is ignored (and should be set to NULL)
         * if the wType parameter is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * <p>
         * If the transaction specified by the wType parameter does not pass
         * data or is XTYP_EXECUTE, this parameter should be zero.</p>
         *
         * <p>
         * If the transaction specified by the wType parameter references
         * non-execute DDE data ( XTYP_POKE, XTYP_ADVSTART, XTYP_ADVSTOP,
         * XTYP_REQUEST), the wFmt value must be either a valid predefined (CF_)
         * DDE format or a valid registered clipboard format.</p>
         *
         * @param transaction The transaction type. This parameter can be one of the
         * following values.
         *
         * <table>
         * <tr><th>Value</th><th>Meaning</th></tr>
         * <tr><td>XTYP_ADVSTART</td><td>Begins an advise loop. Any number of
         * distinct advise loops can exist within a conversation. An application
         * can alter the advise loop type by combining the XTYP_ADVSTART
         * transaction type with one or more of the following flags:
         * <dl>
         * <dt>XTYPF_NODATA.</dt><dd>Instructs the server to notify the client
         * of any data changes without actually sending the data. This flag
         * gives the client the option of ignoring the notification or
         * requesting the changed data from the server.</dd>
         * <dt>XTYPF_ACKREQ.</dt><dd>Instructs the server to wait until the
         * client acknowledges that it received the previous data item before
         * sending the next data item. This flag prevents a fast server from
         * sending data faster than the client can process it.</dd>
         * </dl>
         * </td></tr>
         * <tr><td>XTYP_ADVSTOP</td><td>Ends an advise loop.</td></tr>
         * <tr><td>XTYP_EXECUTE</td><td>Begins an execute transaction.</td></tr>
         * <tr><td>XTYP_POKE</td><td>Begins a poke transaction.</td></tr>
         * <tr><td>XTYP_REQUEST</td><td>Begins a request transaction.</td></tr>
         * </table>
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * @return If the function succeeds, the return value is a data handle
         * that identifies the data for successful synchronous transactions in
         * which the client expects data from the server. The return value is
         * nonzero for successful asynchronous transactions and for synchronous
         * transactions in which the client does not expect data. The return
         * value is zero for all unsuccessful transactions.
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_ADVACKTIMEOUT</li>
         * <li>DMLERR_BUSY</li>
         * <li>DMLERR_DATAACKTIMEOUT</li>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_EXECACKTIMEOUT</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_MEMORY_ERROR</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_NOTPROCESSED</li>
         * <li>DMLERR_POKEACKTIMEOUT</li>
         * <li>DMLERR_POSTMSG_FAILED</li>
         * <li>DMLERR_REENTRANCY</li>
         * <li>DMLERR_SERVER_DIED</li>
         * <li>DMLERR_UNADVACKTIMEOUT</li>
         * </ul>
         */
        public Ddeml.HDDEDATA clientTransaction(Pointer data, int dataLength, String item, int wFmt, int transaction, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Begins a data transaction between a client and a server. Only a
         * Dynamic Data Exchange (DDE) client application can call this
         * function, and the application can use it only after establishing a
         * conversation with the server.
         *
         * @param item A handle to the data item for which data is being
         * exchanged during the transaction. This handle must have been created
         * by a previous call to the DdeCreateStringHandle function. This
         * parameter is ignored (and should be set to 0L) if the wType parameter
         * is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void advstart(Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Begins a data transaction between a client and a server. Only a
         * Dynamic Data Exchange (DDE) client application can call this
         * function, and the application can use it only after establishing a
         * conversation with the server.
         *
         * @param item The data item for which data is being exchanged during
         * the transaction. This parameter is ignored (and should be set to NULL)
         * if the wType parameter is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void advstart(String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * A client uses the XTYP_ADVSTOP transaction to end an advise loop with
         * a server. A Dynamic Data Exchange (DDE) server callback function,
         * DdeCallback, receives this transaction when a client specifies
         * XTYP_ADVSTOP in the DdeClientTransaction function.
         *
         * @param item A handle to the data item for which data is being
         * exchanged during the transaction. This handle must have been created
         * by a previous call to the DdeCreateStringHandle function. This
         * parameter is ignored (and should be set to 0L) if the wType parameter
         * is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void advstop(Ddeml.HSZ item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * A client uses the XTYP_ADVSTOP transaction to end an advise loop with
         * a server. A Dynamic Data Exchange (DDE) server callback function,
         * DdeCallback, receives this transaction when a client specifies
         * XTYP_ADVSTOP in the DdeClientTransaction function.
         *
         * @param item The data item for which data is being exchanged during
         * the transaction. This parameter is ignored (and should be set to NULL)
         * if the wType parameter is XTYP_EXECUTE.
         *
         * @param wFmt The standard clipboard format in which the data item is
         * being submitted or requested.
         *
         * @param timeout The maximum amount of time, in milliseconds, that
         * the client will wait for a response from the server application in a
         * synchronous transaction. This parameter should be TIMEOUT_ASYNC for
         * asynchronous transactions.
         *
         * @param result A pointer to a variable that receives the result of
         * the transaction. An application that does not check the result can
         * use NULL for this value. For synchronous transactions, the low-order
         * word of this variable contains any applicable DDE_ flags resulting
         * from the transaction. This provides support for applications
         * dependent on DDE_APPSTATUS bits. It is, however, recommended that
         * applications no longer use these bits because they may not be
         * supported in future versions of the Dynamic Data Exchange Management
         * Library (DDEML). For asynchronous transactions, this variable is
         * filled with a unique transaction identifier for use with the
         * DdeAbandonTransaction function and the XTYP_XACT_COMPLETE
         * transaction.
         *
         * @param userHandle data to associate with the transaction
         *
         * <p>
         * If an error occurs, a DdemlException is raised with the appropriate
         * error code:</p>
         *
         * <ul>
         * <li>DMLERR_NOTPROCESSED</li>
         * </ul>
         */
        public void advstop(String item, int wFmt, int timeout, WinDef.DWORDByReference result, DWORD_PTR userHandle);

        /**
         * Abandons the specified asynchronous transaction and releases all
         * resources associated with the transaction.
         *
         * @param transactionId The identifier of the transaction to be
         * abandoned. If this parameter is 0L, all active transactions in the
         * specified conversation are abandoned.
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_UNFOUND_QUEUE_ID</li>
         * </ul>
         */
        public void abandonTransaction(int transactionId);

        /**
         * Abandons all transactions of this conversation and releases all
         * resources associated with the transaction.
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_UNFOUND_QUEUE_ID</li>
         * </ul>
         */
        public void abandonTransactions();

        /**
         * Impersonates a Dynamic Data Exchange (DDE) client application in a
         * DDE client conversation.
         */
        public void impersonateClient();

        /**
         * Terminates a conversation started by either the DdeConnect or
         * DdeConnectList function and invalidates the specified conversation
         * handle.
         *
         * <p>Note: This wraps the DdeDisconnect function and aligns the name
         * with the Closable-wording.</p>
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public void close();

        /**
         * Enables a client Dynamic Data Exchange Management Library (DDEML)
         * application to attempt to reestablish a conversation with a service
         * that has terminated a conversation with the client. When the
         * conversation is reestablished, the Dynamic Data Exchange Management
         * Library (DDEML) attempts to reestablish any preexisting advise loops.
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public void reconnect();

        /**
         * Enables or disables transactions for a specific conversation or for
         * all conversations currently established by the calling application.
         *
         * @param wCmd The function code. This parameter can be one of the
         * following values.
         * <table>
         * <tr><th>Value</th><th>Meaning</th></tr>
         * <tr><td>EC_ENABLEALL</td><td>Enables all transactions for the
         * specified conversation.</td></tr>
         * <tr><td>EC_ENABLEONE</td><td>Enables one transaction for the
         * specified conversation.</td></tr>
         * <tr><td>EC_DISABLE</td><td>Disables all blockable transactions for
         * the specified conversation.
         *
         * <p>
         * A server application can disable the following transactions:</p>
         * <ul>
         * <li>XTYP_ADVSTART</li>
         * <li>XTYP_ADVSTOP</li>
         * <li>XTYP_EXECUTE</li>
         * <li>XTYP_POKE</li>
         * <li>XTYP_REQUEST</li>
         * </ul>
         * <p>
         * A client application can disable the following transactions:</p>
         * <ul>
         * <li>XTYP_ADVDATA</li>
         * <li>XTYP_XACT_COMPLETE</li>
         * </ul>
         * </td></tr>
         * <tr><td>EC_QUERYWAITING</td><td>Determines whether any transactions
         * are in the queue for the specified conversation.</td></tr>
         * </table>
         *
         * @return If the function succeeds, the return value is nonzero.
         *
         * <p>
         * If the function fails, the return value is zero.</p>
         *
         * <p>
         * If the wCmd parameter is EC_QUERYWAITING, and the application
         * transaction queue contains one or more unprocessed transactions that
         * are not being processed, the return value is TRUE; otherwise, it is
         * FALSE.</p>
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public boolean enableCallback(int wCmd);

        /**
         * Associates an application-defined value with a conversation handle or
         * a transaction identifier. This is useful for simplifying the
         * processing of asynchronous transactions. An application can use the
         * DdeQueryConvInfo function to retrieve this value.
         *
         * @param id The transaction identifier to associate with the value
         * specified by the hUser parameter. An application should set this
         * parameter to QID_SYNC to associate hUser with the conversation
         * identified by the hConv parameter.
         * @param hUser The value to be associated with the conversation handle.
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_UNFOUND_QUEUE_ID</li>
         * </ul>
         */
        public void setUserHandle(int id, DWORD_PTR hUser) throws DdemlException;

        /**
         * Retrieves information about a Dynamic Data Exchange (DDE) transaction
         * and about the conversation in which the transaction takes place.
         *
         * @param idTransaction The transaction. For asynchronous transactions,
         * this parameter should be a transaction identifier returned by the
         * DdeClientTransaction function. For synchronous transactions, this
         * parameter should be QID_SYNC.
         * @return The CONVINFO structure
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_UNFOUND_QUEUE_ID</li>
         * </ul>
         */
        public CONVINFO queryConvInfo(int idTransaction) throws DdemlException;
    }

    /**
     * The IDdeClient defines functions that wrap a ddeml instance. and are
     * not tied to conversation.
     */
    public interface IDdeClient extends Closeable {
        public Integer getInstanceIdentitifier();
        /**
         * Registers an application with the Dynamic Data Exchange Management
         * Library (DDEML). An application must call this function before
         * calling any other Dynamic Data Exchange Management Library (DDEML)
         * function.
         *
         * @param afCmd A set of APPCMD_, CBF_, and MF_ flags. The APPCMD_ flags
         * provide special instructions to DdeInitialize. The CBF_ flags specify
         * filters that prevent specific types of transactions from reaching the
         * callback function. The MF_ flags specify the types of DDE activity
         * that a DDE monitoring application monitors. Using these flags
         * enhances the performance of a DDE application by eliminating
         * unnecessary calls to the callback function.
         *
         * <p>
         * This parameter can be one or more of the following values.</p>
         *
         * <table>
         * <tr ><th>Value</th><th>Meaning</th></tr>
         * <tr><td><dl>
         * <dt><strong>APPCLASS_MONITOR</strong></dt>
         * <dt>0x00000001L</dt>
         * </dl>
         * </td><td>
         * <p>
         * Makes it possible for the application to monitor DDE activity in the
         * system. This flag is for use by DDE monitoring applications. The
         * application specifies the types of DDE activity to monitor by
         * combining one or more monitor flags with the APPCLASS_MONITOR flag.
         * For details, see the following Remarks section.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>APPCLASS_STANDARD</strong></dt>
         * <dt>0x00000000L</dt>
         * </dl>
         * </td><td>
         * <p>
         * Registers the application as a standard (nonmonitoring) DDEML
         * application.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>APPCMD_CLIENTONLY</strong></dt>
         * <dt>0x00000010L</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the application from becoming a server in a DDE
         * conversation. The application can only be a client. This flag reduces
         * consumption of resources by the DDEML. It includes the functionality
         * of the CBF_FAIL_ALLSVRXACTIONS flag.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>APPCMD_FILTERINITS</strong></dt>
         * <dt>0x00000020L</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the DDEML from sending XTYP_CONNECT and XTYP_WILDCONNECT
         * transactions to the application until the application has created its
         * string handles and registered its service names or has turned off
         * filtering by a subsequent call to the DdeNameService or DdeInitialize
         * function. This flag is always in effect when an application calls
         * DdeInitialize for the first time, regardless of whether the
         * application specifies the flag. On subsequent calls to DdeInitialize,
         * not specifying this flag turns off the application's service-name
         * filters, but specifying it turns on the application's service name
         * filters.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_ALLSVRXACTIONS</strong></dt>
         * <dt>0x0003f000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving server transactions.
         * The system returns DDE_FNOTPROCESSED to each client that sends a
         * transaction to this application. This flag is equivalent to combining
         * all CBF_FAIL_ flags.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_ADVISES</strong></dt>
         * <dt>0x00004000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_ADVSTART and
         * XTYP_ADVSTOP transactions. The system returns DDE_FNOTPROCESSED to
         * each client that sends an XTYP_ADVSTART or XTYP_ADVSTOP transaction
         * to the server.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_CONNECTIONS</strong></dt>
         * <dt>0x00002000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_CONNECT and
         * XTYP_WILDCONNECT transactions.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_EXECUTES</strong></dt>
         * <dt>0x00008000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_EXECUTE
         * transactions. The system returns DDE_FNOTPROCESSED to a client that
         * sends an XTYP_EXECUTE transaction to the server.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_POKES</strong></dt>
         * <dt>0x00010000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_POKE transactions.
         * The system returns DDE_FNOTPROCESSED to a client that sends an
         * XTYP_POKE transaction to the server.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_REQUESTS</strong></dt>
         * <dt>0x00020000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_REQUEST
         * transactions. The system returns DDE_FNOTPROCESSED to a client that
         * sends an XTYP_REQUEST transaction to the server.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_FAIL_SELFCONNECTIONS</strong></dt>
         * <dt>0x00001000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_CONNECT
         * transactions from the application's own instance. This flag prevents
         * an application from establishing a DDE conversation with its own
         * instance. An application should use this flag if it needs to
         * communicate with other instances of itself but not with itself.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_SKIP_ALLNOTIFICATIONS</strong></dt>
         * <dt>0x003c0000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving any notifications. This
         * flag is equivalent to combining all CBF_SKIP_ flags.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_SKIP_CONNECT_CONFIRMS</strong></dt>
         * <dt>0x00040000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_CONNECT_CONFIRM
         * notifications.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_SKIP_DISCONNECTS</strong></dt>
         * <dt>0x00200000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_DISCONNECT
         * notifications.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_SKIP_REGISTRATIONS</strong></dt>
         * <dt>0x00080000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_REGISTER
         * notifications.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>CBF_SKIP_UNREGISTRATIONS</strong></dt>
         * <dt>0x00100000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Prevents the callback function from receiving XTYP_UNREGISTER
         * notifications.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_CALLBACKS</strong></dt>
         * <dt>0x08000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever a transaction is sent to any
         * DDE callback function in the system.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_CONV</strong></dt>
         * <dt>0x40000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever a conversation is established
         * or terminated.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_ERRORS</strong></dt>
         * <dt>0x10000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever a DDE error occurs.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_HSZ_INFO</strong></dt>
         * <dt>0x01000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever a DDE application creates,
         * frees, or increments the usage count of a string handle or whenever a
         * string handle is freed as a result of a call to the DdeUninitialize
         * function.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_LINKS</strong></dt>
         * <dt>0x20000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever an advise loop is started or
         * ended.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_POSTMSGS</strong></dt>
         * <dt>0x04000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever the system or an application
         * posts a DDE message.</p>
         * </td></tr>
         * <tr><td><dl>
         * <dt><strong>MF_SENDMSGS</strong></dt>
         * <dt>0x02000000</dt>
         * </dl>
         * </td><td>
         * <p>
         * Notifies the callback function whenever the system or an application
         * sends a DDE message.</p>
         * </td></tr>
         * </table>
         *
         * <p>
         * If the function failsa DdemlException is raised with one of the
         * following errroCodes:</p>
         * <ul>
         * <li>DMLERR_DLL_USAGE</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_SYS_ERROR</li>
         * </ul>
         */
        public void initialize(int afCmd) throws DdemlException;
        /**
         * Creates a handle that identifies the specified string. A Dynamic Data
         * Exchange (DDE) client or server application can pass the string
         * handle as a parameter to other Dynamic Data Exchange Management
         * Library (DDEML) functions.
         *
         * @param value The string for which a handle is to be
         * created. This string can be up to 255 characters. The reason for this
         * limit is that DDEML string management functions are implemented using
         * atoms.
         *
         * @return If the function succeeds, the return value is a string
         * handle. A parameter NULL will cause NULL to be returned.
         *
         * <p>If the function fails, a DdemlException is raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_SYS_ERROR</li>
         * </ul>
         */
        public Ddeml.HSZ createStringHandle(String value) throws DdemlException;

        /**
         * Copies text associated with a string handle into a buffer.
         *
         * @param value A handle to the string to copy. This handle must have been
         * created by a previous call to the DdeCreateStringHandle function.
         * @return String corresponding to the supplied handle
         */
        public String queryString(Ddeml.HSZ value) throws DdemlException;

        /**
         * Frees a string handle in the calling application.
         *
         * @param value A handle to the string handle to be freed. This handle
         * must have been created by a previous call to the
         * createStringHandle function. A NULL value will be silently ignored.
         *
         * @return true if the function succeeds.
         */
        public boolean freeStringHandle(Ddeml.HSZ value);

        /**
         * Increments the usage count associated with the specified handle. This
         * function enables an application to save a string handle passed to the
         * application's Dynamic Data Exchange (DDE) callback function.
         * Otherwise, a string handle passed to the callback function is deleted
         * when the callback function returns. This function should also be used
         * to keep a copy of a string handle referenced by the CONVINFO
         * structure returned by the DdeQueryConvInfo function.
         *
         * @param value A handle to the string handle to be saved.
         * @return true if the function succeeded
         */
        public boolean keepStringHandle(Ddeml.HSZ value);

        /**
         * Registers or unregisters the service names a Dynamic Data Exchange
         * (DDE) server supports. This function causes the system to send
         * XTYP_REGISTER or XTYP_UNREGISTER transactions to other running
         * Dynamic Data Exchange Management Library (DDEML) client applications.
         *
         * @param name A handle to the string that specifies the service name
         * the server is registering or unregistering. An application that is
         * unregistering all of its service names should set this parameter to
         * 0L.
         * @param afCmd The service name options. This parameter can be one of
         * the following values.
         *
         * <table>
         * <tr><th>Value</th><th>Meaning</th></tr>
         * <tr><td>DNS_REGISTER</td><td>Registers the error code service
         * name.</td></tr>
         * <tr><td>DNS_UNREGISTER</td><td>Unregisters the error code service
         * name. If the hsz1 parameter is 0L, all service names registered by
         * the server will be unregistered.</td></tr>
         * <tr><td>DNS_FILTERON</td><td>Turns on service name initiation
         * filtering. The filter prevents a server from receiving XTYP_CONNECT
         * transactions for service names it has not registered. This is the
         * default setting for this filter.
         * <br><br>
         * If a server application does not register any service names, the
         * application cannot receive XTYP_WILDCONNECT transactions.
         * </td></tr>
         * <tr><td>DNS_FILTEROFF</td><td>Turns off service name initiation
         * filtering. If this flag is specified, the server receives an
         * XTYP_CONNECT transaction whenever another DDE application calls the
         * DdeConnect function, regardless of the service name.</td></tr>
         * </table>
         * <p>
         * If the function fails, a DdemlException is raised with the
         * corresponding errorCode:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_DLL_USAGE</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public void nameService(Ddeml.HSZ name, int afCmd) throws DdemlException;

        /**
         * Registers or unregisters the service names a Dynamic Data Exchange
         * (DDE) server supports. This function causes the system to send
         * XTYP_REGISTER or XTYP_UNREGISTER transactions to other running
         * Dynamic Data Exchange Management Library (DDEML) client applications.
         *
         * @param name A string that specifies the service name the server is
         * registering or unregistering. An application that is unregistering
         * all of its service names should set this parameter to NULL.
         * @param afCmd The service name options. This parameter can be one of
         * the following values.
         *
         * <table>
         * <tr><th>Value</th><th>Meaning</th></tr>
         * <tr><td>DNS_REGISTER</td><td>Registers the error code service
         * name.</td></tr>
         * <tr><td>DNS_UNREGISTER</td><td>Unregisters the error code service
         * name. If the hsz1 parameter is 0L, all service names registered by
         * the server will be unregistered.</td></tr>
         * <tr><td>DNS_FILTERON</td><td>Turns on service name initiation
         * filtering. The filter prevents a server from receiving XTYP_CONNECT
         * transactions for service names it has not registered. This is the
         * default setting for this filter.
         * <br><br>
         * If a server application does not register any service names, the
         * application cannot receive XTYP_WILDCONNECT transactions.
         * </td></tr>
         * <tr><td>DNS_FILTEROFF</td><td>Turns off service name initiation
         * filtering. If this flag is specified, the server receives an
         * XTYP_CONNECT transaction whenever another DDE application calls the
         * DdeConnect function, regardless of the service name.</td></tr>
         * </table>
         * <p>
         * If the function fails, a DdemlException is raised with the
         * corresponding errorCode:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_DLL_USAGE</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public void nameService(String name, int afCmd) throws DdemlException;

        /**
         * @return See {@link Ddeml}.DMLERR_*
         */
        public int getLastError();

        /**
         * Establishes a conversation with a server application that supports
         * the specified service name and topic name pair. If more than one such
         * server exists, the system selects only one.
         *
         * @param service A handle to the string that specifies the service
         * name of the server application with which a conversation is to be
         * established. This handle must have been created by a previous call to
         * the DdeCreateStringHandle function. If this parameter is 0L, a
         * conversation is established with any available server.
         *
         * @param topic A handle to the string that specifies the name of the
         * topic on which a conversation is to be established. This handle must
         * have been created by a previous call to DdeCreateStringHandle. If
         * this parameter is 0L, a conversation on any topic supported by the
         * selected server is established.
         *
         * @param convcontext A pointer to the CONVCONTEXT structure that contains
         * conversation context information. If this parameter is NULL, the
         * server receives the default CONVCONTEXT structure during the
         * XTYP_CONNECT or XTYP_WILDCONNECT transaction.
         *
         * @return an established connection
         *
         * <p>If the function fails, a DdemlException is raised with the
         * corresponding errorCode:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public IDdeConnection connect(Ddeml.HSZ service, Ddeml.HSZ topic, Ddeml.CONVCONTEXT convcontext);

        /**
         * Establishes a conversation with a server application that supports
         * the specified service name and topic name pair. If more than one such
         * server exists, the system selects only one.
         *
         * @param service The service name of the server application with which
         * a conversation is to be established. If this parameter is NULL, a
         * conversation is established with any available server.
         *
         * @param topic The name of the topic on which a conversation is to be
         * established. If this parameter is NULL, a conversation on any topic
         * supported by the selected server is established.
         *
         * @param convcontext A pointer to the CONVCONTEXT structure that contains
         * conversation context information. If this parameter is NULL, the
         * server receives the default CONVCONTEXT structure during the
         * XTYP_CONNECT or XTYP_WILDCONNECT transaction.
         *
         * @return an established connection
         *
         * <p>If the function fails, a DdemlException is raised with the
         * corresponding errorCode:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public IDdeConnection connect(String service, String topic, Ddeml.CONVCONTEXT convcontext);

        /**
         * Creates a Dynamic Data Exchange (DDE) object and fills the object
         * with data from the specified buffer. A DDE application uses this
         * function during transactions that involve passing data to the partner
         * application.
         *
         * @param pSrc The data to be copied to the DDE object. If this
         * parameter is NULL, no data is copied to the object.
         *
         * @param cb The amount of memory, in bytes, to copy from the buffer
         * pointed to by pSrc. (include the terminating NULL, if the data is a
         * string). If this parameter is zero, the pSrc parameter is ignored.
         *
         * @param cbOff An offset, in bytes, from the beginning of the buffer
         * pointed to by the pSrc parameter. The data beginning at this offset
         * is copied from the buffer to the DDE object.
         *
         * @param hszItem A handle to the string that specifies the data item
         * corresponding to the DDE object. This handle must have been created
         * by a previous call to the DdeCreateStringHandle function. If the data
         * handle is to be used in an XTYP_EXECUTE transaction, this parameter
         * must be 0L.
         *
         * @param wFmt The standard clipboard format of the data.
         *
         * @param afCmd The creation flags. This parameter can be
         * HDATA_APPOWNED, which specifies that the server application calling
         * the DdeCreateDataHandle function owns the data handle this function
         * creates. This flag enables the application to share the data handle
         * with other DDEML applications rather than creating a separate handle
         * to pass to each application. If this flag is specified, the
         * application must eventually free the shared memory object associated
         * with the handle by using the DdeFreeDataHandle function. If this flag
         * is not specified, the handle becomes invalid in the application that
         * created the handle after the data handle is returned by the
         * application's DDE callback function or is used as a parameter in
         * another DDEML function.
         *
         * @return If the function succeeds, the return value is a data handle.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_MEMORY_ERROR</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public Ddeml.HDDEDATA createDataHandle(
                Pointer pSrc,
                int cb,
                int cbOff,
                Ddeml.HSZ hszItem,
                int wFmt,
                int afCmd);

        /**
         * Frees a Dynamic Data Exchange (DDE) object and deletes the data
         * handle associated with the object.
         *
         * @param hData A handle to the DDE object to be freed. This handle must
         * have been created by a previous call to the DdeCreateDataHandle
         * function or returned by the DdeClientTransaction function.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         *
         * <ul>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public void freeDataHandle(Ddeml.HDDEDATA hData);

        /**
         * Adds data to the specified Dynamic Data Exchange (DDE) object. An
         * application can add data starting at any offset from the beginning of
         * the object. If new data overlaps data already in the object, the new
         * data overwrites the old data in the bytes where the overlap occurs.
         * The contents of locations in the object that have not been written to
         * are undefined.
         *
         * @param hData A handle to the DDE object that receives additional
         * data.
         *
         * @param pSrc The data to be added to the DDE object.
         *
         * @param cb The length, in bytes, of the data to be added to the DDE
         * object, including the terminating NULL, if the data is a string.
         *
         * @param cbOff An offset, in bytes, from the beginning of the DDE
         * object. The additional data is copied to the object beginning at this
         * offset.
         *
         * @return If the function succeeds, the return value is a new handle to
         * the DDE object. The new handle is used in all references to the
         * object.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_MEMORY_ERROR</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public Ddeml.HDDEDATA addData(Ddeml.HDDEDATA hData, Pointer pSrc, int cb, int cbOff);

        /**
         * Copies data from the specified Dynamic Data Exchange (DDE) object to
         * the specified local buffer.
         *
         * @param hData A handle to the DDE object that contains the data to
         * copy.
         *
         * @param pDst A pointer to the buffer that receives the data. If this
         * parameter is NULL, the DdeGetData function returns the amount of
         * data, in bytes, that would be copied to the buffer.
         *
         * @param cbMax The maximum amount of data, in bytes, to copy to the
         * buffer pointed to by the pDst parameter. Typically, this parameter
         * specifies the length of the buffer pointed to by pDst.
         *
         * @param cbOff An offset within the DDE object. Data is copied from the
         * object beginning at this offset.
         *
         * @return If the pDst parameter points to a buffer, the return value is
         * the size, in bytes, of the memory object associated with the data
         * handle or the size specified in the cbMax parameter, whichever is
         * lower.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public int getData(Ddeml.HDDEDATA hData, Pointer pDst, int cbMax, int cbOff);

        /**
         * An application must call the DdeUnaccessData function when it has
         * finished accessing the data in the object.
         *
         * @param hData A handle to the DDE object to be accessed.
         *
         * @param pcbDataSize A pointer to a variable that receives the size, in
         * bytes, of the DDE object identified by the hData parameter. If this
         * parameter is NULL, no size information is returned.
         *
         * @return If the function succeeds, the return value is a pointer to
         * the first byte of data in the DDE object.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public Pointer accessData(Ddeml.HDDEDATA hData, WinDef.DWORDByReference pcbDataSize);

        /**
         * Unaccesses a Dynamic Data Exchange (DDE) object. An application must
         * call this function after it has finished accessing the object.
         *
         * @param hData A handle to the DDE object.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public void unaccessData(Ddeml.HDDEDATA hData);

        /**
         * Causes the system to send an XTYP_ADVREQ transaction to the calling
         * (server) application's Dynamic Data Exchange (DDE) callback function
         * for each client with an active advise loop on the specified topic and
         * item. A server application should call this function whenever the
         * data associated with the topic name or item name pair changes.
         *
         * @param hszTopic A handle to a string that specifies the topic name.
         * To send notifications for all topics with active advise loops, an
         * application can set this parameter to 0L.
         *
         * @param hszItem A handle to a string that specifies the item name. To
         * send notifications for all items with active advise loops, an
         * application can set this parameter to 0L.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_DLL_USAGE</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         *
         */
        public void postAdvise(Ddeml.HSZ hszTopic, Ddeml.HSZ hszItem);

        /**
         * Causes the system to send an XTYP_ADVREQ transaction to the calling
         * (server) application's Dynamic Data Exchange (DDE) callback function
         * for each client with an active advise loop on the specified topic and
         * item. A server application should call this function whenever the
         * data associated with the topic name or item name pair changes.
         *
         * @param hszTopic A string that specifies the topic name. To send
         * notifications for all topics with active advise loops, an application
         * can set this parameter to NULL.
         *
         * @param hszItem A string that specifies the item name. To send
         * notifications for all items with active advise loops, an application
         * can set this parameter to NULL.
         *
         * <p>
         * If the function fails a DdeException is raised with the following
         * errorCodes:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_DLL_USAGE</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         *
         */
        public void postAdvise(String hszTopic, String hszItem);

        /**
         * Abandons all asynchronous transaction and releases all
         * resources associated with the transaction.
         *
         * <p>
         * If the method fails a DdeException will be raised with the
         * corresponding errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_UNFOUND_QUEUE_ID</li>
         * </ul>
         */
        public void abandonTransactions();

        /**
         * Establishes a conversation with all server applications that support
         * the specified service name and topic name pair. An application can
         * also use this function to obtain a list of conversation handles by
         * passing the function an existing conversation handle. The Dynamic
         * Data Exchange Management Library removes the handles of any
         * terminated conversations from the conversation list. The resulting
         * conversation list contains the handles of all currently established
         * conversations that support the specified service name and topic name.
         *
         * @param service A handle to the string that specifies the service
         * name of the server application with which a conversation is to be
         * established. If this parameter is 0L, the system attempts to
         * establish conversations with all available servers that support the
         * specified topic name.
         *
         * @param topic A handle to the string that specifies the name of the
         * topic on which a conversation is to be established. This handle must
         * have been created by a previous call to the DdeCreateStringHandle
         * function. If this parameter is 0L, the system will attempt to
         * establish conversations on all topics supported by the selected
         * server (or servers).
         *
         * @param existingList An existinct conversation list to be enumerated.
         * This parameter should be NULL if a new conversation list is to be
         * established.
         *
         * @param ctx A pointer to the CONVCONTEXT structure that contains
         * conversation-context information. If this parameter is NULL, the
         * server receives the default CONVCONTEXT structure during the
         * XTYP_CONNECT or XTYP_WILDCONNECT transaction.
         *
         * @return The new ConnectionList
         *
         * <p>
         * If the function fails a DdeException is raised with the
         * appropriate errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_SYS_ERROR</li>
         * </ul>
         */
        public IDdeConnectionList connectList(Ddeml.HSZ service, Ddeml.HSZ topic, IDdeConnectionList existingList, Ddeml.CONVCONTEXT ctx);

        /**
         * Establishes a conversation with all server applications that support
         * the specified service name and topic name pair. An application can
         * also use this function to obtain a list of conversation handles by
         * passing the function an existing conversation handle. The Dynamic
         * Data Exchange Management Library removes the handles of any
         * terminated conversations from the conversation list. The resulting
         * conversation list contains the handles of all currently established
         * conversations that support the specified service name and topic name.
         *
         * @param service A string that specifies the service name of the server
         * application with which a conversation is to be established. If this
         * parameter is NULL, the system attempts to establish conversations with
         * all available servers that support the specified topic name.
         *
         * @param topic A string that specifies the name of the topic on which a
         * conversation is to be established. If this parameter is NULL, the
         * system will attempt to establish conversations on all topics
         * supported by the selected server (or servers).
         *
         * @param existingList An existinct conversation list to be enumerated.
         * This parameter should be NULL if a new conversation list is to be
         * established.
         *
         * @param ctx A pointer to the CONVCONTEXT structure that contains
         * conversation-context information. If this parameter is NULL, the
         * server receives the default CONVCONTEXT structure during the
         * XTYP_CONNECT or XTYP_WILDCONNECT transaction.
         *
         * @return The new ConnectionList
         *
         * <p>
         * If the function fails a DdeException is raised with the
         * appropriate errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_CONV_ESTABLISHED</li>
         * <li>DMLERR_NO_ERROR</li>
         * <li>DMLERR_SYS_ERROR</li>
         * </ul>
         */
        public IDdeConnectionList connectList(String service, String topic, IDdeConnectionList existingList, Ddeml.CONVCONTEXT ctx);

        /**
         * Enables or disables transactions for a specific conversation or for
         * all conversations currently established by the calling application.
         *
         * @param wCmd The function code. This parameter can be one of the
         * following values.
         * <table>
         * <tr><th>Value</th><th>Meaning</th></tr>
         * <tr><td>EC_ENABLEALL</td><td>Enables all transactions for the
         * specified conversation.</td></tr>
         * <tr><td>EC_ENABLEONE</td><td>Enables one transaction for the
         * specified conversation.</td></tr>
         * <tr><td>EC_DISABLE</td><td>Disables all blockable transactions for
         * the specified conversation.
         *
         * <p>
         * A server application can disable the following transactions:</p>
         * <ul>
         * <li>XTYP_ADVSTART</li>
         * <li>XTYP_ADVSTOP</li>
         * <li>XTYP_EXECUTE</li>
         * <li>XTYP_POKE</li>
         * <li>XTYP_REQUEST</li>
         * </ul>
         * <p>
         * A client application can disable the following transactions:</p>
         * <ul>
         * <li>XTYP_ADVDATA</li>
         * <li>XTYP_XACT_COMPLETE</li>
         * </ul>
         * </td></tr>
         * <tr><td>EC_QUERYWAITING</td><td>Determines whether any transactions
         * are in the queue for the specified conversation.</td></tr>
         * </table>
         *
         * @return If the function succeeds, the return value is nonzero.
         *
         * <p>
         * If the function fails, the return value is zero.</p>
         *
         * <p>
         * If the wCmd parameter is EC_QUERYWAITING, and the application
         * transaction queue contains one or more unprocessed transactions that
         * are not being processed, the return value is TRUE; otherwise, it is
         * FALSE.</p>
         *
         * <p>
         * If the function fails a DdeException is raised with the
         * appropriate errorCode:</p>
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         */
        public boolean enableCallback(int wCmd);

        /**
         * Frees all Dynamic Data Exchange Management Library (DDEML) resources
         * associated with the calling application.
         *
         * @return true if function succeeded
         */
        public boolean uninitialize();

        /**
         * Wrap a connection handle into a IDdeConnection helper class.
         *
         * @param conv HCONV structure to wrap
         * @return wrapped IDdeConnection
         */
        public IDdeConnection wrap(HCONV conv);

        public void registerAdvstartHandler(AdvstartHandler handler);

        public void unregisterAdvstartHandler(AdvstartHandler handler);

        public void registerAdvstopHandler(AdvstopHandler handler);

        public void unregisterAdvstopHandler(AdvstopHandler handler);

        public void registerConnectHandler(ConnectHandler handler);

        public void unregisterConnectHandler(ConnectHandler handler);

        public void registerAdvReqHandler(AdvreqHandler handler);

        public void unregisterAdvReqHandler(AdvreqHandler handler);

        public void registerRequestHandler(RequestHandler handler);

        public void unregisterRequestHandler(RequestHandler handler);

        public void registerWildconnectHandler(WildconnectHandler handler);

        public void unregisterWildconnectHandler(WildconnectHandler handler);

        public void registerAdvdataHandler(AdvdataHandler handler);

        public void unregisterAdvdataHandler(AdvdataHandler handler);

        public void registerExecuteHandler(ExecuteHandler handler);

        public void unregisterExecuteHandler(ExecuteHandler handler);

        public void registerPokeHandler(PokeHandler handler);

        public void unregisterPokeHandler(PokeHandler handler);

        public void registerConnectConfirmHandler(ConnectConfirmHandler handler);

        public void unregisterConnectConfirmHandler(ConnectConfirmHandler handler);

        public void registerDisconnectHandler(DisconnectHandler handler);

        public void unregisterDisconnectHandler(DisconnectHandler handler);

        public void registerErrorHandler(ErrorHandler handler);

        public void unregisterErrorHandler(ErrorHandler handler);

        public void registerRegisterHandler(RegisterHandler handler);

        public void unregisterRegisterHandler(RegisterHandler handler);

        public void registerXactCompleteHandler(XactCompleteHandler handler);

        public void unregisterXactCompleteHandler(XactCompleteHandler handler);

        public void registerUnregisterHandler(UnregisterHandler handler);

        public void unregisterUnregisterHandler(UnregisterHandler handler);

        public void registerMonitorHandler(MonitorHandler handler);

        public void unregisterMonitorHandler(MonitorHandler handler);
    }

    /**
     * The IDdeConnectionList wraps a connectionlist.
     */
    public interface IDdeConnectionList extends Closeable {
        public Ddeml.HCONVLIST getHandle();

        /**
         * Retrieves the next conversation handle in the specified conversation
         * list.
         *
         * @param prevConnection A handle to the conversation handle previously
         * returned by this function. If this parameter is NULL, the function
         * returns the first conversation handle in the list.
         *
         * @return If the list contains any more conversation handles, the
         * return value is the next conversation IDdeConnection in the list;
         * otherwise it is NULL.
         */
        public IDdeConnection queryNextServer(IDdeConnection prevConnection);

        /**
         * Destroys the specified conversation list and terminates all
         * conversations associated with the list.
         *
         * <p>
         * If the function fails a DdeException is raised with the
         * appropriate errorCode:</p>
         *
         * <ul>
         * <li>DMLERR_DLL_NOT_INITIALIZED</li>
         * <li>DMLERR_INVALIDPARAMETER</li>
         * <li>DMLERR_NO_ERROR</li>
         * </ul>
         *
         * <p>Note: This wraps DdeDisconnectList to align with Closeable wording.</p>
         */
        public void close();
    }
}
