- implemented correctly the HSZ as local atoms and added the needed
  conversions to global atoms
- enhanced internal handle <=> pointer conversions, as well as
  validity of such objects (life time, destruction, mutual access...)
- fixed a few ANSI/Unicode issues, stores most of the data as Unicode
- started having both Ansi/Unicode DDE window procs for message A/W
  transformation
- fixed a few segmented pointer issues (mainly in DdeInitialize &
  DdeGetData)
- added most of the CBF_ flags handling
- implemented the conversation announcement (XTYP_CONNECT_CONFIRM) on
  server side
- enhanced DdeQueryConfig and implemented DdeReconnect
- implemented conversation termination (including XTYP_UNREGISTER)
- several others code clean up
- added transaction support on server side too

diff --git a/dlls/user/Makefile.in b/dlls/user/Makefile.in
index 5975585..0884e3e 100644
--- a/dlls/user/Makefile.in
+++ b/dlls/user/Makefile.in
@@ -37,7 +37,9 @@
 	resources/mouse.rc \
 	resources/version16.rc
 
-GLUE = thunk.c
+GLUE = \
+	dde/ddeml16.c \
+	thunk.c
 
 EXTRA_OBJS = \
 	$(TOPOBJDIR)/controls/controls.o \
diff --git a/dlls/user/dde/.cvsignore b/dlls/user/dde/.cvsignore
new file mode 100644
index 0000000..9fd25db
--- /dev/null
+++ b/dlls/user/dde/.cvsignore
@@ -0,0 +1 @@
+ddeml16.glue.c
diff --git a/dlls/user/dde/client.c b/dlls/user/dde/client.c
index 1086c42..cc29252 100644
--- a/dlls/user/dde/client.c
+++ b/dlls/user/dde/client.c
@@ -16,6 +16,7 @@
 #include "wingdi.h"
 #include "winuser.h"
 #include "winerror.h"
+#include "winnls.h"
 #include "dde.h"
 #include "ddeml.h"
 #include "debugtools.h"
@@ -24,7 +25,8 @@
 DEFAULT_DEBUG_CHANNEL(ddeml);
 
 static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM);	/* only for one client, not conv list */
-static const char szClientClassA[] = "DdeClientAnsi";
+const char  WDML_szClientConvClassA[] = "DdeClientAnsi";
+const WCHAR WDML_szClientConvClassW[] = {'D','d','e','C','l','i','e','n','t','U','n','i','c','o','d','e',0};
 
 /******************************************************************************
  * DdeConnectList [USER32.@]  Establishes conversation with DDE servers
@@ -44,7 +46,7 @@
 				HCONVLIST hConvList, LPCONVCONTEXT pCC)
 {
     FIXME("(%ld,%d,%d,%d,%p): stub\n", idInst, hszService, hszTopic,
-	  hConvList,pCC);
+	  hConvList, pCC);
     return (HCONVLIST)1;
 }
 
@@ -53,19 +55,22 @@
  */
 HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
 {
-    FIXME("(%d,%d): stub\n",hConvList,hConvPrev);
+    FIXME("(%d,%d): stub\n", hConvList, hConvPrev);
     return 0;
 }
 
 /******************************************************************************
  * DdeDisconnectList [USER32.@]  Destroys list and terminates conversations
  *
+ *
+ * PARAMS
+ *    hConvList  [I] Handle to conversation list
+ *
  * RETURNS
  *    Success: TRUE
  *    Failure: FALSE
  */
-BOOL WINAPI DdeDisconnectList(
-    HCONVLIST hConvList) /* [in] Handle to conversation list */
+BOOL WINAPI DdeDisconnectList(HCONVLIST hConvList)
 {
     FIXME("(%d): stub\n", hConvList);
     return TRUE;
@@ -79,156 +84,250 @@
 {
     HWND		hwndClient;
     LPARAM		lParam = 0;
-    UINT		uiLow, uiHi;
-    WNDCLASSEXA	wndclass;
-    WDML_INSTANCE*	thisInstance;
-    WDML_CONV*		pConv;
+    WDML_INSTANCE*	pInstance;
+    WDML_CONV*		pConv = NULL;
+    ATOM		aSrv = 0, aTpc = 0;
     
-    TRACE("(0x%lx,%d,%d,%p)\n",idInst,hszService,hszTopic,pCC);
-    
-    thisInstance = WDML_FindInstance(idInst);
-    if (!thisInstance)
+    TRACE("(0x%lx,0x%x,0x%x,%p)\n", idInst, hszService, hszTopic, pCC);
+
+    EnterCriticalSection(&WDML_CritSect);
+
+    pInstance = WDML_GetInstance(idInst);
+    if (!pInstance)
     {
-	return 0;
+	goto theEnd;
     }
     
     /* make sure this conv is never created */
-    pConv = WDML_FindConv(thisInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
+    pConv = WDML_FindConv(pInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
     if (pConv != NULL)
     {
 	ERR("This Conv already exists: (0x%lx)\n", (DWORD)pConv);
-	return (HCONV)pConv;
+	goto theEnd;
     }
     
     /* we need to establish a conversation with
        server, so create a window for it       */
     
-    wndclass.cbSize        = sizeof(wndclass);
-    wndclass.style         = 0;
-    wndclass.lpfnWndProc   = WDML_ClientProc;
-    wndclass.cbClsExtra    = 0;
-    wndclass.cbWndExtra    = 2 * sizeof(DWORD);
-    wndclass.hInstance     = 0;
-    wndclass.hIcon         = 0;
-    wndclass.hCursor       = 0;
-    wndclass.hbrBackground = 0;
-    wndclass.lpszMenuName  = NULL;
-    wndclass.lpszClassName = szClientClassA;
-    wndclass.hIconSm       = 0;
+    if (pInstance->unicode)
+    {
+	WNDCLASSEXW	wndclass;
+
+	wndclass.cbSize        = sizeof(wndclass);
+	wndclass.style         = 0;
+	wndclass.lpfnWndProc   = WDML_ClientProc;
+	wndclass.cbClsExtra    = 0;
+	wndclass.cbWndExtra    = 2 * sizeof(DWORD);
+	wndclass.hInstance     = 0;
+	wndclass.hIcon         = 0;
+	wndclass.hCursor       = 0;
+	wndclass.hbrBackground = 0;
+	wndclass.lpszMenuName  = NULL;
+	wndclass.lpszClassName = WDML_szClientConvClassW;
+	wndclass.hIconSm       = 0;
+	
+	RegisterClassExW(&wndclass);
     
-    RegisterClassExA(&wndclass);
+	hwndClient = CreateWindowW(WDML_szClientConvClassW, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
+    }
+    else
+    {
+	WNDCLASSEXA	wndclass;
+
+	wndclass.cbSize        = sizeof(wndclass);
+	wndclass.style         = 0;
+	wndclass.lpfnWndProc   = WDML_ClientProc;
+	wndclass.cbClsExtra    = 0;
+	wndclass.cbWndExtra    = 2 * sizeof(DWORD);
+	wndclass.hInstance     = 0;
+	wndclass.hIcon         = 0;
+	wndclass.hCursor       = 0;
+	wndclass.hbrBackground = 0;
+	wndclass.lpszMenuName  = NULL;
+	wndclass.lpszClassName = WDML_szClientConvClassA;
+	wndclass.hIconSm       = 0;
+	
+	RegisterClassExA(&wndclass);
     
-    hwndClient = CreateWindowA(szClientClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
+	hwndClient = CreateWindowA(WDML_szClientConvClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
+    }
+
+    SetWindowLongA(hwndClient, GWL_WDML_INSTANCE, (DWORD)pInstance);
     
-    SetWindowLongA(hwndClient, 0, (DWORD)thisInstance);
+    if (hszService)
+    {
+	aSrv = WDML_MakeAtomFromHsz(hszService);
+	if (!aSrv) goto theEnd;
+    }
+    if (hszTopic)
+    {
+	aTpc = WDML_MakeAtomFromHsz(hszTopic);
+	if (!aTpc) goto theEnd;
+    }
+
+    LeaveCriticalSection(&WDML_CritSect);
+
+    lParam = PackDDElParam(WM_DDE_INITIATE, aSrv, aTpc);
+    SendMessageA(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, lParam);
+    FreeDDElParam(WM_DDE_INITIATE, lParam);
     
-    SendMessageA(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient,
-		 PackDDElParam(WM_DDE_INITIATE, (UINT)hszService, (UINT)hszTopic));
-    
-    if (UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLow, &uiHi))
-	FreeDDElParam(WM_DDE_INITIATE, lParam);
+    EnterCriticalSection(&WDML_CritSect);
+
+    pInstance = WDML_GetInstance(idInst);
+    if (!pInstance)
+    {
+	goto theEnd;
+    }
     
     TRACE("WM_DDE_INITIATE was processed\n");
     /* At this point, Client WM_DDE_ACK should have saved hwndServer
        for this instance id and hwndClient if server responds.
        So get HCONV and return it. And add it to conv list */
-    pConv = (WDML_CONV*)GetWindowLongA(hwndClient, 4);
+    pConv = WDML_GetConvFromWnd(hwndClient);
     if (pConv == NULL || pConv->hwndServer == 0)
     {
 	ERR(".. but no Server window available\n");
-	return 0;
+	pConv = NULL;
+	goto theEnd;
     }
+    pConv->wConvst = XST_CONNECTED;
+
     /* finish init of pConv */
     if (pCC != NULL)
     {
 	pConv->convContext = *pCC;
     }
-    
+    else
+    {
+	memset(&pConv->convContext, 0, sizeof(pConv->convContext));
+	pConv->convContext.cb = sizeof(pConv->convContext);
+	pConv->convContext.iCodePage = (pInstance->unicode) ? CP_WINUNICODE : CP_WINANSI;
+    }
+
+ theEnd:
+    LeaveCriticalSection(&WDML_CritSect);
+
+    if (aSrv) GlobalDeleteAtom(aSrv);
+    if (aTpc) GlobalDeleteAtom(aTpc);
     return (HCONV)pConv;
 }
 
 /*****************************************************************
- *            DdeDisconnect   (USER32.@)
- */
-BOOL WINAPI DdeDisconnect(HCONV hConv)
-{
-    WDML_CONV*	pConv = NULL;
-    
-    TRACE("(%ld)\n", (DWORD)hConv);
-    
-    if (hConv == 0)
-    {
-	ERR("DdeDisconnect(): hConv = 0\n");
-	return 0;
-    }
-    
-    pConv = WDML_GetConv(hConv);
-    if (pConv == NULL)
-    {
-	return FALSE;
-    }
-    if (!PostMessageA(pConv->hwndServer, WM_DDE_TERMINATE,
-		      (WPARAM)pConv->hwndClient, (LPARAM)hConv))
-    {
-	ERR("DdeDisconnect(): PostMessage returned 0\n");
-	return 0;
-    }
-    return TRUE;
-}
-
-
-/*****************************************************************
  *            DdeReconnect   (DDEML.37)
  *            DdeReconnect   (USER32.@)
  */
 HCONV WINAPI DdeReconnect(HCONV hConv)
 {
-    FIXME("empty stub\n");
-    return 0;
+    WDML_CONV*	pConv;
+    WDML_CONV*	pNewConv = NULL;
+    ATOM	aSrv = 0, aTpc = 0;
+
+    EnterCriticalSection(&WDML_CritSect);
+    pConv = WDML_GetConv(hConv, FALSE);
+    if (pConv != NULL && (pConv->wStatus & ST_CLIENT))
+    {
+	LPARAM	lParam;
+	BOOL	ret;
+
+	/* to reestablist a connection, we have to make sure that:
+	 * 1/ pConv is the converstation attached to the client window (it wouldn't be
+	 *    if a call to DdeReconnect would have already been done...) 
+	 *    FIXME: is this really an error ???
+	 * 2/ the pConv conversation had really been deconnected
+	 */
+	if (pConv == WDML_GetConvFromWnd(pConv->hwndClient) &&
+	    (pConv->wStatus & ST_TERMINATED) && !(pConv->wStatus & ST_CONNECTED))
+	{
+	    HWND	hwndClient = pConv->hwndClient;
+	    HWND	hwndServer = pConv->hwndServer;
+	    ATOM	aSrv, aTpc;
+
+	    SetWindowLongA(pConv->hwndClient, GWL_WDML_CONVERSATION, 0);
+
+	    aSrv = WDML_MakeAtomFromHsz(pConv->hszService);
+	    aTpc = WDML_MakeAtomFromHsz(pConv->hszTopic);
+	    if (!aSrv || !aTpc)	goto theEnd;
+
+	    LeaveCriticalSection(&WDML_CritSect);
+
+	    lParam = PackDDElParam(WM_DDE_INITIATE, aSrv, aTpc);
+	    ret = SendMessageA(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient, lParam);
+	    FreeDDElParam(WM_DDE_INITIATE, lParam);
+	    
+	    EnterCriticalSection(&WDML_CritSect);
+
+	    pConv = WDML_GetConv(hConv, FALSE);
+	    if (pConv == NULL)
+	    {
+		FIXME("Should fail reconnection\n");
+		goto theEnd;
+	    }
+
+	    if (ret && (pNewConv = WDML_GetConvFromWnd(pConv->hwndClient)) != NULL)
+	    {
+		/* re-establish all links... */
+		WDML_LINK* pLink;
+
+		for (pLink = pConv->instance->links[WDML_CLIENT_SIDE]; pLink; pLink = pLink->next)
+		{
+		    if (pLink->hConv == hConv)
+		    {
+			/* try to reestablish the links... */
+			DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt, 
+					     pLink->transactionType, 1000, NULL);
+		    }
+		}
+	    }
+	    else
+	    {
+		/* reset the conversation as it was */
+		SetWindowLongA(pConv->hwndClient, GWL_WDML_CONVERSATION, (DWORD)pConv);
+	    }
+	}
+    }
+
+ theEnd:
+    LeaveCriticalSection(&WDML_CritSect);
+
+    if (aSrv) GlobalDeleteAtom(aSrv);
+    if (aTpc) GlobalDeleteAtom(aTpc);
+    return (HCONV)pNewConv;
 }
 
-typedef enum {
-    WDML_QS_ERROR, WDML_QS_HANDLED, WDML_QS_PASS
-} WDML_QUEUE_STATE;
-
 /******************************************************************
- *		WDML_QueueAdvise
+ *		WDML_ClientQueueAdvise
  *
  * Creates and queue an WM_DDE_ADVISE transaction
  */
-static WDML_XACT*	WDML_QueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
+static WDML_XACT*	WDML_ClientQueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
 {
     DDEADVISE*		pDdeAdvise;
     WDML_XACT*		pXAct;
+    ATOM		atom;
 
     TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : "");
 
-    pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_ADVISE);
+    atom = WDML_MakeAtomFromHsz(hszItem);
+    if (!atom) return NULL;
+     
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, wFmt, hszItem);
     if (!pXAct)
+    {
+	GlobalDeleteAtom(atom);
 	return NULL;
+    }
 
-    pXAct->u.advise.wType = wType & ~0x0F;
-    pXAct->u.advise.wFmt = wFmt;
-    pXAct->u.advise.hszItem = hszItem;
-    pXAct->u.advise.hDdeAdvise = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
+    pXAct->wType = wType & ~0x0F;
+    pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
     
     /* pack DdeAdvise	*/
-    pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->u.advise.hDdeAdvise);
+    pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
     pDdeAdvise->fAckReq   = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
     pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
     pDdeAdvise->cfFormat  = wFmt;
-    GlobalUnlock(pXAct->u.advise.hDdeAdvise);
+    GlobalUnlock(pXAct->hMem);
 
-    WDML_QueueTransaction(pConv, pXAct);
-
-    if (!PostMessageA(pConv->hwndServer, WM_DDE_ADVISE, (WPARAM)pConv->hwndClient,
-		      PackDDElParam(WM_DDE_ADVISE, (UINT)pXAct->u.advise.hDdeAdvise, (UINT)hszItem)))
-    {
-	GlobalFree(pXAct->u.advise.hDdeAdvise);
-	WDML_UnQueueTransaction(pConv, pXAct);
-	WDML_FreeTransaction(pXAct);
-	return NULL;
-    }
+    pXAct->lParam = PackDDElParam(WM_DDE_ADVISE, (UINT)pXAct->hMem, atom);
 
     return pXAct;
 }
@@ -242,82 +341,78 @@
 {
     DDEACK		ddeAck;
     UINT		uiLo, uiHi;
-    WORD		wStatus;
-    
+    HSZ			hsz;
+
     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
     {
 	return WDML_QS_PASS;
     }
 
     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
+    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
 
-    if (DdeCmpStringHandles(uiHi, pXAct->u.advise.hszItem) != 0)
+    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
 	return WDML_QS_PASS;
 
     GlobalDeleteAtom(uiHi);
+    FreeDDElParam(WM_DDE_ACK, msg->lParam);
 
-    wStatus = uiLo;
-    ddeAck = *((DDEACK*)&wStatus);
+    WDML_ExtractAck(uiLo, &ddeAck);
 	    
     if (ddeAck.fAck)
     {
 	WDML_LINK*	pLink;
 	
 	/* billx: first to see if the link is already created. */
-	pLink = WDML_FindLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, 
-			      pXAct->u.advise.hszItem, pXAct->u.advise.wFmt);
+	pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, 
+			      pXAct->hszItem, pXAct->wFmt);
 	if (pLink != NULL)	
 	{	
 	    /* we found a link, and only need to modify it in case it changes */
-	    pLink->transactionType = pXAct->u.advise.wType;
+	    pLink->transactionType = pXAct->wType;
 	}
 	else
 	{
-	    TRACE("Adding Link with hConv = 0x%lx\n", (DWORD)pConv);
-	    WDML_AddLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, 
-			 pXAct->u.advise.wType, pXAct->u.advise.hszItem, 
-			 pXAct->u.advise.wFmt);
+	    WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, 
+			 pXAct->wType, pXAct->hszItem, pXAct->wFmt);
 	}
     }
     else
     {
 	TRACE("Returning TRUE on XTYP_ADVSTART - fAck was FALSE\n");
-	GlobalFree(pXAct->u.advise.hDdeAdvise);
+	GlobalFree(pXAct->hMem);
     }
+
     pXAct->hDdeData = (HDDEDATA)1;
     return WDML_QS_HANDLED;
 }
 
 /******************************************************************
- *		WDML_QueueUnadvise
+ *		WDML_ClientQueueUnadvise
  *
  * queues an unadvise transaction
  */
-static WDML_XACT*	WDML_QueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
+static WDML_XACT*	WDML_ClientQueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
 {
     WDML_XACT*	pXAct;
+    ATOM	atom;
     
     TRACE("XTYP_ADVSTOP transaction\n");
 
-    pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_UNADVISE);
+    atom = WDML_MakeAtomFromHsz(hszItem);
+    if (!atom) return NULL;
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem);
     if (!pXAct)
-	return NULL;
-
-    pXAct->u.unadvise.wFmt = wFmt;
-    pXAct->u.unadvise.hszItem = hszItem;
-
-    WDML_QueueTransaction(pConv, pXAct);
-
-   /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
-       on the specified item. */
-	    
-    if (!PostMessageA(pConv->hwndServer, WM_DDE_UNADVISE, (WPARAM)pConv->hwndClient,
-		      PackDDElParam(WM_DDE_UNADVISE, wFmt, (UINT)hszItem)))
     {
-	WDML_UnQueueTransaction(pConv, pXAct);
-	WDML_FreeTransaction(pXAct);
+	GlobalDeleteAtom(atom);
 	return NULL;
     }
+
+    /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
+     * on the specified item. 
+     */
+    pXAct->lParam = PackDDElParam(WM_DDE_UNADVISE, wFmt, atom);
     return pXAct;
 }
     
@@ -330,7 +425,7 @@
 {
     DDEACK	ddeAck;
     UINT	uiLo, uiHi;
-    WORD	wStatus;
+    HSZ		hsz;
 
     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
     {
@@ -338,14 +433,15 @@
     }
 
     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
+    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
 
-    if (DdeCmpStringHandles(uiHi, pXAct->u.unadvise.hszItem) != 0)
+    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
 	return WDML_QS_PASS;
 
+    FreeDDElParam(WM_DDE_ACK, msg->lParam);
     GlobalDeleteAtom(uiHi);
-		    
-    wStatus = uiLo;
-    ddeAck = *((DDEACK*)&wStatus);
+
+    WDML_ExtractAck(uiLo, &ddeAck);
 		    
     TRACE("WM_DDE_ACK received while waiting for a timeout\n");
 	    
@@ -356,43 +452,37 @@
     else
     {
 	/* billx: remove the link */
-	WDML_RemoveLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, 
-			pXAct->u.unadvise.hszItem, pXAct->u.unadvise.wFmt);
+	WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, 
+			pXAct->hszItem, pXAct->wFmt);
     }
     pXAct->hDdeData = (HDDEDATA)1;
     return WDML_QS_HANDLED;
 }
 
 /******************************************************************
- *		WDML_QueueRequest
+ *		WDML_ClientQueueRequest
  *
  *
  */
-static WDML_XACT*	WDML_QueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
+static WDML_XACT*	WDML_ClientQueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
 {
     WDML_XACT*	pXAct;
-    
+    ATOM	atom;
+
     TRACE("XTYP_REQUEST transaction\n");
 
-    pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_REQUEST);
+    atom = WDML_MakeAtomFromHsz(hszItem);
+    if (!atom) return NULL;
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, wFmt, hszItem);
     if (!pXAct)
-	return NULL;
-
-    pXAct->u.request.hszItem = hszItem;
-
-    WDML_QueueTransaction(pConv, pXAct);
-
-   /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
-    * on the specified item. 
-    */
-	    
-    if (!PostMessageA(pConv->hwndServer, WM_DDE_REQUEST, (WPARAM)pConv->hwndClient,
-		      PackDDElParam(WM_DDE_REQUEST, wFmt, (UINT)hszItem)))
     {
-	WDML_UnQueueTransaction(pConv, pXAct);
-	WDML_FreeTransaction(pXAct);
+	GlobalDeleteAtom(atom);
 	return NULL;
     }
+
+    pXAct->lParam = PackDDElParam(WM_DDE_REQUEST, wFmt, atom);
+
     return pXAct;
 }
 
@@ -403,33 +493,49 @@
  */
 static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
 {
-    DDEACK	ddeAck;
-    UINT	uiLo, uiHi;
-    WORD	wStatus;
+    DDEACK		ddeAck;
+    WINE_DDEHEAD	wdh;
+    UINT		uiLo, uiHi;
+    HSZ			hsz;
+
+    if (msg->wParam != pConv->hwndServer)
+	return WDML_QS_PASS;
+    UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
 
     switch (msg->message)
     {
     case WM_DDE_ACK:
-	if (msg->wParam != pConv->hwndServer)
-	    return WDML_QS_PASS;
-	UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
-	wStatus = uiLo;
-	ddeAck = *((DDEACK*)&wStatus);
+	GlobalDeleteAtom(uiHi);
+	WDML_ExtractAck(uiLo, &ddeAck);
 	pXAct->hDdeData = 0;
+	if (ddeAck.fAck)
+	    ERR("Positive answer should appear in NACK for a request, assuming negative\n");
 	TRACE("Negative answer...\n");
-		
-	/* FIXME: billx: we should return 0 and post a negatve WM_DDE_ACK. */
 	break;
 
     case WM_DDE_DATA:
-	if (msg->wParam != pConv->hwndServer)
-	    return WDML_QS_PASS;
-	UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
 	TRACE("Got the result (%08lx)\n", (DWORD)uiLo);
-	if (DdeCmpStringHandles(uiHi, pXAct->u.request.hszItem) != 0)
+
+	hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
+
+	if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
 	    return WDML_QS_PASS;
+
 	/* FIXME: memory clean up ? */
-	pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo);
+	pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
+	if (wdh.fRelease)
+	{
+	    GlobalFree((HGLOBAL)uiLo);
+	}
+	if (wdh.fAckReq)
+	{
+	    WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, (HSZ)uiHi, msg->lParam, 
+			 WM_DDE_DATA);
+	}
+	else
+	{
+	    GlobalDeleteAtom(uiHi);
+	}
 	break;
 
     default:
