[User32]
[reactos.git] / reactos / win32ss / user / user32 / misc / dde.c
index 30498d1..d600ba4 100644 (file)
@@ -36,7 +36,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
 
 static WDML_INSTANCE*  WDML_InstanceList = NULL;
 static LONG            WDML_MaxInstanceID = 0;  /* OK for present, have to worry about wrap-around later */
-const WCHAR            WDML_szEventClass[] = {'D','d','e','E','v','e','n','t','C','l','a','s','s',0};
+const WCHAR            WDML_szEventClass[] = {'D','D','E','M','L','E','v','e','n','t',0};
 
 /* protection for instance list */
 CRITICAL_SECTION WDML_CritSect;
@@ -91,7 +91,7 @@ LPARAM WINAPI PackDDElParam(UINT msg, UINT_PTR uiLo, UINT_PTR uiHi)
         return uiHi;
 
     default:
-        return MAKELPARAM(uiLo, uiHi);
+        return MAKELONG(uiLo, uiHi);
     }
 }
 
@@ -229,7 +229,7 @@ BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
  *            DdeSetQualityOfService (USER32.@)
  */
 
-BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
+BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, const SECURITY_QUALITY_OF_SERVICE *pqosNew,
                                   PSECURITY_QUALITY_OF_SERVICE pqosPrev)
 {
      FIXME("(%p %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
@@ -238,1024 +238,1038 @@ BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SE
 
 /* ================================================================
  *
- *                     Instance management
+ *                     WDML Error management
  *
  * ================================================================ */
 
 /******************************************************************************
- *             IncrementInstanceId
+ * DdeGetLastError [USER32.@]  Gets most recent error code
  *
- *     generic routine to increment the max instance Id and allocate a new application instance
+ * PARAMS
+ *    idInst [I] Instance identifier
+ *
+ * RETURNS
+ *    Last error code
  */
-static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
+UINT WINAPI DdeGetLastError(DWORD idInst)
 {
-    DWORD      id = InterlockedIncrement(&WDML_MaxInstanceID);
+    DWORD              error_code;
+    WDML_INSTANCE*     pInstance;
 
-    pInstance->instanceID = id;
-    TRACE("New instance id %d allocated\n", id);
+    /*  First check instance
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if  (pInstance == NULL)
+    {
+       error_code = DMLERR_INVALIDPARAMETER;
+    }
+    else
+    {
+       error_code = pInstance->lastError;
+       pInstance->lastError = 0;
+    }
+
+    return error_code;
 }
 
 /******************************************************************
- *             WDML_EventProc
+ *             WDML_SetAllLastError
  *
  *
  */
-static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
+static void    WDML_SetAllLastError(DWORD lastError)
 {
+    DWORD              threadID;
     WDML_INSTANCE*     pInstance;
-    HSZ                        hsz1, hsz2;
-
-    switch (uMsg)
+    threadID = GetCurrentThreadId();
+    pInstance = WDML_InstanceList;
+    while (pInstance)
     {
-    case WM_WDML_REGISTER:
-       pInstance = WDML_GetInstanceFromWnd(hwndEvent);
-        /* try calling the Callback */
-       if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
-       {
-           hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
-           hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
-           WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
-           WDML_DecHSZ(pInstance, hsz1);
-           WDML_DecHSZ(pInstance, hsz2);
-       }
-       break;
-
-    case WM_WDML_UNREGISTER:
-       pInstance = WDML_GetInstanceFromWnd(hwndEvent);
-       if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
-       {
-           hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
-           hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
-           WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
-           WDML_DecHSZ(pInstance, hsz1);
-           WDML_DecHSZ(pInstance, hsz2);
-       }
-       break;
-
-    case WM_WDML_CONNECT_CONFIRM:
-       pInstance = WDML_GetInstanceFromWnd(hwndEvent);
-       if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
-       {
-           WDML_CONV*  pConv;
-           /* confirm connection...
-            * lookup for this conv handle
-            */
-            HWND client = (HWND)wParam;
-            HWND server = (HWND)lParam;
-           for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
-           {
-               if (pConv->hwndClient == client && pConv->hwndServer == server)
-                   break;
-           }
-           if (pConv)
-           {
-               pConv->wStatus |= ST_ISLOCAL;
-
-               WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
-                                   pConv->hszTopic, pConv->hszService, 0, 0,
-                                   (pConv->wStatus & ST_ISSELF) ? 1 : 0);
-           }
-       }
-       break;
-    default:
-       return DefWindowProcW(hwndEvent, uMsg, wParam, lParam);
+       if (pInstance->threadID == threadID)
+           pInstance->lastError = lastError;
+       pInstance = pInstance->next;
     }
-    return 0;
 }
 
+/* ================================================================
+ *
+ *                     String management
+ *
+ * ================================================================ */
+
+
 /******************************************************************
- *             WDML_Initialize
+ *             WDML_FindNode
  *
  *
  */
-UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
-                    DWORD afCmd, DWORD ulRes, BOOL bUnicode)
+static HSZNode*        WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
 {
-    WDML_INSTANCE*             pInstance;
-    WDML_INSTANCE*             reference_inst;
-    UINT                       ret;
-    WNDCLASSEXW                        wndclass;
-
-    TRACE("(%p,%p,0x%x,%d,0x%x)\n",
-         pidInst, pfnCallback, afCmd, ulRes, bUnicode);
+    HSZNode*   pNode;
 
-    if (ulRes)
-    {
-       ERR("Reserved value not zero?  What does this mean?\n");
-       /* trap this and no more until we know more */
-       return DMLERR_NO_ERROR;
-    }
+    if (pInstance == NULL) return NULL;
 
-    /* grab enough heap for one control struct - not really necessary for re-initialise
-     * but allows us to use same validation routines */
-    pInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
-    if (pInstance == NULL)
+    for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
     {
-       /* catastrophe !! warn user & abort */
-       ERR("Instance create failed - out of memory\n");
-       return DMLERR_SYS_ERROR;
+       if (pNode->hsz == hsz) break;
     }
-    pInstance->next = NULL;
-    pInstance->monitor = (afCmd | APPCLASS_MONITOR);
+    if (!pNode) WARN("HSZ %p not found\n", hsz);
+    return pNode;
+}
 
-    /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
+/******************************************************************
+ *             WDML_MakeAtomFromHsz
+ *
+ * Creates a global atom from an existing HSZ
+ * Generally used before sending an HSZ as an atom to a remote app
+ */
+ATOM   WDML_MakeAtomFromHsz(HSZ hsz)
+{
+    WCHAR nameBuffer[MAX_BUFFER_LEN];
 
-    pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
-    pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
-    pInstance->threadID = GetCurrentThreadId();
-    pInstance->callback = *pfnCallback;
-    pInstance->unicode = bUnicode;
-    pInstance->nodeList = NULL; /* node will be added later */
-    pInstance->monitorFlags = afCmd & MF_MASK;
-    pInstance->wStatus = 0;
-    pInstance->lastError = DMLERR_NO_ERROR;
-    pInstance->servers = NULL;
-    pInstance->convs[0] = NULL;
-    pInstance->convs[1] = NULL;
-    pInstance->links[0] = NULL;
-    pInstance->links[1] = NULL;
+    if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
+       return GlobalAddAtomW(nameBuffer);
+    WARN("HSZ %p not found\n", hsz);
+    return 0;
+}
 
-    /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
+/******************************************************************
+ *             WDML_MakeHszFromAtom
+ *
+ * Creates a HSZ from an existing global atom
+ * Generally used while receiving a global atom and transforming it
+ * into an HSZ
+ */
+HSZ    WDML_MakeHszFromAtom(const WDML_INSTANCE* pInstance, ATOM atom)
+{
+    WCHAR nameBuffer[MAX_BUFFER_LEN];
 
-    pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
+    if (!atom) return NULL;
 
-    if (!pInstance->clientOnly)
+    if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
     {
-       /* Check for other way of setting Client-only !! */
-       pInstance->clientOnly =
-           (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
+       TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
+       return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
     }
+    WARN("ATOM 0x%x not found\n", atom);
+    return 0;
+}
 
-    TRACE("instance created - checking validity\n");
-
-    if (*pidInst == 0)
-    {
-       /*  Initialisation of new Instance Identifier */
-       TRACE("new instance, callback %p flags %X\n",pfnCallback,afCmd);
+/******************************************************************
+ *             WDML_IncHSZ
+ *
+ *
+ */
+BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
+{
+    HSZNode*   pNode;
 
-       EnterCriticalSection(&WDML_CritSect);
+    pNode = WDML_FindNode(pInstance, hsz);
+    if (!pNode) return FALSE;
 
-       if (WDML_InstanceList == NULL)
-       {
-           /* can't be another instance in this case, assign to the base pointer */
-           WDML_InstanceList = pInstance;
+    pNode->refCount++;
+    return TRUE;
+}
 
-           /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
-            *          present
-            *  -------------------------------      NOTE NOTE NOTE    --------------------------
-            *
-            *  the manual is not clear if this condition
-            *  applies to the first call to DdeInitialize from an application, or the
-            *  first call for a given callback !!!
-            */
+/******************************************************************************
+ *           WDML_DecHSZ    (INTERNAL)
+ *
+ * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
+ * of HSZ nodes
+ * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
+ */
+BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
+{
+    HSZNode*   pPrev = NULL;
+    HSZNode*   pCurrent;
 
-           pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
-           TRACE("First application instance detected OK\n");
-           /*  allocate new instance ID */
-           WDML_IncrementInstanceId(pInstance);
-       }
-       else
+    for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
+    {
+       /* If we found the node we were looking for and its ref count is one,
+        * we can remove it
+        */
+       if (pCurrent->hsz == hsz)
        {
-           /* really need to chain the new one in to the latest here, but after checking conditions
-            *  such as trying to start a conversation from an application trying to monitor */
-           reference_inst = WDML_InstanceList;
-           TRACE("Subsequent application instance - starting checks\n");
-           while (reference_inst->next != NULL)
+           if (--pCurrent->refCount == 0)
            {
-               /*
-                *      This set of tests will work if application uses same instance Id
-                *      at application level once allocated - which is what manual implies
-                *      should happen. If someone tries to be
-                *      clever (lazy ?) it will fail to pick up that later calls are for
-                *      the same application - should we trust them ?
-                */
-               if (pInstance->instanceID == reference_inst->instanceID)
+               if (pCurrent == pInstance->nodeList)
                {
-                   /* Check 1 - must be same Client-only state */
-
-                   if (pInstance->clientOnly != reference_inst->clientOnly)
-                   {
-                       ret = DMLERR_DLL_USAGE;
-                       goto theError;
-                   }
-
-                   /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
-
-                   if (pInstance->monitor != reference_inst->monitor)
-                   {
-                       ret = DMLERR_INVALIDPARAMETER;
-                       goto theError;
-                   }
-
-                   /* Check 3 - must supply different callback address */
-
-                   if (pInstance->callback == reference_inst->callback)
-                   {
-                       ret = DMLERR_DLL_USAGE;
-                       goto theError;
-                   }
-               }
-               reference_inst = reference_inst->next;
-           }
-           /*  All cleared, add to chain */
-
-           TRACE("Application Instance checks finished\n");
-           WDML_IncrementInstanceId(pInstance);
-           reference_inst->next = pInstance;
-       }
-       LeaveCriticalSection(&WDML_CritSect);
-
-       *pidInst = pInstance->instanceID;
-
-       /* for deadlock issues, windows must always be created when outside the critical section */
-       wndclass.cbSize        = sizeof(wndclass);
-       wndclass.style         = 0;
-       wndclass.lpfnWndProc   = WDML_EventProc;
-       wndclass.cbClsExtra    = 0;
-       wndclass.cbWndExtra    = sizeof(ULONG_PTR);
-       wndclass.hInstance     = 0;
-       wndclass.hIcon         = 0;
-       wndclass.hCursor       = 0;
-       wndclass.hbrBackground = 0;
-       wndclass.lpszMenuName  = NULL;
-       wndclass.lpszClassName = WDML_szEventClass;
-       wndclass.hIconSm       = 0;
-
-       RegisterClassExW(&wndclass);
-
-       pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL,
-                                               WS_POPUP, 0, 0, 0, 0,
-                                               0, 0, 0, 0);
-
-       SetWindowLongPtrW(pInstance->hwndEvent, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
-
-       TRACE("New application instance processing finished OK\n");
-    }
-    else
-    {
-       /* Reinitialisation situation   --- FIX  */
-       TRACE("reinitialisation of (%p,%p,0x%x,%d): stub\n", pidInst, pfnCallback, afCmd, ulRes);
-
-       EnterCriticalSection(&WDML_CritSect);
-
-       if (WDML_InstanceList == NULL)
-       {
-           ret = DMLERR_INVALIDPARAMETER;
-           goto theError;
-       }
-       /* can't reinitialise if we have initialised nothing !! */
-       reference_inst = WDML_InstanceList;
-       /* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
-       /*
-        *      MS allows initialisation without specifying a callback, should we allow addition of the
-        *      callback by a later call to initialise ? - if so this lot will have to change
-        */
-       while (reference_inst->next != NULL)
-       {
-           if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
-           {
-               /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
-
-               if (reference_inst->clientOnly)
-               {
-                   if  ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
-                   {
-                               /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
-
-                       if (!(afCmd & APPCMD_CLIENTONLY))
-                       {
-                           ret = DMLERR_INVALIDPARAMETER;
-                           goto theError;
-                       }
-                   }
-               }
-               /* Check 2 - cannot change monitor modes */
-
-               if (pInstance->monitor != reference_inst->monitor)
-               {
-                   ret = DMLERR_INVALIDPARAMETER;
-                   goto theError;
+                   pInstance->nodeList = pCurrent->next;
                }
-
-               /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
-
-               if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
+               else
                {
-                   ret = DMLERR_INVALIDPARAMETER;
-                   goto theError;
+                   pPrev->next = pCurrent->next;
                }
-               break;
+               HeapFree(GetProcessHeap(), 0, pCurrent);
+               DeleteAtom(HSZ2ATOM(hsz));
            }
-           reference_inst = reference_inst->next;
-       }
-       if (reference_inst->next == NULL)
-       {
-           ret = DMLERR_INVALIDPARAMETER;
-           goto theError;
+           return TRUE;
        }
-       /* All checked - change relevant flags */
-
-       reference_inst->CBFflags = pInstance->CBFflags;
-       reference_inst->clientOnly = pInstance->clientOnly;
-       reference_inst->monitorFlags = pInstance->monitorFlags;
-
-       HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
-
-       LeaveCriticalSection(&WDML_CritSect);
     }
+    WARN("HSZ %p not found\n", hsz);
 
-    return DMLERR_NO_ERROR;
- theError:
-    HeapFree(GetProcessHeap(), 0, pInstance);
-    LeaveCriticalSection(&WDML_CritSect);
-    return ret;
+    return FALSE;
 }
 
 /******************************************************************************
- *            DdeInitializeA   (USER32.@)
+ *            WDML_FreeAllHSZ    (INTERNAL)
  *
- * See DdeInitializeW.
+ * Frees up all the strings still allocated in the list and
+ * remove all the nodes from the list of HSZ nodes.
  */
-UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
-                          DWORD afCmd, DWORD ulRes)
+void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
 {
-    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE);
+    /* Free any strings created in this instance.
+     */
+    while (pInstance->nodeList != NULL)
+    {
+       DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
+    }
 }
 
 /******************************************************************************
- * DdeInitializeW [USER32.@]
- * Registers an application with the DDEML
- *
- * PARAMS
- *    pidInst     [I] Pointer to instance identifier
- *    pfnCallback [I] Pointer to callback function
- *    afCmd       [I] Set of command and filter flags
- *    ulRes       [I] Reserved
+ *            InsertHSZNode    (INTERNAL)
  *
- * RETURNS
- *    Success: DMLERR_NO_ERROR
- *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
+ * Insert a node to the head of the list.
  */
-UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
-                          DWORD afCmd, DWORD ulRes)
+static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
 {
-    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE);
+    if (hsz != 0)
+    {
+       HSZNode* pNew = NULL;
+       /* Create a new node for this HSZ.
+        */
+       pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
+       if (pNew != NULL)
+       {
+           pNew->hsz      = hsz;
+           pNew->next     = pInstance->nodeList;
+           pNew->refCount = 1;
+           pInstance->nodeList = pNew;
+       }
+       else
+       {
+           ERR("Primary HSZ Node allocation failed - out of memory\n");
+       }
+    }
 }
 
