From 78a4134e514e4a32ec392e1f5af8c65d54230174 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 9 Aug 2001 22:07:22 +0000 Subject: [PATCH] - 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 --- dlls/user/Makefile.in | 4 +- dlls/user/dde/.cvsignore | 1 + dlls/user/dde/client.c | 1086 +++++++++++-------- dlls/user/dde/dde_private.h | 173 +-- dlls/user/dde/ddeml16.c | 45 +- dlls/user/dde/misc.c | 2005 ++++++++++++++++++----------------- dlls/user/dde/server.c | 1029 ++++++++++-------- dlls/user/user32.spec | 6 +- dlls/user/user_main.c | 3 + include/ddeml.h | 3 + 10 files changed, 2433 insertions(+), 1922 deletions(-) create mode 100644 dlls/user/dde/.cvsignore diff --git a/dlls/user/Makefile.in b/dlls/user/Makefile.in index 59755858bf..0884e3e99b 100644 --- a/dlls/user/Makefile.in +++ b/dlls/user/Makefile.in @@ -37,7 +37,9 @@ RC_SRCS16 = \ 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 0000000000..9fd25db2ba --- /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 1086c426c9..cc29252fd7 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 WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic, 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 @@ HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic, */ 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,103 +84,133 @@ HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic, { HWND hwndClient; LPARAM lParam = 0; - UINT uiLow, uiHi; - WNDCLASSEXA wndclass; - WDML_INSTANCE* thisInstance; - WDML_CONV* pConv; - - TRACE("(0x%lx,%d,%d,%p)\n",idInst,hszService,hszTopic,pCC); + WDML_INSTANCE* pInstance; + WDML_CONV* pConv = NULL; + ATOM aSrv = 0, aTpc = 0; - 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; - - RegisterClassExA(&wndclass); + 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); - hwndClient = CreateWindowA(szClientClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0); + 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); - SetWindowLongA(hwndClient, 0, (DWORD)thisInstance); + hwndClient = CreateWindowA(WDML_szClientConvClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0); + } + + SetWindowLongA(hwndClient, GWL_WDML_INSTANCE, (DWORD)pInstance); - SendMessageA(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, - PackDDElParam(WM_DDE_INITIATE, (UINT)hszService, (UINT)hszTopic)); + 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); - 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; } - - 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)) + else { - ERR("DdeDisconnect(): PostMessage returned 0\n"); - return 0; + memset(&pConv->convContext, 0, sizeof(pConv->convContext)); + pConv->convContext.cb = sizeof(pConv->convContext); + pConv->convContext.iCodePage = (pInstance->unicode) ? CP_WINUNICODE : CP_WINANSI; } - return TRUE; -} + theEnd: + LeaveCriticalSection(&WDML_CritSect); + + if (aSrv) GlobalDeleteAtom(aSrv); + if (aTpc) GlobalDeleteAtom(aTpc); + return (HCONV)pConv; +} /***************************************************************** * DdeReconnect (DDEML.37) @@ -183,52 +218,116 @@ BOOL WINAPI DdeDisconnect(HCONV hConv) */ 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); + } + } + } -typedef enum { - WDML_QS_ERROR, WDML_QS_HANDLED, WDML_QS_PASS -} WDML_QUEUE_STATE; + theEnd: + LeaveCriticalSection(&WDML_CritSect); + + if (aSrv) GlobalDeleteAtom(aSrv); + if (aTpc) GlobalDeleteAtom(aTpc); + return (HCONV)pNewConv; +} /****************************************************************** - * 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 @@ static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_ { 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); - if (!pXAct) - return NULL; - - pXAct->u.unadvise.wFmt = wFmt; - pXAct->u.unadvise.hszItem = hszItem; - - WDML_QueueTransaction(pConv, pXAct); + atom = WDML_MakeAtomFromHsz(hszItem); + if (!atom) return NULL; - /* 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))) + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem); + if (!pXAct) { - 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 @@ static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDM { DDEACK ddeAck; UINT uiLo, uiHi; - WORD wStatus; + HSZ hsz; if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer) { @@ -338,14 +433,15 @@ static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDM } 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 @@ static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDM 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; - - TRACE("XTYP_REQUEST transaction\n"); - - pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_REQUEST); - if (!pXAct) - return NULL; + ATOM atom; - pXAct->u.request.hszItem = hszItem; + TRACE("XTYP_REQUEST transaction\n"); - WDML_QueueTransaction(pConv, pXAct); + atom = WDML_MakeAtomFromHsz(hszItem); + if (!atom) return NULL; - /* 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))) + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, wFmt, hszItem); + if (!pXAct) { - 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_XACT* WDML_QueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem) */ 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 @@ static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML } /****************************************************************** - * 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 WDML_XACT* WDML_QueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData) +static HGLOBAL WDML_BuildExecuteCommand(WDML_CONV* pConv, LPCVOID pData, DWORD cbData) { - WDML_XACT* pXAct; + HGLOBAL hMem; + BOOL clientUnicode, serverUnicode; + DWORD memSize; - TRACE("XTYP_EXECUTE transaction\n"); - - pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_EXECUTE); - if (!pXAct) - return NULL; + clientUnicode = IsWindowUnicode(pConv->hwndClient); + serverUnicode = IsWindowUnicode(pConv->hwndServer); - if (cbData == (DWORD)-1) + if (clientUnicode == serverUnicode) { - HDDEDATA hDdeData = (HDDEDATA)pData; - DWORD dwSize; - - pData = DdeAccessData(hDdeData, &dwSize); - if (pData) + memSize = cbData; + } + else + { + if (clientUnicode) { - pXAct->u.execute.hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize); - if (pXAct->u.execute.hMem) - { - LPBYTE pDst; + 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(pXAct->u.execute.hMem); - if (pDst) + pDst = GlobalLock(hMem); + if (pDst) + { + if (clientUnicode == serverUnicode) + { + memcpy(pDst, pData, cbData); + } + else + { + if (clientUnicode) { - memcpy(pDst, pData, dwSize); - GlobalUnlock(pXAct->u.execute.hMem); + WideCharToMultiByte( CP_ACP, 0, pData, cbData, pDst, memSize, NULL, NULL); } else { - GlobalFree(pXAct->u.execute.hMem); - pXAct->u.execute.hMem = 0; + MultiByteToWideChar( CP_ACP, 0, pData, cbData, (LPWSTR)pDst, memSize); } } - DdeUnaccessData(hDdeData); + + GlobalUnlock(hMem); } else { - pXAct->u.execute.hMem = 0; + GlobalFree(hMem); + hMem = 0; } } - else - { - LPSTR ptr; + return hMem; +} + +/****************************************************************** + * WDML_ClientQueueExecute + * + * + */ +static WDML_XACT* WDML_ClientQueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData) +{ + WDML_XACT* pXAct; + + TRACE("XTYP_EXECUTE transaction\n"); + + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0); + if (!pXAct) + return NULL; - pXAct->u.execute.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, cbData); - ptr = GlobalLock(pXAct->u.execute.hMem); - if (ptr) + if (cbData == (DWORD)-1) + { + HDDEDATA hDdeData = (HDDEDATA)pData; + + pData = DdeAccessData(hDdeData, &cbData); + if (pData) { - memcpy(ptr, pData, cbData); - GlobalUnlock(pXAct->u.execute.hMem); + pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData); + DdeUnaccessData(hDdeData); } } - - WDML_QueueTransaction(pConv, pXAct); - - if (!PostMessageA(pConv->hwndServer, WM_DDE_EXECUTE, (WPARAM)pConv->hwndClient, - pXAct->u.execute.hMem)) + else { - 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->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData); } + + pXAct->lParam = pXAct->hMem; + return pXAct; } @@ -522,7 +657,6 @@ static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML { DDEACK ddeAck; UINT uiLo, uiHi; - WORD wStatus; if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer) { @@ -532,69 +666,64 @@ static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML 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 @@ static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XA { DDEACK ddeAck; UINT uiLo, uiHi; - WORD wStatus; + HSZ hsz; if (msg->message != WM_DDE_ACK && msg->wParam != pConv->hwndServer) { @@ -615,117 +744,124 @@ static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XA } 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); + + WDML_ExtractAck(uiLo, &ddeAck); + GlobalFree(pXAct->hMem); - wStatus = uiLo; - ddeAck = *((DDEACK*)&wStatus); - if (!ddeAck.fAck) - { - GlobalFree(pXAct->u.poke.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. */ + pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz, wdh.cfFormat); + if (!pLink) + { + WDML_DecHSZ(pConv->instance, hsz); + return WDML_QS_PASS; + } - for (pLink = pConv->thisInstance->links[WDML_CLIENT_SIDE]; pLink != NULL; pLink = pLink->next) + if (hDdeDataIn != 0 && wdh.fAckReq) { - 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_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 @@ static WDML_QUEUE_STATE WDML_HandleReplyData(WDML_CONV* pConv, MSG* msg, HDDEDAT } /****************************************************************** - * 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_HandleReplyTerminate(WDML_CONV* pConv, MSG* msg, HD */ 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 @@ static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* h 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 @@ static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* h switch (qs) { case WDML_QS_ERROR: + case WDML_QS_SWALLOWED: *hdd = 0; break; case WDML_QS_HANDLED: @@ -800,39 +949,34 @@ static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* h * 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 WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* h */ 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 @@ static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML 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 @@ static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML (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 @@ static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML } TRACE("Timeout !!\n"); - if (WDML_WaitForMutex(handle_mutex)) - { - DWORD err; - WDML_CONV* pConv; - pConv = WDML_GetConv(hConv); - if (pConv == NULL) - { - return 0; - } - switch (pConv->transactions->ddeMsg) + EnterCriticalSection(&WDML_CritSect); + + pConv = WDML_GetConv(hConv, FALSE); + if (pConv != NULL) + { + if (pConv->transactions) { - 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; - } + 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->thisInstance->lastError = err; - WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); + pConv->instance->lastError = err; + } } + LeaveCriticalSection(&WDML_CritSect); + return 0; } @@ -926,9 +1074,8 @@ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HS 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 @@ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HS 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 @@ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HS 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 @@ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HS 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 @@ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HS *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 @@ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HS */ 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 @@ static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPA 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 @@ BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction 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 @@ BOOL WINAPI DdeImpersonateClient(HCONV hConv) 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 65b0639e37..aa5c682164 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 @@ typedef struct tagWDML_INSTANCE { 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 @@ typedef struct tagWDML_INSTANCE 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 @@ typedef struct tagWDML_INSTANCE 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 @@ typedef enum tagWDML_SIDE 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 1d20e26dbb..8de932ae31 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 @@ typedef struct 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 @@ typedef struct 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 DdeDisconnect16(HCONV hConv) */ 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 @@ HSZ WINAPI DdeCreateStringHandle16(DWORD idInst, LPCSTR str, INT16 codepage) if (codepage) { return DdeCreateStringHandleA(idInst, str, codepage); - } else { + } + else + { TRACE("Default codepage supplied\n"); return DdeCreateStringHandleA(idInst, str, CP_WINANSI); } @@ -178,7 +195,7 @@ HSZ WINAPI DdeCreateStringHandle16(DWORD idInst, LPCSTR str, INT16 codepage) */ 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 @@ HDDEDATA WINAPI DdeClientTransaction16(LPVOID pData, DWORD cbData, BOOL16 WINAPI DdeAbandonTransaction16(DWORD idInst, HCONV hConv, DWORD idTransaction) { - FIXME("empty stub\n"); - return TRUE; + return DdeAbandonTransaction(idInst, hConv, idTransaction); } /***************************************************************** @@ -256,7 +272,8 @@ DWORD WINAPI DdeGetData16( */ 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 @@ LPBYTE WINAPI DdeAccessData16(HDDEDATA hData, LPDWORD pcbDataSize) */ BOOL16 WINAPI DdeUnaccessData16(HDDEDATA hData) { - return DdeUnaccessData(hData); + return DdeUnaccessData(hData); } /***************************************************************** @@ -272,7 +289,7 @@ BOOL16 WINAPI DdeUnaccessData16(HDDEDATA hData) */ BOOL16 WINAPI DdeEnableCallback16(DWORD idInst, HCONV hConv, UINT16 wCmd) { - return DdeEnableCallback(idInst, hConv, wCmd); + return DdeEnableCallback(idInst, hConv, wCmd); } /***************************************************************** @@ -281,7 +298,7 @@ BOOL16 WINAPI DdeEnableCallback16(DWORD idInst, HCONV hConv, UINT16 wCmd) 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 @@ HDDEDATA WINAPI DdeNameService16(DWORD idInst, HSZ hsz1, HSZ hsz2, */ UINT16 WINAPI DdeGetLastError16(DWORD idInst) { - return (UINT16)DdeGetLastError(idInst); + return (UINT16)DdeGetLastError(idInst); } /***************************************************************** @@ -297,7 +314,7 @@ UINT16 WINAPI DdeGetLastError16(DWORD idInst) */ 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 c1ef012b65..62835c5898 100644 --- a/dlls/user/dde/misc.c +++ b/dlls/user/dde/misc.c @@ -11,6 +11,7 @@ */ #include +#include #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 @@ LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut, /***************************************************************** * 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 @@ BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SE 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)) + { + 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)) { - if (thisInstance->CBFflags & CBF_SKIP_DISCONNECTS) + 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 @@ UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, { /* 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 @@ UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, * 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 @@ UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, * 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 @@ UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, /* 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 @@ UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, } /* 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 @@ UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, 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,386 +618,408 @@ BOOL WINAPI DdeUninitialize(DWORD idInst) { /* 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; + + EnterCriticalSection(&WDML_CritSect); - 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; - } /* 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)) - { - /* should record something here, but nothing left to hang it from !! - */ - return FALSE; - } + HeapFree(GetProcessHeap(), 0, pInstance); + LeaveCriticalSection(&WDML_CritSect); return TRUE; } -/****************************************************************************** - * RemoveHSZNodes (INTERNAL) +/****************************************************************** + * WDML_NotifyThreadExit + * * - * Remove a node from the list of HSZ nodes. */ -static void WDML_RemoveHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz) +void WDML_NotifyThreadDetach(void) { - 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) + WDML_INSTANCE* pInstance; + WDML_INSTANCE* next; + DWORD tid = GetCurrentThreadId(); + + EnterCriticalSection(&WDML_CritSect); + for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next) { - /* If we found the node we were looking for. - */ - if (pCurrent->hsz == hsz) + next = pInstance->next; + if (pInstance->threadID == tid) { - /* 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) - { - thisInstance->nodeList = pCurrent->next; - } - /* 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; + DdeUninitialize(pInstance->instanceID); } - /* Save the previous node pointer. - */ - pPrev = pCurrent; - /* Move on to the next node. - */ - pCurrent = pCurrent->next; } + LeaveCriticalSection(&WDML_CritSect); } -/****************************************************************************** - * FreeAndRemoveHSZNodes (INTERNAL) +/****************************************************************** + * WDML_InvokeCallback * - * 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) -{ - /* Free any strings created in this instance. - */ - while (thisInstance->nodeList != NULL) - { - DdeFreeStringHandle(thisInstance->instanceID, thisInstance->nodeList->hsz); - } -} - -/****************************************************************************** - * GetSecondaryHSZValue (INTERNAL) * - * Insert a node to the head of the list. */ -static HSZ WDML_GetSecondaryHSZValue(WDML_INSTANCE* thisInstance, HSZ hsz) +HDDEDATA WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv, + HSZ hsz1, HSZ hsz2, HDDEDATA hdata, + DWORD dwData1, DWORD dwData2) { - HSZ hsz2 = 0; - - if (hsz != 0) + 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) { - /* 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); - } - } + ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv, + hsz1, hsz2, hdata, dwData1, dwData2); } - return hsz2; -} - -/****************************************************************************** - * InsertHSZNode (INTERNAL) - * - * Insert a node to the head of the list. - */ -static void WDML_InsertHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz) -{ - if (hsz != 0) + else { - HSZNode* pNew = NULL; - /* Create a new node for this HSZ. - */ - 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; - } - else - { - ERR("Primary HSZ Node allocation failed - out of memory\n"); - } + ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2); } + TRACE("done => %08lx\n", (DWORD)ret); + return ret; } /***************************************************************************** - * Find_Instance_Entry + * 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 * - * ASSUMES the mutex protecting the handle entry list is reserved before calling */ -WDML_INSTANCE* WDML_FindInstance(DWORD InstId) +WDML_INSTANCE* WDML_GetInstance(DWORD instId) { - WDML_INSTANCE* thisInstance; + WDML_INSTANCE* pInstance; - thisInstance = WDML_InstanceList; - while (thisInstance != NULL) + for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next) { - if (thisInstance->instanceID == InstId) + if (pInstance->instanceID == instId) { - return thisInstance; + if (GetCurrentThreadId() != pInstance->threadID) + { + FIXME("Tried to get instance from wrong thread\n"); + continue; + } + return pInstance; } - thisInstance = thisInstance->next; } TRACE("Instance entry missing\n"); return NULL; } -/****************************************************************************** - * WDML_ReleaseMutex +/****************************************************************** + * WDML_GetInstanceFromWnd + * * - * generic routine to release a reserved mutex */ -DWORD WDML_ReleaseMutex(HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m) +WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd) { - if (!ReleaseMutex(mutex)) - { - ERR("ReleaseMutex failed - %s mutex %li\n", mutex_name, GetLastError()); - if (release_handle_m) - { - ReleaseMutex(handle_mutex); - } - return DMLERR_SYS_ERROR; - } - return DMLERR_NO_ERROR; + return (WDML_INSTANCE*)GetWindowLongA(hWnd, GWL_WDML_INSTANCE); } /****************************************************************************** - * WDML_WaitForMutex + * DdeGetLastError [USER32.@] Gets most recent error code + * + * PARAMS + * idInst [I] Instance identifier * - * generic routine to wait for the mutex + * RETURNS + * Last error code */ -BOOL WDML_WaitForMutex(HANDLE mutex) +UINT WINAPI DdeGetLastError(DWORD idInst) { - DWORD result; + DWORD error_code; + WDML_INSTANCE* pInstance; + + FIXME("(%ld): error reporting is weakly implemented\n", idInst); - result = WaitForSingleObject(mutex, INFINITE); + EnterCriticalSection(&WDML_CritSect); - /* both errors should never occur */ - if (WAIT_TIMEOUT == result) + /* First check instance + */ + pInstance = WDML_GetInstance(idInst); + if (pInstance == NULL) { - ERR("WaitForSingleObject timed out\n"); - return FALSE; + error_code = DMLERR_DLL_NOT_INITIALIZED; } - - if (WAIT_FAILED == result) + else { - ERR("WaitForSingleObject failed - error %li\n", GetLastError()); - return FALSE; + error_code = pInstance->lastError; + pInstance->lastError = 0; } - /* TRACE("Handle Mutex created/reserved\n"); */ - return TRUE; + LeaveCriticalSection(&WDML_CritSect); + return error_code; } -/****************************************************************************** - * WDML_ReserveAtom +/* ================================================================ * - * Routine to make an extra Add on an atom to reserve it a bit longer - */ + * String management + * + * ================================================================ */ -void WDML_ReserveAtom(WDML_INSTANCE* thisInstance, HSZ hsz) + +/****************************************************************** + * WDML_FindNode + * + * + */ +static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz) { - if (thisInstance->unicode) + HSZNode* pNode; + + if (pInstance == NULL) return NULL; + + for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next) { - 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); + if (pNode->hsz == hsz) break; } + if (!pNode) WARN("HSZ 0x%x not found\n", hsz); + return pNode; } - -/****************************************************************************** - * WDML_ReleaseAtom +/****************************************************************** + * WDML_MakeAtomFromHsz * - * Routine to make a delete on an atom to release it a bit sooner + * Creates a global atom from an existing HSZ + * Generally used before sending an HSZ as an atom to a remote app */ - -void WDML_ReleaseAtom(WDML_INSTANCE* thisInstance, HSZ hsz) +ATOM WDML_MakeAtomFromHsz(HSZ hsz) { - GlobalDeleteAtom(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; +} -/***************************************************************** - * DdeQueryStringA [USER32.@] +/****************************************************************** + * WDML_MakeHszFromAtom + * + * Creates a HSZ from an existing global atom + * Generally used while receiving a global atom and transforming it + * into an HSZ */ -DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage) +HSZ WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom) { - DWORD ret = 0; - CHAR pString[MAX_BUFFER_LEN]; - WDML_INSTANCE* thisInstance; - - TRACE("(%ld, 0x%x, %p, %ld, %d): partial stub\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)) + 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; +} + +/****************************************************************************** + * WDML_DecHSZ (INTERNAL) + * + * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list + * of HSZ nodes + * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count + */ +BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz) +{ + HSZNode* pPrev = NULL; + HSZNode* pCurrent; + + for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next) { - return FALSE; + /* If we found the node we were looking for and its ref count is one, + * we can remove it + */ + if (pCurrent->hsz == hsz) + { + if (--pCurrent->refCount == 0) + { + if (pCurrent == pInstance->nodeList) + { + pInstance->nodeList = pCurrent->next; + } + else + { + pPrev->next = pCurrent->next; + } + HeapFree(GetProcessHeap(), 0, pCurrent); + DeleteAtom(hsz); + } + return TRUE; + } } - - /* First check instance + WARN("HSZ 0x%xnot found\n", hsz); + + return FALSE; +} + +/****************************************************************************** + * 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* pInstance) +{ + /* Free any strings created in this instance. */ - thisInstance = WDML_FindInstance(idInst); - if (thisInstance == NULL) + while (pInstance->nodeList != NULL) { - if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE; - /* - Needs something here to record NOT_INITIALIZED ready for DdeGetLastError - */ - return FALSE; + DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz); } - - if (iCodePage == CP_WINANSI) +} + +/****************************************************************************** + * InsertHSZNode (INTERNAL) + * + * Insert a node to the head of the list. + */ +static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz) +{ + if (hsz != 0) { - /* If psz is null, we have to return only the length - * of the string. + HSZNode* pNew = NULL; + /* Create a new node for this HSZ. */ - if (psz == NULL) + pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode)); + if (pNew != NULL) { - psz = pString; - cchMax = MAX_BUFFER_LEN; + pNew->hsz = hsz; + pNew->next = pInstance->nodeList; + pNew->refCount = 1; + pInstance->nodeList = pNew; + } + else + { + ERR("Primary HSZ Node allocation failed - out of memory\n"); } - - ret = GlobalGetAtomNameA(hsz, (LPSTR)psz, cchMax); } +} + +/****************************************************************** + * WDML_QueryString + * + * + */ +static int WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax, + int codepage) +{ + WCHAR pString[MAX_BUFFER_LEN]; + int ret; + /* If psz is null, we have to return only the length + * of the string. + */ + if (ptr == NULL) + { + ptr = pString; + cchMax = MAX_BUFFER_LEN; + } + + switch (codepage) + { + 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 ret; +} + +/***************************************************************** + * DdeQueryStringA [USER32.@] + */ +DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage) +{ + DWORD ret = 0; + WDML_INSTANCE* pInstance; + + TRACE("(%ld, 0x%x, %p, %ld, %d)\n", idInst, hsz, psz, cchMax, iCodePage); - WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); + EnterCriticalSection(&WDML_CritSect); - TRACE("returning pointer\n"); + /* First check instance + */ + pInstance = WDML_GetInstance(idInst); + if (pInstance != NULL) + { + if (iCodePage == 0) iCodePage = CP_WINANSI; + ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage); + } + LeaveCriticalSection(&WDML_CritSect); + + TRACE("returning %s\n", debugstr_a(psz)); return ret; } @@ -1017,32 +1029,55 @@ DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT 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 @@ DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, IN 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) - { - /* 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; - } + EnterCriticalSection(&WDML_CritSect); - if (codepage == CP_WINANSI) + pInstance = WDML_GetInstance(idInst); + if (pInstance) { - 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); - - - 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 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; - } + TRACE("(%ld,%p,%d)\n", idInst, psz, codepage); - FIXME("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage); + EnterCriticalSection(&WDML_CritSect); - if (codepage == CP_WINUNICODE) + pInstance = WDML_GetInstance(idInst); + if (pInstance) { - /* - * 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 @@ HSZ WINAPI DdeCreateStringHandleW( */ BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz) { - WDML_INSTANCE* thisInstance; - HSZ hsz2; - - TRACE("(%ld,%d): \n",idInst,hsz); + WDML_INSTANCE* pInstance; + BOOL ret = FALSE; + + TRACE("(%ld,0x%x): \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,38 +1170,90 @@ BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz) */ BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz) { + WDML_INSTANCE* pInstance; + BOOL ret = FALSE; + + TRACE("(%ld,0x%x): \n", idInst, hsz); - WDML_INSTANCE* thisInstance; + EnterCriticalSection(&WDML_CritSect); - TRACE("(%ld,%d): \n",idInst,hsz); + /* First check instance + */ + 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; - if (WDML_MaxInstanceID == 0) + 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) { - /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */ - return FALSE; + /* If both are not found, return both "zero strings". */ + ret = 0; } - - - if (!WDML_WaitForMutex(handle_mutex)) + else if (ret1 == 0) { - return FALSE; + /* If hsz1 is a not found, return hsz1 is "zero string". */ + ret = -1; } - - /* First check instance - */ - thisInstance = WDML_FindInstance(idInst); - if ((thisInstance == NULL) || (thisInstance->nodeList == NULL)) + else if (ret2 == 0) { - 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; + /* If hsz2 is a not found, return hsz2 is "zero string". */ + ret = 1; } - WDML_ReserveAtom(thisInstance, hsz); - WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); - return TRUE; + 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.@) */ @@ -1330,57 +1321,20 @@ HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff) 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; @@ -1475,247 +1429,64 @@ BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData) * 0 16 bit fields for options (release, ackreq, response...) * 2 16 clipboard format * 4 ? data to be used - */ -HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem) -{ - DDEDATA* pDd; - - if (hMem) - { - pDd = GlobalLock(hMem); - if (pDd) - { - return DdeCreateDataHandle(0, pDd->Value, - GlobalSize(hMem) - (sizeof(DDEDATA) - 1), - 0, 0, pDd->cfFormat, 0); - } - } - return 0; -} - -HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease, - BOOL fDeferUpd, BOOL fAckReq) -{ - DDE_DATAHANDLE_HEAD* pDdh; - DWORD dwSize; - HGLOBAL hMem = 0; - - dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD); - pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData); - if (dwSize && pDdh) - { - hMem = GlobalAlloc(sizeof(DDEDATA) - 1 + dwSize, - GMEM_MOVEABLE | GMEM_DDESHARE); - if (hMem) - { - DDEDATA* ddeData; - - ddeData = GlobalLock(hMem); - if (ddeData) - { - ddeData->fResponse = fResponse; - ddeData->fRelease = fRelease; - ddeData->reserved /*fDeferUpd*/ = fDeferUpd; - ddeData->fAckReq = fAckReq; - ddeData->cfFormat = pDdh->cfFormat; - memcpy(ddeData->Value, pDdh + 1, dwSize); - GlobalUnlock(hMem); - } - } - GlobalUnlock(hDdeData); - } - - 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; - } + */ +HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p) +{ + DDEDATA* pDd; + HDDEDATA ret = 0; + + if (hMem) + { + pDd = GlobalLock(hMem); + if (pDd) + { + 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 ret; } - + /****************************************************************** - * DdeQueryConvInfo (USER32.@) + * WDML_DataHandle2Global + * * */ -UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo) +HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease, + BOOL fDeferUpd, BOOL fAckReq) { - 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 + DDE_DATAHANDLE_HEAD* pDdh; + DWORD dwSize; + HGLOBAL hMem = 0; + + dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD); + pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData); + if (dwSize && pDdh) { - 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; - } + hMem = GlobalAlloc(sizeof(WINE_DDEHEAD) + dwSize, GMEM_MOVEABLE | GMEM_DDESHARE); + if (hMem) + { + WINE_DDEHEAD* wdh; + + wdh = GlobalLock(hMem); + if (wdh) + { + wdh->fResponse = fResponse; + wdh->fRelease = fRelease; + wdh->fDeferUpd = fDeferUpd; + wdh->fAckReq = fAckReq; + wdh->cfFormat = pDdh->cfFormat; + memcpy(wdh + 1, pDdh + 1, dwSize); + GlobalUnlock(hMem); + } + } + GlobalUnlock(hDdeData); } - - WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE); - memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci))); - return ret; + + return hMem; } /* ================================================================ @@ -1729,19 +1500,28 @@ UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo) * * */ -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 @@ WDML_SERVER* WDML_AddServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hsz * * */ -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; - - pCurrent = thisInstance->servers; + WDML_SERVER* pPrev = NULL; + WDML_SERVER* pServer = NULL; + WDML_CONV* pConv; + WDML_CONV* pConvNext; + + pServer = pInstance->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) + { + 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) { - thisInstance->servers = pCurrent->next; + 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 @@ void WDML_RemoveServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic * 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_SERVER* WDML_FindServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hs * * */ -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 @@ WDML_CONV* WDML_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, /* 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_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, * * */ -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 @@ WDML_CONV* WDML_FindConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, * * */ -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 @@ void WDML_RemoveConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, HCONV hConv) } } +/***************************************************************** + * 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 @@ WDML_CONV* WDML_GetConv(HCONV hConv) * * */ -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 @@ void WDML_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 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_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, * * */ -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 @@ void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, 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 @@ void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, { DdeFreeDataHandle(pCurrent->hDdeData); } - + + WDML_DecHSZ(pInstance, pCurrent->hszItem); HeapFree(GetProcessHeap(), 0, pCurrent); break; } @@ -1988,21 +2045,21 @@ void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side, * * */ -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 @@ void WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE sid { DdeFreeDataHandle(pCurrent->hDdeData); } + WDML_DecHSZ(pInstance, pCurrent->hszItem); HeapFree(GetProcessHeap(), 0, pCurrent); pCurrent = NULL; @@ -2037,15 +2095,14 @@ void WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE sid * * */ -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 @@ WDML_LINK* WDML_FindLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE si * * 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 @@ WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg) 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 @@ WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg) 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 @@ BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct) * * */ -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 @@ WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid) 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 656bbc9f89..7fdd75d21d 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 @@ static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM); /****************************************************************************** * 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); - TRACE("(%ld,%ld,%ld)\n",idInst,(DWORD)hszTopic,(DWORD)hszItem); + EnterCriticalSection(&WDML_CritSect); + + pInstance = WDML_GetInstance(idInst); - if (idInst == 0) + if (pInstance == NULL || pInstance->links == NULL) { - return FALSE; + goto theError; } - thisInstance = WDML_FindInstance(idInst); - - if (thisInstance == NULL || thisInstance->links == NULL) + 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) + { + count++; + } + } + if (count >= CADV_LATEACK) { - return FALSE; + FIXME("too high value for count\n"); + count &= 0xFFFF; } - - 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) + + 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()*/) - { - - 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"); + hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv, + hszTopic, hszItem, 0, count--, 0); + if (hDdeData == CBR_BLOCK) + { + /* 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 @@ BOOL WINAPI DdePostAdvise( hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE); } - pConv = WDML_GetConv(pLink->hConv); + pConv = WDML_GetConv(pLink->hConv, TRUE); + + if (pConv == NULL) + { + /* FIXME: wrong if app owned... */ + DdeFreeDataHandle(hDdeData); + goto theError; + } - if (pConv == NULL || - !PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer, - PackDDElParam(WM_DDE_DATA, (UINT)hItemData, (DWORD)hszItem))) + 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 @@ BOOL WINAPI DdePostAdvise( 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); - - if (WDML_MaxInstanceID == 0) - { - /* Nothing has been initialised - exit now ! - * needs something for DdeGetLastError */ - return 0; - } + TRACE("(%ld,0x%x,0x%x,%d)\n", idInst, hsz1, hsz2, afCmd); - 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 - */ - 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 + /* don't know if we should check this but it makes sense + * why supply REGISTER or filter flags if de-registering all */ - 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 @@ HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd) 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) - { - /* 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); - if (!pServer) + break; + + case DNS_UNREGISTER: + if (hsz1 == 0L) { - /* trying to filter where no service names !! + /* General unregister situation + * terminate all server side pending conversations */ - thisInstance->lastError = DMLERR_DLL_USAGE; - WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); - return FALSE; - } - else + while (pInstance->servers) + WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0); + pInstance->servers = NULL; + TRACE("General de-register - finished\n"); + } + else { - pServer->filterOn = TRUE; + WDML_RemoveServer(pInstance, hsz1, 0L); } - } - if (afCmd & DNS_FILTEROFF) - { + break; + case DNS_FILTERON: + case DNS_FILTEROFF: /* Set filter flags on to hold notifications of connection */ - 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 = FALSE; + pServer->filterOn = (afCmd == DNS_FILTERON); } + break; } - WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); + LeaveCriticalSection(&WDML_CritSect); return (HDDEDATA)TRUE; + + theError: + LeaveCriticalSection(&WDML_CritSect); + return FALSE; } /****************************************************************** @@ -321,56 +287,85 @@ HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd) * * */ -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; + if (pInstance->unicode) + { + WNDCLASSEXW 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_szServerConvClassW; + wndclass.hIconSm = 0; + + RegisterClassExW(&wndclass); - RegisterClassExA(&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(szServerConvClassA, 0, - WS_CHILD, 0, 0, 0, 0, - hwndServerName, 0, 0, 0); + 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, thisInstance->instanceID); - - pConv = WDML_AddConv(thisInstance, WDML_SERVER_SIDE, hszApp, hszTopic, hwndClient, hwndServerConv); + hwndServerConv, hwndServerName, pInstance->instanceID); - 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()*/) + pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic, + hwndClient, hwndServerConv); + if (pConv) { - /* 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... + 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... */ - (thisInstance->callback)(XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv, hszApp, hszTopic, 0, 0, 0); + pConv->wStatus |= ST_CONNECTED; } -#endif - - return TRUE; + else + { + DestroyWindow(hwndServerConv); + } + return pConv; } /****************************************************************** @@ -383,8 +378,8 @@ static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM w 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 @@ static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM w 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); - - hszApp = (HSZ)uiLow; - hszTop = (HSZ)uiHi; + UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLo, &uiHi); - thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndServer, 0); - TRACE("idInst=%ld, ProcessID=0x%lx\n", thisInstance->instanceID, GetCurrentProcessId()); + hszApp = WDML_MakeHszFromAtom(pInstance, uiLo); + hszTop = WDML_MakeHszFromAtom(pInstance, uiHi); - 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,119 +513,126 @@ static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM w } /****************************************************************** - * WDML_ServerHandleRequest + * WDML_ServerQueueRequest * * */ -static LRESULT WDML_ServerHandleRequest(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, - HWND hwndServer, HWND hwndClient, LPARAM lParam) +static WDML_XACT* WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam) { - UINT uiLow, uiHi; - HSZ hszItem; - HDDEDATA hDdeData; + UINT uiLo, uiHi; + WDML_XACT* pXAct; - TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam); - - UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLow, &uiHi); + UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi); - hszItem = (HSZ)uiHi; + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, + uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi)); + if (pXAct) pXAct->atom = uiHi; + return pXAct; +} - hDdeData = 0; - if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */) +/****************************************************************** + * WDML_ServerHandleRequest + * + * + */ +static WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct) +{ + HDDEDATA hDdeData = 0; + WDML_QUEUE_STATE ret = WDML_QS_HANDLED; + + 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; - - ddeAck.bAppReturnCode = 0; - ddeAck.reserved = 0; - ddeAck.fBusy = FALSE; - ddeAck.fAck = FALSE; - - 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; + WDML_DecHSZ(pConv->instance, pXAct->hszItem); + return ret; } /****************************************************************** - * WDML_ServerHandleAdvise + * WDML_ServerQueueAdvise * * */ -static LRESULT WDML_ServerHandleAdvise(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, - HWND hwndServer, HWND hwndClient, LPARAM lParam) +static WDML_XACT* WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam) { - UINT uiLo, uiHi, uType; - HGLOBAL hDdeAdvise; - HSZ hszItem; - WDML_LINK* pLink; - DDEADVISE* pDdeAdvise; - HDDEDATA hDdeData; - DDEACK ddeAck; + UINT uiLo, uiHi; + WDML_XACT* pXAct; /* 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) + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, + 0, WDML_MakeHszFromAtom(pConv->instance, uiHi)); + if (pXAct) { - ERR("Got an advise on a not known conversation, dropping request\n"); - FreeDDElParam(WM_DDE_ADVISE, lParam); - return 0; + pXAct->hMem = (HGLOBAL)uiLo; + pXAct->atom = uiHi; } + return pXAct; +} - pDdeAdvise = (DDEADVISE*)GlobalLock(hDdeAdvise); +/****************************************************************** + * WDML_ServerHandleAdvise + * + * + */ +static WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct) +{ + UINT uType; + WDML_LINK* pLink; + DDEADVISE* pDdeAdvise; + HDDEDATA hDdeData; + BOOL fAck; + + 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); + } + else + { + hDdeData = 0; } - - ddeAck.bAppReturnCode = 0; - ddeAck.reserved = 0; - ddeAck.fBusy = FALSE; - if ((UINT)hDdeData || TRUE) /* FIXME (from Corel ?) some apps don't return this value */ + if ((UINT)hDdeData) { - ddeAck.fAck = TRUE; + 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,130 +643,135 @@ static LRESULT WDML_ServerHandleAdvise(WDML_INSTANCE* thisInstance, WDML_CONV* p { 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); + + WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE); + + WDML_DecHSZ(pConv->instance, pXAct->hszItem); - return 0L; + return WDML_QS_HANDLED; } /****************************************************************** - * WDML_ServerHandleUnadvise + * WDML_ServerQueueUnadvise * * */ -static LRESULT WDML_ServerHandleUnadvise(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, - HWND hwndServer, HWND hwndClient, LPARAM lParam) +static WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam) { UINT uiLo, uiHi; - HSZ hszItem; - WDML_LINK* pLink; - DDEACK ddeAck; + WDML_XACT* pXAct; - 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 */ + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, + uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi)); + if (pXAct) pXAct->atom = uiHi; + return pXAct; +} + +/****************************************************************** + * WDML_ServerHandleUnadvise + * + * + */ +static WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct) +{ + WDML_LINK* pLink; - 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_ServerHandleExecute + * WDML_QueueExecute * * */ -static LRESULT WDML_ServerHandleExecute(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, - HWND hwndServer, HWND hwndClient, LPARAM lParam) +static WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam) { - DDEACK ddeAck; - HDDEDATA hDdeData; + WDML_XACT* pXAct; - TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam); - - if (hwndClient != pConv->hwndClient) - WARN("hmmm source window (%04x)\n", hwndClient); + pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0); + if (pXAct) + { + pXAct->hMem = (HGLOBAL)lParam; + } + return pXAct; +} + + /****************************************************************** + * WDML_ServerHandleExecute + * + * + */ +static WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct) +{ + HDDEDATA hDdeData = DDE_FNOTPROCESSED; + BOOL fAck = FALSE, fBusy = FALSE; - 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,63 +779,69 @@ static LRESULT WDML_ServerHandleExecute(WDML_INSTANCE* thisInstance, WDML_CONV* 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_ServerHandlePoke + * WDML_ServerQueuePoke * * */ -static LRESULT WDML_ServerHandlePoke(WDML_INSTANCE* thisInstance, WDML_CONV* pConv, - HWND hwndServer, HWND hwndClient, LPARAM lParam) +static WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam) { UINT uiLo, uiHi; - HSZ hszItem; - DDEACK ddeAck; + 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; +} + +/****************************************************************** + * WDML_ServerHandlePoke + * + * + */ +static WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct) +{ 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 @@ static LRESULT WDML_ServerHandlePoke(WDML_INSTANCE* thisInstance, WDML_CONV* pCo DdeFreeDataHandle(hDdeData); } } - GlobalUnlock((HGLOBAL)uiLo); - - if (!ddeAck.fAck) - GlobalFree((HGLOBAL)uiHi); + GlobalUnlock(pXAct->hMem); - 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); + + WDML_DecHSZ(pConv->instance, pXAct->hszItem); + + return WDML_QS_HANDLED; +} - return 0L; +/****************************************************************** + * 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_ServerHandlePoke(WDML_INSTANCE* thisInstance, WDML_CONV* pCo * * */ -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; + /* billx: two things to remove: the conv, and associated links. + * 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); + } - TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam); + PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0); + WDML_RemoveConv(pConv, WDML_SERVER_SIDE); - 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); + 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; - hszApp = (HSZ)uiLo; - hszTop = (HSZ)uiHi; + case WM_DDE_UNADVISE: + qs = WDML_ServerHandleUnadvise(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_EXECUTE: + qs = WDML_ServerHandleExecute(pConv, pXAct); + break; - return 0; + 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 WDML_ServerHandleTerminate(WDML_INSTANCE* thisInstance, WDML_CONV */ 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 @@ static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM w 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 fb0215b7a1..f94c176b44 100644 --- a/dlls/user/user32.spec +++ b/dlls/user/user32.spec @@ -26,7 +26,7 @@ debug_channels (accel caret class clipboard combo cursor dc dde ddeml dialog dri @ 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 @@ debug_channels (accel caret class clipboard combo cursor dc dde ddeml dialog dri @ 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 202b86e6b4..7932948d53 100644 --- a/dlls/user/user_main.c +++ b/dlls/user/user_main.c @@ -262,6 +262,9 @@ static void thread_detach(void) { 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 7aef612616..7c4a77c755 100644 --- a/include/ddeml.h +++ b/include/ddeml.h @@ -177,6 +177,8 @@ extern "C" { #define TIMEOUT_ASYNC 0xFFFFFFFF +#define CADV_LATEACK 0xFFFF + /************************************************** End of Message Types Section @@ -332,6 +334,7 @@ BOOL WINAPI DdeFreeStringHandle(DWORD,HSZ); 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); -- 2.32.0.93.g670b81a890