@@ -440,76 +546,105 @@
 }	
 
 /******************************************************************
- *		WDML_QueueExecute
+ *		WDML_BuildExecuteCommand
+ *
+ * Creates a DDE block suitable for sending in WM_DDE_COMMAND
+ * It also takes care of string conversion between the two window procedures
+ */
+static	HGLOBAL	WDML_BuildExecuteCommand(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
+{
+    HGLOBAL	hMem;
+    BOOL	clientUnicode, serverUnicode;
+    DWORD	memSize;
+
+    clientUnicode = IsWindowUnicode(pConv->hwndClient);
+    serverUnicode = IsWindowUnicode(pConv->hwndServer);
+
+    if (clientUnicode == serverUnicode)
+    {
+	memSize = cbData;
+    }
+    else
+    {
+	if (clientUnicode)
+	{
+	    memSize = WideCharToMultiByte( CP_ACP, 0, pData, cbData, NULL, 0, NULL, NULL);
+	}
+	else
+	{
+	    memSize = MultiByteToWideChar( CP_ACP, 0, pData, cbData, NULL, 0);
+	}
+    }
+
+    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, memSize);
+
+    if (hMem)
+    {
+	LPBYTE	pDst;
+	    
+	pDst = GlobalLock(hMem);
+	if (pDst)
+	{
+	    if (clientUnicode == serverUnicode)
+	    {
+		memcpy(pDst, pData, cbData);
+	    }
+	    else
+	    {
+		if (clientUnicode)
+		{
+		    WideCharToMultiByte( CP_ACP, 0, pData, cbData, pDst, memSize, NULL, NULL);
+		}
+		else
+		{
+		    MultiByteToWideChar( CP_ACP, 0, pData, cbData, (LPWSTR)pDst, memSize);
+		}
+	    }
+
+	    GlobalUnlock(hMem);
+	}
+	else
+	{
+	    GlobalFree(hMem);
+	    hMem = 0;
+	}
+    }
+    return hMem;
+}
+
+/******************************************************************
+ *		WDML_ClientQueueExecute
  *
  *
  */
-static WDML_XACT*	WDML_QueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
+static WDML_XACT*	WDML_ClientQueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
 {
     WDML_XACT*	pXAct;
 
     TRACE("XTYP_EXECUTE transaction\n");
 	
-    pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_EXECUTE);
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
     if (!pXAct)
 	return NULL;
 
     if (cbData == (DWORD)-1)
     {
-	HDDEDATA		hDdeData = (HDDEDATA)pData;
-	DWORD			dwSize;
+	HDDEDATA	hDdeData = (HDDEDATA)pData;
     
-	pData = DdeAccessData(hDdeData, &dwSize);
+	pData = DdeAccessData(hDdeData, &cbData);
 	if (pData)
 	{
-	    pXAct->u.execute.hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize);
-	    if (pXAct->u.execute.hMem)
-	    {
-		LPBYTE	pDst;
-	    
-		pDst = GlobalLock(pXAct->u.execute.hMem);
-		if (pDst)
-		{
-		    memcpy(pDst, pData, dwSize);
-		    GlobalUnlock(pXAct->u.execute.hMem);
-		}
-		else
-		{
-		    GlobalFree(pXAct->u.execute.hMem);
-		    pXAct->u.execute.hMem = 0;
-		}
-	    }
+	    pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
 	    DdeUnaccessData(hDdeData);
 	}
-	else
-	{
-	    pXAct->u.execute.hMem = 0;
-	}
     }
     else
     {
-	LPSTR	ptr;
-
-	pXAct->u.execute.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, cbData);
-	ptr = GlobalLock(pXAct->u.execute.hMem);
-	if (ptr) 
-	{
-	    memcpy(ptr, pData, cbData);
-	    GlobalUnlock(pXAct->u.execute.hMem);
-	}
+	pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
     }
 
-    WDML_QueueTransaction(pConv, pXAct);
-	
-    if (!PostMessageA(pConv->hwndServer, WM_DDE_EXECUTE, (WPARAM)pConv->hwndClient, 
-		      pXAct->u.execute.hMem))
-    {
-	GlobalFree(pXAct->u.execute.hMem);
-	WDML_UnQueueTransaction(pConv, pXAct);
-	WDML_FreeTransaction(pXAct);
-	TRACE("Returning FALSE on XTYP_EXECUTE - PostMessage returned FALSE\n");
-	return NULL;
-    }
+    pXAct->lParam = pXAct->hMem;
+
     return pXAct;
 }
 
@@ -522,7 +657,6 @@
 {
     DDEACK	ddeAck;
     UINT	uiLo, uiHi;
-    WORD	wStatus;
 
     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
     {
@@ -532,69 +666,64 @@
     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
     FreeDDElParam(WM_DDE_ACK, msg->lParam);
 
-    if (uiHi != pXAct->u.execute.hMem)
+    if (uiHi != pXAct->hMem)
     {
 	return WDML_QS_PASS;
     }
 
-    wStatus = uiLo;
-    ddeAck = *((DDEACK*)&wStatus);
+    WDML_ExtractAck(uiLo, &ddeAck);
     if (!ddeAck.fAck)
     {
-	GlobalFree(pXAct->u.execute.hMem);
+	GlobalFree(pXAct->hMem);
     }
     pXAct->hDdeData = (HDDEDATA)1;
     return WDML_QS_HANDLED;
 }
 
 /******************************************************************
- *		WDML_QueuePoke
+ *		WDML_ClientQueuePoke
  *
  *
  */
-static WDML_XACT*	WDML_QueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData, 
-				       UINT wFmt, HSZ hszItem)
+static WDML_XACT*	WDML_ClientQueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData, 
+					     UINT wFmt, HSZ hszItem)
 {
     WDML_XACT*	pXAct;
+    ATOM	atom;
 
     TRACE("XTYP_POKE transaction\n");
 	
-    pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_POKE);
+    atom = WDML_MakeAtomFromHsz(hszItem);
+    if (!atom) return NULL;
+     
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
     if (!pXAct)
+    {
+	GlobalDeleteAtom(atom);
 	return NULL;
+    }
 
     if (cbData == (DWORD)-1)
     {
-	pXAct->u.poke.hMem = (HDDEDATA)pData;
+	pXAct->hMem = (HDDEDATA)pData;
     }
     else
     {
 	DDEPOKE*	ddePoke;
 
-	pXAct->u.poke.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
-	ddePoke = GlobalLock(pXAct->u.poke.hMem);
+	pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
+	ddePoke = GlobalLock(pXAct->hMem);
 	if (ddePoke) 
 	{
 	    memcpy(ddePoke->Value, pData, cbData);
 	    ddePoke->fRelease = FALSE; /* FIXME: app owned ? */
 	    ddePoke->cfFormat = wFmt;
-	    GlobalUnlock(pXAct->u.poke.hMem);
+	    GlobalUnlock(pXAct->hMem);
 	}
     }
 
-    pXAct->u.poke.hszItem = hszItem;
+    pXAct->lParam = PackDDElParam(WM_DDE_POKE, pXAct->hMem, atom);
 
-    WDML_QueueTransaction(pConv, pXAct);
-	
-    if (!PostMessageA(pConv->hwndServer, WM_DDE_POKE, (WPARAM)pConv->hwndClient, 
-		      PackDDElParam(WM_DDE_POKE, pXAct->u.execute.hMem, hszItem)))
-    {
-	GlobalFree(pXAct->u.execute.hMem);
-	WDML_UnQueueTransaction(pConv, pXAct);
-	WDML_FreeTransaction(pXAct);
-	TRACE("Returning FALSE on XTYP_POKE - PostMessage returned FALSE\n");
-	return NULL;
-    }
     return pXAct;
 }
 
@@ -607,7 +736,7 @@
 {
     DDEACK	ddeAck;
     UINT	uiLo, uiHi;
-    WORD	wStatus;
+    HSZ		hsz;
 
     if (msg->message != WM_DDE_ACK && msg->wParam != pConv->hwndServer)
     {
@@ -615,117 +744,124 @@
     }
 
     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
-    if (uiHi != pXAct->u.poke.hszItem)
+    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
+    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
     {
 	return WDML_QS_PASS;
     }
     FreeDDElParam(WM_DDE_ACK, msg->lParam);
+    GlobalDeleteAtom(uiHi);
 
-    wStatus = uiLo;
-    ddeAck = *((DDEACK*)&wStatus);
-    if (!ddeAck.fAck)
-    {
-	GlobalFree(pXAct->u.poke.hMem);
-    }
+    WDML_ExtractAck(uiLo, &ddeAck);
+    GlobalFree(pXAct->hMem);
+
     pXAct->hDdeData = (HDDEDATA)TRUE;
     return TRUE;
 }
 
 /******************************************************************
+ *		WDML_ClientQueueTerminate
+ *
+ * Creates and queue an WM_DDE_TERMINATE transaction
+ */
+static WDML_XACT*	WDML_ClientQueueTerminate(WDML_CONV* pConv)
+{
+    WDML_XACT*		pXAct;
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
+    if (!pXAct)
+	return NULL;
+
+    pXAct->lParam = 0;
+    pConv->wStatus &= ~ST_CONNECTED;
+
+    return pXAct;
+}
+
+/******************************************************************
+ *		WDML_HandleTerminateReply
+ *
+ * handles the reply to a terminate request
+ */
+static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
+{
+    if (msg->message != WM_DDE_TERMINATE)
+    {
+	/* FIXME: should delete data passed here */
+	return WDML_QS_SWALLOWED;
+    }
+
+    if (msg->wParam != pConv->hwndServer)
+    {
+	FIXME("hmmm shouldn't happen\n");
+	return WDML_QS_PASS;
+    }
+    if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
+    {
+	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 
+			    0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
+    }
+    WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
+    return WDML_QS_HANDLED;
+}
+
+/******************************************************************
  *		WDML_HandleReplyData
  *
  *
  */
-static WDML_QUEUE_STATE WDML_HandleReplyData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
+static WDML_QUEUE_STATE WDML_HandleIncomingData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
 {
-    UINT	uiLo, uiHi;
-    HDDEDATA	hDdeDataIn, hDdeDataOut;
-    WDML_LINK*	pLink;
+    UINT		uiLo, uiHi;
+    HDDEDATA		hDdeDataIn, hDdeDataOut;
+    WDML_LINK*		pLink;
+    WINE_DDEHEAD	wdh;
+    HSZ			hsz;
 
     TRACE("WM_DDE_DATA message received in the Client Proc!\n");
     /* wParam -- sending window handle	*/
     /* lParam -- hDdeData & item HSZ	*/
 	
     UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
-	
-    hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo);
+    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
+
+    hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
 
     /* billx: 
      *  For hot link, data should be passed to its callback with
      * XTYP_ADVDATA and callback should return the proper status.
      */
-	
-    for (pLink = pConv->thisInstance->links[WDML_CLIENT_SIDE]; pLink != NULL; pLink = pLink->next)
+    pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz, wdh.cfFormat);
+    if (!pLink)	
     {
-	if (DdeCmpStringHandles((HSZ)uiHi, pLink->hszItem) == 0)
-	{
-	    BOOL	fRelease = FALSE;
-	    BOOL	fAckReq = FALSE;
-	    DDEDATA*	pDdeData;
-
-	    /* item in the advise loop */
-	    pConv = WDML_GetConv(pLink->hConv);
-	    if (pConv == NULL)
-	    {
-		continue;
-	    }
-	    if ((pDdeData = GlobalLock(uiLo)) != NULL)
-	    {
-		fRelease = pDdeData->fRelease;
-		fAckReq = pDdeData->fAckReq;
-	    }
-
-	    if (hDdeDataIn != 0)
-	    {
-		if (fAckReq)
-		{
-		    DDEACK	ddeAck;
-		    
-		    ddeAck.bAppReturnCode = 0;
-		    ddeAck.reserved       = 0;
-		    ddeAck.fBusy          = FALSE;
-		    ddeAck.fAck           = TRUE;
-		    
-		    if (msg->lParam) {
-			PostMessageA(pConv->hwndServer, WM_DDE_ACK, pConv->hwndClient, 
-				     ReuseDDElParam(msg->lParam, WM_DDE_DATA, WM_DDE_ACK, 
-						    *(WORD*)&ddeAck, (UINT)pLink->hszItem));
-			msg->lParam = 0L;
-		    }
-		    else 
-		    {
-			PostMessageA(pConv->hwndServer, WM_DDE_ACK, pConv->hwndClient, 
-				     PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, (UINT)pLink->hszItem));
-		    }	
-		}
-	    }
-	    hDdeDataOut = 0;
-	    if (pConv->thisInstance->callback != NULL /*&& thisInstance->processID == GetCurrentProcessId() */)
-	    {
-		TRACE("Calling the callback, type = XTYP_ADVDATA, CB = 0x%lx, hConv = 0x%lx\n", 
-		      (DWORD)pConv->thisInstance->callback, (DWORD)pLink->hConv);
-		hDdeDataOut = (pConv->thisInstance->callback)(XTYP_ADVDATA,
-							      pLink->uFmt,
-							      pLink->hConv,
-							      pConv->hszTopic,
-							      pLink->hszItem,
-							      hDdeDataIn, 
-							      0, 0);
-		if (hDdeDataOut == (HDDEDATA)DDE_FACK)
-		{
-		    pLink->hDdeData = hDdeDataIn;
-		}
-	    }
-#if 0
-	    if (fRelease) 
-	    {
-		DdeFreeDataHandle(hDdeDataIn);
-	    }
-#endif
-	    break;
-	}
+	WDML_DecHSZ(pConv->instance, hsz);
+	return WDML_QS_PASS;
+    }
+	
+    if (hDdeDataIn != 0 && wdh.fAckReq)
+    {
+	WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, 
+		     msg->lParam, WM_DDE_DATA);
+	if (msg->lParam)
+	    msg->lParam = 0;
+    }
+    else
+    {
+	GlobalDeleteAtom(uiHi);
+    }
+	
+    hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_ADVDATA, pLink->uFmt, pLink->hConv, 
+				      pConv->hszTopic, pLink->hszItem, hDdeDataIn, 0, 0);
+    if (hDdeDataOut == (HDDEDATA)DDE_FACK)
+    {
+	pLink->hDdeData = hDdeDataIn;
+    }
+    if (wdh.fRelease) 
+    {
+	DdeFreeDataHandle(hDdeDataIn);
     }
     
+    WDML_DecHSZ(pConv->instance, hsz);
     if (msg->lParam)
 	FreeDDElParam(WM_DDE_DATA, msg->lParam);
 	
@@ -733,19 +869,28 @@
 }
 
 /******************************************************************
- *		WDML_HandleReplyTerminate
+ *		WDML_HandleIncomingTerminate
  *
  *
  */
-static WDML_QUEUE_STATE WDML_HandleReplyTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
+static WDML_QUEUE_STATE WDML_HandleIncomingTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
 {
-    if ((LPARAM)pConv != msg->lParam)
+    if (pConv->hwndServer != (HWND)msg->wParam)
 	return WDML_QS_PASS;
-
-    /* billx: clean up the conv and associated links */
-    WDML_RemoveAllLinks(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE);
-    WDML_RemoveConv(pConv->thisInstance, WDML_CLIENT_SIDE, (HCONV)pConv);
-    DestroyWindow(msg->hwnd);
+    
+    pConv->wStatus |= ST_TERMINATED;
+    if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
+    {
+	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 
+			    0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
+    }
+    if (pConv->wStatus & ST_CONNECTED)
+    {
+	/* don't care about result code (if server exists or not) */
+	PostMessageA(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
+	pConv->wStatus &= ~ST_CONNECTED;
+    }
+    /* have to keep connection around to allow reconnection */
     return WDML_QS_HANDLED;
 }
 
@@ -756,7 +901,7 @@
  */
 static WDML_QUEUE_STATE	WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
 {
-    WDML_XACT*	pXAct = pConv->transactions;
+    WDML_XACT*		pXAct = pConv->transactions;
     WDML_QUEUE_STATE	qs;
 
     if (pConv->transactions) 
@@ -779,6 +924,9 @@
 	case WM_DDE_POKE:
 	    qs = WDML_HandlePokeReply(pConv, msg, pXAct);
 	    break;
+	case WM_DDE_TERMINATE:
+	    qs = WDML_HandleTerminateReply(pConv, msg, pXAct);
+	    break;
 	default:
 	    qs = WDML_QS_ERROR;
 	    FIXME("oooch\n");
@@ -793,6 +941,7 @@
     switch (qs) 
     {
     case WDML_QS_ERROR:
+    case WDML_QS_SWALLOWED:
 	*hdd = 0;
 	break;
     case WDML_QS_HANDLED:
@@ -800,39 +949,34 @@
 	 * notify callback if asynchronous, and remove it in any case
 	 */
 	WDML_UnQueueTransaction(pConv, pXAct);
-	if (pXAct->dwTimeout == TIMEOUT_ASYNC)
+	if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
 	{
-	    if (pConv->thisInstance->callback != NULL /*&& thisInstance->processID == GetCurrentProcessId() */)
-	    {
-		TRACE("Calling the callback, type = XTYP_XACT_COMPLETE, CB = 0x%lx, hConv = 0x%lx\n",
-		      (DWORD)pConv->thisInstance->callback, (DWORD)pConv);
-		(pConv->thisInstance->callback)(XTYP_XACT_COMPLETE, 0 /* FIXME */,
-						(HCONV)pConv,
-						pConv->hszTopic, 0 /* FIXME */,
-						pXAct->hDdeData, 
-						MAKELONG(0, pXAct->xActID), 
-						0 /* FIXME */);
-		qs = WDML_QS_PASS;
-	    }
+	    WDML_InvokeCallback(pConv->instance, XTYP_XACT_COMPLETE, pXAct->wFmt,
+				(HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
+				pXAct->hDdeData, MAKELONG(0, pXAct->xActID), 0 /* FIXME */);
+	    qs = WDML_QS_PASS;
 	}	
 	else
 	{
 	    *hdd = pXAct->hDdeData;
 	}
-	WDML_FreeTransaction(pXAct);
+	WDML_FreeTransaction(pConv->instance, pXAct, FALSE); /* FIXME: should we free intermediate pmts ? */
 	break;
     case WDML_QS_PASS:
 	/* no pending transaction found, try a warm link or a termination request */
 	switch (msg->message)
 	{
 	case WM_DDE_DATA:
-	    qs = WDML_HandleReplyData(pConv, msg, hdd);
+	    qs = WDML_HandleIncomingData(pConv, msg, hdd);
 	    break;
 	case WM_DDE_TERMINATE:
-	    qs = WDML_HandleReplyTerminate(pConv, msg, hdd);
+	    qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
 	    break;
 	}
 	break;
+    case WDML_QS_BLOCK:
+	FIXME("shouldn't be used on client side\n");
+	break;
     }
 
     return qs;
@@ -846,7 +990,9 @@
  */
 static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct)
 {
-    DWORD		dwTime;
+    DWORD	dwTime;
+    DWORD	err;
+    WDML_CONV*	pConv;
 
     TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout);
 
@@ -855,21 +1001,23 @@
 	    
     while ((dwTime = GetCurrentTime()) < dwTimeout)
     {
-	/* we cannot hold the mutex all the time because when client and server run in a
+	/* we cannot be in the crit sect all the time because when client and server run in a
 	 * single process they need to share the access to the internal data
 	 */
 	if (MsgWaitForMultipleObjects(0, NULL, FALSE, 
-				      dwTime - dwTimeout, QS_POSTMESSAGE) == WAIT_OBJECT_0 &&
-	    WDML_WaitForMutex(handle_mutex))
+				      dwTime - dwTimeout, QS_POSTMESSAGE) == WAIT_OBJECT_0)
 	{
 	    BOOL	ret = FALSE;
 	    MSG		msg;
 	    WDML_CONV*	pConv;
 	    HDDEDATA	hdd;
 	    
-	    pConv = WDML_GetConv(hConv);
+	    EnterCriticalSection(&WDML_CritSect);
+
+	    pConv = WDML_GetConv(hConv, FALSE);
 	    if (pConv == NULL)
 	    {
+		LeaveCriticalSection(&WDML_CritSect);
 		/* conversation no longer available... return failure */
 		break;
 	    }
@@ -881,7 +1029,7 @@
 		    (pConv->transactions == NULL || ret);
 		if (ret) break;
 	    }
-	    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
+	    LeaveCriticalSection(&WDML_CritSect);
 	    if (ret)
 	    {
 		return hdd;
@@ -890,29 +1038,29 @@
     }
 
     TRACE("Timeout !!\n");
-    if (WDML_WaitForMutex(handle_mutex))
+
+    EnterCriticalSection(&WDML_CritSect);
+
+    pConv = WDML_GetConv(hConv, FALSE);
+    if (pConv != NULL)
     {
-	DWORD		err;
-	WDML_CONV*	pConv;
+	if (pConv->transactions)
+	{
+	    switch (pConv->transactions->ddeMsg)
+	    {
+	    case WM_DDE_ADVISE:		err = DMLERR_ADVACKTIMEOUT;	break;
+	    case WM_DDE_REQUEST:	err = DMLERR_DATAACKTIMEOUT; 	break;
+	    case WM_DDE_EXECUTE:	err = DMLERR_EXECACKTIMEOUT;	break;
+	    case WM_DDE_POKE:		err = DMLERR_POKEACKTIMEOUT;	break;
+	    case WM_DDE_UNADVISE:	err = DMLERR_UNADVACKTIMEOUT;	break;
+	    default:			err = DMLERR_INVALIDPARAMETER;	break;
+	    }
 
-	pConv = WDML_GetConv(hConv);
-	if (pConv == NULL)
-	{
-	    return 0;
-	} 
-	switch (pConv->transactions->ddeMsg)
-	{
-	case WM_DDE_ADVISE:	err = DMLERR_ADVACKTIMEOUT;	break;
-	case WM_DDE_REQUEST:	err = DMLERR_DATAACKTIMEOUT; 	break;
-	case WM_DDE_EXECUTE:	err = DMLERR_EXECACKTIMEOUT;	break;
-	case WM_DDE_POKE:	err = DMLERR_POKEACKTIMEOUT;	break;
-	case WM_DDE_UNADVISE:	err = DMLERR_UNADVACKTIMEOUT;	break;
-	default:		err = DMLERR_INVALIDPARAMETER;	break;
+	    pConv->instance->lastError = err;
 	}
-
-	pConv->thisInstance->lastError = err;
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
     }
+    LeaveCriticalSection(&WDML_CritSect);
+
     return 0;
 }
 
@@ -926,9 +1074,8 @@
     WDML_XACT*		pXAct;
     HDDEDATA		hDdeData = 0;
     
-    TRACE("(0x%lx,%ld,0x%lx,0x%lx,%d,%d,%ld,0x%lx)\n",
-	  (ULONG)pData,cbData,(DWORD)hConv,(DWORD)hszItem,wFmt,wType,
-	  dwTimeout,(ULONG)pdwResult);
+    TRACE("(%p,%ld,0x%lx,0x%x,%d,%d,%ld,%p)\n",
+	  pData, cbData, (DWORD)hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
     
     if (hConv == 0)
     {
@@ -936,12 +1083,9 @@
 	return 0;
     }
     
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return FALSE;
-    }
+    EnterCriticalSection(&WDML_CritSect);
 
-    pConv = WDML_GetConv(hConv);
+    pConv = WDML_GetConv(hConv, TRUE);
     if (pConv == NULL)
     {
 	/* cannot set error... cannot get back to DDE instance */
@@ -953,13 +1097,13 @@
     case XTYP_EXECUTE:
 	if (hszItem != 0 || wFmt != 0)
 	{
-	    pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
+	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
 	    goto theError;
 	}
-	pXAct = WDML_QueueExecute(pConv, pData, cbData);
+	pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
 	break;
     case XTYP_POKE:
-	pXAct = WDML_QueuePoke(pConv, pData, cbData, wFmt, hszItem);
+	pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
 	break;
     case XTYP_ADVSTART|XTYPF_NODATA:
     case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
@@ -967,34 +1111,52 @@
     case XTYP_ADVSTART|XTYPF_ACKREQ:
 	if (pData)
 	{
-	    pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
+	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
 	    goto theError;
 	}
-	pXAct = WDML_QueueAdvise(pConv, wType, wFmt, hszItem);
+	pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
 	break;
     case XTYP_ADVSTOP:
 	if (pData)
 	{
-	    pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
+	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
 	    goto theError;
 	}
-	pXAct = WDML_QueueUnadvise(pConv, wFmt, hszItem);
+	pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
 	break;
     case XTYP_REQUEST:
 	if (pData)
 	{
-	    pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
+	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
 	    goto theError;
 	}
-	pXAct = WDML_QueueRequest(pConv, wFmt, hszItem);
+	pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
 	break;
     default:
 	FIXME("Unknown transation\n");
 	/* unknown transaction type */
-	pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
+	pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
 	goto theError;
     }
 
+    if (pXAct == NULL)
+    {
+	pConv->instance->lastError = DMLERR_MEMORY_ERROR;
+	goto theError;
+    }
+
+    WDML_QueueTransaction(pConv, pXAct);
+
+    if (!PostMessageA(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam))
+    {
+	TRACE("Failed posting message %d to 0x%04x (error=0x%lx)\n", 
+	      pXAct->ddeMsg, pConv->hwndServer, GetLastError());
+	pConv->wStatus &= ~ST_CONNECTED;
+	WDML_UnQueueTransaction(pConv, pXAct);
+	WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
+	goto theError;
+
+    }
     pXAct->dwTimeout = dwTimeout;
     /* FIXME: should set the app bits on *pdwResult */
     
@@ -1005,27 +1167,27 @@
 	    *pdwResult = MAKELONG(0, pXAct->xActID);
 	}
 	hDdeData = (HDDEDATA)1;