-/*****************************************************************
- * DdeUninitialize [USER32.@]  Frees DDEML resources
+/******************************************************************
+ *             WDML_QueryString
  *
- * PARAMS
- *    idInst [I] Instance identifier
  *
- * RETURNS
- *    Success: TRUE
- *    Failure: FALSE
  */
-
-BOOL WINAPI DdeUninitialize(DWORD idInst)
+static int     WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
+                                int codepage)
 {
-    /*  Stage one - check if we have a handle for this instance
+    WCHAR      pString[MAX_BUFFER_LEN];
+    int                ret;
+    /* If psz is null, we have to return only the length
+     * of the string.
      */
-    WDML_INSTANCE*             pInstance;
-    WDML_CONV*                 pConv;
-    WDML_CONV*                 pConvNext;
-
-    TRACE("(%d)\n", idInst);
+    if (ptr == NULL)
+    {
+       ptr = pString;
+       cchMax = MAX_BUFFER_LEN;
+    }
 
-    /*  First check instance
-     */
-    pInstance = WDML_GetInstance(idInst);
-    if (pInstance == NULL)
+    /* if there is no input windows returns a NULL string */
+    if (hsz == NULL)
     {
-       /*
-        *      Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
-        */
-       return FALSE;
+       CHAR *t_ptr = ptr;
+       *t_ptr = '\0';
+       return 1;
     }
 
-    /* first terminate all conversations client side
-     * this shall close existing links...
-     */
-    for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
+    switch (codepage)
     {
-       pConvNext = pConv->next;
-       DdeDisconnect((HCONV)pConv);
+    case CP_WINANSI:
+       ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
+       break;
+    case CP_WINUNICODE:
+       ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
+        break;
+    default:
+       ERR("Unknown code page %d\n", codepage);
+       ret = 0;
     }
-    if (pInstance->convs[WDML_CLIENT_SIDE])
-       FIXME("still pending conversations\n");
+    return ret;
+}
 