-    } 
-
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-
-    if (dwTimeout != TIMEOUT_ASYNC)
+    }
+    else
     {
-	DWORD	count = 0;
+	DWORD	count, i;
 
 	if (pdwResult)
 	{
 	    *pdwResult = 0L;
 	}
-	while (ReleaseMutex(handle_mutex))
-	    count++;
+	count = WDML_CritSect.RecursionCount;
+	for (i = 0; i < count; i++)
+	    LeaveCriticalSection(&WDML_CritSect);
 	hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct);
-	while (count-- != 0)
-	    WDML_WaitForMutex(handle_mutex);
+	for (i = 0; i < count; i++)
+	    EnterCriticalSection(&WDML_CritSect);
     }
+    LeaveCriticalSection(&WDML_CritSect);
+
     return hDdeData;
  theError:
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
+    LeaveCriticalSection(&WDML_CritSect);
     return 0;
 }
 
@@ -1036,29 +1198,65 @@
  */
 static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
 {
-    UINT		uiLow, uiHi;
-    
+    UINT	uiLo, uiHi;
+    WDML_CONV*	pConv = NULL;
+    HSZ		hszSrv, hszTpc;
+
     if (iMsg == WM_DDE_ACK &&
-	/* In response to WM_DDE_INITIATE, save server window  */
-	UnpackDDElParam(WM_DDE_ACK, lParam, &uiLow, &uiHi) &&
-	(WDML_CONV*)GetWindowLongA(hwnd, 4) == NULL)
+	UnpackDDElParam(WM_DDE_ACK, lParam, &uiLo, &uiHi) &&
+	/* in the initial WM_INITIATE sendmessage */
+	((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
     {
-	WDML_INSTANCE*	thisInstance = NULL;
-	WDML_CONV*	pConv = NULL;
+	/* In response to WM_DDE_INITIATE, save server window  */
+	char		buf[256];
+	WDML_INSTANCE*	pInstance;
 
 	FreeDDElParam(WM_DDE_ACK, lParam);
-	/* no converstation yet, add it */
-	thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwnd, 0);
-	pConv = WDML_AddConv(thisInstance, WDML_CLIENT_SIDE, (HSZ)uiLow, (HSZ)uiHi, 
+
+	/* FIXME: convlist should be handled here */
+	if (pConv)
+	{
+	    /* we already have started the conv with a server, drop other replies */
+	    GlobalDeleteAtom(uiLo);
+	    GlobalDeleteAtom(uiHi);
+	    return 0;
+	}
+
+	pInstance = WDML_GetInstanceFromWnd(hwnd);
+
+	hszSrv = WDML_MakeHszFromAtom(pInstance, uiLo);
+	hszTpc = WDML_MakeHszFromAtom(pInstance, uiHi);
+
+	pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, 
 			     hwnd, (HWND)wParam);
-	SetWindowLongA(hwnd, 4, (DWORD)pConv);
-	/* FIXME: so far we only use the first window in the list... */
-	return 0;
+
+	SetWindowLongA(hwnd, GWL_WDML_CONVERSATION, (DWORD)pConv);
+	pConv->wStatus |= ST_CONNECTED;
+	pConv->wConvst = XST_INIT1;
+
+	/* check if server is handled by DDEML */
+	if ((GetClassNameA((HWND)wParam, buf, sizeof(buf)) && 
+	     strcmp(buf, WDML_szServerConvClassA) == 0) ||
+	    (GetClassNameW((HWND)wParam, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) && 
+	     lstrcmpW((LPWSTR)buf, WDML_szServerConvClassW) == 0))
+	{
+	    pConv->wStatus |= ST_ISLOCAL;
+	}
+
+	WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_CONNECT_CONFIRM, hwnd, (HWND)wParam);
+
+	GlobalDeleteAtom(uiLo);
+	GlobalDeleteAtom(uiHi);
+
+	/* accept conversation */
+	return 1;
     }
-	
-    if ((iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST) && WDML_WaitForMutex(handle_mutex))
+
+    if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
     {
-	WDML_CONV*	pConv = (WDML_CONV*)GetWindowLongA(hwnd, 4);
+	EnterCriticalSection(&WDML_CritSect);
+
+	pConv = WDML_GetConvFromWnd(hwnd);
 
 	if (pConv) 
 	{
@@ -1073,14 +1271,14 @@
 	    WDML_HandleReply(pConv, &msg, &hdd);
 	}
 
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
+	LeaveCriticalSection(&WDML_CritSect);
 	return 0;
     }
-	
-    return DefWindowProcA(hwnd, iMsg, wParam, lParam);
+    
+    return (IsWindowUnicode(hwnd)) ? 
+	DefWindowProcA(hwnd, iMsg, wParam, lParam) : DefWindowProcW(hwnd, iMsg, wParam, lParam);
 }
 
-
 /*****************************************************************
  *            DdeAbandonTransaction (USER32.@)
  */
@@ -1090,6 +1288,57 @@
     return TRUE;
 }
 
+/*****************************************************************
+ *            DdeDisconnect   (USER32.@)
+ */
+BOOL WINAPI DdeDisconnect(HCONV hConv)
+{
+    WDML_CONV*	pConv = NULL;
+    WDML_XACT*	pXAct;
+    DWORD	count, i;
+    BOOL	ret = FALSE;
+
+    TRACE("(%ld)\n", (DWORD)hConv);
+    
+    if (hConv == 0)
+    {
+	ERR("DdeDisconnect(): hConv = 0\n");
+	return FALSE;
+    }
+    
+    EnterCriticalSection(&WDML_CritSect);
+    pConv = WDML_GetConv(hConv, FALSE);
+    if (pConv != NULL)
+    {
+	if (pConv->wStatus & ST_CONNECTED)
+	{
+	    if (pConv->wStatus & ST_CLIENT)
+	    {
+		/* FIXME: should abandon all pending transactions */
+		pXAct = WDML_ClientQueueTerminate(pConv);
+		if (pXAct != NULL)
+		{
+		    count = WDML_CritSect.RecursionCount;
+		    for (i = 0; i < count; i++)
+			LeaveCriticalSection(&WDML_CritSect);
+		    WDML_SyncWaitTransactionReply(hConv, 10000, pXAct);
+		    for (i = 0; i < count; i++)
+			EnterCriticalSection(&WDML_CritSect);
+		    ret = TRUE;
+		}
+		else
+		{
+		    FIXME("Not implemented yet for a server side conversation\n");
+		}
+	    }
+	}
+	/* still have to destroy data assosiated with conversation */
+	WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
+    }
+    LeaveCriticalSection(&WDML_CritSect);
+
+    return ret;
+}
 
 /*****************************************************************
  *            DdeImpersonateClient (USER32.@)
@@ -1099,15 +1348,12 @@
     WDML_CONV*	pConv;
     BOOL	ret = FALSE;
     
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return FALSE;
-    }
-    pConv = WDML_GetConv(hConv);
+    EnterCriticalSection(&WDML_CritSect);
+    pConv = WDML_GetConv(hConv, TRUE);
     if (pConv)
     {
 	ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
     }
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
+    LeaveCriticalSection(&WDML_CritSect);
     return ret;
 }
diff --git a/dlls/user/dde/dde_private.h b/dlls/user/dde/dde_private.h
index 65b0639..aa5c682 100644
--- a/dlls/user/dde/dde_private.h
+++ b/dlls/user/dde/dde_private.h
@@ -15,27 +15,17 @@
 
 /* defined in atom.c file.
  */
-
 #define MAX_ATOM_LEN              255
 
 /* Maximum buffer size ( including the '\0' ).
  */
 #define MAX_BUFFER_LEN            (MAX_ATOM_LEN + 1)
 
-/* This is a simple list to keep track of the strings created
- * by DdeCreateStringHandle.  The list is used to free
- * the strings whenever DdeUninitialize is called.
- * This mechanism is not complete and does not handle multiple instances.
- * Most of the DDE API use a DWORD parameter indicating which instance
- * of a given program is calling them.  The API are supposed to
- * associate the data to the instance that created it.
- */
-
 /* The internal structures (prefixed by WDML) are used as follows:
  * + a WDML_INSTANCE is created for each instance creation (DdeInitialize)
- *      - a popup windows (InstanceClass) is created for each instance. It will be
- *	  used to receive all the DDEML events (server registration, conversation 
- *	  confirmation...)
+ *      - a popup window (InstanceClass) is created for each instance.
+ *      - this window is used to receive all the DDEML events (server registration, 
+ *	  conversation confirmation...). See the WM_WDML_???? messages for details
  * + when registring a server (DdeNameService) a WDML_SERVER is created
  *	- a popup window (ServerNameClass) is created
  * + a conversation is represented by two WDML_CONV structures:
@@ -47,6 +37,13 @@
  *		o a child window (of the ServerName) on the server side
  *		  (ServerConvClass)
  *	- all the exchanges then take place between those two windows
+ *	- windows for the conversation exist in two forms (Ansi & Unicode). This
+ *	  is only needed when a partner in a conv is not handled by DDEML. The
+ *	  type (A/W) of the window is used to handle the ansi <=> unicode 
+ *        transformations
+ *	- two handles are created for a conversation (on each side). Each handle
+ *	  is linked to a structure. To help differentiate those handles, the
+ *	  local one has an even value, whereas the remote one has an odd value.
  * + a (warm or link) is represented by two WDML_LINK structures:
  *	- one on client side, the other one on server side
  *	- therefore, two lists of links are kept for each instance
@@ -55,67 +52,77 @@
  *	- offset 0: the DDE instance
  *	- offset 4: the current conversation (for ClientConv and ServerConv only)
  *
+ * All the implementation (client & server) makes the assumption that the other side
+ * is not always a DDEML partner. However, if it's the case, supplementary services
+ * are available (most notably the REGISTER/UNREGISTER and CONNECT_CONFIRM messages
+ * to the callback function). To be correct in every situation, all the basic 
+ * exchanges are made using the 'pure' DDE protocol. A (future !) enhancement would
+ * be to provide a new protocol in the case were both partners are handled by DDEML.
+ *
+ * The StringHandles are in fact stored as local atoms. So an HSZ and a (local) atom
+ * can be used interchangably. However, in order to keep track of the allocated HSZ, 
+ * and to free them upon instance termination, all HSZ are stored in a link list.
+ * When the HSZ need to be passed thru DDE messages, we need to convert them back and
+ * forth to global atoms.
  */
 
+/* this struct has the same mapping as all the DDE??? structures */
+typedef struct {
+    unsigned short unused:12,
+		   fResponse:1,
+                   fRelease:1,
+		   fDeferUpd:1,
+                   fAckReq:1;
+    short    cfFormat;
+} WINE_DDEHEAD;
+
 typedef struct tagHSZNode
 {
-    struct tagHSZNode*	next;
+    struct tagHSZNode*		next;
     HSZ 			hsz;
-    HSZ 			hsz2;
+    unsigned			refCount;
 } HSZNode;
 
 typedef struct tagWDML_SERVER
 {
     struct tagWDML_SERVER*	next;
     HSZ				hszService;
-    HSZ				hszTopic;
+    HSZ				hszServiceSpec;
+    ATOM			atomService;
+    ATOM			atomServiceSpec;
     BOOL			filterOn;
     HWND			hwndServer;
 } WDML_SERVER;
 
 typedef struct tagWDML_XACT {
-    struct tagWDML_XACT*	next;
+    struct tagWDML_XACT*	next;		/* list of transactions in conversation */
     DWORD			xActID;
     UINT			ddeMsg;
     HDDEDATA			hDdeData;
     DWORD			dwTimeout;
     DWORD			hUser;
-    union {
-	struct {
-	    UINT	wType;
-	    UINT	wFmt;
-	    HSZ		hszItem;
-	    HGLOBAL	hDdeAdvise;
-	} advise;
-	struct {
-	    UINT	wFmt;
-	    HSZ		hszItem;
-	} unadvise;
-	struct {
-	    HGLOBAL	hMem;
-	} execute;
-	struct {
-	    HGLOBAL	hMem;
-	    HSZ		hszItem;
-	} poke;
-	struct {
-	    HSZ		hszItem;
-	} request;
-    } u;
+    UINT			wType;
+    UINT			wFmt;
+    HSZ				hszItem;
+    ATOM			atom;		/* as converted from or to hszItem */
+    HGLOBAL			hMem;
+    LPARAM			lParam; 	/* useful for reusing */
 } WDML_XACT;
 
 typedef struct tagWDML_CONV 
 {
     struct tagWDML_CONV*	next;		/* to link all the conversations */
-    struct tagWDML_INSTANCE*	thisInstance;
+    struct tagWDML_INSTANCE*	instance;
     HSZ				hszService;	/* pmt used for connection */
     HSZ				hszTopic;	/* pmt used for connection */
     UINT			afCmd;		/* service name flag */
     CONVCONTEXT			convContext;
     HWND			hwndClient;	/* source of conversation (ClientConvClass) */
     HWND			hwndServer;	/* destination of conversation (ServerConvClass) */
-    WDML_XACT*			transactions;	/* pending transactions (client only) */
+    WDML_XACT*			transactions;	/* pending transactions */
     DWORD			hUser;		/* user defined value */
+    DWORD			wStatus;	/* same bits as convinfo.wStatus */
+    DWORD			wConvst;	/* same values as convinfo.wConvst */
 } WDML_CONV;
 
 /* DDE_LINK struct defines hot, warm, and cold links */
@@ -132,6 +139,7 @@
 {
     struct tagWDML_INSTANCE*	next;
     DWORD           		instanceID;	/* needed to track monitor usage */
+    DWORD			threadID;	/* needed to keep instance linked to a unique thread */
     BOOL			monitor;        /* have these two as full Booleans cos they'll be tested frequently */
     BOOL			clientOnly;	/* bit wasteful of space but it will be faster */
     BOOL			unicode;        /* Flag to indicate Win32 API used to initialise */
@@ -140,7 +148,6 @@
     PFNCALLBACK     		callback;
     DWORD           		CBFflags;
     DWORD           		monitorFlags;
-    UINT			txnCount;      	/* count transactions open to simplify closure */
     DWORD			lastError; 
     HWND			hwndEvent;
     WDML_SERVER*		servers;	/* list of registered servers */
@@ -148,9 +155,7 @@
     WDML_LINK*			links[2];	/* active links for this instance (client and server) */
 } WDML_INSTANCE;
 
-extern WDML_INSTANCE*	WDML_InstanceList;	/* list of created instances, a process can create many */
-extern DWORD 		WDML_MaxInstanceID;	/* FIXME: OK for present, may have to worry about wrap-around later */
-extern HANDLE		handle_mutex;
+extern CRITICAL_SECTION WDML_CritSect;		/* protection for instance list */
 
 /* header for the DDE Data objects */
 typedef struct tagDDE_DATAHANDLE_HEAD
@@ -163,46 +168,80 @@
     WDML_CLIENT_SIDE = 0, WDML_SERVER_SIDE = 1
 } WDML_SIDE;
 
-/* server calls this. */
-extern	WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic);
-extern	void		WDML_RemoveServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic);
-extern	WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic);
+typedef enum {
+    WDML_QS_ERROR, WDML_QS_HANDLED, WDML_QS_PASS, WDML_QS_SWALLOWED, WDML_QS_BLOCK,
+} WDML_QUEUE_STATE;
+
+extern	HDDEDATA 	WDML_InvokeCallback(WDML_INSTANCE* pInst, UINT uType, UINT uFmt, HCONV hConv,
+					    HSZ hsz1, HSZ hsz2, HDDEDATA hdata, 
+					    DWORD dwData1, DWORD dwData2);
+extern	HDDEDATA 	WDML_InvokeCallback16(PFNCALLBACK pfn, UINT uType, UINT uFmt, HCONV hConv,
+					      HSZ hsz1, HSZ hsz2, HDDEDATA hdata, 
+					      DWORD dwData1, DWORD dwData2);
+extern	WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic);
+extern	void		WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic);
+extern	WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic);
 /* called both in DdeClientTransaction and server side. */		
-extern	WDML_CONV* 	WDML_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, 
+extern	UINT		WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+					DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16);
+extern	WDML_CONV* 	WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side, 
 				     HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer);
-extern	void		WDML_RemoveConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, HCONV hConv);
-extern	WDML_CONV*	WDML_GetConv(HCONV hConv);
-extern	WDML_CONV*	WDML_FindConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, 
+extern	void		WDML_RemoveConv(WDML_CONV* pConv, WDML_SIDE side);
+extern	WDML_CONV*	WDML_GetConv(HCONV hConv, BOOL checkConnected);
+extern	WDML_CONV*	WDML_GetConvFromWnd(HWND hWnd);
+extern	WDML_CONV*	WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side, 
 				      HSZ hszService, HSZ hszTopic);	
-extern	void		WDML_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 
+extern  LPARAM		WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode, 
+				     BOOL fBusy, BOOL fAck, ATOM atom, LPARAM lParam, UINT oldMsg);
+extern	void		WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
 				     UINT wType, HSZ hszItem, UINT wFmt);
-extern	WDML_LINK*	WDML_FindLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 
+extern	WDML_LINK*	WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
 				      HSZ hszItem, UINT uFmt);
-extern	void 		WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 
+extern	void 		WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
 					HSZ hszItem, UINT wFmt);
-extern	void		WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side);
+extern	inline void	WDML_ExtractAck(WORD status, DDEACK* da) {*da = *((DDEACK*)&status);}
+extern	void		WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side);
+/* string internals */
+extern	void 		WDML_FreeAllHSZ(WDML_INSTANCE* pInstance);
+extern	BOOL		WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz);
+extern	BOOL		WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz);
+extern	ATOM		WDML_MakeAtomFromHsz(HSZ hsz);
+extern	HSZ		WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom);
 /* client calls these */
-extern	WDML_XACT*	WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg);
+extern	WDML_XACT*	WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg, UINT wFmt, HSZ hszItem);
 extern	void		WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct);
 extern	BOOL		WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct);
-extern	void		WDML_FreeTransaction(WDML_XACT* pXAct);
+extern	void		WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt);
 extern	WDML_XACT*	WDML_FindTransaction(WDML_CONV* pConv, DWORD tid);
 extern	HGLOBAL		WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease, 
 					       BOOL fDeferUpd, BOOL dAckReq);
-extern	HDDEDATA	WDML_Global2DataHandle(HGLOBAL hMem);
-extern	WDML_INSTANCE*	WDML_FindInstance(DWORD InstId);
-extern	BOOL 		WDML_WaitForMutex(HANDLE mutex);
-extern	DWORD		WDML_ReleaseMutex(HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m);
-extern	void 		WDML_FreeAllHSZ(WDML_INSTANCE* thisInstance);
-extern	void 		WDML_ReleaseAtom(WDML_INSTANCE* thisInstance, HSZ hsz);
-extern	void 		WDML_ReserveAtom(WDML_INSTANCE* thisInstance, HSZ hsz);
+extern	HDDEDATA	WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* da);
+extern	WDML_INSTANCE*	WDML_GetInstance(DWORD InstId);
+extern	WDML_INSTANCE*	WDML_GetInstanceFromWnd(HWND hWnd);
 /* broadcasting to DDE windows */
 extern	void		WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, 
 						 WPARAM wParam, LPARAM lParam);
+extern	void		WDML_NotifyThreadExit(DWORD tid);
 
-extern const char WDML_szEventClass[]; /* class of window for events (aka instance) */
+extern const char 	WDML_szEventClass[]; 	   /* class of window for events (aka instance) */
+extern const char  	WDML_szServerConvClassA[]; /* class of window for server side conv (ansi) */
+extern const WCHAR 	WDML_szServerConvClassW[]; /* class of window for server side conv (unicode) */
+extern const char  	WDML_szClientConvClassA[]; /* class of window for client side conv (ansi) */
+extern const WCHAR 	WDML_szClientConvClassW[]; /* class of window for client side conv (unicode) */
 
 #define WM_WDML_REGISTER	(WM_USER + 0x200)
 #define WM_WDML_UNREGISTER	(WM_USER + 0x201)
+#define WM_WDML_CONNECT_CONFIRM	(WM_USER + 0x202)
+
+/* parameters for messages: 
+ *			wParam				lParam
+ * Register		atom for service name		atom for service spec
+ * Unregister		atom for service name		atom for service spec
+ * ConnectConfirm	client window handle		server window handle
+ */
+
+#define	GWL_WDML_INSTANCE	(0)
+#define	GWL_WDML_CONVERSATION	(4)
+#define	GWL_WDML_SERVER		(4)
 
 #endif  /* __WINE_DDEML_PRIVATE_H */
diff --git a/dlls/user/dde/ddeml16.c b/dlls/user/dde/ddeml16.c
index 1d20e26..8de932a 100644
--- a/dlls/user/dde/ddeml16.c
+++ b/dlls/user/dde/ddeml16.c
@@ -19,6 +19,7 @@
 #include "winerror.h"
 #include "dde.h"
 #include "ddeml.h"
+#include "dde/dde_private.h"
 #include "debugtools.h"
 
 DEFAULT_DEBUG_CHANNEL(ddeml);
@@ -54,6 +55,22 @@
     CONVCONTEXT16  ConvCtxt;
 } CONVINFO16, *LPCONVINFO16;
 
+/* ### start build ### */
+extern LONG CALLBACK WDML_CallTo16_long_llllllll    (FARPROC16,LONG,LONG,LONG,LONG,LONG,LONG,LONG,LONG);
+/* ### stop build ### */
+
+/******************************************************************
+ *		WDML_InvokeCallback16
+ *
+ *
+ */
+HDDEDATA	WDML_InvokeCallback16(PFNCALLBACK pfn, UINT uType, UINT uFmt, HCONV hConv,
+				      HSZ hsz1, HSZ hsz2, HDDEDATA hdata, 
+				      DWORD dwData1, DWORD dwData2)
+{
+	return WDML_CallTo16_long_llllllll((FARPROC16)pfn, uType, uFmt, hConv, 
+					   hsz1, hsz2, hdata, dwData1, dwData2);
+}
 
 /******************************************************************************
  *            DdeInitialize   (DDEML.2)
@@ -61,8 +78,7 @@
 UINT16 WINAPI DdeInitialize16(LPDWORD pidInst, PFNCALLBACK16 pfnCallback,
 			      DWORD afCmd, DWORD ulRes)
 {
-     return (UINT16)DdeInitializeA(pidInst,(PFNCALLBACK)pfnCallback,
-				   afCmd, ulRes);
+    return WDML_Initialize(pidInst, (PFNCALLBACK)pfnCallback, afCmd, ulRes, FALSE, TRUE);
 }
 
 /*****************************************************************
@@ -145,8 +161,7 @@
  */
 BOOL16 WINAPI DdeSetUserHandle16(HCONV hConv, DWORD id, DWORD hUser)
 {
-     FIXME("(%d,%ld,%ld): stub\n",hConv,id, hUser);
-     return 0;
+     return DdeSetUserHandle(hConv, id, hUser);
 }
 
 /*****************************************************************
@@ -167,7 +182,9 @@
      if  (codepage)
      {
 	  return DdeCreateStringHandleA(idInst, str, codepage);
-     } else {
+     } 
+     else 
+     {
 	  TRACE("Default codepage supplied\n");
 	  return DdeCreateStringHandleA(idInst, str, CP_WINANSI);
      }
@@ -178,7 +195,7 @@
  */
 BOOL16 WINAPI DdeFreeStringHandle16(DWORD idInst, HSZ hsz)
 {
-     FIXME("idInst %ld hsz 0x%x\n",idInst,hsz);
+     TRACE("idInst %ld hsz 0x%x\n",idInst,hsz);
      return (BOOL)DdeFreeStringHandle(idInst, hsz);
 }
 
@@ -218,8 +235,7 @@
 BOOL16 WINAPI DdeAbandonTransaction16(DWORD idInst, HCONV hConv, 
 				      DWORD idTransaction)
 {
-     FIXME("empty stub\n");
-     return TRUE;
+     return DdeAbandonTransaction(idInst, hConv, idTransaction);
 }
 
 /*****************************************************************
@@ -256,7 +272,8 @@
  */
 LPBYTE WINAPI DdeAccessData16(HDDEDATA hData, LPDWORD pcbDataSize)
 {
-     return DdeAccessData(hData, pcbDataSize);
+    /* FIXME: there's a memory leak here... */
+    return (LPBYTE)MapLS(DdeAccessData(hData, pcbDataSize));
 }
 
 /*****************************************************************
@@ -264,7 +281,7 @@
  */
 BOOL16 WINAPI DdeUnaccessData16(HDDEDATA hData)
 {
-     return DdeUnaccessData(hData);
+    return DdeUnaccessData(hData);
 }
 
 /*****************************************************************
@@ -272,7 +289,7 @@
  */
 BOOL16 WINAPI DdeEnableCallback16(DWORD idInst, HCONV hConv, UINT16 wCmd)
 {
-     return DdeEnableCallback(idInst, hConv, wCmd);
+    return DdeEnableCallback(idInst, hConv, wCmd);
 }
 
 /*****************************************************************
@@ -281,7 +298,7 @@
 HDDEDATA WINAPI DdeNameService16(DWORD idInst, HSZ hsz1, HSZ hsz2,
 				 UINT16 afCmd)
 {
-     return DdeNameService(idInst, hsz1, hsz2, afCmd);
+    return DdeNameService(idInst, hsz1, hsz2, afCmd);
 }
 
 /*****************************************************************
@@ -289,7 +306,7 @@
  */
 UINT16 WINAPI DdeGetLastError16(DWORD idInst)
 {
-     return (UINT16)DdeGetLastError(idInst);
+    return (UINT16)DdeGetLastError(idInst);
 }
 
 /*****************************************************************
@@ -297,7 +314,7 @@
  */
 INT16 WINAPI DdeCmpStringHandles16(HSZ hsz1, HSZ hsz2)
 {
-     return DdeCmpStringHandles(hsz1, hsz2);
+    return DdeCmpStringHandles(hsz1, hsz2);
 }
 
 /******************************************************************
diff --git a/dlls/user/dde/misc.c b/dlls/user/dde/misc.c
index c1ef012..62835c5 100644
--- a/dlls/user/dde/misc.c
+++ b/dlls/user/dde/misc.c
@@ -11,6 +11,7 @@
  */
 
 #include <string.h>
+#include <stdio.h>
 #include "winbase.h"
 #include "windef.h"
 #include "wingdi.h"
@@ -23,18 +24,16 @@
 
 DEFAULT_DEBUG_CHANNEL(ddeml);
 
-WDML_INSTANCE*		WDML_InstanceList = NULL;
-DWORD 			WDML_MaxInstanceID = 0;  /* OK for present, may have to worry about wrap-around later */
-static const char	DDEInstanceAccess[] = "DDEMaxInstance";
-static const char	DDEHandleAccess[] = "DDEHandleAccess";
-HANDLE			handle_mutex = 0;
+static WDML_INSTANCE*	WDML_InstanceList = NULL;
+static DWORD		WDML_MaxInstanceID = 0;  /* OK for present, have to worry about wrap-around later */
 const char		WDML_szEventClass[] = "DdeEventClass";
+CRITICAL_SECTION	WDML_CritSect = CRITICAL_SECTION_INIT;
 
-/* FIXME
- * currently the msg parameter is not used in the packing functions.
- * it should be used to identify messages which don't actually require the packing operation
- * but would do with the simple DWORD for lParam
- */
+/* ================================================================
+ *
+ * 			Pure DDE (non DDEML) management
+ *
+ * ================================================================ */
 
 static BOOL DDE_RequirePacking(UINT msg)
 {
@@ -218,11 +217,11 @@
 /*****************************************************************
  *            ImpersonateDdeClientWindow (USER32.@)
  *
+ * PARAMS
+ * hWndClient	  [I] handle to DDE client window
+ * hWndServer	  [I] handle to DDE server window
  */
-BOOL WINAPI ImpersonateDdeClientWindow(
-     HWND hWndClient,  /* [in] handle to DDE client window */
-     HWND hWndServer   /* [in] handle to DDE server window */
-)
+BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
 {
      FIXME("(%04x %04x): stub\n", hWndClient, hWndServer);
      return FALSE;
@@ -239,188 +238,153 @@
      return TRUE;
 }
 
+/* ================================================================
+ *
+ * 			Instance management
+ *
+ * ================================================================ */
 
 /******************************************************************************
  *		IncrementInstanceId
  *
  *	generic routine to increment the max instance Id and allocate a new application instance
  */
-static DWORD WDML_IncrementInstanceId(WDML_INSTANCE* thisInstance)
+static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
 {
     DWORD	id = InterlockedIncrement(&WDML_MaxInstanceID);
 
-    thisInstance->instanceID = id;
+    pInstance->instanceID = id;
     TRACE("New instance id %ld allocated\n", id);
-    return DMLERR_NO_ERROR;
 }
 
-/******************************************************************************
- *            DdeInitializeA   (USER32.@)
+/******************************************************************
+ *		WDML_EventProc
+ *
+ *
  */
-UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
-			   DWORD afCmd, DWORD ulRes)
-{
-    UINT	ret = DdeInitializeW(pidInst, pfnCallback, afCmd, ulRes);
-    
-    if (ret == DMLERR_NO_ERROR) {
-	WDML_INSTANCE*	thisInstance = WDML_FindInstance(*pidInst);
-	if (thisInstance)
-	    thisInstance->unicode = FALSE;
-    }
-    return ret;
-}
-
 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-    WDML_INSTANCE*	thisInstance;
-    HDDEDATA		hDdeData;
+    WDML_INSTANCE*	pInstance;
+    HSZ			hsz1, hsz2;
 
     switch (uMsg)
     {
     case WM_WDML_REGISTER:
-	thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndEvent, 0);
-	
+	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
         /* try calling the Callback */
-	if (thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId()*/)
+	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
 	{
-	    TRACE("Calling the callback, type=XTYP_REGISTER, CB=0x%lx\n",
-		  (DWORD)thisInstance->callback);
-		
-	    hDdeData = (thisInstance->callback)(XTYP_REGISTER, 0, 0,
-						(HSZ)wParam, (HSZ)lParam, 0, 0, 0);
-		
-	    TRACE("Callback function called - result=%d\n", (INT)hDdeData);
+	    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:
-	thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndEvent, 0);
-	
-	if (thisInstance && thisInstance->callback != NULL)
+	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
+	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
 	{
-	    if (thisInstance->CBFflags & CBF_SKIP_DISCONNECTS)
+	    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
+	     */
+	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
 	    {
-		FIXME("skip callback XTYP_UNREGISTER\n");
+		if (pConv->hwndClient == (HWND)wParam && pConv->hwndServer == (HWND)lParam)
+		    break;
 	    }
-	    else
+	    if (pConv)
 	    {
-		TRACE("calling callback XTYP_UNREGISTER, idInst=%ld\n", 
-		      thisInstance->instanceID);
-		(thisInstance->callback)(XTYP_UNREGISTER, 0, 0,
-					 (HSZ)wParam, (HSZ)lParam, 0, 0, 0);
+		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 DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
     }
-    return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
+    return 0;
 }
 
-/******************************************************************************
- * DdeInitializeW [USER32.@]
- * Registers an application with the DDEML
+/******************************************************************
+ *		WDML_Initialize
  *
- * 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
  */
-UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
-			   DWORD afCmd, DWORD ulRes)
+UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+		     DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16)
 {
-    
-/*  probably not really capable of handling multiple processes, but should handle
- *	multiple instances within one process */
-    
-    SECURITY_ATTRIBUTES		s_attrib;
-    DWORD 	       		err_no = 0;
-    WDML_INSTANCE*		thisInstance;
+    WDML_INSTANCE*		pInstance;
     WDML_INSTANCE*		reference_inst;
     UINT			ret;
     WNDCLASSEXA			wndclass;
 
+    TRACE("(%p,%p,0x%lx,%ld)\n", 
+	  pidInst, pfnCallback, afCmd, ulRes);
+
     if (ulRes)
     {
 	ERR("Reserved value not zero?  What does this mean?\n");
-	FIXME("(%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback,
-	      afCmd,ulRes);
 	/* trap this and no more until we know more */
 	return DMLERR_NO_ERROR;
     }
-    if (!pfnCallback) 
-    {
-	/*  this one may be wrong - MS dll seems to accept the condition, 
-	    leave this until we find out more !! */
-	
-	
-	/* can't set up the instance with nothing to act as a callback */
-	TRACE("No callback provided\n");
-	return DMLERR_INVALIDPARAMETER; /* might be DMLERR_DLL_USAGE */
-    }
     
     /* grab enough heap for one control struct - not really necessary for re-initialise
      *	but allows us to use same validation routines */
-    thisInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
-    if (thisInstance == NULL)
+    pInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
+    if (pInstance == NULL)
     {
 	/* catastrophe !! warn user & abort */
 	ERR("Instance create failed - out of memory\n");
 	return DMLERR_SYS_ERROR;
     }
-    thisInstance->next = NULL;
-    thisInstance->monitor = (afCmd | APPCLASS_MONITOR);
+    pInstance->next = NULL;
+    pInstance->monitor = (afCmd | APPCLASS_MONITOR);
     
     /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
     
-    thisInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
-    thisInstance->instanceID = *pidInst; /* May need to add calling proc Id */
-    thisInstance->callback = *pfnCallback;
-    thisInstance->txnCount = 0;
-    thisInstance->unicode = TRUE;
-    thisInstance->win16 = FALSE;
-    thisInstance->nodeList = NULL; /* node will be added later */
-    thisInstance->monitorFlags = afCmd & MF_MASK;
-    thisInstance->servers = NULL;
-    thisInstance->convs[0] = NULL;
-    thisInstance->convs[1] = NULL;
-    thisInstance->links[0] = NULL;
-    thisInstance->links[1] = NULL;
-
-    wndclass.cbSize        = sizeof(wndclass);
-    wndclass.style         = 0;
-    wndclass.lpfnWndProc   = WDML_EventProc;
-    wndclass.cbClsExtra    = 0;
-    wndclass.cbWndExtra    = sizeof(DWORD);
-    wndclass.hInstance     = 0;
-    wndclass.hIcon         = 0;
-    wndclass.hCursor       = 0;
-    wndclass.hbrBackground = 0;
-    wndclass.lpszMenuName  = NULL;
-    wndclass.lpszClassName = WDML_szEventClass;
-    wndclass.hIconSm       = 0;
-	
-    RegisterClassExA(&wndclass);
-	
-    thisInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
-					    WS_POPUP, 0, 0, 0, 0,
-					    0, 0, 0, 0);
-	
-    SetWindowLongA(thisInstance->hwndEvent, 0, (DWORD)thisInstance);
+    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->win16 = b16;
+    pInstance->nodeList = NULL; /* node will be added later */
+    pInstance->monitorFlags = afCmd & MF_MASK;
+    pInstance->servers = NULL;
+    pInstance->convs[0] = NULL;
+    pInstance->convs[1] = NULL;
+    pInstance->links[0] = NULL;
+    pInstance->links[1] = NULL;
 
     /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
     
-    thisInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
+    pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
     
-    if (!thisInstance->clientOnly)
+    if (!pInstance->clientOnly)
     {
 	
 	/* Check for other way of setting Client-only !! */
 	
-	thisInstance->clientOnly = 
-	    (thisInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
+	pInstance->clientOnly = 
+	    (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
     }
     
     TRACE("instance created - checking validity \n");
@@ -429,30 +393,13 @@
     {
 	/*  Initialisation of new Instance Identifier */
 	TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
-	if (WDML_MaxInstanceID == 0)
-	{
-	    /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
-	    /*  Need to set up Mutex in case it is not already present */
-	    s_attrib.bInheritHandle = TRUE;
-	    s_attrib.lpSecurityDescriptor = NULL;
-	    s_attrib.nLength = sizeof(s_attrib);
-	    handle_mutex = CreateMutexA(&s_attrib,0,DDEHandleAccess);
-	    if (!handle_mutex) 
-	    {
-		ERR("CreateMutex failed - handle list  %li\n",GetLastError());
-		HeapFree(GetProcessHeap(), 0, thisInstance);
-		return DMLERR_SYS_ERROR;
-	    }
-	}
-	if (!WDML_WaitForMutex(handle_mutex))
-	{
-	    return DMLERR_SYS_ERROR;
-	}
+
+	EnterCriticalSection(&WDML_CritSect);
 	
 	if (WDML_InstanceList == NULL) 
 	{
 	    /* can't be another instance in this case, assign to the base pointer */
-	    WDML_InstanceList = thisInstance;
+	    WDML_InstanceList = pInstance;
 	    
 	    /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
 	     *		present 
@@ -463,11 +410,10 @@
 	     *	first call for a given callback !!!
 	     */
 	    
-	    thisInstance->CBFflags = thisInstance->CBFflags|APPCMD_FILTERINITS;
+	    pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
 	    TRACE("First application instance detected OK\n");
 	    /*	allocate new instance ID */
-	    if ((err_no = WDML_IncrementInstanceId(thisInstance))) return err_no;
-	    
+	    WDML_IncrementInstanceId(pInstance);
 	} 
 	else 
 	{
@@ -484,27 +430,27 @@
 		 *	clever (lazy ?) it will fail to pick up that later calls are for
 		 *	the same application - should we trust them ?
 		 */
-		if (thisInstance->instanceID == reference_inst->instanceID) 
+		if (pInstance->instanceID == reference_inst->instanceID) 
 		{
-				/* Check 1 - must be same Client-only state */
+		    /* Check 1 - must be same Client-only state */
 		    
-		    if (thisInstance->clientOnly != reference_inst->clientOnly)
+		    if (pInstance->clientOnly != reference_inst->clientOnly)
 		    {
 			ret = DMLERR_DLL_USAGE;
 			goto theError;
 		    }
 		    
-				/* Check 2 - cannot use 'Monitor' with any non-monitor modes */
+		    /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
 		    
-		    if (thisInstance->monitor != reference_inst->monitor) 
+		    if (pInstance->monitor != reference_inst->monitor) 
 		    {
 			ret = DMLERR_INVALIDPARAMETER;
 			goto theError;
 		    }
 		    
-				/* Check 3 - must supply different callback address */
+		    /* Check 3 - must supply different callback address */
 		    
-		    if (thisInstance->callback == reference_inst->callback)
+		    if (pInstance->callback == reference_inst->callback)
 		    {
 			ret = DMLERR_DLL_USAGE;
 			goto theError;
@@ -515,30 +461,50 @@
 	    /*  All cleared, add to chain */
 	    
 	    TRACE("Application Instance checks finished\n");
-	    if ((err_no = WDML_IncrementInstanceId(thisInstance))) return err_no;
-	    reference_inst->next = thisInstance;
+	    WDML_IncrementInstanceId(pInstance);
+	    reference_inst->next = pInstance;
 	}
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return DMLERR_SYS_ERROR;
-	*pidInst = thisInstance->instanceID;
+	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(DWORD);
+	wndclass.hInstance     = 0;
+	wndclass.hIcon         = 0;
+	wndclass.hCursor       = 0;
+	wndclass.hbrBackground = 0;
+	wndclass.lpszMenuName  = NULL;
+	wndclass.lpszClassName = WDML_szEventClass;
+	wndclass.hIconSm       = 0;
+	
+	RegisterClassExA(&wndclass);
+	
+	pInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
+						WS_POPUP, 0, 0, 0, 0,
+						0, 0, 0, 0);
+	
+	SetWindowLongA(pInstance->hwndEvent, GWL_WDML_INSTANCE, (DWORD)pInstance);
+
 	TRACE("New application instance processing finished OK\n");
     } 
     else 
     {
 	/* Reinitialisation situation   --- FIX  */
-	TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes);
+	TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback, afCmd, ulRes);
 	
-	if (!WDML_WaitForMutex(handle_mutex))
-	{
-	    HeapFree(GetProcessHeap(), 0, thisInstance);
-	    return DMLERR_SYS_ERROR;
-	}
+	EnterCriticalSection(&WDML_CritSect);
 	
 	if (WDML_InstanceList == NULL) 
 	{
 	    ret = DMLERR_DLL_USAGE;
 	    goto theError;
 	}
-	HeapFree(GetProcessHeap(), 0, thisInstance); /* finished - release heap space used as work store */
+	HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
 	/* 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 ? */
@@ -567,7 +533,7 @@
 		}
 		/* Check 2 - cannot change monitor modes */
 		
-		if (thisInstance->monitor != reference_inst->monitor) 
+		if (pInstance->monitor != reference_inst->monitor) 
 		{
 		    ret = DMLERR_DLL_USAGE;
 		    goto theError;
@@ -593,26 +559,50 @@
 	    ret = DMLERR_INVALIDPARAMETER;
 	    goto theError;
 	}
-	/*		All checked - change relevant flags */
+	/* All checked - change relevant flags */
 	
-	reference_inst->CBFflags = thisInstance->CBFflags;
-	reference_inst->clientOnly = thisInstance->clientOnly;
-	reference_inst->monitorFlags = thisInstance->monitorFlags;
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
-	{
-	    HeapFree(GetProcessHeap(), 0, thisInstance);
-	    return DMLERR_SYS_ERROR;
-	}
+	reference_inst->CBFflags = pInstance->CBFflags;
+	reference_inst->clientOnly = pInstance->clientOnly;
+	reference_inst->monitorFlags = pInstance->monitorFlags;
+	LeaveCriticalSection(&WDML_CritSect);
     }
     
     return DMLERR_NO_ERROR;
  theError:
-    HeapFree(GetProcessHeap(), 0, thisInstance);
-    if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", 0))
-	return DMLERR_SYS_ERROR;
+    HeapFree(GetProcessHeap(), 0, pInstance);
+    LeaveCriticalSection(&WDML_CritSect);
     return ret;
 }
 
+/******************************************************************************
+ *            DdeInitializeA   (USER32.@)
+ */
+UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+			   DWORD afCmd, DWORD ulRes)
+{
+    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, 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
+ */
+UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
+			   DWORD afCmd, DWORD ulRes)
+{
+    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
+}
+
 /*****************************************************************
  * DdeUninitialize [USER32.@]  Frees DDEML resources
  *
@@ -628,196 +618,331 @@
 {
     /*  Stage one - check if we have a handle for this instance
      */
-    WDML_INSTANCE*		thisInstance;
+    WDML_INSTANCE*		pInstance;
     WDML_INSTANCE*		reference_inst;
+    WDML_CONV*			pConv;
+    WDML_CONV*			pConvNext;
 
-    if (WDML_MaxInstanceID == 0)
-    {
-	/*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
-	return TRUE;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return DMLERR_SYS_ERROR;
-    }
+    EnterCriticalSection(&WDML_CritSect);
+
     /*  First check instance 
      */
-    thisInstance = WDML_FindInstance(idInst);
-    if (thisInstance == NULL)
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance == NULL)
     {
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
+	LeaveCriticalSection(&WDML_CritSect);
 	/*
 	 *	Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
 	 */
 	return FALSE;
     }
-    FIXME("(%ld): partial stub\n", idInst);
-    
-    /*   FIXME	++++++++++++++++++++++++++++++++++++++++++
-     *	Needs to de-register all service names
-     *	
+
+    /* 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);
     
     /* Free the nodes that were not freed by this instance
      * and remove the nodes from the list of HSZ nodes.
      */
-    WDML_FreeAllHSZ(thisInstance);
+    WDML_FreeAllHSZ(pInstance);
 
-    DestroyWindow(thisInstance->hwndEvent);
+    DestroyWindow(pInstance->hwndEvent);
     
     /* OK now delete the instance handle itself */
     
-    if (WDML_InstanceList == thisInstance)
+    if (WDML_InstanceList == pInstance)
     {
 	/* special case - the first/only entry
 	 */
-	WDML_InstanceList = thisInstance->next;
+	WDML_InstanceList = pInstance->next;
     }
     else
     {
 	/* general case
 	 */
 	reference_inst = WDML_InstanceList;
-	while (reference_inst->next != thisInstance)
+	while (reference_inst->next != pInstance)
 	{
-	    reference_inst = thisInstance->next;
+	    reference_inst = pInstance->next;
 	}
-	reference_inst->next = thisInstance->next;
+	reference_inst->next = pInstance->next;
     }
-    /* release the mutex and the heap entry
+    /* leave crit sect and release the heap entry
      */
-    HeapFree(GetProcessHeap(), 0, thisInstance);
-    if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) 
+    HeapFree(GetProcessHeap(), 0, pInstance);
+    LeaveCriticalSection(&WDML_CritSect);
+    return TRUE;
+}
+
+/******************************************************************
+ *		WDML_NotifyThreadExit
+ *
+ *
+ */
+void WDML_NotifyThreadDetach(void)
+{
+    WDML_INSTANCE*	pInstance;
+    WDML_INSTANCE*	next;
+    DWORD		tid = GetCurrentThreadId();
+
+    EnterCriticalSection(&WDML_CritSect);
+    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
     {
-	/* should record something here, but nothing left to hang it from !!
-	 */
-	return FALSE;
+	next = pInstance->next;
+	if (pInstance->threadID == tid)
+	{
+	    DdeUninitialize(pInstance->instanceID);
+	}
     }
+    LeaveCriticalSection(&WDML_CritSect);
+}
+
+/******************************************************************
+ *		WDML_InvokeCallback
+ *
+ *
+ */
+HDDEDATA 	WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
+				    HSZ hsz1, HSZ hsz2, HDDEDATA hdata, 
+				    DWORD dwData1, DWORD dwData2)
+{
+    HDDEDATA	ret;
+
+    if (pInstance == NULL)
+	return (HDDEDATA)0;
+    TRACE("invoking CB%d[%08lx] (%u %u %08lx 0x%x 0x%x %u %lu %lu)\n",
+	  pInstance->win16 ? 16 : 32, (DWORD)pInstance->callback, uType, uFmt, 
+	  (DWORD)hConv, hsz1, hsz2, hdata, dwData1, dwData2);
+    if (pInstance->win16)
+    {
+	ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv, 
+				    hsz1, hsz2, hdata, dwData1, dwData2);
+    }
+    else
+    {
+	ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
+    }
+    TRACE("done => %08lx\n", (DWORD)ret);
+    return ret;
+}
+
+/*****************************************************************************
+ *	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
+ *
+ */
+WDML_INSTANCE*	WDML_GetInstance(DWORD instId)
+{
+    WDML_INSTANCE*	pInstance;
+    
+    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
+    {
+	if (pInstance->instanceID == instId)
+	{
+	    if (GetCurrentThreadId() != pInstance->threadID)
+	    {
+		FIXME("Tried to get instance from wrong thread\n");
+		continue;
+	    }
+	    return pInstance;
+	}
+    }
+    TRACE("Instance entry missing\n");
+    return NULL;
+}
+
+/******************************************************************
+ *		WDML_GetInstanceFromWnd
+ *
+ *
+ */
+WDML_INSTANCE*	WDML_GetInstanceFromWnd(HWND hWnd)
+{
+    return (WDML_INSTANCE*)GetWindowLongA(hWnd, GWL_WDML_INSTANCE);
+}
+
+/******************************************************************************
+ * DdeGetLastError [USER32.@]  Gets most recent error code
+ *
+ * PARAMS
+ *    idInst [I] Instance identifier
+ *
+ * RETURNS
+ *    Last error code
+ */
+UINT WINAPI DdeGetLastError(DWORD idInst)
+{
+    DWORD		error_code;
+    WDML_INSTANCE*	pInstance;
+    
+    FIXME("(%ld): error reporting is weakly implemented\n", idInst);
+    
+    EnterCriticalSection(&WDML_CritSect);
+    
+    /*  First check instance
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if  (pInstance == NULL) 
+    {
+	error_code = DMLERR_DLL_NOT_INITIALIZED;
+    }
+    else
+    {
+	error_code = pInstance->lastError;
+	pInstance->lastError = 0;
+    }
+    
+    LeaveCriticalSection(&WDML_CritSect);
+    return error_code;
+}
+
+/* ================================================================
+ *
+ * 			String management
+ *
+ * ================================================================ */
+
+
+/******************************************************************
+ *		WDML_FindNode
+ *
+ *
+ */
+static HSZNode*	WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
+{
+    HSZNode*	pNode;
+
+    if (pInstance == NULL) return NULL;
+
+    for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
+    {
+	if (pNode->hsz == hsz) break;
+    }
+    if (!pNode) WARN("HSZ 0x%x not found\n", hsz);
+    return pNode;
+}
+
+/******************************************************************
+ *		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];
+
+    if (GetAtomNameW((ATOM)hsz, nameBuffer, MAX_BUFFER_LEN))
+	return GlobalAddAtomW(nameBuffer);
+    WARN("HSZ 0x%xnot 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(WDML_INSTANCE* pInstance, ATOM atom)
+{
+    WCHAR nameBuffer[MAX_BUFFER_LEN];
+
+    GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN);
+    return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
+}
+
+/******************************************************************
+ *		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;
 }
 
 /******************************************************************************
- *            RemoveHSZNodes    (INTERNAL)
+ *           WDML_DecHSZ    (INTERNAL)
  *
- * Remove a node from the list of HSZ nodes.
+ * 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
  */
-static void WDML_RemoveHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz)
+BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
 {
-    HSZNode* pPrev = NULL;
-    HSZNode* pCurrent = NULL;
-    
-    /* Set the current node at the start of the list.
-     */
-    pCurrent = thisInstance->nodeList;
-    /* While we have more nodes.
-     */
-    while (pCurrent != NULL)
+    HSZNode* 	pPrev = NULL;
+    HSZNode* 	pCurrent;
+
+    for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
     {
-	/* If we found the node we were looking for.
+	/* If we found the node we were looking for and its ref count is one,
+	 * we can remove it
 	 */
 	if (pCurrent->hsz == hsz)
 	{
-	    /* Remove the node.
-	     */
-	    /* If the first node in the list is to to be removed.
-	     * Set the global list pointer to the next node.
-	     */
-	    if (pCurrent == thisInstance->nodeList)
+	    if (--pCurrent->refCount == 0)
 	    {
-		thisInstance->nodeList = pCurrent->next;
+		if (pCurrent == pInstance->nodeList)
+		{
+		    pInstance->nodeList = pCurrent->next;
+		}
+		else
+		{
+		    pPrev->next = pCurrent->next;
+		}
+		HeapFree(GetProcessHeap(), 0, pCurrent);
+		DeleteAtom(hsz);
 	    }
-	    /* Just fix the pointers has to skip the current
-	     * node so we can delete it.
-	     */
-	    else
-	    {
-		pPrev->next = pCurrent->next;
-	    }
-	    /* Destroy this node.
-	     */
-	    HeapFree(GetProcessHeap(), 0, pCurrent);
-	    break;
+	    return TRUE;
 	}
-	/* Save the previous node pointer.
-	 */
-	pPrev = pCurrent;
-	/* Move on to the next node.
-	 */
-	pCurrent = pCurrent->next;
     }
+    WARN("HSZ 0x%xnot found\n", hsz);
+   
+    return FALSE;
 }
 
 /******************************************************************************
- *            FreeAndRemoveHSZNodes    (INTERNAL)
+ *            WDML_FreeAllHSZ    (INTERNAL)
  *
  * Frees up all the strings still allocated in the list and
  * remove all the nodes from the list of HSZ nodes.
  */