-    /* then unregister all known service names */
-    DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
+/*****************************************************************
+ * DdeQueryStringA [USER32.@]
+ */
+DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
+{
+    DWORD              ret = 0;
+    WDML_INSTANCE*     pInstance;
 
-    /* Free the nodes that were not freed by this instance
-     * and remove the nodes from the list of HSZ nodes.
-     */
-    WDML_FreeAllHSZ(pInstance);
-
-    DestroyWindow(pInstance->hwndEvent);
-
-    /* OK now delete the instance handle itself */
+    TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
 
-    if (WDML_InstanceList == pInstance)
-    {
-       /* special case - the first/only entry */
-       WDML_InstanceList = pInstance->next;
-    }
-    else
+    /*  First check instance
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance != NULL)
     {
-       /* general case, remove entry */
-       WDML_INSTANCE*  inst;
-
-       for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
-       inst->next = pInstance->next;
+       if (iCodePage == 0) iCodePage = CP_WINANSI;
+       ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
     }
-    /* release the heap entry
-     */
-    HeapFree(GetProcessHeap(), 0, pInstance);
 
-    return TRUE;
+    TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
+    return ret;
 }
 
-/******************************************************************
- *             WDML_NotifyThreadExit
- *
- *
+/*****************************************************************
+ * DdeQueryStringW [USER32.@]
  */
-void WDML_NotifyThreadDetach(void)
+
+DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
 {
+    DWORD              ret = 0;
     WDML_INSTANCE*     pInstance;
-    WDML_INSTANCE*     next;
-    DWORD              tid = GetCurrentThreadId();
 
-    EnterCriticalSection(&WDML_CritSect);
-    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
+    TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
+
+    /*  First check instance
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance != NULL)
     {
-       next = pInstance->next;
-       if (pInstance->threadID == tid)
-       {
-            LeaveCriticalSection(&WDML_CritSect);
-           DdeUninitialize(pInstance->instanceID);
-            EnterCriticalSection(&WDML_CritSect);
-       }
+       if (iCodePage == 0) iCodePage = CP_WINUNICODE;
+       ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
     }
-    LeaveCriticalSection(&WDML_CritSect);
+
+    TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
+    return ret;
 }
 
 /******************************************************************
- *             WDML_InvokeCallback
+ *             DML_CreateString
  *
  *
  */
-HDDEDATA       WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
-                                   HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
-                                   ULONG_PTR dwData1, ULONG_PTR dwData2)
+static HSZ     WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
 {
-    HDDEDATA   ret;
-
-    if (pInstance == NULL)
-       return NULL;
+    HSZ                hsz;
 
-    TRACE("invoking CB[%p] (%x %x %p %p %p %p %lx %lx)\n",
-         pInstance->callback, uType, uFmt,
-         hConv, hsz1, hsz2, hdata, dwData1, dwData2);
-    ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
-    TRACE("done => %p\n", ret);
-    return ret;
+    switch (codepage)
+    {
+    case CP_WINANSI:
+       hsz = ATOM2HSZ(AddAtomA(ptr));
+       TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz);
+       break;
+    case CP_WINUNICODE:
+       hsz = ATOM2HSZ(AddAtomW(ptr));
+       TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz);
+       break;
+    default:
+       ERR("Unknown code page %d\n", codepage);
+       return 0;
+    }
+    WDML_InsertHSZNode(pInstance, hsz);
+    return hsz;
 }
 
-/*****************************************************************************
- *     WDML_GetInstance
- *
- *     generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
- *     for an instance Id, or NULL if the entry does not exist
+/*****************************************************************
+ * DdeCreateStringHandleA [USER32.@]
  *
+ * See DdeCreateStringHandleW.
  */
-WDML_INSTANCE* WDML_GetInstance(DWORD instId)
+HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
 {
+    HSZ                        hsz = 0;
     WDML_INSTANCE*     pInstance;
 
-    EnterCriticalSection(&WDML_CritSect);
+    TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage);
 
-    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance == NULL)
+       WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
+    else
     {
-       if (pInstance->instanceID == instId)
-       {
-           if (GetCurrentThreadId() != pInstance->threadID)
-           {
-               FIXME("Tried to get instance from wrong thread\n");
-               continue;
-           }
-           break;
-       }
+       if (codepage == 0) codepage = CP_WINANSI;
+       hsz = WDML_CreateString(pInstance, psz, codepage);
     }
 
-    LeaveCriticalSection(&WDML_CritSect);
-
-    if (!pInstance)
-        WARN("Instance entry missing for id %04x\n", instId);
-    return pInstance;
+    return hsz;
 }
 
-/******************************************************************
- *             WDML_GetInstanceFromWnd
- *
- *
- */
-WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd)
-{
-    return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE);
-}
 
 /******************************************************************************
- * DdeGetLastError [USER32.@]  Gets most recent error code
+ * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
  *
  * PARAMS
- *    idInst [I] Instance identifier
- *
+ *     idInst   [I] Instance identifier
+ *     psz      [I] Pointer to string
+ *     codepage [I] Code page identifier
  * RETURNS
- *    Last error code
+ *    Success: String handle
+ *    Failure: 0
  */
-UINT WINAPI DdeGetLastError(DWORD idInst)
+HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
 {
-    DWORD              error_code;
     WDML_INSTANCE*     pInstance;
+    HSZ                        hsz = 0;
 
-    /*  First check instance
-     */
     pInstance = WDML_GetInstance(idInst);
-    if  (pInstance == NULL)
-    {
-       error_code = DMLERR_INVALIDPARAMETER;
-    }
+    if (pInstance == NULL)
+       WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
     else
     {
-       error_code = pInstance->lastError;
-       pInstance->lastError = 0;
+       if (codepage == 0) codepage = CP_WINUNICODE;
+       hsz = WDML_CreateString(pInstance, psz, codepage);
     }
 
-    return error_code;
+    return hsz;
 }
 