-void WDML_FreeAllHSZ(WDML_INSTANCE* thisInstance)
+void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
 {
     /* Free any strings created in this instance.
      */
-    while (thisInstance->nodeList != NULL)
+    while (pInstance->nodeList != NULL)
     {
-	DdeFreeStringHandle(thisInstance->instanceID, thisInstance->nodeList->hsz);
+	DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
     }
 }
 
 /******************************************************************************
- *            GetSecondaryHSZValue    (INTERNAL)
- *
- * Insert a node to the head of the list.
- */
-static HSZ WDML_GetSecondaryHSZValue(WDML_INSTANCE* thisInstance, HSZ hsz)
-{
-    HSZ hsz2 = 0;
-    
-    if (hsz != 0)
-    {
-	/* Create and set the Secondary handle */
-	if (thisInstance->unicode)
-	{
-	    WCHAR wSecondaryString[MAX_BUFFER_LEN];
-	    WCHAR wUniqueNum[MAX_BUFFER_LEN];
-    
-	    if (DdeQueryStringW(thisInstance->instanceID, hsz,
-				wSecondaryString,
-				MAX_BUFFER_LEN, CP_WINUNICODE))
-	    {
-                static const WCHAR format[] = {'(','%','l','d',')',0};
-		wsprintfW(wUniqueNum, format,
-			  (DWORD)thisInstance->instanceID);
-		lstrcatW(wSecondaryString, wUniqueNum);
-		
-		hsz2 = GlobalAddAtomW(wSecondaryString);
-	    }
-	}
-	else
-	{
-	    CHAR SecondaryString[MAX_BUFFER_LEN];
-	    CHAR UniqueNum[MAX_BUFFER_LEN];
-    
-	    if (DdeQueryStringA(thisInstance->instanceID, hsz,
-				SecondaryString,
-				MAX_BUFFER_LEN, CP_WINANSI))
-	    {
-		wsprintfA(UniqueNum,"(%ld)", thisInstance->instanceID);
-		lstrcatA(SecondaryString, UniqueNum);
-		
-		hsz2 = GlobalAddAtomA(SecondaryString);
-	    }
-	}
-    }
-    return hsz2;
-}
-
-/******************************************************************************
  *            InsertHSZNode    (INTERNAL)
  *
  * Insert a node to the head of the list.
  */
-static void WDML_InsertHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz)
+static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
 {
     if (hsz != 0)
     {
@@ -827,21 +952,10 @@
 	pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
 	if (pNew != NULL)
 	{
-	    /* Set the handle value.
-	     */
-	    pNew->hsz = hsz;
-	    
-	    /* Create and set the Secondary handle */
-	    pNew->hsz2 = WDML_GetSecondaryHSZValue(thisInstance, hsz);
-	    
-	    /* Attach the node to the head of the list. i.e most recently added is first
-	     */
-	    pNew->next = thisInstance->nodeList;
-	    
-	    /* The new node is now at the head of the list
-	     * so set the global list pointer to it.
-	     */
-	    thisInstance->nodeList = pNew;
+	    pNew->hsz      = hsz;
+	    pNew->next     = pInstance->nodeList;
+	    pNew->refCount = 1;
+	    pInstance->nodeList = pNew;
 	}
 	else
 	{
@@ -850,164 +964,62 @@
     }
 }
 
-/*****************************************************************************
- *	Find_Instance_Entry
+/******************************************************************
+ *		WDML_QueryString
  *
- *	generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
- *	for an instance Id, or NULL if the entry does not exist
  *
- *	ASSUMES the mutex protecting the handle entry list is reserved before calling
  */
-WDML_INSTANCE*	WDML_FindInstance(DWORD InstId)
+static int	WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax, 
+				 int codepage)
 {
-    WDML_INSTANCE*	thisInstance;
-    
-    thisInstance = WDML_InstanceList;
-    while (thisInstance != NULL)
+    WCHAR	pString[MAX_BUFFER_LEN];
+    int		ret;
+    /* If psz is null, we have to return only the length
+     * of the string.
+     */
+    if (ptr == NULL)
     {
-	if (thisInstance->instanceID == InstId)
-	{
-	    return thisInstance;
-	}
-	thisInstance = thisInstance->next;
+	ptr = pString;
+	cchMax = MAX_BUFFER_LEN;
     }
-    TRACE("Instance entry missing\n");
-    return NULL;
-}
 
-/******************************************************************************
- *	WDML_ReleaseMutex
- *
- *	generic routine to release a reserved mutex
- */
-DWORD WDML_ReleaseMutex(HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m)
-{
-    if (!ReleaseMutex(mutex))
+    switch (codepage)
     {
-	ERR("ReleaseMutex failed - %s mutex %li\n", mutex_name, GetLastError());
-	if (release_handle_m)
-	{
-	    ReleaseMutex(handle_mutex);
-	}
-	return DMLERR_SYS_ERROR;
+    case CP_WINANSI:
+	ret = GetAtomNameA(hsz, ptr, cchMax);
+	break;
+    case CP_WINUNICODE:
+	ret = GetAtomNameW(hsz, ptr, cchMax);
+    default:
+	ERR("Unknown code page %d\n", codepage);
+	ret = 0;
     }
-    return DMLERR_NO_ERROR;
+    return ret;
 }
 
-/******************************************************************************
- *	WDML_WaitForMutex
- *
- *	generic routine to wait for the mutex
- */
-BOOL WDML_WaitForMutex(HANDLE mutex)
-{
-    DWORD result;
-    
-    result = WaitForSingleObject(mutex, INFINITE);
-    
-    /* both errors should never occur */
-    if (WAIT_TIMEOUT == result)
-    {
-	ERR("WaitForSingleObject timed out\n");
-	return FALSE;
-    }
-    
-    if (WAIT_FAILED == result)
-    {
-	ERR("WaitForSingleObject failed - error %li\n", GetLastError());
-	return FALSE;
-    }
-   /* TRACE("Handle Mutex created/reserved\n"); */
-    
-    return TRUE;
-}
-
-/******************************************************************************
- *              WDML_ReserveAtom
- *
- *      Routine to make an extra Add on an atom to reserve it a bit longer
- */
-
-void WDML_ReserveAtom(WDML_INSTANCE* thisInstance, HSZ hsz)
-{
-    if (thisInstance->unicode)
-    {
-	WCHAR SNameBuffer[MAX_BUFFER_LEN];
-	GlobalGetAtomNameW(hsz, SNameBuffer, MAX_BUFFER_LEN);
-	GlobalAddAtomW(SNameBuffer);
-    } else {
-	CHAR SNameBuffer[MAX_BUFFER_LEN];
-	GlobalGetAtomNameA(hsz, SNameBuffer, MAX_BUFFER_LEN);
-	GlobalAddAtomA(SNameBuffer);
-    }
-}
-
-
-/******************************************************************************
- *              WDML_ReleaseAtom
- *
- *      Routine to make a delete on an atom to release it a bit sooner
- */
-
-void WDML_ReleaseAtom(WDML_INSTANCE* thisInstance, HSZ hsz)
-{
-    GlobalDeleteAtom(hsz);
-}
-
-
 /*****************************************************************
  * DdeQueryStringA [USER32.@]
  */
 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
 {
     DWORD		ret = 0;
-    CHAR		pString[MAX_BUFFER_LEN];
-    WDML_INSTANCE*	thisInstance;
+    WDML_INSTANCE*	pInstance;
     
-    TRACE("(%ld, 0x%x, %p, %ld, %d): partial stub\n",
-	  idInst, hsz, psz, cchMax, iCodePage);
+    TRACE("(%ld, 0x%x, %p, %ld, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
     
-    if (WDML_MaxInstanceID == 0)
-    {
-	/*  Nothing has been initialised - exit now ! */
-	/*  needs something for DdeGetLAstError even if the manual doesn't say so */
-	return FALSE;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return FALSE;
-    }
+    EnterCriticalSection(&WDML_CritSect);
     
     /*  First check instance 
      */
-    thisInstance = WDML_FindInstance(idInst);
-    if (thisInstance == NULL)
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance != NULL)
     {
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
-	/*
-	  Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
-	*/
-	return FALSE;
+	if (iCodePage == 0) iCodePage = CP_WINANSI;
+	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
     }
+    LeaveCriticalSection(&WDML_CritSect);
     
-    if (iCodePage == CP_WINANSI)
-    {
-	/* If psz is null, we have to return only the length
-	 * of the string.
-	 */
-	if (psz == NULL)
-	{
-	    psz = pString;
-	    cchMax = MAX_BUFFER_LEN;
-	}
-	
-	ret = GlobalGetAtomNameA(hsz, (LPSTR)psz, cchMax);
-    }
-    
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    
-    TRACE("returning pointer\n"); 
+    TRACE("returning %s\n", debugstr_a(psz)); 
     return ret;
 }
 
@@ -1017,32 +1029,55 @@
 
 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
 {
-    DWORD	ret = 0;
-    WCHAR	pString[MAX_BUFFER_LEN];
-    int		factor = 1;
-    
-    TRACE("(%ld, 0x%x, %p, %ld, %d): partial-stub\n",
+    DWORD		ret = 0;
+    WDML_INSTANCE*	pInstance;
+
+    TRACE("(%ld, 0x%x, %p, %ld, %d)\n",
 	  idInst, hsz, psz, cchMax, iCodePage);
     
-    if (iCodePage == CP_WINUNICODE)
+    EnterCriticalSection(&WDML_CritSect);
+    
+    /*  First check instance 
+     */
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance != NULL)
     {
-	/* If psz is null, we have to return only the length
-	 * of the string.
-	 */
-	if (psz == NULL)
-	{
-	    psz = pString;
-	    cchMax = MAX_BUFFER_LEN;
-	    /* Note: According to documentation if the psz parameter
-	     * was NULL this API must return the length of the string in bytes.
-	     */
-	    factor = (int)sizeof(WCHAR)/sizeof(BYTE);
-	}
-	ret = GlobalGetAtomNameW(hsz, (LPWSTR)psz, cchMax) * factor;
+	if (iCodePage == 0) iCodePage = CP_WINUNICODE;
+	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
     }
+    LeaveCriticalSection(&WDML_CritSect);
+
+    TRACE("returning %s\n", debugstr_w(psz)); 
     return ret;
 }
 
+/******************************************************************
+ *		DML_CreateString
+ *
+ *
+ */
+static	HSZ	WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
+{
+    HSZ		hsz;
+
+    switch (codepage)
+    {
+    case CP_WINANSI:
+	hsz = AddAtomA(ptr);
+	TRACE("added atom %s with HSZ 0x%x, \n", debugstr_a(ptr), hsz);
+	break;
+    case CP_WINUNICODE:
+	hsz = AddAtomW(ptr);
+	TRACE("added atom %s with HSZ 0x%x, \n", debugstr_w(ptr), hsz);
+	break;
+    default:
+	ERR("Unknown code page %d\n", codepage);
+	return 0;
+    }
+    WDML_InsertHSZNode(pInstance, hsz);
+    return hsz;
+}
+
 /*****************************************************************
  * DdeCreateStringHandleA [USER32.@]
  *
@@ -1053,126 +1088,53 @@
 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
 {
     HSZ			hsz = 0;
-    WDML_INSTANCE*	thisInstance;
+    WDML_INSTANCE*	pInstance;
     
-    TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_a(psz),codepage);
+    TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
     
-    if (WDML_MaxInstanceID == 0)
+    EnterCriticalSection(&WDML_CritSect);
+    
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance)
     {
-	/*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
-	return FALSE;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return DMLERR_SYS_ERROR;
-    }
-    
-    
-    /*  First check instance 
-     */
-    thisInstance = WDML_FindInstance(idInst);
-    if (thisInstance == NULL)
-    {
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return 0;
-	/*
-	  Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
-	*/
-	return 0;
-    }
-    
-    if (codepage == CP_WINANSI)
-    {
-	hsz = GlobalAddAtomA(psz);
-	/* Save the handle so we know to clean it when
-	 * uninitialize is called.
-	 */
-	TRACE("added atom %s with HSZ 0x%x, \n",debugstr_a(psz),hsz);
-	WDML_InsertHSZNode(thisInstance, hsz);
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) 
-	{
-	    thisInstance->lastError = DMLERR_SYS_ERROR;
-	    return 0;
-	}
-	TRACE("Returning pointer\n");
-	return hsz;
+	if (codepage == 0) codepage = CP_WINANSI;
+	hsz = WDML_CreateString(pInstance, psz, codepage);
     } 
-    else 
-    {
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    }
-    TRACE("Returning error\n");
-    return 0;  
+
+    LeaveCriticalSection(&WDML_CritSect);
+    return hsz;  
 }
 
 
 /******************************************************************************
  * 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,   /* [in] Instance identifier */
-    LPCWSTR psz,    /* [in] Pointer to string */
-    INT codepage)   /* [in] Code page identifier */
+HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
 {
-    WDML_INSTANCE*	thisInstance;
+    WDML_INSTANCE*	pInstance;
     HSZ			hsz = 0;
     
-    TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
+    TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
     
+    EnterCriticalSection(&WDML_CritSect);
     
-    if (WDML_MaxInstanceID == 0)
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance)
     {
-	/*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
-	return FALSE;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return DMLERR_SYS_ERROR;
-    }
-    
-    /*  First check instance 
-     */
-    thisInstance = WDML_FindInstance(idInst);
-    if (thisInstance == NULL)
-    {
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return 0;
-	/*
-	  Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
-	*/
-	return 0;
-    }
-    
-    FIXME("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
-    
-    if (codepage == CP_WINUNICODE)
-    {
-	/*
-	 *  Should we be checking this against the unicode/ascii nature of the call to DdeInitialize ?
-	 */
-	hsz = GlobalAddAtomW(psz);
-	/* Save the handle so we know to clean it when
-	 * uninitialize is called.
-	 */
-	WDML_InsertHSZNode(thisInstance, hsz);
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) 
-	{
-	    thisInstance->lastError = DMLERR_SYS_ERROR;
-	    return 0;
-	}
-	TRACE("Returning pointer\n");
-	return hsz;
+	if (codepage == 0) codepage = CP_WINUNICODE;
+	hsz = WDML_CreateString(pInstance, psz, codepage);
     } 
-    else 
-    {
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    }
-    TRACE("Returning error\n");
-    return 0;
+    LeaveCriticalSection(&WDML_CritSect);
+
+    return hsz;
 }
 
 /*****************************************************************
@@ -1182,45 +1144,22 @@
  */
 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
 {
-    WDML_INSTANCE*	thisInstance;
-    HSZ			hsz2;
+    WDML_INSTANCE*	pInstance;
+    BOOL		ret = FALSE;
+
+    TRACE("(%ld,0x%x): \n", idInst, hsz);
     
-    TRACE("(%ld,%d): \n",idInst,hsz);
-    
-    if (WDML_MaxInstanceID == 0)
-    {
-	/*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
-	return TRUE;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return DMLERR_SYS_ERROR;
-    }
+    EnterCriticalSection(&WDML_CritSect);
     
     /*  First check instance 
      */
-    thisInstance = WDML_FindInstance(idInst);
-    if ((thisInstance == NULL) || (thisInstance->nodeList == NULL))
-    {
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return TRUE;
-	/*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
-	return TRUE;
-    }
-    
-    /* Remove the node associated with this HSZ.
-     */
-    hsz2 = thisInstance->nodeList->hsz2;	/* save this value first */
-    
-    WDML_RemoveHSZNode(thisInstance, hsz);
-    /* Free the string associated with this HSZ.
-     */
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    if (hsz2 != 0)
-    {
-	GlobalDeleteAtom(hsz2);
-    }
-    return GlobalDeleteAtom(hsz) ? 0 : hsz;
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance)
+	ret = WDML_DecHSZ(pInstance, hsz);
+
+    LeaveCriticalSection(&WDML_CritSect);
+
+    return ret;
 }
 
 /*****************************************************************
@@ -1231,39 +1170,91 @@
  */
 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
 {
+    WDML_INSTANCE*	pInstance;
+    BOOL		ret = FALSE;
+
+    TRACE("(%ld,0x%x): \n", idInst, hsz);
     
-    WDML_INSTANCE*	thisInstance;
-    
-    TRACE("(%ld,%d): \n",idInst,hsz);
-    
-    if (WDML_MaxInstanceID == 0)
-    {
-	/*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
-	return FALSE;
-    }
-    
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return FALSE;
-    }
+    EnterCriticalSection(&WDML_CritSect);
     
     /*  First check instance
      */
-    thisInstance = WDML_FindInstance(idInst);
-    if ((thisInstance == NULL) || (thisInstance->nodeList == NULL))
-    {
-	if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
-	/*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
-	return FALSE;
-	return FALSE;
-    }
-    WDML_ReserveAtom(thisInstance, hsz);
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    return TRUE;
+    pInstance = WDML_GetInstance(idInst);
+    if (pInstance)
+	ret = WDML_IncHSZ(pInstance, hsz);
+
+    LeaveCriticalSection(&WDML_CritSect);
+    return ret;
 }
 
 /*****************************************************************
+ *            DdeCmpStringHandles (USER32.@)
+ *
+ * Compares the value of two string handles.  This comparison is
+ * not case sensitive.
+ *
+ * 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)
+{
+    WCHAR	psz1[MAX_BUFFER_LEN];
+    WCHAR	psz2[MAX_BUFFER_LEN];
+    int		ret = 0;
+    int		ret1, ret2;
+    
+    ret1 = GetAtomNameW(hsz1, psz1, MAX_BUFFER_LEN);
+    ret2 = GetAtomNameW(hsz2, psz2, MAX_BUFFER_LEN);
+
+    TRACE("(%x<%s> %x<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
+    
+    /* 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
+    {
+	/* 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)
+	{
+	    ret = 1;
+	}
+    }
+    
+    return ret;
+}
+
+/* ================================================================
+ *
+ * 			Data handle management
+ *
+ * ================================================================ */
+
+/*****************************************************************
  *            DdeCreateDataHandle (USER32.@)
  */
 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, 
@@ -1330,57 +1321,20 @@
     return hData;
 }
 
-/*****************************************************************
- *            DdeSetUserHandle (USER32.@)
- */
-BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
-{
-    WDML_CONV*	pConv;
-    BOOL	ret = TRUE;
-
-    WDML_WaitForMutex(handle_mutex);
-
-    pConv = WDML_GetConv(hConv);
-    if (pConv == NULL)
-    {
-	ret = FALSE;
-	goto theError;
-    }
-    if (id == QID_SYNC) 
-    {
-	pConv->hUser = hUser;
-    }
-    else
-    {
-	WDML_XACT*	pXAct;
-
-	pXAct = WDML_FindTransaction(pConv, id);
-	if (pXAct)
-	{
-	    pXAct->hUser = hUser;
-	}
-	else
-	{
-	    pConv->thisInstance->lastError = DMLERR_UNFOUND_QUEUE_ID;
-	    ret = FALSE;
-	}
-    }
- theError:
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    return ret;
-}
-
 /******************************************************************************
  * DdeGetData [USER32.@]  Copies data from DDE object to local buffer
  *
+ *
+ * PARAMS
+ * hData	[I] Handle to DDE object
+ * pDst		[I] Pointer to destination buffer
+ * cbMax	[I] Amount of data to copy
+ * cbOff	[I] Offset to beginning of data
+ *
  * RETURNS
  *    Size of memory object associated with handle
  */
-DWORD WINAPI DdeGetData(
-    HDDEDATA hData, /* [in] Handle to DDE object */
-    LPBYTE pDst,    /* [in] Pointer to destination buffer */
-    DWORD cbMax,    /* [in] Amount of data to copy */
-    DWORD cbOff)    /* [in] Offset to beginning of data */
+DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
 {
     DWORD   dwSize, dwRet;
     LPBYTE  pByte;
@@ -1476,23 +1430,31 @@
  *	2	   16	clipboard format
  *	4	   ?	data to be used
  */
-HDDEDATA        WDML_Global2DataHandle(HGLOBAL hMem)
+HDDEDATA        WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
 {
     DDEDATA*    pDd;
- 
+    HDDEDATA	ret = 0;
+
     if (hMem)
     {
         pDd = GlobalLock(hMem);
         if (pDd)
         {
-            return DdeCreateDataHandle(0, pDd->Value,
-                                       GlobalSize(hMem) - (sizeof(DDEDATA) - 1),
-                                       0, 0, pDd->cfFormat, 0);
+	    if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
+            ret = DdeCreateDataHandle(0, pDd->Value,
+				      GlobalSize(hMem) - sizeof(WINE_DDEHEAD),
+				      0, 0, pDd->cfFormat, 0);
+	    GlobalUnlock(hMem);
         }
     }
-    return 0;
+    return ret;
 }
  
+/******************************************************************
+ *		WDML_DataHandle2Global
+ *
+ *
+ */
 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease, 
 			       BOOL fDeferUpd, BOOL fAckReq)
 {
@@ -1504,21 +1466,20 @@
     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
     if (dwSize && pDdh)
     {
-        hMem = GlobalAlloc(sizeof(DDEDATA) - 1 + dwSize,
-                           GMEM_MOVEABLE | GMEM_DDESHARE);
+        hMem = GlobalAlloc(sizeof(WINE_DDEHEAD) + dwSize, GMEM_MOVEABLE | GMEM_DDESHARE);
         if (hMem)
         {
-            DDEDATA*    ddeData;
+            WINE_DDEHEAD*    wdh;
  
-            ddeData = GlobalLock(hMem);
-            if (ddeData)
+            wdh = GlobalLock(hMem);
+            if (wdh)
             {
-                ddeData->fResponse = fResponse;
-                ddeData->fRelease = fRelease;
-		ddeData->reserved /*fDeferUpd*/ = fDeferUpd;
-                ddeData->fAckReq = fAckReq;
-                ddeData->cfFormat = pDdh->cfFormat;
-                memcpy(ddeData->Value, pDdh + 1, dwSize);
+                wdh->fResponse = fResponse;
+                wdh->fRelease = fRelease;
+		wdh->fDeferUpd = fDeferUpd;
+                wdh->fAckReq = fAckReq;
+                wdh->cfFormat = pDdh->cfFormat;
+                memcpy(wdh + 1, pDdh + 1, dwSize);
                 GlobalUnlock(hMem);
             }
         }
@@ -1526,196 +1487,6 @@
     }
  
     return hMem;
-}                                                                                                                                                                                 
-
-/*****************************************************************
- *            DdeEnableCallback (USER32.@)
- */
-BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
-{
-    FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
-    
-    return 0;
-}
-
-/******************************************************************************
- * DdeGetLastError [USER32.@]  Gets most recent error code
- *
- * PARAMS
- *    idInst [I] Instance identifier
- *
- * RETURNS
- *    Last error code
- */
-UINT WINAPI DdeGetLastError(DWORD idInst)
-{
-    DWORD		error_code;
-    WDML_INSTANCE*	thisInstance;
-    
-    FIXME("(%ld): error reporting is weakly implemented\n",idInst);
-    
-    if (WDML_MaxInstanceID == 0)
-    {
-	/*  Nothing has been initialised - exit now ! */
-	return DMLERR_DLL_NOT_INITIALIZED;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	return DMLERR_SYS_ERROR;
-    }
-    
-    /*  First check instance
-     */
-    thisInstance = WDML_FindInstance(idInst);
-    if  (thisInstance == NULL) 
-    {
-	error_code = DMLERR_DLL_NOT_INITIALIZED;
-    }
-    else
-    {
-	error_code = thisInstance->lastError;
-	thisInstance->lastError = 0;
-    }
-    
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-    return error_code;
-}
-
-/*****************************************************************
- *            DdeCmpStringHandles (USER32.@)
- *
- * Compares the value of two string handles.  This comparison is
- * not case sensitive.
- *
- * 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)
-{
-    CHAR psz1[MAX_BUFFER_LEN];
-    CHAR psz2[MAX_BUFFER_LEN];
-    int ret = 0;
-    int ret1, ret2;
-    
-    ret1 = GlobalGetAtomNameA(hsz1, psz1, MAX_BUFFER_LEN);
-    ret2 = GlobalGetAtomNameA(hsz2, psz2, MAX_BUFFER_LEN);
-    TRACE("(%04lx<%s> %04lx<%s>);\n", (DWORD)hsz1, psz1, (DWORD)hsz2, psz2);
-    
-    /* 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
-    {
-	/* Compare the two strings we got (case insensitive).
-	 */
-	ret = strcasecmp(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)
-	{
-	    ret = 1;
-	}
-    }
-    
-    return ret;
-}
-
-/******************************************************************
- *		DdeQueryConvInfo (USER32.@)
- *
- */
-UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
-{
-    UINT	ret = lpConvInfo->cb;
-    CONVINFO	ci;
-    WDML_CONV*	pConv;
-
-    FIXME("semi-stub.\n");
-
-    WDML_WaitForMutex(handle_mutex);
-
-    pConv = WDML_GetConv(hConv);
-    if (pConv == NULL)
-    {
-	WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE);
-	return 0;
-    }
-
-    ci.hConvPartner = 0; /* FIXME */
-    ci.hszSvcPartner = pConv->hszService;
-    ci.hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
-    ci.hszTopic = pConv->hszTopic;
-    ci.wStatus = ST_CLIENT /* FIXME */ | ST_CONNECTED;
-    ci.wConvst = 0; /* FIXME */
-    ci.wLastError = 0; /* FIXME: note it's not the instance last error */
-    ci.hConvList = 0;
-    ci.ConvCtxt = pConv->convContext;
-    if (ci.wStatus & ST_CLIENT)
-    {
-	ci.hwnd = pConv->hwndClient;
-	ci.hwndPartner = pConv->hwndServer;
-    }
-    else
-    {
-	ci.hwnd = pConv->hwndServer;
-	ci.hwndPartner = pConv->hwndClient;
-    }
-    if (id == QID_SYNC)
-    {
-	ci.hUser = pConv->hUser;
-	ci.hszItem = 0;
-	ci.wFmt = 0;
-	ci.wType = 0;
-    }
-    else
-    {
-	WDML_XACT*	pXAct;
-
-	pXAct = WDML_FindTransaction(pConv, id);
-	if (pXAct)
-	{
-	    ci.hUser = pXAct->hUser;
-	    ci.hszItem = 0; /* FIXME */
-	    ci.wFmt = 0; /* FIXME */
-	    ci.wType = 0; /* FIXME */
-	}
-	else
-	{
-	    ret = 0;
-	    pConv->thisInstance->lastError = DMLERR_UNFOUND_QUEUE_ID;
-	}
-    }
-
-    WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE);
-    memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
-    return ret;
 }
 
 /* ================================================================
@@ -1729,19 +1500,28 @@
  *
  *
  */
-WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
+WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
 {
-    WDML_SERVER* pServer;
+    WDML_SERVER* 	pServer;
+    char		buf1[256];
+    char		buf2[256];
     
     pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
     if (pServer == NULL) return NULL;
     
-    pServer->hszService = hszService;
-    pServer->hszTopic = 0;
+    WDML_IncHSZ(pInstance, pServer->hszService = hszService);
+
+    DdeQueryStringA(pInstance->instanceID, hszService, buf1, sizeof(buf1), CP_WINANSI);
+    snprintf(buf2, sizeof(buf2), "%s(0x%08lx)", buf1, GetCurrentProcessId());
+    pServer->hszServiceSpec = DdeCreateStringHandleA(pInstance->instanceID, buf2, CP_WINANSI);
+
+    pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
+    pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
+
     pServer->filterOn = TRUE;
     
-    pServer->next = thisInstance->servers;
-    thisInstance->servers = pServer;
+    pServer->next = pInstance->servers;
+    pInstance->servers = pServer;
     return pServer;
 }
 
@@ -1750,34 +1530,54 @@
  *
  *
  */
-void WDML_RemoveServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
+void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
 {
-    WDML_SERVER* pPrev = NULL;
-    WDML_SERVER* pCurrent = NULL;
+    WDML_SERVER*	pPrev = NULL;
+    WDML_SERVER*	pServer = NULL;
+    WDML_CONV*		pConv;
+    WDML_CONV*		pConvNext;
+
+    pServer = pInstance->servers;
     
-    pCurrent = thisInstance->servers;
-    
-    while (pCurrent != NULL)
+    while (pServer != NULL)
     {
-	if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0)
+	if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
 	{
-	    if (pCurrent == thisInstance->servers)
+	    WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER, 
+				     pServer->atomService, pServer->atomServiceSpec);
+	    /* terminate all conversations for given topic */
+	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
 	    {
-		thisInstance->servers = pCurrent->next;
+		pConvNext = pConv->next;
+		if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
+		{
+		    WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
+		    /* don't care about return code (whether client window is present or not) */
+		    PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0L);
+		}
+	    }
+	    if (pServer == pInstance->servers)
+	    {
+		pInstance->servers = pServer->next;
 	    }
 	    else
 	    {
-		pPrev->next = pCurrent->next;
+		pPrev->next = pServer->next;
 	    }
 	    
-	    DestroyWindow(pCurrent->hwndServer);
-	    
-	    HeapFree(GetProcessHeap(), 0, pCurrent);
+	    DestroyWindow(pServer->hwndServer);
+	    WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
+	    WDML_DecHSZ(pInstance, pServer->hszService);
+
+	    GlobalDeleteAtom(pServer->atomService);
+	    GlobalDeleteAtom(pServer->atomServiceSpec);
+
+	    HeapFree(GetProcessHeap(), 0, pServer);
 	    break;
 	}
 	
-	pPrev = pCurrent;
-	pCurrent = pCurrent->next;
+	pPrev = pServer;
+	pServer = pServer->next;
     }
 }
 
@@ -1787,13 +1587,12 @@
  *	generic routine to return a pointer to the relevant ServiceNode
  *	for a given service name, or NULL if the entry does not exist
  *
- *	ASSUMES the mutex protecting the handle entry list is reserved before calling
  */
-WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
+WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
 {
     WDML_SERVER*	pServer;
     
-    for (pServer = thisInstance->servers; pServer != NULL; pServer = pServer->next)
+    for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
     {
 	if (hszService == pServer->hszService)
 	{
@@ -1815,7 +1614,7 @@
  *
  *
  */
-WDML_CONV*	WDML_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
+WDML_CONV*	WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
 			     HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
 {
     WDML_CONV*	pConv;
@@ -1823,17 +1622,25 @@
     /* no converstation yet, add it */
     pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
     if (!pConv) return NULL;
-    
-    pConv->thisInstance = thisInstance;
-    pConv->hszService = hszService;
-    pConv->hszTopic = hszTopic;
+
+    pConv->instance = pInstance;
+    WDML_IncHSZ(pInstance, pConv->hszService = hszService);
+    WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
     pConv->hwndServer = hwndServer;
     pConv->hwndClient = hwndClient;
     pConv->transactions = NULL;
     pConv->hUser = 0;
+    pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
+    /* check if both side of the conversation are of the same instance */
+    if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
+	WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
+    {
+	pConv->wStatus |= ST_ISSELF;
+    }
+    pConv->wConvst = XST_NULL;
 
-    pConv->next = thisInstance->convs[side];
-    thisInstance->convs[side] = pConv;
+    pConv->next = pInstance->convs[side];
+    pInstance->convs[side] = pConv;
     
     return pConv;
 }
@@ -1843,12 +1650,12 @@
  *
  *
  */
-WDML_CONV*	WDML_FindConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, 
+WDML_CONV*	WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side, 
 			      HSZ hszService, HSZ hszTopic)
 {
     WDML_CONV*	pCurrent = NULL;
     
-    for (pCurrent = thisInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
+    for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
     {
 	if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
 	    DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
@@ -1865,21 +1672,48 @@
  *
  *
  */
-void WDML_RemoveConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, HCONV hConv)
+void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
 {
-    WDML_CONV* pPrev = NULL;
-    WDML_CONV* pRef = WDML_GetConv(hConv);
-    WDML_CONV* pCurrent = NULL;
+    WDML_CONV*	pPrev = NULL;
+    WDML_CONV* 	pCurrent;
+    WDML_XACT*	pXAct;
+    WDML_XACT*	pXActNext;
+    HWND	hWnd;
 
     if (!pRef)
 	return;
-    for (pCurrent = thisInstance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
+
+    /* remove any pending transaction */
+    for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
+    {
+	pXActNext = pXAct->next;
+	WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
+    }
+
+    WDML_RemoveAllLinks(pRef->instance, pRef, side);
+
+    /* FIXME: should we keep the window around ? it seems so (at least on client side
+     * to let QueryConvInfo work after conv termination, but also to implement
+     * DdeReconnect...
+     */
+    /* destroy conversation window, but first remove pConv from hWnd.
+     * this would help the wndProc do appropriate handling upon a WM_DESTROY message
+     */
+    hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
+    SetWindowLongA(hWnd, GWL_WDML_CONVERSATION, 0);
+
+    DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
+
+    WDML_DecHSZ(pRef->instance, pRef->hszService);
+    WDML_DecHSZ(pRef->instance, pRef->hszTopic);
+
+    for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
     {
 	if (pCurrent == pRef)
 	{
-	    if (pCurrent == thisInstance->convs[side])
+	    if (pCurrent == pRef->instance->convs[side])
 	    {
-		thisInstance->convs[side] = pCurrent->next;
+		pRef->instance->convs[side] = pCurrent->next;
 	    }
 	    else
 	    {
@@ -1892,15 +1726,239 @@
     }
 }
 
+/*****************************************************************
+ *            DdeEnableCallback (USER32.@)
+ */
+BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
+{
+    FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
+    
+    return 0;
+}
+
 /******************************************************************
  *		WDML_GetConv
  *
  * 
  */
-WDML_CONV*	WDML_GetConv(HCONV hConv)
+WDML_CONV*	WDML_GetConv(HCONV hConv, BOOL checkConnected)
 {
+    WDML_CONV*	pConv = (WDML_CONV*)hConv;
+
     /* FIXME: should do better checking */
-    return (WDML_CONV*)hConv;
+
+    if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
+    {
+	FIXME("found conv but ain't connected\n");
+	return NULL;
+    }
+    if (GetCurrentThreadId() != pConv->instance->threadID)
+    {
+	FIXME("wrong thread ID\n");
+	return NULL;
+    }
+
+    return pConv;
+}
+
+/******************************************************************
+ *		WDML_GetConvFromWnd
+ *
+ *
+ */
+WDML_CONV*	WDML_GetConvFromWnd(HWND hWnd)
+{
+    return (WDML_CONV*)GetWindowLongA(hWnd, GWL_WDML_CONVERSATION);
+}
+
+/******************************************************************
+ *		WDML_PostAck
+ *
+ *
+ */
+LPARAM		WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode, 
+			     BOOL fBusy, BOOL fAck, ATOM atom, LPARAM lParam, UINT oldMsg)
+{
+    DDEACK	ddeAck;
+    HWND	from, to;
+
+    if (side == WDML_SERVER_SIDE)
+    {
+	from = pConv->hwndServer;
+	to   = pConv->hwndClient;
+    }
+    else
+    {
+	to   = pConv->hwndServer;
+	from = pConv->hwndClient;
+    }
+
+    ddeAck.bAppReturnCode = appRetCode;
+    ddeAck.reserved       = 0;
+    ddeAck.fBusy          = fBusy;
+    ddeAck.fAck           = fAck;
+
+    TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
+		    
+    if (lParam) {
+	PostMessageA(to, WM_DDE_ACK, (WPARAM)from, 
+		     ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, atom));
+    }
+    else 
+    {
+	lParam = PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, atom);
+	PostMessageA(to, WM_DDE_ACK, (WPARAM)from, lParam);
+    }
+    return lParam;
+}
+
+/*****************************************************************
+ *            DdeSetUserHandle (USER32.@)
+ */
+BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
+{
+    WDML_CONV*	pConv;
+    BOOL	ret = TRUE;
+
+    EnterCriticalSection(&WDML_CritSect);
+
+    pConv = WDML_GetConv(hConv, FALSE);
+    if (pConv == NULL)
+    {
+	ret = FALSE;
+	goto theError;
+    }
+    if (id == QID_SYNC) 
+    {
+	pConv->hUser = hUser;
+    }
+    else
+    {
+	WDML_XACT*	pXAct;
+
+	pXAct = WDML_FindTransaction(pConv, id);
+	if (pXAct)
+	{
+	    pXAct->hUser = hUser;
+	}
+	else
+	{
+	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
+	    ret = FALSE;
+	}
+    }
+ theError:
+    LeaveCriticalSection(&WDML_CritSect);
+    return ret;
+}
+
+/******************************************************************
+ *		WDML_GetLocalConvInfo
+ *
+ *
+ */
+static	BOOL	WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
+{
+    BOOL 	ret = TRUE;
+    WDML_LINK*	pLink;
+    WDML_SIDE	side;
+
+    ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((DWORD)pConv | 1) : 0;
+    ci->hszSvcPartner = pConv->hszService;
+    ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
+    ci->hszTopic = pConv->hszTopic;
+    ci->wStatus = pConv->wStatus;
+
+    side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
+
+    for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
+    {
+	if (pLink->hConv == (HWND)pConv)
+	{
+	    ci->wStatus |= ST_ADVISE;
+	    break;
+	}
+    }
+
+    /* FIXME: non handled status flags:
+       ST_BLOCKED
+       ST_BLOCKNEXT
+       ST_INLIST
+    */
+
+    ci->wConvst = pConv->wConvst; /* FIXME */
+
+    ci->wLastError = 0; /* FIXME: note it's not the instance last error */
+    ci->hConvList = 0;
+    ci->ConvCtxt = pConv->convContext;
+    if (ci->wStatus & ST_CLIENT)
+    {
+	ci->hwnd = pConv->hwndClient;
+	ci->hwndPartner = pConv->hwndServer;
+    }
+    else
+    {
+	ci->hwnd = pConv->hwndServer;
+	ci->hwndPartner = pConv->hwndClient;
+    }
+    if (id == QID_SYNC)
+    {
+	ci->hUser = pConv->hUser;
+	ci->hszItem = 0;
+	ci->wFmt = 0;
+	ci->wType = 0;
+    }
+    else
+    {
+	WDML_XACT*	pXAct;
+
+	pXAct = WDML_FindTransaction(pConv, id);
+	if (pXAct)
+	{
+	    ci->hUser = pXAct->hUser;
+	    ci->hszItem = pXAct->hszItem;
+	    ci->wFmt = pXAct->wFmt;
+	    ci->wType = pXAct->wType;
+	}
+	else
+	{
+	    ret = 0;
+	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
+	}
+    }
+    return ret;
+}
+
+/******************************************************************
+ *		DdeQueryConvInfo (USER32.@)
+ *
+ */
+UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
+{
+    UINT	ret = lpConvInfo->cb;
+    CONVINFO	ci;
+    WDML_CONV*	pConv;
+
+    EnterCriticalSection(&WDML_CritSect);
+
+    pConv = WDML_GetConv(hConv, FALSE);
+    if (pConv != NULL && !WDML_GetLocalConvInfo(pConv, &ci, id))
+    {
+	ret = 0;
+    }
+    else if (hConv & 1)
+    {
+	pConv = WDML_GetConv(hConv & ~1, FALSE);
+	if (pConv != NULL)
+	{
+	    FIXME("Request on remote conversation information is not implemented yet\n");
+	    ret = 0;
+	}
+    }
+    LeaveCriticalSection(&WDML_CritSect);
+    if (ret != 0)
+	memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
+    return ret;
 }
 
 /* ================================================================
@@ -1914,13 +1972,11 @@
  *
  *
  */
-void WDML_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 
+void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
 		  UINT wType, HSZ hszItem, UINT wFmt)
 {
     WDML_LINK*	pLink;
     
-    TRACE("AddDdeLink was called...\n");
-    
     pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
     if (pLink == NULL)
     {
@@ -1930,11 +1986,11 @@
 
     pLink->hConv = hConv;
     pLink->transactionType = wType;
-    pLink->hszItem = hszItem;
+    WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
     pLink->uFmt = wFmt;
     pLink->hDdeData = 0;
-    pLink->next = thisInstance->links[side];
-    thisInstance->links[side] = pLink;
+    pLink->next = pInstance->links[side];
+    pInstance->links[side] = pLink;
 }
 
 /******************************************************************
@@ -1942,13 +1998,13 @@
  *
  *
  */
-void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 
+void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
 		     HSZ hszItem, UINT uFmt)
 {
     WDML_LINK* pPrev = NULL;
     WDML_LINK* pCurrent = NULL;
     
-    pCurrent = thisInstance->links[side];
+    pCurrent = pInstance->links[side];
     
     while (pCurrent != NULL)
     {
@@ -1956,9 +2012,9 @@
 	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
 	    pCurrent->uFmt == uFmt)
 	{
-	    if (pCurrent == thisInstance->links[side])
+	    if (pCurrent == pInstance->links[side])
 	    {
-		thisInstance->links[side] = pCurrent->next;
+		pInstance->links[side] = pCurrent->next;
 	    }
 	    else
 	    {
@@ -1969,7 +2025,8 @@
 	    {
 		DdeFreeDataHandle(pCurrent->hDdeData);
 	    }
-	    
+
+	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
 	    HeapFree(GetProcessHeap(), 0, pCurrent);
 	    break;
 	}
@@ -1988,21 +2045,21 @@
  *
  *
  */
-void WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side)
+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 = thisInstance->links[side];
+    pCurrent = pInstance->links[side];
     
     while (pCurrent != NULL)
     {
-	if (pCurrent->hConv == hConv)
+	if (pCurrent->hConv == (HCONV)pConv)
 	{
-	    if (pCurrent == thisInstance->links[side])
+	    if (pCurrent == pInstance->links[side])
 	    {
-		thisInstance->links[side] = pCurrent->next;
+		pInstance->links[side] = pCurrent->next;
 		pNext = pCurrent->next;
 	    }
 	    else
@@ -2015,6 +2072,7 @@
 	    {
 		DdeFreeDataHandle(pCurrent->hDdeData);
 	    }
+	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
 	    
 	    HeapFree(GetProcessHeap(), 0, pCurrent);
 	    pCurrent = NULL;
@@ -2037,15 +2095,14 @@
  *
  *
  */
-WDML_LINK* 	WDML_FindLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 
+WDML_LINK* 	WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
 			      HSZ hszItem, UINT uFmt)
 {
     WDML_LINK*	pCurrent = NULL;
     
-    for (pCurrent = thisInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
+    for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
     {
-	/* we don't need to check for transaction type as
-	   it can be altered */
+	/* we don't need to check for transaction type as it can be altered */
 	
 	if (pCurrent->hConv == hConv &&
 	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
@@ -2070,7 +2127,8 @@
  *
  * Alloc a transaction structure for handling the message ddeMsg
  */
-WDML_XACT*	WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg)
+WDML_XACT*	WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
+				      UINT wFmt, HSZ hszItem)
 {
     WDML_XACT*		pXAct;
     static WORD		tid = 1;	/* FIXME: wrap around */
@@ -2078,7 +2136,7 @@
     pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
     if (!pXAct) 
     {
-	thisInstance->lastError = DMLERR_MEMORY_ERROR;
+	pInstance->lastError = DMLERR_MEMORY_ERROR;
 	return NULL;
     }
 
@@ -2087,7 +2145,13 @@
     pXAct->hDdeData = 0;
     pXAct->hUser = 0;
     pXAct->next = NULL;
-    
+    pXAct->wType = 0;
+    pXAct->wFmt = wFmt;
+    WDML_IncHSZ(pInstance, pXAct->hszItem = hszItem);
+    pXAct->atom = 0;
+    pXAct->hMem = 0;
+    pXAct->lParam = 0;
+
     return pXAct;
 }
 
@@ -2130,8 +2194,13 @@
  *
  *
  */
-void	WDML_FreeTransaction(WDML_XACT* pXAct)
+void	WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
 {
+    /* free pmt(s) in pXAct too */
+    if (doFreePmt && pXAct->hMem)
+	GlobalFree(pXAct->hMem);
+    WDML_DecHSZ(pInstance, pXAct->hszItem);
+
     HeapFree(GetProcessHeap(), 0, pXAct);
 }
 
@@ -2153,6 +2222,12 @@
     return pXAct;
 }
 
+/* ================================================================
+ *
+ * 	   Information broadcast across DDEML implementations
+ *
+ * ================================================================ */
+
 struct tagWDML_BroadcastPmt
 {
     LPCSTR	clsName;
diff --git a/dlls/user/dde/server.c b/dlls/user/dde/server.c
index 656bbc9..7fdd75d 100644
--- a/dlls/user/dde/server.c
+++ b/dlls/user/dde/server.c
@@ -23,8 +23,9 @@
 
 DEFAULT_DEBUG_CHANNEL(ddeml);
 
-static const char szServerNameClassA[] = "DdeServerNameAnsi";
-static const char szServerConvClassA[] = "DdeServerConvAnsi";
+static const char  szServerNameClassA[] = "DdeServerNameAnsi";
+const char  WDML_szServerConvClassA[] = "DdeServerConvAnsi";
+const WCHAR WDML_szServerConvClassW[] = {'D','d','e','S','e','r','v','e','r','C','o','n','v','U','n','i','c','o','d','e',0};
 
 static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM);
 static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM);
@@ -32,60 +33,66 @@
 /******************************************************************************
  * DdePostAdvise [USER32.@]  Send transaction to DDE callback function.
  *
+ * PARAMS
+ *	idInst	  [I] Instance identifier
+ *	hszTopic  [I] Handle to topic name string
+ *	hszItem	  [I] Handle to item name string
+ *
  * RETURNS
  *    Success: TRUE
  *    Failure: FALSE
  */
-BOOL WINAPI DdePostAdvise(
-    DWORD idInst, /* [in] Instance identifier */
-    HSZ hszTopic, /* [in] Handle to topic name string */
-    HSZ hszItem)  /* [in] Handle to item name string */
+BOOL WINAPI DdePostAdvise(DWORD idInst, HSZ hszTopic, HSZ hszItem)
 {
-    WDML_INSTANCE*	thisInstance = NULL;
+    WDML_INSTANCE*	pInstance = NULL;
     WDML_LINK*		pLink = NULL;
     HDDEDATA		hDdeData = 0, hItemData = 0;
     WDML_CONV*		pConv = NULL;
-    CHAR 		pszTopic[MAX_BUFFER_LEN];
-    CHAR 		pszItem[MAX_BUFFER_LEN];
+    ATOM		atom = 0;
+    UINT		count;
+
+    TRACE("(%ld,0x%x,0x%x)\n", idInst, hszTopic, hszItem);
     
+    EnterCriticalSection(&WDML_CritSect);
+
+    pInstance = WDML_GetInstance(idInst);
     
-    TRACE("(%ld,%ld,%ld)\n",idInst,(DWORD)hszTopic,(DWORD)hszItem);
-    
-    if (idInst == 0)
+    if (pInstance == NULL || pInstance->links == NULL)
     {
-	return FALSE;
+	goto theError;
     }
     
-    thisInstance = WDML_FindInstance(idInst);
-    
-    if (thisInstance == NULL || thisInstance->links == NULL)
-    {
-	return FALSE;
-    }
-    
-    GlobalGetAtomNameA(hszTopic, (LPSTR)pszTopic, MAX_BUFFER_LEN);
-    GlobalGetAtomNameA(hszItem, (LPSTR)pszItem, MAX_BUFFER_LEN);
-    
-    for (pLink = thisInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
+    atom = WDML_MakeAtomFromHsz(hszItem);
+    if (!atom) goto theError;
+
+    /* first compute the number of links which will trigger a message */
+    count = 0;
+    for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
     {
 	if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
 	{
-	    hDdeData = 0;
-	    if (thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId()*/)
+	    count++;
+	}
+    }
+    if (count >= CADV_LATEACK)
+    {
+	FIXME("too high value for count\n");
+	count &= 0xFFFF;
+    }
+
+    for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
+    {
+	if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
+	{
+	    hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv,
+					   hszTopic, hszItem, 0, count--, 0);
+		
+	    if (hDdeData == CBR_BLOCK)
 	    {
-		
-		TRACE("Calling the callback, type=XTYP_ADVREQ, CB=0x%lx, hConv=0x%lx, Topic=%s, Item=%s\n",
-		      (DWORD)thisInstance->callback, (DWORD)pLink->hConv, pszTopic, pszItem);
-		hDdeData = (thisInstance->callback)(XTYP_ADVREQ,
-						    pLink->uFmt,
-						    pLink->hConv,
-						    hszTopic,
-						    hszItem,
-						    0, 0, 0);
-		TRACE("Callback was called\n");
-		
+		/* MS doc is not consistent here */
+		FIXME("CBR_BLOCK returned for ADVREQ\n");
+		continue;
 	    }
-	    
 	    if (hDdeData)
 	    {
 		if (pLink->transactionType & XTYPF_NODATA)
@@ -100,21 +107,33 @@
 		    hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
 		}
 		
-		pConv = WDML_GetConv(pLink->hConv);
+		pConv = WDML_GetConv(pLink->hConv, TRUE);
 		
-		if (pConv == NULL ||
-		    !PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
-				  PackDDElParam(WM_DDE_DATA, (UINT)hItemData, (DWORD)hszItem)))
+		if (pConv == NULL)
+		{
+		    /* FIXME: wrong if app owned... */
+		    DdeFreeDataHandle(hDdeData);
+		    goto theError;
+		}
+		
+		if (!PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
+				  PackDDElParam(WM_DDE_DATA, (UINT)hItemData, atom)))
 		{
 		    ERR("post message failed\n");
+		    /* FIXME: wrong if app owned... */
 		    DdeFreeDataHandle(hDdeData);
-		    return FALSE;
+		    GlobalFree(hItemData);
+		    goto theError;
 		}		    
 	    }
 	}
     }
-    
+    LeaveCriticalSection(&WDML_CritSect);
     return TRUE;
+ theError:
+    LeaveCriticalSection(&WDML_CritSect);
+    if (atom) GlobalDeleteAtom(atom);
+    return FALSE;
 }
 
 
@@ -134,113 +153,64 @@
 HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd)
 {
     WDML_SERVER*	pServer;
-    WDML_SERVER*	pServerTmp;
-    WDML_INSTANCE*	thisInstance;
+    WDML_INSTANCE*	pInstance;
     HDDEDATA 		hDdeData;
-    HSZ 		hsz2nd = 0;
     HWND 		hwndServer;
     WNDCLASSEXA  	wndclass;
     
     hDdeData = (HDDEDATA)NULL;
     
-    TRACE("(%ld,%d,%d,%d): stub\n",idInst,hsz1,hsz2,afCmd);
+    TRACE("(%ld,0x%x,0x%x,%d)\n", idInst, hsz1, hsz2, afCmd);
     
-    if (WDML_MaxInstanceID == 0)
-    {
-	/*  Nothing has been initialised - exit now ! 
-	 *	needs something for DdeGetLastError */
-	return 0;
-    }
-    
-    if (!WDML_WaitForMutex(handle_mutex))
-    {
-	/* FIXME: setError DMLERR_SYS_ERROR; */
-	return 0;
-    }
+    EnterCriticalSection(&WDML_CritSect);
     
     /*  First check instance
      */
-    thisInstance = WDML_FindInstance(idInst);
-    if  (thisInstance == NULL)
+    pInstance = WDML_GetInstance(idInst);
+    if  (pInstance == NULL)
     {
 	TRACE("Instance not found as initialised\n");
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
 	/*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
-	return FALSE;
+	goto theError;
     }
     
     if (hsz2 != 0L)
     {
 	/*	Illegal, reserved parameter
 	 */
-	thisInstance->lastError = DMLERR_INVALIDPARAMETER;
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-	FIXME("Reserved parameter no-zero !!\n");
-	return FALSE;
+	pInstance->lastError = DMLERR_INVALIDPARAMETER;
+	WARN("Reserved parameter no-zero !!\n");
+	goto theError;
     }
-    if (hsz1 == 0L)
+    if (hsz1 == 0 && afCmd != DNS_UNREGISTER)
     {
-	/*
-	 *	General unregister situation
+	/*	don't know if we should check this but it makes sense
+	 *	why supply REGISTER or filter flags if de-registering all
 	 */
-	if (afCmd != DNS_UNREGISTER)
-	{
-	    /*	don't know if we should check this but it makes sense
-	     *	why supply REGISTER or filter flags if de-registering all
-	     */
-	    TRACE("General unregister unexpected flags\n");
-	    thisInstance->lastError = DMLERR_DLL_USAGE;
-	    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-	    return FALSE;
-	}
-	/*	Loop to find all registered service and de-register them
-	 */
-	if (thisInstance->servers == NULL)
-	{
-	    /*  None to unregister !!  
-	     */
-	    TRACE("General de-register - nothing registered\n");
-	    thisInstance->lastError = DMLERR_DLL_USAGE;
-	    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-	    return FALSE;
-	}
-	else
-	{
-	    pServer = thisInstance->servers;
-	    while (pServer != NULL)
-	    {
-		TRACE("general deregister - iteration\n");
-		pServerTmp = pServer;
-		pServer = pServer->next;
-		WDML_ReleaseAtom(thisInstance, pServerTmp->hszService);
-		/* finished - release heap space used as work store */
-		HeapFree(GetProcessHeap(), 0, pServerTmp); 
-	    }
-	    thisInstance->servers = NULL;
-	    TRACE("General de-register - finished\n");
-	}
-	WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-	return (HDDEDATA)TRUE;
+	TRACE("General unregister unexpected flags\n");
+	pInstance->lastError = DMLERR_INVALIDPARAMETER;
+	goto theError;
     }
-    TRACE("Specific name action detected\n");
-    if (afCmd & DNS_REGISTER)
+    
+    switch (afCmd)
     {
-	/* Register new service name
-	 */
-	
-	pServer = WDML_FindServer(thisInstance, hsz1, 0);
+    case DNS_REGISTER:
+	pServer = WDML_FindServer(pInstance, hsz1, 0);
 	if (pServer)
-	    ERR("Trying to register already registered service!\n");
-	else
 	{
-	    TRACE("Adding service name\n");
+	    ERR("Trying to register already registered service!\n");
+	    pInstance->lastError = DMLERR_DLL_USAGE;
+	    goto theError;
+	}	    
+
+	TRACE("Adding service name\n");
 	    
-	    WDML_ReserveAtom(thisInstance, hsz1);
+	WDML_IncHSZ(pInstance, hsz1);
 	    
-	    pServer = WDML_AddServer(thisInstance, hsz1, 0);
+	pServer = WDML_AddServer(pInstance, hsz1, 0);
 	    
-	    WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER, hsz1, hsz2nd);
-	}
+	WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER, 
+				 pServer->atomService, pServer->atomServiceSpec);
 	
 	wndclass.cbSize        = sizeof(wndclass);
 	wndclass.style         = 0;