-/******************************************************************
- *             WDML_SetAllLastError
- *
- *
+/*****************************************************************
+ *            DdeFreeStringHandle   (USER32.@)
+ * RETURNS
+ *  success: nonzero
+ *  fail:    zero
  */
-static void    WDML_SetAllLastError(DWORD lastError)
+BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
 {
-    DWORD              threadID;
     WDML_INSTANCE*     pInstance;
-    threadID = GetCurrentThreadId();
-    pInstance = WDML_InstanceList;
-    while (pInstance)
-    {
-       if (pInstance->threadID == threadID)
-           pInstance->lastError = lastError;
-       pInstance = pInstance->next;
-    }
-}
+    BOOL               ret = FALSE;
 
-/* ================================================================
- *
- *                     String management
- *
- * ================================================================ */
+    TRACE("(%d,%p):\n", idInst, hsz);
+
+    /*  First check instance
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance)
+       ret = WDML_DecHSZ(pInstance, hsz);
 
+    return ret;
+}
 
-/******************************************************************
- *             WDML_FindNode
- *
+/*****************************************************************
+ *            DdeKeepStringHandle  (USER32.@)
  *
+ * RETURNS
+ *  success: nonzero
+ *  fail:    zero
  */
-static HSZNode*        WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
+BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
 {
-    HSZNode*   pNode;
+    WDML_INSTANCE*     pInstance;
+    BOOL               ret = FALSE;
 
-    if (pInstance == NULL) return NULL;
+    TRACE("(%d,%p):\n", idInst, hsz);
 
-    for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
-    {
-       if (pNode->hsz == hsz) break;
-    }
-    if (!pNode) WARN("HSZ %p not found\n", hsz);
-    return pNode;
+    /*  First check instance
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance)
+       ret = WDML_IncHSZ(pInstance, hsz);
+
+    return ret;
 }
 
-/******************************************************************
- *             WDML_MakeAtomFromHsz
+/*****************************************************************
+ *            DdeCmpStringHandles (USER32.@)
  *
- * Creates a global atom from an existing HSZ
- * Generally used before sending an HSZ as an atom to a remote app
+ * Compares the value of two string handles.  This comparison is
+ * not case sensitive.
+ *
+ * PARAMS
+ *  hsz1    [I] Handle to the first string
+ *  hsz2    [I] Handle to the second string
+ *
+ * RETURNS
+ *  -1 The value of hsz1 is zero or less than hsz2
+ *  0  The values of hsz 1 and 2 are the same or both zero.
+ *  1  The value of hsz2 is zero of less than hsz1
  */
-ATOM   WDML_MakeAtomFromHsz(HSZ hsz)
+INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
 {
-    WCHAR nameBuffer[MAX_BUFFER_LEN];
+    WCHAR      psz1[MAX_BUFFER_LEN];
+    WCHAR      psz2[MAX_BUFFER_LEN];
+    int                ret = 0;
+    int                ret1, ret2;
 
-    if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
-       return GlobalAddAtomW(nameBuffer);
-    WARN("HSZ %p not found\n", hsz);
-    return 0;
-}
-
-/******************************************************************
- *             WDML_MakeHszFromAtom
- *
- * Creates a HSZ from an existing global atom
- * Generally used while receiving a global atom and transforming it
- * into an HSZ
- */
-HSZ    WDML_MakeHszFromAtom(const WDML_INSTANCE* pInstance, ATOM atom)
-{
-    WCHAR nameBuffer[MAX_BUFFER_LEN];
+    ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
+    ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
 
-    if (!atom) return NULL;
+    TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
 
-    if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
+    /* Make sure we found both strings. */
+    if (ret1 == 0 && ret2 == 0)
     {
-       TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
-       return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
+       /* If both are not found, return both  "zero strings". */
+       ret = 0;
     }
-    WARN("ATOM 0x%x not found\n", atom);
-    return 0;
-}
-
-/******************************************************************
- *             WDML_IncHSZ
- *
- *
- */
-BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
-{
-    HSZNode*   pNode;
-
-    pNode = WDML_FindNode(pInstance, hsz);
-    if (!pNode) return FALSE;
-
-    pNode->refCount++;
-    return TRUE;
-}
-
-/******************************************************************************
- *           WDML_DecHSZ    (INTERNAL)
- *
- * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
- * of HSZ nodes
- * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
- */
-BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
-{
-    HSZNode*   pPrev = NULL;
-    HSZNode*   pCurrent;
-
-    for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
+    else if (ret1 == 0)
     {
-       /* If we found the node we were looking for and its ref count is one,
-        * we can remove it
+       /* If hsz1 is a not found, return hsz1 is "zero string". */
+       ret = -1;
+    }
+    else if (ret2 == 0)
+    {
+       /* If hsz2 is a not found, return hsz2 is "zero string". */
+       ret = 1;
+    }
+    else
+    {
+       /* Compare the two strings we got (case insensitive). */
+       ret = lstrcmpiW(psz1, psz2);
+       /* Since strcmp returns any number smaller than
+        * 0 when the first string is found to be less than
+        * the second one we must make sure we are returning
+        * the proper values.
         */
-       if (pCurrent->hsz == hsz)
+       if (ret < 0)
        {
-           if (--pCurrent->refCount == 0)
-           {
-               if (pCurrent == pInstance->nodeList)
-               {
-                   pInstance->nodeList = pCurrent->next;
-               }
-               else
-               {
-                   pPrev->next = pCurrent->next;
-               }
-               HeapFree(GetProcessHeap(), 0, pCurrent);
-               DeleteAtom(HSZ2ATOM(hsz));
-           }
-           return TRUE;
+           ret = -1;
+       }
+       else if (ret > 0)
+       {
+           ret = 1;
        }
     }
-    WARN("HSZ %p not found\n", hsz);
 
-    return FALSE;
+    return ret;
 }
 
+/* ================================================================
+ *
+ *                     Instance management
+ *
+ * ================================================================ */
+
 /******************************************************************************
- *            WDML_FreeAllHSZ    (INTERNAL)
+ *             IncrementInstanceId
  *
- * Frees up all the strings still allocated in the list and
- * remove all the nodes from the list of HSZ nodes.
+ *     generic routine to increment the max instance Id and allocate a new application instance
  */
-void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
+static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
 {
-    /* Free any strings created in this instance.
-     */
-    while (pInstance->nodeList != NULL)
-    {
-       DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
-    }
+    DWORD      id = InterlockedIncrement(&WDML_MaxInstanceID);
+
+    pInstance->instanceID = id;
+    TRACE("New instance id %d allocated\n", id);
 }
 
-/******************************************************************************
- *            InsertHSZNode    (INTERNAL)
+/******************************************************************
+ *             WDML_EventProc
+ *
  *
- * Insert a node to the head of the list.
  */
-static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
+static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-    if (hsz != 0)
+    WDML_INSTANCE*     pInstance;
+    HSZ                        hsz1, hsz2;
+
+    switch (uMsg)
     {
-       HSZNode* pNew = NULL;
-       /* Create a new node for this HSZ.
-        */
-       pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
-       if (pNew != NULL)
+    case WM_WDML_REGISTER:
+       pInstance = WDML_GetInstanceFromWnd(hwndEvent);
+        /* try calling the Callback */
+       if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
        {
-           pNew->hsz      = hsz;
-           pNew->next     = pInstance->nodeList;
-           pNew->refCount = 1;
-           pInstance->nodeList = pNew;
+           hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
+           hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
+           WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
+           WDML_DecHSZ(pInstance, hsz1);
+           WDML_DecHSZ(pInstance, hsz2);
        }
-       else
+       break;
+
+    case WM_WDML_UNREGISTER:
+       pInstance = WDML_GetInstanceFromWnd(hwndEvent);
+       if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
        {
-           ERR("Primary HSZ Node allocation failed - out of memory\n");
+           hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
+           hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
+           WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
+           WDML_DecHSZ(pInstance, hsz1);
+           WDML_DecHSZ(pInstance, hsz2);
        }
+       break;
+
+    case WM_WDML_CONNECT_CONFIRM:
+       pInstance = WDML_GetInstanceFromWnd(hwndEvent);
+       if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
+       {
+           WDML_CONV*  pConv;
+           /* confirm connection...
+            * lookup for this conv handle
+            */
+            HWND client = (HWND)wParam;
+            HWND server = (HWND)lParam;
+           for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
+           {
+               if (pConv->hwndClient == client && pConv->hwndServer == server)
+                   break;
+           }
+           if (pConv)
+           {
+               pConv->wStatus |= ST_ISLOCAL;
+
+               WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
+                                   pConv->hszTopic, pConv->hszService, 0, 0,
+                                   (pConv->wStatus & ST_ISSELF) ? 1 : 0);
+           }
+       }
+       break;
+    default:
+       return DefWindowProcW(hwndEvent, uMsg, wParam, lParam);
     }