@@ -257,63 +227,59 @@
 	
 	RegisterClassExA(&wndclass);
 	
+	LeaveCriticalSection(&WDML_CritSect);
 	hwndServer = CreateWindowA(szServerNameClassA, NULL,
 				   WS_POPUP, 0, 0, 0, 0,
 				   0, 0, 0, 0);
-	
-	SetWindowLongA(hwndServer, 0, (DWORD)thisInstance);
+	EnterCriticalSection(&WDML_CritSect);
+
+	SetWindowLongA(hwndServer, GWL_WDML_INSTANCE, (DWORD)pInstance);
+	SetWindowLongA(hwndServer, GWL_WDML_SERVER, (DWORD)pServer);
 	TRACE("Created nameServer=%04x for instance=%08lx\n", hwndServer, idInst);
 	
 	pServer->hwndServer = hwndServer;
-    }
-    if (afCmd & DNS_UNREGISTER)
-    {
-	TRACE("Broadcasting WM_DDE_TERMINATE message\n");
-	SendMessageA(HWND_BROADCAST, WM_DDE_TERMINATE, (WPARAM)NULL,
-		     PackDDElParam(WM_DDE_TERMINATE, (UINT)hsz1, (UINT)hsz2));
-	
-	WDML_RemoveServer(thisInstance, hsz1, hsz2);
-    }
-    if (afCmd & DNS_FILTERON)
-    {
+	break;
+
+    case DNS_UNREGISTER:
+	if (hsz1 == 0L)
+	{
+	    /* General unregister situation
+	     * terminate all server side pending conversations 
+	     */
+	    while (pInstance->servers)
+		WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0);
+	    pInstance->servers = NULL;
+	    TRACE("General de-register - finished\n");
+	}
+	else
+	{
+	    WDML_RemoveServer(pInstance, hsz1, 0L);
+	}
+	break;
+    case DNS_FILTERON:
+    case DNS_FILTEROFF:
 	/*	Set filter flags on to hold notifications of connection
-	 *
-	 *	test coded this way as this is the default setting
 	 */
-	pServer = WDML_FindServer(thisInstance, hsz1, 0);
+	pServer = WDML_FindServer(pInstance, hsz1, 0);
 	if (!pServer)
 	{
 	    /*  trying to filter where no service names !!
 	     */
-	    thisInstance->lastError = DMLERR_DLL_USAGE;
-	    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-	    return FALSE;
+	    pInstance->lastError = DMLERR_DLL_USAGE;
+	    goto theError;
 	} 
 	else 
 	{
-	    pServer->filterOn = TRUE;
+	    pServer->filterOn = (afCmd == DNS_FILTERON);
 	}
+	break;
     }
-    if (afCmd & DNS_FILTEROFF)
-    {
-	/*	Set filter flags on to hold notifications of connection
-	 */
-	pServer = WDML_FindServer(thisInstance, hsz1, 0);
-	if (!pServer)
-	{
-	    /*  trying to filter where no service names !!
-	     */
-	    thisInstance->lastError = DMLERR_DLL_USAGE;
-	    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
-	    return FALSE;
-	} 	
-	else 
-	{
-	    pServer->filterOn = FALSE;
-	}
-    }
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
+    LeaveCriticalSection(&WDML_CritSect);
     return (HDDEDATA)TRUE;
+
+ theError:
+    LeaveCriticalSection(&WDML_CritSect);
+    return FALSE;
 }
 
 /******************************************************************
@@ -321,56 +287,85 @@
  *
  *
  */
-static BOOL WDML_CreateServerConv(WDML_INSTANCE* thisInstance, HWND hwndClient, HWND hwndServerName,
-				  HSZ hszApp, HSZ hszTopic)
+static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClient, 
+					HWND hwndServerName, HSZ hszApp, HSZ hszTopic)
 {
-    WNDCLASSEXA	wndclass;
     HWND	hwndServerConv;
     WDML_CONV*	pConv;
     
-    wndclass.cbSize        = sizeof(wndclass);
-    wndclass.style         = 0;
-    wndclass.lpfnWndProc   = WDML_ServerConvProc;
-    wndclass.cbClsExtra    = 0;
-    wndclass.cbWndExtra    = 2 * sizeof(DWORD);
-    wndclass.hInstance     = 0;
-    wndclass.hIcon         = 0;
-    wndclass.hCursor       = 0;
-    wndclass.hbrBackground = 0;
-    wndclass.lpszMenuName  = NULL;
-    wndclass.lpszClassName = szServerConvClassA;
-    wndclass.hIconSm       = 0;
-    
-    RegisterClassExA(&wndclass);
-    
-    hwndServerConv = CreateWindowA(szServerConvClassA, 0,
-				   WS_CHILD, 0, 0, 0, 0,
-				   hwndServerName, 0, 0, 0);
-    TRACE("Created convServer=%04x (nameServer=%04x) for instance=%08lx\n", 
-	  hwndServerConv, hwndServerName, thisInstance->instanceID);
-    
-    pConv = WDML_AddConv(thisInstance, WDML_SERVER_SIDE, hszApp, hszTopic, hwndClient, hwndServerConv);
-    
-    SetWindowLongA(hwndServerConv, 0, (DWORD)thisInstance);
-    SetWindowLongA(hwndServerConv, 4, (DWORD)pConv);
-    
-    /* this should be the only place using SendMessage for WM_DDE_ACK */
-    SendMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
-		 PackDDElParam(WM_DDE_ACK, (UINT)hszApp, (UINT)hszTopic));
-
-#if 0
-    if (thisInstance && thisInstance->callback != NULL /*&& thisInstance->Process_id == GetCurrentProcessId()*/)
+    if (pInstance->unicode)
     {
-	/* confirm connection...
-	 * FIXME: a better way would be to check for any incoming message if the conversation
-	 * exists (and/or) has been confirmed...
-	 * Anyway, always pretend we use a connection from a different instance...
-	 */
-	(thisInstance->callback)(XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv, hszApp, hszTopic, 0, 0, 0);
-    }
-#endif
+	WNDCLASSEXW	wndclass;
 
-    return TRUE;
+	wndclass.cbSize        = sizeof(wndclass);
+	wndclass.style         = 0;
+	wndclass.lpfnWndProc   = WDML_ServerConvProc;
+	wndclass.cbClsExtra    = 0;
+	wndclass.cbWndExtra    = 2 * sizeof(DWORD);
+	wndclass.hInstance     = 0;
+	wndclass.hIcon         = 0;
+	wndclass.hCursor       = 0;
+	wndclass.hbrBackground = 0;
+	wndclass.lpszMenuName  = NULL;
+	wndclass.lpszClassName = WDML_szServerConvClassW;
+	wndclass.hIconSm       = 0;
+
+	RegisterClassExW(&wndclass);
+    
+	hwndServerConv = CreateWindowW(WDML_szServerConvClassW, 0,
+				       WS_CHILD, 0, 0, 0, 0,
+				       hwndServerName, 0, 0, 0);
+    }
+    else
+    {
+	WNDCLASSEXA	wndclass;
+
+	wndclass.cbSize        = sizeof(wndclass);
+	wndclass.style         = 0;
+	wndclass.lpfnWndProc   = WDML_ServerConvProc;
+	wndclass.cbClsExtra    = 0;
+	wndclass.cbWndExtra    = 2 * sizeof(DWORD);
+	wndclass.hInstance     = 0;
+	wndclass.hIcon         = 0;
+	wndclass.hCursor       = 0;
+	wndclass.hbrBackground = 0;
+	wndclass.lpszMenuName  = NULL;
+	wndclass.lpszClassName = WDML_szServerConvClassA;
+	wndclass.hIconSm       = 0;
+
+	RegisterClassExA(&wndclass);
+    
+	hwndServerConv = CreateWindowA(WDML_szServerConvClassA, 0,
+				       WS_CHILD, 0, 0, 0, 0,
+				       hwndServerName, 0, 0, 0);
+    }
+
+    TRACE("Created convServer=%04x (nameServer=%04x) for instance=%08lx\n", 
+	  hwndServerConv, hwndServerName, pInstance->instanceID);
+    
+    pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic, 
+			 hwndClient, hwndServerConv);
+    if (pConv)
+    {
+	SetWindowLongA(hwndServerConv, GWL_WDML_INSTANCE, (DWORD)pInstance);
+	SetWindowLongA(hwndServerConv, GWL_WDML_CONVERSATION, (DWORD)pConv);
+
+	/* this should be the only place using SendMessage for WM_DDE_ACK */
+	SendMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
+		     PackDDElParam(WM_DDE_ACK, 
+				   WDML_MakeAtomFromHsz(hszApp), 
+				   WDML_MakeAtomFromHsz(hszTopic)));
+	/* we assume we're connected since we've sent an answer... 
+	 * I'm not sure what we can do... it doesn't look like the return value
+	 * of SendMessage is used... sigh...
+	 */
+	pConv->wStatus |= ST_CONNECTED;
+    }
+    else
+    {
+	DestroyWindow(hwndServerConv);
+    }
+    return pConv;
 }
 
 /******************************************************************
@@ -383,8 +378,8 @@
     HWND		hwndClient;
     HSZ			hszApp, hszTop;
     HDDEDATA		hDdeData = 0;
-    WDML_INSTANCE*	thisInstance;
-    UINT		uiLow, uiHi;
+    WDML_INSTANCE*	pInstance;
+    UINT		uiLo, uiHi;
     
     switch (iMsg)
     {
@@ -394,68 +389,97 @@
 	   LOWORD(lParam) -- application atom
 	   HIWORD(lParam) -- topic atom */
 	
-	TRACE("WM_DDE_INITIATE message received in the Server Proc!\n");
+	TRACE("WM_DDE_INITIATE message received!\n");
 	hwndClient = (HWND)wParam;
 	
+	pInstance = WDML_GetInstanceFromWnd(hwndServer);
+	TRACE("idInst=%ld, threadID=0x%lx\n", pInstance->instanceID, GetCurrentThreadId());
+	if (!pInstance) return 0;
+
 	/* don't free DDEParams, since this is a broadcast */
-	UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLow, &uiHi);	
+	UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLo, &uiHi);	
 	
-	hszApp = (HSZ)uiLow;
-	hszTop = (HSZ)uiHi;
+	hszApp = WDML_MakeHszFromAtom(pInstance, uiLo);
+	hszTop = WDML_MakeHszFromAtom(pInstance, uiHi);
 	
-	thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndServer, 0);
-	TRACE("idInst=%ld, ProcessID=0x%lx\n", thisInstance->instanceID, GetCurrentProcessId());
-	
-	if (hszApp && hszTop) 
+	if (!(pInstance->CBFflags & CBF_FAIL_CONNECTIONS))
 	{
-	    /* pass on to the callback  */
-	    if (thisInstance && thisInstance->callback != NULL /*&& thisInstance->Process_id == GetCurrentProcessId()*/)
+	    BOOL 		self = FALSE;
+	    CONVCONTEXT		cc;
+	    CONVCONTEXT*	pcc = NULL;
+	    WDML_CONV*		pConv;
+	    char		buf[256];
+
+	    if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
+		WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
 	    {
+		self = TRUE;
+	    }
+	    /* FIXME: so far, we don't grab distant convcontext, so only check if remote is
+	     * handled under DDEML, and if so build a default context
+	     */
+	    if ((GetClassNameA(hwndClient, buf, sizeof(buf)) && 
+		 strcmp(buf, WDML_szClientConvClassA) == 0) ||
+		(GetClassNameW(hwndClient, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) && 
+		 lstrcmpW((LPWSTR)buf, WDML_szClientConvClassW) == 0))
+	    {
+		pcc = &cc;
+		memset(pcc, 0, sizeof(*pcc));
+		pcc->cb = sizeof(*pcc);
+		pcc->iCodePage = IsWindowUnicode(hwndClient) ? CP_WINUNICODE : CP_WINANSI;
+	    }
+	    if ((pInstance->CBFflags & CBF_FAIL_SELFCONNECTIONS) && self)
+	    {
+		TRACE("Don't do self connection as requested\n");
+	    }
+	    else if (hszApp && hszTop) 
+	    {
+		WDML_SERVER*	pServer = (WDML_SERVER*)GetWindowLongA(hwndServer, GWL_WDML_SERVER);
 		
-		TRACE("calling the Callback, type = XTYP_CONNECT, CB=0x%lx\n",
-		      (DWORD)thisInstance->callback);
-		hDdeData = (thisInstance->callback)(XTYP_CONNECT,
-						    0, 0,
-						    hszTop,
-						    hszApp,
-						    0, 0, 0);
-		if ((UINT)hDdeData)
+		/* check filters for name service */
+		if (!pServer->filterOn || DdeCmpStringHandles(pServer->hszService, hszApp) == 0)
 		{
-		    WDML_CreateServerConv(thisInstance, hwndClient, hwndServer, hszApp, hszTop);
+		    /* pass on to the callback  */
+		    hDdeData = WDML_InvokeCallback(pInstance, XTYP_CONNECT,
+						   0, 0, hszTop, hszApp, 0, (DWORD)pcc, self);
+		    if ((UINT)hDdeData)
+		    {
+			pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer, 
+						      hszApp, hszTop);
+			if (pConv && pcc) pConv->wStatus |= ST_ISLOCAL;
+		    }
 		}
 	    }
-	}
-	else
-	{
-	    /* pass on to the callback  */
-	    if (thisInstance && thisInstance->callback != NULL /*&& thisInstance->Process_id == GetCurrentProcessId()*/)
+	    else if (pInstance->servers)
 	    {
-		TRACE("calling the Callback, type=XTYP_WILDCONNECT, CB=0x%lx\n",
-		      (DWORD)thisInstance->callback);
-		hDdeData = (thisInstance->callback)(XTYP_WILDCONNECT,
-						    0, 0,
-						    hszTop,
-						    hszApp,
-						    0, 0, 0);
-		if ((UINT)hDdeData)
+		/* pass on to the callback  */
+		hDdeData = WDML_InvokeCallback(pInstance, XTYP_WILDCONNECT,
+					       0, 0, hszTop, hszApp, 0, (DWORD)pcc, self);
+
+		if (hDdeData == CBR_BLOCK)
+		{
+		    /* MS doc is not consistent here */
+		    FIXME("CBR_BLOCK returned for WILDCONNECT\n");
+		}
+		else if ((UINT)hDdeData != 0)
 		{
 		    HSZPAIR*	hszp;
-		    
+		
 		    hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL);
 		    if (hszp)
 		    {
 			int	i;
 			for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++)
 			{
-			    WDML_CreateServerConv(thisInstance, hwndClient, hwndServer, 
-						  hszp[i].hszSvc, hszp[i].hszTopic);
-			}
+			    pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer, 
+							  hszp[i].hszSvc, hszp[i].hszTopic);
+			    if (pConv && pcc) pConv->wStatus |= ST_ISLOCAL;
+			}	
 			DdeUnaccessData(hDdeData);
 		    }
 		}
 	    }
 	}
-	
 	/*
 	  billx: make a conv and add it to the server list - 
 	  this can be delayed when link is created for the conv. NO NEED !!!
@@ -489,58 +513,89 @@
 }
 
 /******************************************************************
+ *		WDML_ServerQueueRequest
+ *
+ *
+ */
+static	WDML_XACT*	WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam)
+{
+    UINT		uiLo, uiHi;
+    WDML_XACT*		pXAct;
+
+    UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi);
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, 
+				  uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
+    if (pXAct) pXAct->atom = uiHi;
+    return pXAct;
+}
+
+/******************************************************************
  *		WDML_ServerHandleRequest
  *
  *
  */
-static	LRESULT	WDML_ServerHandleRequest(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, 
-					 HWND hwndServer, HWND hwndClient, LPARAM lParam)
+static	WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct)
 {
-    UINT		uiLow, uiHi;
-    HSZ			hszItem;
-    HDDEDATA		hDdeData;
+    HDDEDATA		hDdeData = 0;
+    WDML_QUEUE_STATE	ret = WDML_QS_HANDLED;
 
-    TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
-	
-    UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLow, &uiHi);
-
-    hszItem = (HSZ)uiHi;
-
-    hDdeData = 0;
-    if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */)
+    if (!(pConv->instance->CBFflags & CBF_FAIL_REQUESTS))
     {
 	    
-	TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x\n",
-	      thisInstance->instanceID, (DWORD)thisInstance->callback, XTYP_REQUEST);
-	hDdeData = (thisInstance->callback)(XTYP_REQUEST, uiLow, (HCONV)pConv, 
-					    pConv->hszTopic, hszItem, 0, 0, 0);
+	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_REQUEST, pXAct->wFmt, (HCONV)pConv, 
+				       pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
     }
-	
-    if (hDdeData)
+
+    switch (hDdeData)
     {
-	HGLOBAL	hMem = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
-	if (!PostMessageA(hwndClient, WM_DDE_DATA, (WPARAM)hwndServer,
-			  ReuseDDElParam(lParam, WM_DDE_REQUEST, WM_DDE_DATA, (UINT)hMem, (UINT)hszItem)))
-	{
-	    DdeFreeDataHandle(hDdeData);
-	    GlobalFree(hMem);
+    case 0:
+	WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, FALSE, pXAct->hszItem, 
+		     pXAct->lParam, WM_DDE_REQUEST);
+	break;
+    case CBR_BLOCK:
+	ret = WDML_QS_BLOCK;
+	break;
+    default:
+        {
+	    HGLOBAL	hMem = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
+	    if (!PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
+			      ReuseDDElParam(pXAct->lParam, WM_DDE_REQUEST, WM_DDE_DATA, 
+					     (UINT)hMem, (UINT)pXAct->atom)))
+	    {
+		DdeFreeDataHandle(hDdeData);
+		GlobalFree(hMem);
+	    }
 	}
+	break;
     }
-    else 
-    {
-	DDEACK	ddeAck;
+    WDML_DecHSZ(pConv->instance, pXAct->hszItem);
+    return ret;
+}
 