+    return 0;
 }
 
 /******************************************************************
- *             WDML_QueryString
+ *             WDML_Initialize
  *
  *
  */
-static int     WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
-                                int codepage)
+UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+                            DWORD afCmd, DWORD ulRes, BOOL bUnicode)
 {
-    WCHAR      pString[MAX_BUFFER_LEN];
-    int                ret;
-    /* If psz is null, we have to return only the length
-     * of the string.
-     */
-    if (ptr == NULL)
-    {
-       ptr = pString;
-       cchMax = MAX_BUFFER_LEN;
-    }
+    WDML_INSTANCE*             pInstance;
+    WDML_INSTANCE*             reference_inst;
+    UINT                       ret;
+    WNDCLASSEXW                        wndclass;
 
-    /* if there is no input windows returns a NULL string */
-    if (hsz == NULL)
+    TRACE("(%p,%p,0x%x,%d,0x%x)\n",
+         pidInst, pfnCallback, afCmd, ulRes, bUnicode);
+
+    if (ulRes)
     {
-       CHAR *t_ptr = ptr;
-       *t_ptr = '\0';
-       return 1;
+       ERR("Reserved value not zero?  What does this mean?\n");
+       /* trap this and no more until we know more */
+       return DMLERR_INVALIDPARAMETER;
     }
 
-    switch (codepage)
+    /* grab enough heap for one control struct - not really necessary for re-initialise
+     * but allows us to use same validation routines */
+    pInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
+    if (pInstance == NULL)
     {
-    case CP_WINANSI:
-       ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
-       break;
-    case CP_WINUNICODE:
-       ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
-        break;
-    default:
-       ERR("Unknown code page %d\n", codepage);
-       ret = 0;
+       /* catastrophe !! warn user & abort */
+       ERR("Instance create failed - out of memory\n");
+       return DMLERR_SYS_ERROR;
     }
-    return ret;
-}
+    pInstance->next = NULL;
+    pInstance->monitor = (afCmd | APPCLASS_MONITOR);
 
-/*****************************************************************
- * DdeQueryStringA [USER32.@]
- */
-DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
-{
-    DWORD              ret = 0;
-    WDML_INSTANCE*     pInstance;
+    /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
 
-    TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
+    pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
+    pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
+    pInstance->threadID = GetCurrentThreadId();
+    pInstance->callback = *pfnCallback;
+    pInstance->unicode = bUnicode;
+    pInstance->nodeList = NULL; /* node will be added later */
+    pInstance->monitorFlags = afCmd & MF_MASK;
+    pInstance->wStatus = 0;
+    pInstance->lastError = DMLERR_NO_ERROR;
+    pInstance->servers = NULL;
+    pInstance->convs[0] = NULL;
+    pInstance->convs[1] = NULL;
+    pInstance->links[0] = NULL;
+    pInstance->links[1] = NULL;
 
-    /*  First check instance
-     */
-    pInstance = WDML_GetInstance(idInst);
-    if (pInstance != NULL)
+    /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
+
+    pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
+
+    if (!pInstance->clientOnly)
     {
-       if (iCodePage == 0) iCodePage = CP_WINANSI;
-       ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
+       /* Check for other way of setting Client-only !! */
+       pInstance->clientOnly =
+           (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
     }
 
-    TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
-    return ret;
-}
+    ERR("instance created - checking validity\n");
 
-/*****************************************************************
- * DdeQueryStringW [USER32.@]
- */
+    if (*pidInst == 0)
+    {
+       /*  Initialisation of new Instance Identifier */
+       ERR("new instance, callback %p flags %X\n",pfnCallback,afCmd);
 
-DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
-{
-    DWORD              ret = 0;
-    WDML_INSTANCE*     pInstance;
+       EnterCriticalSection(&WDML_CritSect);
 
-    TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
+       if (WDML_InstanceList == NULL)
+       {
+           /* can't be another instance in this case, assign to the base pointer */
+           WDML_InstanceList = pInstance;
 
-    /*  First check instance
-     */
-    pInstance = WDML_GetInstance(idInst);
-    if (pInstance != NULL)
+           /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
+            *          present
+            *  -------------------------------      NOTE NOTE NOTE    --------------------------
+            *
+            *  the manual is not clear if this condition
+            *  applies to the first call to DdeInitialize from an application, or the
+            *  first call for a given callback !!!
+            */
+
+           pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
+           ERR("First application instance detected OK\n");
+           /*  allocate new instance ID */
+           WDML_IncrementInstanceId(pInstance);
+       }
+       else
+       {
+           /* really need to chain the new one in to the latest here, but after checking conditions
+            *  such as trying to start a conversation from an application trying to monitor */
+           reference_inst = WDML_InstanceList;
+           ERR("Subsequent application instance - starting checks\n");
+           while (reference_inst->next != NULL)
+           {
+               /*
+                *      This set of tests will work if application uses same instance Id
+                *      at application level once allocated - which is what manual implies
+                *      should happen. If someone tries to be
+                *      clever (lazy ?) it will fail to pick up that later calls are for
+                *      the same application - should we trust them ?
+                */
+               if (pInstance->instanceID == reference_inst->instanceID)
+               {
+                   /* Check 1 - must be same Client-only state */
+
+                   if (pInstance->clientOnly != reference_inst->clientOnly)
+                   {
+                       ERR("WDML_Initialize Mustbe Client-only\n");
+                       ret = DMLERR_DLL_USAGE;
+                       goto theError;
+                   }
+
+                   /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
+
+                   if (pInstance->monitor != reference_inst->monitor)
+                   {
+                       ERR("WDML_Initialize cannot use monitor w/any modes\n");
+                       ret = DMLERR_INVALIDPARAMETER;
+                       goto theError;
+                   }
+
+                   /* Check 3 - must supply different callback address */
+
+                   if (pInstance->callback == reference_inst->callback)
+                   {
+                       ret = DMLERR_DLL_USAGE;
+                       goto theError;
+                   }
+               }
+               reference_inst = reference_inst->next;
+           }
+           /*  All cleared, add to chain */
+
+           ERR("Application Instance checks finished\n");
+           WDML_IncrementInstanceId(pInstance);
+           reference_inst->next = pInstance;
+       }
+       LeaveCriticalSection(&WDML_CritSect);
+
+       *pidInst = pInstance->instanceID;
+
+       /* for deadlock issues, windows must always be created when outside the critical section */
+       wndclass.cbSize        = sizeof(wndclass);
+       wndclass.style         = 0;
+       wndclass.lpfnWndProc   = WDML_EventProc;
+       wndclass.cbClsExtra    = 0;
+       wndclass.cbWndExtra    = sizeof(ULONG_PTR);
+       wndclass.hInstance     = 0;
+       wndclass.hIcon         = 0;
+       wndclass.hCursor       = 0;
+       wndclass.hbrBackground = 0;
+       wndclass.lpszMenuName  = NULL;
+       wndclass.lpszClassName = WDML_szEventClass;
+       wndclass.hIconSm       = 0;
+
+       RegisterClassExW(&wndclass);
+
+       pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL,
+                                               WS_POPUP, 0, 0, 0, 0,
+                                               0, 0, 0, 0);
+
+       SetWindowLongPtrW(pInstance->hwndEvent, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
+
+       ERR("New application instance processing finished OK\n");
+    }
+    else
     {
-       if (iCodePage == 0) iCodePage = CP_WINUNICODE;
-       ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
+       /* Reinitialisation situation   --- FIX  */
+       ERR("reinitialisation of (%p,%p,0x%x,%d): stub\n", pidInst, pfnCallback, afCmd, ulRes);
+
+       EnterCriticalSection(&WDML_CritSect);
+
+       if (WDML_InstanceList == NULL)
+       {
+           ERR("WDML_Initialize No instance list\n");
+           ret = DMLERR_INVALIDPARAMETER;
+           goto theError;
+       }
+       /* can't reinitialise if we have initialised nothing !! */
+       reference_inst = WDML_InstanceList;
+       /* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
+       /*
+        *      MS allows initialisation without specifying a callback, should we allow addition of the
+        *      callback by a later call to initialise ? - if so this lot will have to change
+        */
+       while (reference_inst->next != NULL)
+       {
+           if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
+           {
+               /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
+
+               if (reference_inst->clientOnly)
+               {
+                   if  ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
+                   {
+                               /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
+
+                       if (!(afCmd & APPCMD_CLIENTONLY))
+                       {
+                           ERR("WDML_Initialize AppCmd Client-only 2\n");
+                           ret = DMLERR_INVALIDPARAMETER;
+                           goto theError;
+                       }
+                   }
+               }
+               /* Check 2 - cannot change monitor modes */
+
+               if (pInstance->monitor != reference_inst->monitor)
+               {
+                   ERR("WDML_Initialize cannot change monitor modes 2\n");
+                   ret = DMLERR_INVALIDPARAMETER;
+                   goto theError;
+               }
+
+               /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
+
+               if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
+               {
+                   ERR("WDML_Initialize trying to set Client-only via APPCMD\n");
+                   ret = DMLERR_INVALIDPARAMETER;
+                   goto theError;
+               }
+               break;
+           }
+           reference_inst = reference_inst->next;
+       }
+       if (reference_inst->next == NULL)
+       {
+           ERR("WDML_Initialize Nothing Next\n");
+           ret = DMLERR_INVALIDPARAMETER;
+           goto theError;
+       }
+       /* All checked - change relevant flags */
+
+       reference_inst->CBFflags = pInstance->CBFflags;
+       reference_inst->clientOnly = pInstance->clientOnly;
+       reference_inst->monitorFlags = pInstance->monitorFlags;
+
+       HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
+
+       LeaveCriticalSection(&WDML_CritSect);
     }
 
-    TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
+    return DMLERR_NO_ERROR;
+ theError:
+    ERR("WDML_Initialize error %x\n",ret);
+    HeapFree(GetProcessHeap(), 0, pInstance);
+    LeaveCriticalSection(&WDML_CritSect);
     return ret;
 }
 
-/******************************************************************
- *             DML_CreateString
+/******************************************************************************
+ *            DdeInitializeA   (USER32.@)
+ *
+ * See DdeInitializeW.
+ */
+UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+                          DWORD afCmd, DWORD ulRes)
+{
+    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE);
+}
+
+/******************************************************************************
+ * DdeInitializeW [USER32.@]
+ * Registers an application with the DDEML
  *
+ * PARAMS
+ *    pidInst     [I] Pointer to instance identifier
+ *    pfnCallback [I] Pointer to callback function
+ *    afCmd       [I] Set of command and filter flags
+ *    ulRes       [I] Reserved
  *
+ * RETURNS
+ *    Success: DMLERR_NO_ERROR
+ *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
  */