-	ddeAck.bAppReturnCode = 0;
-	ddeAck.reserved       = 0;
-	ddeAck.fBusy          = FALSE;
-	ddeAck.fAck           = FALSE;
+/******************************************************************
+ *		WDML_ServerQueueAdvise
+ *
+ *
+ */
+static	WDML_XACT*	WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam)
+{
+    UINT		uiLo, uiHi;
+    WDML_XACT*		pXAct;
+
+    /* XTYP_ADVSTART transaction: 
+       establish link and save link info to InstanceInfoTable */
 	
-	TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
-	PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer, 
-		     ReuseDDElParam(lParam, WM_DDE_REQUEST, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
-    }	
-
-    return 0;
+    UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi);
+	
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, 
+				  0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
+    if (pXAct)
+    {
+	pXAct->hMem = (HGLOBAL)uiLo;
+	pXAct->atom = uiHi;
+    }
+    return pXAct;
 }
 
 /******************************************************************
@@ -548,60 +603,36 @@
  *
  *
  */
-static	LRESULT	WDML_ServerHandleAdvise(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, 
-					HWND hwndServer, HWND hwndClient, LPARAM lParam)
+static	WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct)
 {
-    UINT		uiLo, uiHi, uType;
-    HGLOBAL		hDdeAdvise;
-    HSZ			hszItem;
+    UINT		uType;
     WDML_LINK*		pLink;
     DDEADVISE*		pDdeAdvise;
     HDDEDATA		hDdeData;
-    DDEACK		ddeAck;
+    BOOL		fAck;
 
-    /* XTYP_ADVSTART transaction: 
-       establish link and save link info to InstanceInfoTable */
-	
-    TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
-
-    UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi);
-	
-    hDdeAdvise = (HGLOBAL)uiLo;
-    hszItem    = (HSZ)uiHi; /* FIXME: it should be a global atom */
-
-    if (!pConv) 
-    {
-	ERR("Got an advise on a not known conversation, dropping request\n");
-	FreeDDElParam(WM_DDE_ADVISE, lParam);
-	return 0;
-    }
-
-    pDdeAdvise = (DDEADVISE*)GlobalLock(hDdeAdvise);
+    pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
     uType = XTYP_ADVSTART | 
 	    (pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) |
 	    (pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0);
 	
-    hDdeData = 0;
-    if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */)
+    if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
     {
-	TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x, uFmt=%x\n",
-	      thisInstance->instanceID, (DWORD)thisInstance->callback, 
-	      uType, pDdeAdvise->cfFormat);
-	hDdeData = (thisInstance->callback)(XTYP_ADVSTART, pDdeAdvise->cfFormat, (HCONV)pConv, 
-					    pConv->hszTopic, hszItem, 0, 0, 0);
+	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_ADVSTART, pDdeAdvise->cfFormat, 
+				       (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
     }
-    
-    ddeAck.bAppReturnCode = 0;
-    ddeAck.reserved       = 0;
-    ddeAck.fBusy          = FALSE;
-
-    if ((UINT)hDdeData || TRUE)	/* FIXME (from Corel ?) some apps don't return this value */
+    else
     {
-	ddeAck.fAck           = TRUE;
+	hDdeData = 0;
+    }
+
+    if ((UINT)hDdeData)
+    {
+	fAck           = TRUE;
 	
 	/* billx: first to see if the link is already created. */
-	pLink = WDML_FindLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE, 
-			      hszItem, pDdeAdvise->cfFormat);
+	pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
+			      pXAct->hszItem, pDdeAdvise->cfFormat);
 
 	if (pLink != NULL)
 	{
@@ -612,26 +643,43 @@
 	{
 	    TRACE("Adding Link with hConv=0x%lx\n", (DWORD)pConv);
 	    
-	    WDML_AddLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE, 
-			 uType, hszItem, pDdeAdvise->cfFormat);
+	    WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
+			 uType, pXAct->hszItem, pDdeAdvise->cfFormat);
 	}
     }
     else
     {
 	TRACE("No data returned from the Callback\n");
-	
-	ddeAck.fAck           = FALSE;
+	fAck = FALSE;
     }
 	
-    GlobalUnlock(hDdeAdvise);
-    if (ddeAck.fAck)
-	GlobalFree(hDdeAdvise);
-	
-    TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
-    PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer, 
-		 ReuseDDElParam(lParam, WM_DDE_ADVISE, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
+    GlobalUnlock(pXAct->hMem);
+    if (fAck)
+	GlobalFree(pXAct->hMem);
 
-    return 0L;
+    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE);
+
+    WDML_DecHSZ(pConv->instance, pXAct->hszItem);
+
+    return WDML_QS_HANDLED;
+}
+
+/******************************************************************
+ *		WDML_ServerQueueUnadvise
+ *
+ *
+ */
+static	WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam)
+{
+    UINT		uiLo, uiHi;
+    WDML_XACT*		pXAct;
+
+    UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
+	
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, 
+				  uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
+    if (pXAct) pXAct->atom = uiHi;
+    return pXAct;
 }
 
 /******************************************************************
@@ -639,103 +687,91 @@
  *
  *
  */
-static	LRESULT WDML_ServerHandleUnadvise(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, 
-					  HWND hwndServer, HWND hwndClient, LPARAM lParam)
+static	WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct)
 {
-    UINT		uiLo, uiHi;
-    HSZ			hszItem;
-    WDML_LINK*		pLink;
-    DDEACK		ddeAck;
+    WDML_LINK*	pLink;
 
-    TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
-	
-    /* billx: XTYP_ADVSTOP transaction */
-    UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
-	
-    /* uiLow: wFmt */
-    hszItem    = (HSZ)uiHi; /* FIXME: it should be a global atom */
-
-    if (hszItem == (HSZ)0 || uiLo == 0)
+    if (pXAct->hszItem == (HSZ)0 || pXAct->wFmt == 0)
     {
-	ERR("Unsupported yet options (null item or clipboard format\n");
+	ERR("Unsupported yet options (null item or clipboard format)\n");
+	return WDML_QS_ERROR;
     }
 
-    pLink = WDML_FindLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE, hszItem, uiLo);
+    pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
+			  pXAct->hszItem, pXAct->wFmt);
     if (pLink == NULL)
     {
-	ERR("Couln'd find link for %08lx, dropping request\n", (DWORD)hszItem);
-	FreeDDElParam(WM_DDE_UNADVISE, lParam);
-	return 0;
+	ERR("Couln'd find link for %08lx, dropping request\n", (DWORD)pXAct->hszItem);
+	FreeDDElParam(WM_DDE_UNADVISE, pXAct->lParam);
+	return WDML_QS_ERROR;
     }
 
-    /* callback shouldn't be invoked if CBF_FAIL_ADVISES is on. */
-    if (thisInstance && thisInstance->callback != NULL &&
-	!(thisInstance->CBFflags & CBF_SKIP_DISCONNECTS) /* && thisInstance->Process_id == GetCurrentProcessId() */)
+    if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
     {
-	TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x\n",
-	      thisInstance->instanceID, (DWORD)thisInstance->callback, XTYP_ADVSTOP);
-	(thisInstance->callback)(XTYP_ADVSTOP, uiLo, (HCONV)pConv, pConv->hszTopic, 
-				 hszItem, 0, 0, 0);
+	WDML_InvokeCallback(pConv->instance, XTYP_ADVSTOP, pXAct->wFmt, (HCONV)pConv, 
+			    pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
     }
 	
-    WDML_RemoveLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE, hszItem, uiLo);
+    WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
+		    pXAct->hszItem, pXAct->wFmt);
 	
     /* send back ack */
-    ddeAck.bAppReturnCode = 0;
-    ddeAck.reserved       = 0;
-    ddeAck.fBusy          = FALSE;
-    ddeAck.fAck           = TRUE;
-    
-    PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
-		 ReuseDDElParam(lParam, WM_DDE_UNADVISE, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
+    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, TRUE, pXAct->atom, 
+		 pXAct->lParam, WM_DDE_UNADVISE);
 	
-    return 0;
+    WDML_DecHSZ(pConv->instance, pXAct->hszItem);
+
+    return WDML_QS_HANDLED;
 }
 
 /******************************************************************
+ *		WDML_QueueExecute
+ *
+ *
+ */
+static	WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam)
+{
+    WDML_XACT*	pXAct;
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
+    if (pXAct)
+    {
+	pXAct->hMem    = (HGLOBAL)lParam;
+    }
+    return pXAct;    
+}
+
+ /******************************************************************
  *		WDML_ServerHandleExecute
  *
  *
  */
-static	LRESULT WDML_ServerHandleExecute(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, 
-					 HWND hwndServer, HWND hwndClient, LPARAM lParam)
+static	WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct)
 {
-    DDEACK		ddeAck;
-    HDDEDATA		hDdeData;
+    HDDEDATA	hDdeData = DDE_FNOTPROCESSED;
+    BOOL	fAck = FALSE, fBusy = FALSE;
 
-    TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
-	
-    if (hwndClient != pConv->hwndClient)
-	WARN("hmmm source window (%04x)\n", hwndClient);
-
-    hDdeData = 0;
-    if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */)
+    if (!(pConv->instance->CBFflags & CBF_FAIL_EXECUTES))
     {
-	LPVOID	ptr = GlobalLock((HGLOBAL)lParam);
+	LPVOID	ptr = GlobalLock(pXAct->hMem);
 	
 	if (ptr)
 	{
-	    hDdeData = DdeCreateDataHandle(0, ptr, GlobalSize((HGLOBAL)lParam),
+	    hDdeData = DdeCreateDataHandle(0, ptr, GlobalSize(pXAct->hMem),
 					   0, 0, CF_TEXT, 0);
-	    GlobalUnlock((HGLOBAL)lParam);
+	    GlobalUnlock(pXAct->hMem);
 	}  
-	TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x\n",
-	      thisInstance->instanceID, (DWORD)thisInstance->callback, XTYP_EXECUTE);
-	hDdeData = (thisInstance->callback)(XTYP_EXECUTE, 0, (HCONV)pConv, pConv->hszTopic, 0, 
-					    hDdeData, 0L, 0L);
+	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_EXECUTE, 0, (HCONV)pConv, 
+				       pConv->hszTopic, 0, hDdeData, 0L, 0L);
     }
 	
-    ddeAck.bAppReturnCode = 0;
-    ddeAck.reserved       = 0;
-    ddeAck.fBusy          = FALSE;
-    ddeAck.fAck           = FALSE;
     switch ((UINT)hDdeData)
     {
     case DDE_FACK:	
-	ddeAck.fAck = TRUE;	
+	fAck = TRUE;	
 	break;
     case DDE_FBUSY:	
-	ddeAck.fBusy = TRUE;	
+	fBusy = TRUE;	
 	break;
     default:	
 	WARN("Bad result code\n");
@@ -743,10 +779,31 @@
     case DDE_FNOTPROCESSED:				
 	break;
     }	
-    PostMessageA(pConv->hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
-		 PackDDElParam(WM_DDE_ACK, *((WORD*)&ddeAck), lParam));
+    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->hMem, 0, 0);
 	
-    return 0;
+    return WDML_QS_HANDLED;
+}
+
+/******************************************************************
+ *		WDML_ServerQueuePoke
+ *
+ *
+ */
+static	WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam)
+{
+    UINT		uiLo, uiHi;
+    WDML_XACT*		pXAct;
+
+    UnpackDDElParam(WM_DDE_POKE, lParam, &uiLo, &uiHi);
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, 
+				  0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
+    if (pXAct)
+    {
+	pXAct->atom = uiHi;
+	pXAct->hMem = (HGLOBAL)uiLo;
+    }
+    return pXAct;
 }
 
 /******************************************************************
@@ -754,52 +811,37 @@
  *
  *
  */
-static	LRESULT WDML_ServerHandlePoke(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, 
-				      HWND hwndServer, HWND hwndClient, LPARAM lParam)
+static	WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct)
 {
-    UINT		uiLo, uiHi;
-    HSZ			hszItem;
-    DDEACK		ddeAck;
     DDEPOKE*		pDdePoke;
     HDDEDATA		hDdeData;
+    BOOL		fBusy = FALSE, fAck = FALSE;
 
-    TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
-	
-    UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
-    hszItem = (HSZ)uiHi;
-
-    pDdePoke = (DDEPOKE*)GlobalLock((HGLOBAL)uiLo);
+    pDdePoke = (DDEPOKE*)GlobalLock(pXAct->hMem);
     if (!pDdePoke)
     {
-	return 0;
+	return WDML_QS_ERROR;
     }
 
-    ddeAck.bAppReturnCode = 0;
-    ddeAck.reserved       = 0;
-    ddeAck.fBusy          = FALSE;
-    ddeAck.fAck           = FALSE;
-    if (thisInstance && thisInstance->callback != NULL)
+    if (!(pConv->instance->CBFflags & CBF_FAIL_POKES))
     {
-	hDdeData = DdeCreateDataHandle(thisInstance->instanceID, pDdePoke->Value, 
-				       GlobalSize((HGLOBAL)uiLo) - sizeof(DDEPOKE) + 1, 
+	hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, pDdePoke->Value, 
+				       GlobalSize(pXAct->hMem) - sizeof(DDEPOKE) + 1, 
 				       0, 0, pDdePoke->cfFormat, 0);
 	if (hDdeData) 
 	{
 	    HDDEDATA	hDdeDataOut;
 	    
-	    TRACE("calling callback XTYP_POKE, idInst=%ld\n", 
-		  thisInstance->instanceID);
-	    hDdeDataOut = (thisInstance->callback)(XTYP_POKE,
-						   pDdePoke->cfFormat, (HCONV)pConv,
-						   pConv->hszTopic, (HSZ)uiHi, 
-						   hDdeData, 0, 0);
+	    hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_POKE, pDdePoke->cfFormat, 
+					      (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 
+					      hDdeData, 0, 0);
 	    switch ((UINT)hDdeDataOut) 
 	    {
 	    case DDE_FACK:
-		ddeAck.fAck = TRUE;	
+		fAck = TRUE;	
 		break;
 	    case DDE_FBUSY:
-		ddeAck.fBusy = TRUE;
+		fBusy = TRUE;
 		break;
 	    default:
 		FIXME("Unsupported returned value %08lx\n", (DWORD)hDdeDataOut);
@@ -810,15 +852,29 @@
 	    DdeFreeDataHandle(hDdeData);
 	}
     }
-    GlobalUnlock((HGLOBAL)uiLo);
+    GlobalUnlock(pXAct->hMem);
     
-    if (!ddeAck.fAck)
-	GlobalFree((HGLOBAL)uiHi);
-    
-    PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
-		 ReuseDDElParam(lParam, WM_DDE_POKE, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
+    if (!fAck)
+	GlobalFree(pXAct->hMem);
+   
+    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->atom, pXAct->lParam, WM_DDE_POKE);
 
-    return 0L;
+    WDML_DecHSZ(pConv->instance, pXAct->hszItem);
+
+    return WDML_QS_HANDLED;
+}
+
+/******************************************************************
+ *		WDML_ServerQueueTerminate
+ *
+ *
+ */
+static	WDML_XACT*	WDML_ServerQueueTerminate(WDML_CONV* pConv, LPARAM lParam)
+{
+    WDML_XACT*	pXAct;
+
+    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
+    return pXAct;
 }
 
 /******************************************************************
@@ -826,34 +882,69 @@
  *
  *
  */
-static	LRESULT WDML_ServerHandleTerminate(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, 
-					   HWND hwndServer, HWND hwndClient, LPARAM lParam)
+static	WDML_QUEUE_STATE WDML_ServerHandleTerminate(WDML_CONV* pConv, WDML_XACT* pXAct)
 {
-    UINT		uiLo, uiHi;
-    HSZ			hszApp, hszTop;
-
-    TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
-	
-    TRACE("WM_DDE_TERMINATE!\n");
-    /* XTYP_DISCONNECT transaction */
     /* billx: two things to remove: the conv, and associated links.
-       callback shouldn't be called if CBF_SKIP_DISCONNECTS is on.
-       Respond with another WM_DDE_TERMINATE iMsg.*/
-    
-    /* don't free DDEParams, since this is a broadcast */
-    UnpackDDElParam(WM_DDE_TERMINATE, lParam, &uiLo, &uiHi);	
+     * Respond with another WM_DDE_TERMINATE iMsg.
+     */
+    if (!(pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS))
+    {
+	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 0, 0, 
+			    0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
+    }
+
+    PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
+    WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
 	
-    hszApp = (HSZ)uiLo;
-    hszTop = (HSZ)uiHi;
+    return WDML_QS_HANDLED;
+}
+
+/******************************************************************
+ *		WDML_ServerHandle
+ *
+ *
+ */
+static WDML_QUEUE_STATE	WDML_ServerHandle(WDML_CONV* pConv, WDML_XACT* pXAct)
+{
+    WDML_QUEUE_STATE	qs = WDML_QS_ERROR;
+
+    switch (pXAct->ddeMsg)
+    {
+    case WM_DDE_INITIATE:
+	FIXME("WM_DDE_INITIATE shouldn't be there!\n");
+	break;
+    case WM_DDE_REQUEST:
+	qs = WDML_ServerHandleRequest(pConv, pXAct);
+	break;
+		
+    case WM_DDE_ADVISE:
+	qs = WDML_ServerHandleAdvise(pConv, pXAct);
+	break;
 	
-    WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER, hszApp, hszTop);
-    
-    /* PostMessageA(hwndClient, WM_DDE_TERMINATE, (WPARAM)hwndServer, (LPARAM)hConv); */
-    WDML_RemoveAllLinks(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE);
-    WDML_RemoveConv(thisInstance, WDML_SERVER_SIDE, (HCONV)pConv);
-    /* DestroyWindow(hwnd); don't destroy it now, we may still need it. */
+    case WM_DDE_UNADVISE:
+	qs = WDML_ServerHandleUnadvise(pConv, pXAct);
+	break;
 	
-    return 0;
+    case WM_DDE_EXECUTE:
+	qs = WDML_ServerHandleExecute(pConv, pXAct);
+	break;
+	
+    case WM_DDE_POKE:
+	qs = WDML_ServerHandlePoke(pConv, pXAct);
+	break;
+	
+    case WM_DDE_TERMINATE:
+	qs = WDML_ServerHandleTerminate(pConv, pXAct);
+	break;
+
+    case WM_DDE_ACK:
+	WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
+	break;
+
+    default:
+	FIXME("Unsupported message %d\n", pXAct->ddeMsg);
+    }
+    return qs;
 }
 
 /******************************************************************
@@ -863,52 +954,74 @@
  */
 static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
 {
-    WDML_INSTANCE*	thisInstance;
+    WDML_INSTANCE*	pInstance;
     WDML_CONV*		pConv;
+    WDML_XACT*		pXAct = NULL;
 
+    if (iMsg == WM_DESTROY)
+    {
+	EnterCriticalSection(&WDML_CritSect);
+	pConv = WDML_GetConvFromWnd(hwndServer);
+	if (pConv && !(pConv->wStatus & ST_TERMINATED))
+	{
+	    WDML_ServerHandleTerminate(pConv, NULL);
+	}
+	LeaveCriticalSection(&WDML_CritSect);
+    }
     if (iMsg < WM_DDE_FIRST || iMsg > WM_DDE_LAST)
     {
 	return DefWindowProcA(hwndServer, iMsg, wParam, lParam);
     }
 
-    TRACE("About to wait... \n");
+    EnterCriticalSection(&WDML_CritSect);
 
-    if (!WDML_WaitForMutex(handle_mutex))
+    pInstance = WDML_GetInstanceFromWnd(hwndServer);
+    pConv = WDML_GetConvFromWnd(hwndServer);
+
+    if (!pConv) 
     {
-	return 0;
+	ERR("Got a message (%u) on a not known conversation, dropping request\n", iMsg);
+	goto theError;
+    }
+    if (pConv->hwndClient != (HWND)wParam || pConv->hwndServer != hwndServer)
+    {
+	ERR("mismatch between C/S windows and converstation\n");
+	goto theError;
+    }
+    if (pConv->instance != pInstance || pConv->instance == NULL)
+    {
+	ERR("mismatch in instances\n");
+	goto theError;
     }
 
-    thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndServer, 0);
-    pConv = (WDML_CONV*)GetWindowLongA(hwndServer, 4);
-    
     switch (iMsg)
     {
     case WM_DDE_INITIATE:
-	FIXME("WM_DDE_INITIATE message received in the ServerConv Proc!\n");
+	FIXME("WM_DDE_INITIATE message received!\n");
 	break;
 	
     case WM_DDE_REQUEST:
-	WDML_ServerHandleRequest(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
+	pXAct = WDML_ServerQueueRequest(pConv, lParam);
 	break;
 		
     case WM_DDE_ADVISE:
-	WDML_ServerHandleAdvise(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
+	pXAct = WDML_ServerQueueAdvise(pConv, lParam);
 	break;
 	
     case WM_DDE_UNADVISE:
-	WDML_ServerHandleUnadvise(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
+	pXAct = WDML_ServerQueueUnadvise(pConv, lParam);
 	break;
 	
     case WM_DDE_EXECUTE:
-	WDML_ServerHandleExecute(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
+	pXAct = WDML_ServerQueueExecute(pConv, lParam);
 	break;
 	
     case WM_DDE_POKE:
-	WDML_ServerHandlePoke(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
+	pXAct = WDML_ServerQueuePoke(pConv, lParam);
 	break;
 	
     case WM_DDE_TERMINATE:
-	WDML_ServerHandleTerminate(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
+	pXAct = WDML_ServerQueueTerminate(pConv, lParam);
 	break;
 
     case WM_DDE_ACK:
@@ -918,8 +1031,20 @@
     default:
 	FIXME("Unsupported message %d\n", iMsg);
     }
-
-    WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
     
+    if (pXAct) 
+    {
+	pXAct->lParam = lParam;
+	if (WDML_ServerHandle(pConv, pXAct) == WDML_QS_BLOCK)
+	{
+	    WDML_QueueTransaction(pConv, pXAct);
+	}
+	else
+	{
+	    WDML_FreeTransaction(pInstance, pXAct, TRUE);
+	}
+    }
+ theError:
+    LeaveCriticalSection(&WDML_CritSect);
     return 0;
 }
diff --git a/dlls/user/user32.spec b/dlls/user/user32.spec
index fb0215b..f94c176 100644
--- a/dlls/user/user32.spec
+++ b/dlls/user/user32.spec
@@ -26,7 +26,7 @@
 @ stdcall BringWindowToTop(long) BringWindowToTop
 @ stdcall BroadcastSystemMessage(long ptr long long long) BroadcastSystemMessage
 @ stdcall CalcChildScroll(long long) CalcChildScroll
-@ stub CallMsgFilter
+@ stdcall CallMsgFilter(ptr long) CallMsgFilterA
 @ stdcall CallMsgFilterA(ptr long) CallMsgFilterA
 @ stdcall CallMsgFilterW(ptr long) CallMsgFilterW
 @ stdcall CallNextHookEx(long long long long) CallNextHookEx
@@ -107,8 +107,8 @@
 @ stdcall DdeConnect(long long long ptr) DdeConnect
 @ stdcall DdeConnectList(long long long long ptr) DdeConnectList
 @ stdcall DdeCreateDataHandle(long ptr long long long long long) DdeCreateDataHandle
-@ stdcall DdeCreateStringHandleA(long str long) DdeCreateStringHandleA
-@ stdcall DdeCreateStringHandleW(long wstr long) DdeCreateStringHandleW
+@ stdcall DdeCreateStringHandleA(long ptr long) DdeCreateStringHandleA
+@ stdcall DdeCreateStringHandleW(long ptr long) DdeCreateStringHandleW
 @ stdcall DdeDisconnect(long) DdeDisconnect
 @ stdcall DdeDisconnectList(long) DdeDisconnectList
 @ stdcall DdeEnableCallback(long long long) DdeEnableCallback
diff --git a/dlls/user/user_main.c b/dlls/user/user_main.c
index 202b86e..7932948 100644
--- a/dlls/user/user_main.c
+++ b/dlls/user/user_main.c
@@ -262,6 +262,9 @@
 {
     HQUEUE16 hQueue = GetThreadQueue16( 0 );
 
+    extern void WDML_NotifyThreadDetach(void);
+    WDML_NotifyThreadDetach();
+
     if (hQueue)
     {
         TIMER_RemoveQueueTimers( hQueue );
diff --git a/include/ddeml.h b/include/ddeml.h
index 7aef612..7c4a77c 100644
--- a/include/ddeml.h
+++ b/include/ddeml.h
@@ -177,6 +177,8 @@
 
 #define TIMEOUT_ASYNC           0xFFFFFFFF
 
+#define CADV_LATEACK		0xFFFF
+
 /**************************************************
 
 	End of Message Types Section
@@ -332,6 +334,7 @@
 BOOL      WINAPI DdeFreeDataHandle(HDDEDATA);
 BOOL      WINAPI DdeKeepStringHandle(DWORD,HSZ);
 HDDEDATA  WINAPI DdeClientTransaction(LPBYTE,DWORD,HCONV,HSZ,UINT,UINT,DWORD,LPDWORD);
+BOOL	  WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction); 
 BOOL      WINAPI DdeImpersonateClient(HCONV);
 BOOL      WINAPI DdePostAdvise(DWORD,HSZ,HSZ);
 HDDEDATA  WINAPI DdeAddData(HDDEDATA,LPBYTE,DWORD,DWORD);