-static HSZ     WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
+UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+                          DWORD afCmd, DWORD ulRes)
 {
-    HSZ                hsz;
-
-    switch (codepage)
-    {
-    case CP_WINANSI:
-       hsz = ATOM2HSZ(AddAtomA(ptr));
-       TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz);
-       break;
-    case CP_WINUNICODE:
-       hsz = ATOM2HSZ(AddAtomW(ptr));
-       TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz);
-       break;
-    default:
-       ERR("Unknown code page %d\n", codepage);
-       return 0;
-    }
-    WDML_InsertHSZNode(pInstance, hsz);
-    return hsz;
+    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE);
 }
 
 /*****************************************************************
- * DdeCreateStringHandleA [USER32.@]
+ * DdeUninitialize [USER32.@]  Frees DDEML resources
  *
- * See DdeCreateStringHandleW.
+ * PARAMS
+ *    idInst [I] Instance identifier
+ *
+ * RETURNS
+ *    Success: TRUE
+ *    Failure: FALSE
  */
-HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
+
+BOOL WINAPI DdeUninitialize(DWORD idInst)
 {
-    HSZ                        hsz = 0;
-    WDML_INSTANCE*     pInstance;
+    /*  Stage one - check if we have a handle for this instance
+     */
+    WDML_INSTANCE*             pInstance;
+    WDML_CONV*                 pConv;
+    WDML_CONV*                 pConvNext;
 
-    TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage);
+    TRACE("(%d)\n", idInst);
 
+    /*  First check instance
+     */
     pInstance = WDML_GetInstance(idInst);
     if (pInstance == NULL)
-       WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
-    else
     {
-       if (codepage == 0) codepage = CP_WINANSI;
-       hsz = WDML_CreateString(pInstance, psz, codepage);
+       /*
+        *      Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
+        */
+       return FALSE;
     }
 
-    return hsz;
-}
+    /* first terminate all conversations client side
+     * this shall close existing links...
+     */
+    for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
+    {
+       pConvNext = pConv->next;
+       DdeDisconnect((HCONV)pConv);
+    }
+    if (pInstance->convs[WDML_CLIENT_SIDE])
+       FIXME("still pending conversations\n");
 
+    /* then unregister all known service names */
+    DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
 
-/******************************************************************************
- * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
- *
- * PARAMS
- *     idInst   [I] Instance identifier
- *     psz      [I] Pointer to string
- *     codepage [I] Code page identifier
- * RETURNS
- *    Success: String handle
- *    Failure: 0
- */
-HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
-{
-    WDML_INSTANCE*     pInstance;
-    HSZ                        hsz = 0;
+    /* Free the nodes that were not freed by this instance
+     * and remove the nodes from the list of HSZ nodes.
+     */
+    WDML_FreeAllHSZ(pInstance);
 
-    pInstance = WDML_GetInstance(idInst);
-    if (pInstance == NULL)
-       WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
+    DestroyWindow(pInstance->hwndEvent);
+
+    /* OK now delete the instance handle itself */
+
+    if (WDML_InstanceList == pInstance)
+    {
+       /* special case - the first/only entry */
+       WDML_InstanceList = pInstance->next;
+    }
     else
     {
-       if (codepage == 0) codepage = CP_WINUNICODE;
-       hsz = WDML_CreateString(pInstance, psz, codepage);
+       /* general case, remove entry */
+       WDML_INSTANCE*  inst;
+
+       for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
+       inst->next = pInstance->next;
     }
+    /* release the heap entry
+     */
+    HeapFree(GetProcessHeap(), 0, pInstance);
 
-    return hsz;
+    return TRUE;
 }
 
-/*****************************************************************
- *            DdeFreeStringHandle   (USER32.@)
- * RETURNS
- *  success: nonzero
- *  fail:    zero
+/******************************************************************
+ *             WDML_NotifyThreadExit
+ *
+ *
  */
-BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
+void WDML_NotifyThreadDetach(void)
 {
     WDML_INSTANCE*     pInstance;
-    BOOL               ret = FALSE;
-
-    TRACE("(%d,%p):\n", idInst, hsz);
-
-    /*  First check instance
-     */
-    pInstance = WDML_GetInstance(idInst);
-    if (pInstance)
-       ret = WDML_DecHSZ(pInstance, hsz);
+    WDML_INSTANCE*     next;
+    DWORD              tid = GetCurrentThreadId();
 
-    return ret;
+    EnterCriticalSection(&WDML_CritSect);
+    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
+    {
+       next = pInstance->next;
+       if (pInstance->threadID == tid)
+       {
+            LeaveCriticalSection(&WDML_CritSect);
+           DdeUninitialize(pInstance->instanceID);
+            EnterCriticalSection(&WDML_CritSect);
+       }
+    }
+    LeaveCriticalSection(&WDML_CritSect);
 }
 
-/*****************************************************************
- *            DdeKeepStringHandle  (USER32.@)
+/******************************************************************
+ *             WDML_InvokeCallback
+ *
  *
- * RETURNS
- *  success: nonzero
- *  fail:    zero
  */
-BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
+HDDEDATA       WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
+                                   HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+                                   ULONG_PTR dwData1, ULONG_PTR dwData2)
 {
-    WDML_INSTANCE*     pInstance;
-    BOOL               ret = FALSE;
-
-    TRACE("(%d,%p):\n", idInst, hsz);
+    HDDEDATA   ret;
 
-    /*  First check instance
-     */
-    pInstance = WDML_GetInstance(idInst);
-    if (pInstance)
-       ret = WDML_IncHSZ(pInstance, hsz);
+    if (pInstance == NULL)
+       return NULL;
 
+    TRACE("invoking CB[%p] (%x %x %p %p %p %p %lx %lx)\n",
+         pInstance->callback, uType, uFmt,
+         hConv, hsz1, hsz2, hdata, dwData1, dwData2);
+    ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
+    TRACE("done => %p\n", ret);
     return ret;
 }
 
-/*****************************************************************
- *            DdeCmpStringHandles (USER32.@)
- *
- * Compares the value of two string handles.  This comparison is
- * not case sensitive.
+/*****************************************************************************
+ *     WDML_GetInstance
  *
- * PARAMS
- *  hsz1    [I] Handle to the first string
- *  hsz2    [I] Handle to the second string
+ *     generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
+ *     for an instance Id, or NULL if the entry does not exist
  *
- * RETURNS
- *  -1 The value of hsz1 is zero or less than hsz2
- *  0  The values of hsz 1 and 2 are the same or both zero.
- *  1  The value of hsz2 is zero of less than hsz1
  */
-INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
+WDML_INSTANCE* WDML_GetInstance(DWORD instId)
 {
-    WCHAR      psz1[MAX_BUFFER_LEN];
-    WCHAR      psz2[MAX_BUFFER_LEN];
-    int                ret = 0;
-    int                ret1, ret2;
-
-    ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
-    ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
+    WDML_INSTANCE*     pInstance;
 
-    TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
+    EnterCriticalSection(&WDML_CritSect);
 
-    /* Make sure we found both strings. */
-    if (ret1 == 0 && ret2 == 0)
-    {
-       /* If both are not found, return both  "zero strings". */
-       ret = 0;
-    }
-    else if (ret1 == 0)
-    {
-       /* If hsz1 is a not found, return hsz1 is "zero string". */
-       ret = -1;
-    }
-    else if (ret2 == 0)
-    {
-       /* If hsz2 is a not found, return hsz2 is "zero string". */
-       ret = 1;
-    }
-    else
+    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
     {
-       /* Compare the two strings we got (case insensitive). */
-       ret = lstrcmpiW(psz1, psz2);
-       /* Since strcmp returns any number smaller than
-        * 0 when the first string is found to be less than
-        * the second one we must make sure we are returning
-        * the proper values.
-        */
-       if (ret < 0)
-       {
-           ret = -1;
-       }
-       else if (ret > 0)
+       if (pInstance->instanceID == instId)
        {
-           ret = 1;
+           if (GetCurrentThreadId() != pInstance->threadID)
+           {
+               FIXME("Tried to get instance from wrong thread\n");
+               continue;
+           }
+           break;
        }
     }
 
-    return ret;
+    LeaveCriticalSection(&WDML_CritSect);
+
+    if (!pInstance)
+        WARN("Instance entry missing for id %04x\n", instId);
+    return pInstance;
+}
+
+/******************************************************************
+ *             WDML_GetInstanceFromWnd
+ *
+ *
+ */
+WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd)
+{
+    return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE);
 }
 
 /* ================================================================
@@ -1459,7 +1473,7 @@ BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
 
     /* 1 is the handle value returned by an asynchronous operation. */
     if (hData == (HDDEDATA)1)
-       return TRUE;
+        return TRUE;
 
     return GlobalFree(hData) == 0;
 }
@@ -1514,7 +1528,7 @@ HDDEDATA        WDML_Global2DataHandle(WDML_CONV* pConv, HGLOBAL hMem, WINE_DDEH
             default:
                 FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
                       pDd->cfFormat, hMem);
-                /* fall thru */
+                /* fall through */
             case 0:
             case CF_TEXT:
                 ret = DdeCreateDataHandle(pConv->instance->instanceID, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
@@ -1573,7 +1587,7 @@ HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
         default:
             FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
                    pDdh->cfFormat, hDdeData);
-            /* fall thru */
+            /* fall through */
         case 0:
         case CF_TEXT:
             hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
@@ -1732,8 +1746,261 @@ WDML_SERVER*    WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTo
            return pServer;
        }
     }
-    TRACE("Service name missing\n");
-    return NULL;
+    TRACE("Service name missing\n");
+    return NULL;
+}
+
+/* ================================================================
+ *
+ *                     Link (hot & warm) management
+ *
+ * ================================================================ */
+
+/******************************************************************
+ *             WDML_AddLink
+ *
+ *
+ */
+void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
+                 UINT wType, HSZ hszItem, UINT wFmt)
+{
+    WDML_LINK* pLink;
+
+    pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
+    if (pLink == NULL)
+    {
+       ERR("OOM\n");
+       return;
+    }
+
+    pLink->hConv = hConv;
+    pLink->transactionType = wType;
+    WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
+    pLink->uFmt = wFmt;
+    pLink->next = pInstance->links[side];
+    pInstance->links[side] = pLink;
+}
+
+/******************************************************************
+ *             WDML_RemoveLink
+ *
+ *
+ */
+void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
+                    HSZ hszItem, UINT uFmt)
+{
+    WDML_LINK* pPrev = NULL;
+    WDML_LINK* pCurrent = NULL;
+
+    pCurrent = pInstance->links[side];
+
+    while (pCurrent != NULL)
+    {
+       if (pCurrent->hConv == hConv &&
+           DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
+           pCurrent->uFmt == uFmt)
+       {
+           if (pCurrent == pInstance->links[side])
+           {
+               pInstance->links[side] = pCurrent->next;
+           }
+           else
+           {
+               pPrev->next = pCurrent->next;
+           }
+
+           WDML_DecHSZ(pInstance, pCurrent->hszItem);
+           HeapFree(GetProcessHeap(), 0, pCurrent);
+           break;
+       }
+
+       pPrev = pCurrent;
+       pCurrent = pCurrent->next;
+    }
+}
+
+/* this function is called to remove all links related to the conv.
+   It should be called from both client and server when terminating
+   the conversation.
+*/
+/******************************************************************
+ *             WDML_RemoveAllLinks
+ *
+ *
+ */
+void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
+{
+    WDML_LINK* pPrev = NULL;
+    WDML_LINK* pCurrent = NULL;
+    WDML_LINK* pNext = NULL;
+
+    pCurrent = pInstance->links[side];
+
+    while (pCurrent != NULL)
+    {
+       if (pCurrent->hConv == (HCONV)pConv)
+       {
+           if (pCurrent == pInstance->links[side])
+           {
+               pInstance->links[side] = pCurrent->next;
+               pNext = pCurrent->next;
+           }
+           else
+           {
+               pPrev->next = pCurrent->next;
+               pNext = pCurrent->next;
+           }
+
+           WDML_DecHSZ(pInstance, pCurrent->hszItem);
+
+           HeapFree(GetProcessHeap(), 0, pCurrent);
+           pCurrent = NULL;
+       }
+
+       if (pCurrent)
+       {
+           pPrev = pCurrent;
+           pCurrent = pCurrent->next;
+       }
+       else
+       {
+           pCurrent = pNext;
+       }
+    }
+}
+
+/******************************************************************
+ *             WDML_FindLink
+ *
+ *
+ */
+WDML_LINK*     WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
+                             HSZ hszItem, BOOL use_fmt, UINT uFmt)
+{
+    WDML_LINK* pCurrent = NULL;
+
+    for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
+    {
+       /* we don't need to check for transaction type as it can be altered */
+
+       if (pCurrent->hConv == hConv &&
+           DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
+           (!use_fmt || pCurrent->uFmt == uFmt))
+       {
+           break;
+       }
+
+    }
+
+    return pCurrent;
+}
+
+/* ================================================================
+ *
+ *                     Transaction management
+ *
+ * ================================================================ */
+
+/******************************************************************
+ *             WDML_AllocTransaction
+ *
+ * Alloc a transaction structure for handling the message ddeMsg
+ */
+WDML_XACT*     WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
+                                     UINT wFmt, HSZ hszItem)
+{
+    WDML_XACT*         pXAct;
+    static WORD                tid = 1;        /* FIXME: wrap around */
+
+    pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
+    if (!pXAct)
+    {
+       pInstance->lastError = DMLERR_MEMORY_ERROR;
+       return NULL;
+    }
+
+    pXAct->xActID = tid++;
+    pXAct->ddeMsg = ddeMsg;
+    pXAct->hDdeData = 0;
+    pXAct->hUser = 0;
+    pXAct->next = NULL;
+    pXAct->wType = 0;
+    pXAct->wFmt = wFmt;
+    if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
+    pXAct->atom = 0;
+    pXAct->hMem = 0;
+    pXAct->lParam = 0;
+
+    return pXAct;
+}
+
+/******************************************************************
+ *             WDML_QueueTransaction
+ *
+ * Adds a transaction to the list of transaction
+ */
+void   WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
+{
+    WDML_XACT**        pt;
+
+    /* advance to last in queue */
+    for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
+    *pt = pXAct;
+}
+
+/******************************************************************
+ *             WDML_UnQueueTransaction
+ *
+ *
+ */
+BOOL   WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct)
+{
+    WDML_XACT**        pt;
+
+    for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
+    {
+       if (*pt == pXAct)
+       {
+           *pt = pXAct->next;
+           return TRUE;
+       }
+    }
+    return FALSE;
+}
+
+/******************************************************************
+ *             WDML_FreeTransaction
+ *
+ *
+ */
+void   WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
+{
+    /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
+    if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
+    {
+       GlobalFree(pXAct->hMem);
+    }
+    if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
+
+    HeapFree(GetProcessHeap(), 0, pXAct);
+}
+
+/******************************************************************
+ *             WDML_FindTransaction
+ *
+ *
+ */
+WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
+{
+    WDML_XACT* pXAct;
+
+    tid = HIWORD(tid);
+    for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
+    {
+       if (pXAct->xActID == tid)
+           break;
+    }
+    return pXAct;
 }
 
 /* ================================================================
@@ -1876,7 +2143,7 @@ static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
     }
 
     if (wCmd == EC_QUERYWAITING)
-        return pConv->transactions ? TRUE : FALSE;
+        return pConv->transactions != NULL;
 
     if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
     {
@@ -2201,259 +2468,6 @@ UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
     return ret;
 }
 
-/* ================================================================
- *
- *                     Link (hot & warm) management
- *
- * ================================================================ */
-
-/******************************************************************
- *             WDML_AddLink
- *
- *
- */
-void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
-                 UINT wType, HSZ hszItem, UINT wFmt)
-{
-    WDML_LINK* pLink;
-
-    pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
-    if (pLink == NULL)
-    {
-       ERR("OOM\n");
-       return;
-    }
-
-    pLink->hConv = hConv;
-    pLink->transactionType = wType;
-    WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
-    pLink->uFmt = wFmt;
-    pLink->next = pInstance->links[side];
-    pInstance->links[side] = pLink;
-}
-
-/******************************************************************
- *             WDML_RemoveLink
- *
- *
- */
-void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
-                    HSZ hszItem, UINT uFmt)
-{
-    WDML_LINK* pPrev = NULL;
-    WDML_LINK* pCurrent = NULL;
-
-    pCurrent = pInstance->links[side];
-
-    while (pCurrent != NULL)
-    {
-       if (pCurrent->hConv == hConv &&
-           DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
-           pCurrent->uFmt == uFmt)
-       {
-           if (pCurrent == pInstance->links[side])
-           {
-               pInstance->links[side] = pCurrent->next;
-           }
-           else
-           {
-               pPrev->next = pCurrent->next;
-           }
-
-           WDML_DecHSZ(pInstance, pCurrent->hszItem);
-           HeapFree(GetProcessHeap(), 0, pCurrent);
-           break;
-       }
-
-       pPrev = pCurrent;
-       pCurrent = pCurrent->next;
-    }
-}
-
-/* this function is called to remove all links related to the conv.
-   It should be called from both client and server when terminating
-   the conversation.
-*/
-/******************************************************************
- *             WDML_RemoveAllLinks
- *
- *
- */
-void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
-{
-    WDML_LINK* pPrev = NULL;
-    WDML_LINK* pCurrent = NULL;
-    WDML_LINK* pNext = NULL;
-
-    pCurrent = pInstance->links[side];
-
-    while (pCurrent != NULL)
-    {
-       if (pCurrent->hConv == (HCONV)pConv)
-       {
-           if (pCurrent == pInstance->links[side])
-           {
-               pInstance->links[side] = pCurrent->next;
-               pNext = pCurrent->next;
-           }
-           else
-           {
-               pPrev->next = pCurrent->next;
-               pNext = pCurrent->next;
-           }
-
-           WDML_DecHSZ(pInstance, pCurrent->hszItem);
-
-           HeapFree(GetProcessHeap(), 0, pCurrent);
-           pCurrent = NULL;
-       }
-
-       if (pCurrent)
-       {
-           pPrev = pCurrent;
-           pCurrent = pCurrent->next;
-       }
-       else
-       {
-           pCurrent = pNext;
-       }
-    }
-}
-
-/******************************************************************
- *             WDML_FindLink
- *
- *
- */
-WDML_LINK*     WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
-                             HSZ hszItem, BOOL use_fmt, UINT uFmt)
-{
-    WDML_LINK* pCurrent = NULL;
-
-    for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
-    {
-       /* we don't need to check for transaction type as it can be altered */
-
-       if (pCurrent->hConv == hConv &&
-           DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
-           (!use_fmt || pCurrent->uFmt == uFmt))
-       {
-           break;
-       }
-
-    }
-
-    return pCurrent;
-}
-
-/* ================================================================
- *
- *                     Transaction management
- *
- * ================================================================ */
-
-/******************************************************************
- *             WDML_AllocTransaction
- *
- * Alloc a transaction structure for handling the message ddeMsg
- */
-WDML_XACT*     WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
-                                     UINT wFmt, HSZ hszItem)
-{
-    WDML_XACT*         pXAct;
-    static WORD                tid = 1;        /* FIXME: wrap around */
-
-    pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
-    if (!pXAct)
-    {
-       pInstance->lastError = DMLERR_MEMORY_ERROR;
-       return NULL;
-    }
-
-    pXAct->xActID = tid++;
-    pXAct->ddeMsg = ddeMsg;
-    pXAct->hDdeData = 0;
-    pXAct->hUser = 0;
-    pXAct->next = NULL;
-    pXAct->wType = 0;
-    pXAct->wFmt = wFmt;
-    if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
-    pXAct->atom = 0;
-    pXAct->hMem = 0;
-    pXAct->lParam = 0;
-
-    return pXAct;
-}
-
-/******************************************************************
- *             WDML_QueueTransaction
- *
- * Adds a transaction to the list of transaction
- */
-void   WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
-{
-    WDML_XACT**        pt;
-
-    /* advance to last in queue */
-    for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
-    *pt = pXAct;
-}
-
-/******************************************************************
- *             WDML_UnQueueTransaction
- *
- *
- */
-BOOL   WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct)
-{
-    WDML_XACT**        pt;
-
-    for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
-    {
-       if (*pt == pXAct)
-       {
-           *pt = pXAct->next;
-           return TRUE;
-       }
-    }
-    return FALSE;
-}
-
-/******************************************************************
- *             WDML_FreeTransaction
- *
- *
- */
-void   WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
-{
-    /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
-    if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
-    {
-       GlobalFree(pXAct->hMem);
-    }
-    if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
-
-    HeapFree(GetProcessHeap(), 0, pXAct);
-}
-
-/******************************************************************
- *             WDML_FindTransaction
- *
- *
- */
-WDML_XACT*     WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
-{
-    WDML_XACT* pXAct;
-
-    tid = HIWORD(tid);
-    for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
-    {
-       if (pXAct->xActID == tid)
-           break;
-    }
-    return pXAct;
-}
-
 /* ================================================================
  *
  *        Information broadcast across DDEML implementations