| /* -*- tab-width: 8; c-basic-offset: 4 -*- */ | |
| /* | |
| * DDEML library | |
| * | |
| * Copyright 1997 Alexandre Julliard | |
| * Copyright 1997 Len White | |
| * Copyright 1999 Keith Matthews | |
| * Copyright 2000 Corel | |
| * Copyright 2001 Eric Pouech | |
| * Copyright 2003, 2004, 2005 Dmitry Timoshkov | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winerror.h" | |
| #include "dde.h" | |
| #include "ddeml.h" | |
| #include "win.h" | |
| #include "wine/debug.h" | |
| #include "dde/dde_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(ddeml); | |
| static const WCHAR szServerNameClass[] = {'W','i','n','e','D','d','e','S','e','r','v','e','r','N','a','m','e',0}; | |
| const WCHAR WDML_szServerConvClass[] = {'W','i','n','e','D','d','e','S','e','r','v','e','r','C','o','n','v',0}; | |
| static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM); | |
| 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, HSZ hszTopic, HSZ hszItem) | |
| { | |
| WDML_INSTANCE* pInstance = NULL; | |
| WDML_LINK* pLink = NULL; | |
| HDDEDATA hDdeData = 0; | |
| HGLOBAL hItemData = 0; | |
| WDML_CONV* pConv = NULL; | |
| ATOM atom = 0; | |
| UINT count; | |
| TRACE("(%ld,%p,%p)\n", idInst, hszTopic, hszItem); | |
| EnterCriticalSection(&WDML_CritSect); | |
| pInstance = WDML_GetInstance(idInst); | |
| if (pInstance == NULL || pInstance->links == NULL) | |
| { | |
| goto theError; | |
| } | |
| 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) | |
| { | |
| FIXME("too high value for count\n"); | |
| count &= 0xFFFF; | |
| } | |
| for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next) | |
| { | |
| if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0) | |
| { | |
| hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv, | |
| hszTopic, hszItem, 0, --count, 0); | |
| if (hDdeData == (HDDEDATA)CBR_BLOCK) | |
| { | |
| /* MS doc is not consistent here */ | |
| FIXME("CBR_BLOCK returned for ADVREQ\n"); | |
| continue; | |
| } | |
| if (hDdeData) | |
| { | |
| if (pLink->transactionType & XTYPF_NODATA) | |
| { | |
| TRACE("no data\n"); | |
| hItemData = 0; | |
| } | |
| else | |
| { | |
| TRACE("with data\n"); | |
| hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE); | |
| } | |
| pConv = WDML_GetConv(pLink->hConv, TRUE); | |
| if (pConv == NULL) | |
| { | |
| if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); | |
| goto theError; | |
| } | |
| if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer, | |
| PackDDElParam(WM_DDE_DATA, (UINT_PTR)hItemData, atom))) | |
| { | |
| ERR("post message failed\n"); | |
| pConv->wStatus &= ~ST_CONNECTED; | |
| if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); | |
| GlobalFree(hItemData); | |
| goto theError; | |
| } | |
| if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); | |
| } | |
| } | |
| } | |
| LeaveCriticalSection(&WDML_CritSect); | |
| return TRUE; | |
| theError: | |
| LeaveCriticalSection(&WDML_CritSect); | |
| if (atom) GlobalDeleteAtom(atom); | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * DdeNameService [USER32.@] {Un}registers service name of DDE server | |
| * | |
| * PARAMS | |
| * idInst [I] Instance identifier | |
| * hsz1 [I] Handle to service name string | |
| * hsz2 [I] Reserved | |
| * afCmd [I] Service name flags | |
| * | |
| * RETURNS | |
| * Success: Non-zero | |
| * Failure: 0 | |
| */ | |
| HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd) | |
| { | |
| WDML_SERVER* pServer; | |
| WDML_INSTANCE* pInstance; | |
| HDDEDATA hDdeData; | |
| HWND hwndServer; | |
| WNDCLASSEXW wndclass; | |
| hDdeData = NULL; | |
| TRACE("(%ld,%p,%p,%x)\n", idInst, hsz1, hsz2, afCmd); | |
| EnterCriticalSection(&WDML_CritSect); | |
| /* First check instance | |
| */ | |
| pInstance = WDML_GetInstance(idInst); | |
| if (pInstance == NULL) | |
| { | |
| TRACE("Instance not found as initialised\n"); | |
| /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */ | |
| goto theError; | |
| } | |
| if (hsz2 != 0L) | |
| { | |
| /* Illegal, reserved parameter | |
| */ | |
| pInstance->lastError = DMLERR_INVALIDPARAMETER; | |
| WARN("Reserved parameter no-zero !!\n"); | |
| goto theError; | |
| } | |
| if (hsz1 == 0 && !(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"); | |
| pInstance->lastError = DMLERR_INVALIDPARAMETER; | |
| goto theError; | |
| } | |
| switch (afCmd & (DNS_REGISTER | DNS_UNREGISTER)) | |
| { | |
| case DNS_REGISTER: | |
| pServer = WDML_FindServer(pInstance, hsz1, 0); | |
| if (pServer) | |
| { | |
| ERR("Trying to register already registered service!\n"); | |
| pInstance->lastError = DMLERR_DLL_USAGE; | |
| goto theError; | |
| } | |
| TRACE("Adding service name\n"); | |
| WDML_IncHSZ(pInstance, hsz1); | |
| pServer = WDML_AddServer(pInstance, hsz1, 0); | |
| WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER, | |
| pServer->atomService, pServer->atomServiceSpec); | |
| wndclass.cbSize = sizeof(wndclass); | |
| wndclass.style = 0; | |
| wndclass.lpfnWndProc = WDML_ServerNameProc; | |
| wndclass.cbClsExtra = 0; | |
| wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR); | |
| wndclass.hInstance = 0; | |
| wndclass.hIcon = 0; | |
| wndclass.hCursor = 0; | |
| wndclass.hbrBackground = 0; | |
| wndclass.lpszMenuName = NULL; | |
| wndclass.lpszClassName = szServerNameClass; | |
| wndclass.hIconSm = 0; | |
| RegisterClassExW(&wndclass); | |
| LeaveCriticalSection(&WDML_CritSect); | |
| hwndServer = CreateWindowW(szServerNameClass, NULL, | |
| WS_POPUP, 0, 0, 0, 0, | |
| 0, 0, 0, 0); | |
| EnterCriticalSection(&WDML_CritSect); | |
| SetWindowLongPtrW(hwndServer, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance); | |
| SetWindowLongPtrW(hwndServer, GWL_WDML_SERVER, (ULONG_PTR)pServer); | |
| TRACE("Created nameServer=%p for instance=%08lx\n", hwndServer, idInst); | |
| pServer->hwndServer = hwndServer; | |
| break; | |
| case DNS_UNREGISTER: | |
| if (hsz1 == 0L) | |
| { | |
| /* General unregister situation | |
| * terminate all server side pending conversations | |
| */ | |
| while (pInstance->servers) | |
| WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0); | |
| pInstance->servers = NULL; | |
| TRACE("General de-register - finished\n"); | |
| } | |
| else | |
| { | |
| WDML_RemoveServer(pInstance, hsz1, 0L); | |
| } | |
| break; | |
| } | |
| if (afCmd & (DNS_FILTERON | DNS_FILTEROFF)) | |
| { | |
| /* Set filter flags on to hold notifications of connection | |
| */ | |
| pServer = WDML_FindServer(pInstance, hsz1, 0); | |
| if (!pServer) | |
| { | |
| /* trying to filter where no service names !! | |
| */ | |
| pInstance->lastError = DMLERR_DLL_USAGE; | |
| goto theError; | |
| } | |
| else | |
| { | |
| pServer->filterOn = (afCmd & DNS_FILTERON) != 0; | |
| } | |
| } | |
| LeaveCriticalSection(&WDML_CritSect); | |
| return (HDDEDATA)TRUE; | |
| theError: | |
| LeaveCriticalSection(&WDML_CritSect); | |
| return FALSE; | |
| } | |
| /****************************************************************** | |
| * WDML_CreateServerConv | |
| * | |
| * | |
| */ | |
| static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClient, | |
| HWND hwndServerName, HSZ hszApp, HSZ hszTopic) | |
| { | |
| HWND hwndServerConv; | |
| WDML_CONV* pConv; | |
| WNDCLASSEXW wndclass; | |
| wndclass.cbSize = sizeof(wndclass); | |
| wndclass.style = 0; | |
| wndclass.lpfnWndProc = WDML_ServerConvProc; | |
| wndclass.cbClsExtra = 0; | |
| wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR); | |
| wndclass.hInstance = 0; | |
| wndclass.hIcon = 0; | |
| wndclass.hCursor = 0; | |
| wndclass.hbrBackground = 0; | |
| wndclass.lpszMenuName = NULL; | |
| wndclass.lpszClassName = WDML_szServerConvClass; | |
| wndclass.hIconSm = 0; | |
| RegisterClassExW(&wndclass); | |
| hwndServerConv = CreateWindowW(WDML_szServerConvClass, 0, | |
| WS_CHILD, 0, 0, 0, 0, | |
| hwndServerName, 0, 0, 0); | |
| TRACE("Created convServer=%p (nameServer=%p) for instance=%08lx\n", | |
| hwndServerConv, hwndServerName, pInstance->instanceID); | |
| pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic, | |
| hwndClient, hwndServerConv); | |
| if (pConv) | |
| { | |
| SetWindowLongPtrW(hwndServerConv, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance); | |
| SetWindowLongPtrW(hwndServerConv, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv); | |
| /* this should be the only place using SendMessage for WM_DDE_ACK */ | |
| /* note: sent messages shall not use packing */ | |
| SendMessageW(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv, | |
| MAKELPARAM(WDML_MakeAtomFromHsz(hszApp), WDML_MakeAtomFromHsz(hszTopic))); | |
| /* we assume we're connected since we've sent an answer... | |
| * I'm not sure what we can do... it doesn't look like the return value | |
| * of SendMessage is used... sigh... | |
| */ | |
| pConv->wStatus |= ST_CONNECTED; | |
| } | |
| else | |
| { | |
| DestroyWindow(hwndServerConv); | |
| } | |
| return pConv; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerNameProc | |
| * | |
| * | |
| */ | |
| static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| HWND hwndClient; | |
| HSZ hszApp, hszTop; | |
| HDDEDATA hDdeData = 0; | |
| WDML_INSTANCE* pInstance; | |
| UINT_PTR uiLo, uiHi; | |
| switch (iMsg) | |
| { | |
| case WM_DDE_INITIATE: | |
| /* wParam -- sending window handle | |
| LOWORD(lParam) -- application atom | |
| HIWORD(lParam) -- topic atom */ | |
| 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, &uiLo, &uiHi); | |
| hszApp = WDML_MakeHszFromAtom(pInstance, uiLo); | |
| hszTop = WDML_MakeHszFromAtom(pInstance, uiHi); | |
| if (!(pInstance->CBFflags & CBF_FAIL_CONNECTIONS)) | |
| { | |
| BOOL self = FALSE; | |
| CONVCONTEXT cc; | |
| CONVCONTEXT* pcc = NULL; | |
| WDML_CONV* pConv; | |
| WCHAR 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 (GetClassNameW(hwndClient, buf, sizeof(buf)/sizeof(WCHAR)) && | |
| lstrcmpiW(buf, WDML_szClientConvClass) == 0) | |
| { | |
| pcc = &cc; | |
| memset(pcc, 0, sizeof(*pcc)); | |
| pcc->cb = sizeof(*pcc); | |
| pcc->iCodePage = CP_WINUNICODE; | |
| } | |
| 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*)GetWindowLongPtrW(hwndServer, GWL_WDML_SERVER); | |
| /* check filters for name service */ | |
| if (!pServer->filterOn || DdeCmpStringHandles(pServer->hszService, hszApp) == 0) | |
| { | |
| /* pass on to the callback */ | |
| hDdeData = WDML_InvokeCallback(pInstance, XTYP_CONNECT, | |
| 0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self); | |
| if ((ULONG_PTR)hDdeData) | |
| { | |
| pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer, | |
| hszApp, hszTop); | |
| if (pConv) | |
| { | |
| if (pcc) pConv->wStatus |= ST_ISLOCAL; | |
| WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv, | |
| hszTop, hszApp, 0, (ULONG_PTR)pcc, self); | |
| } | |
| } | |
| } | |
| } | |
| else if (pInstance->servers) | |
| { | |
| /* pass on to the callback */ | |
| hDdeData = WDML_InvokeCallback(pInstance, XTYP_WILDCONNECT, | |
| 0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self); | |
| if (hDdeData == (HDDEDATA)CBR_BLOCK) | |
| { | |
| /* MS doc is not consistent here */ | |
| FIXME("CBR_BLOCK returned for WILDCONNECT\n"); | |
| } | |
| else if ((ULONG_PTR)hDdeData != 0) | |
| { | |
| HSZPAIR* hszp; | |
| hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL); | |
| if (hszp) | |
| { | |
| int i; | |
| for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++) | |
| { | |
| pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer, | |
| hszp[i].hszSvc, hszp[i].hszTopic); | |
| if (pConv) | |
| { | |
| if (pcc) pConv->wStatus |= ST_ISLOCAL; | |
| WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv, | |
| hszp[i].hszTopic, hszp[i].hszSvc, 0, (ULONG_PTR)pcc, self); | |
| } | |
| } | |
| DdeUnaccessData(hDdeData); | |
| } | |
| if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); | |
| } | |
| } | |
| } | |
| return 0; | |
| case WM_DDE_REQUEST: | |
| FIXME("WM_DDE_REQUEST message received!\n"); | |
| return 0; | |
| case WM_DDE_ADVISE: | |
| FIXME("WM_DDE_ADVISE message received!\n"); | |
| return 0; | |
| case WM_DDE_UNADVISE: | |
| FIXME("WM_DDE_UNADVISE message received!\n"); | |
| return 0; | |
| case WM_DDE_EXECUTE: | |
| FIXME("WM_DDE_EXECUTE message received!\n"); | |
| return 0; | |
| case WM_DDE_POKE: | |
| FIXME("WM_DDE_POKE message received!\n"); | |
| return 0; | |
| case WM_DDE_TERMINATE: | |
| FIXME("WM_DDE_TERMINATE message received!\n"); | |
| return 0; | |
| default: | |
| break; | |
| } | |
| return DefWindowProcW(hwndServer, iMsg, wParam, lParam); | |
| } | |
| /****************************************************************** | |
| * WDML_ServerQueueRequest | |
| * | |
| * | |
| */ | |
| static WDML_XACT* WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam) | |
| { | |
| UINT_PTR uiLo, uiHi; | |
| WDML_XACT* pXAct; | |
| UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi); | |
| pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, | |
| uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi)); | |
| if (pXAct) pXAct->atom = uiHi; | |
| return pXAct; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandleRequest | |
| * | |
| * | |
| */ | |
| static WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct) | |
| { | |
| HDDEDATA hDdeData = 0; | |
| BOOL fAck = TRUE; | |
| if (!(pConv->instance->CBFflags & CBF_FAIL_REQUESTS)) | |
| { | |
| hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_REQUEST, pXAct->wFmt, (HCONV)pConv, | |
| pConv->hszTopic, pXAct->hszItem, 0, 0, 0); | |
| } | |
| switch ((ULONG_PTR)hDdeData) | |
| { | |
| case 0: | |
| TRACE("No data returned from the Callback\n"); | |
| fAck = FALSE; | |
| break; | |
| case (ULONG_PTR)CBR_BLOCK: | |
| return WDML_QS_BLOCK; | |
| default: | |
| { | |
| HGLOBAL hMem = WDML_DataHandle2Global(hDdeData, TRUE, FALSE, FALSE, FALSE); | |
| if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer, | |
| ReuseDDElParam(pXAct->lParam, WM_DDE_REQUEST, WM_DDE_DATA, | |
| (UINT_PTR)hMem, (UINT_PTR)pXAct->atom))) | |
| { | |
| DdeFreeDataHandle(hDdeData); | |
| GlobalFree(hMem); | |
| fAck = FALSE; | |
| } | |
| } | |
| break; | |
| } | |
| WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_REQUEST); | |
| WDML_DecHSZ(pConv->instance, pXAct->hszItem); | |
| return WDML_QS_HANDLED; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerQueueAdvise | |
| * | |
| * | |
| */ | |
| static WDML_XACT* WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam) | |
| { | |
| UINT_PTR uiLo, uiHi; | |
| WDML_XACT* pXAct; | |
| /* XTYP_ADVSTART transaction: | |
| establish link and save link info to InstanceInfoTable */ | |
| if (!UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi)) | |
| return NULL; | |
| pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, | |
| 0, WDML_MakeHszFromAtom(pConv->instance, uiHi)); | |
| if (pXAct) | |
| { | |
| pXAct->hMem = (HGLOBAL)uiLo; | |
| pXAct->atom = uiHi; | |
| } | |
| return pXAct; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandleAdvise | |
| * | |
| * | |
| */ | |
| static WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct) | |
| { | |
| UINT uType; | |
| WDML_LINK* pLink; | |
| DDEADVISE* pDdeAdvise; | |
| HDDEDATA hDdeData = 0; | |
| BOOL fAck = TRUE; | |
| pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem); | |
| uType = XTYP_ADVSTART | | |
| (pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) | | |
| (pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0); | |
| if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES)) | |
| { | |
| hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_ADVSTART, pDdeAdvise->cfFormat, | |
| (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 0, 0, 0); | |
| } | |
| switch ((ULONG_PTR)hDdeData) | |
| { | |
| case 0: | |
| TRACE("No data returned from the Callback\n"); | |
| fAck = FALSE; | |
| break; | |
| case (ULONG_PTR)CBR_BLOCK: | |
| return WDML_QS_BLOCK; | |
| default: | |
| /* billx: first to see if the link is already created. */ | |
| pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, | |
| pXAct->hszItem, TRUE, pDdeAdvise->cfFormat); | |
| if (pLink != NULL) | |
| { | |
| /* we found a link, and only need to modify it in case it changes */ | |
| pLink->transactionType = uType; | |
| } | |
| else | |
| { | |
| TRACE("Adding Link with hConv %p\n", pConv); | |
| WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, | |
| uType, pXAct->hszItem, pDdeAdvise->cfFormat); | |
| } | |
| break; | |
| } | |
| GlobalUnlock(pXAct->hMem); | |
| if (fAck) | |
| { | |
| GlobalFree(pXAct->hMem); | |
| } | |
| pXAct->hMem = 0; | |
| WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE); | |
| WDML_DecHSZ(pConv->instance, pXAct->hszItem); | |
| return WDML_QS_HANDLED; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerQueueUnadvise | |
| * | |
| * | |
| */ | |
| static WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam) | |
| { | |
| UINT_PTR uiLo, uiHi; | |
| WDML_XACT* pXAct; | |
| UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi); | |
| pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, | |
| uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi)); | |
| if (pXAct) pXAct->atom = uiHi; | |
| return pXAct; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandleUnadvise | |
| * | |
| * | |
| */ | |
| static WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct) | |
| { | |
| WDML_LINK* pLink; | |
| if (pXAct->hszItem == NULL || pXAct->wFmt == 0) | |
| { | |
| ERR("Unsupported yet options (null item or clipboard format)\n"); | |
| return WDML_QS_ERROR; | |
| } | |
| pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, | |
| pXAct->hszItem, TRUE, pXAct->wFmt); | |
| if (pLink == NULL) | |
| { | |
| ERR("Couln'd find link for %p, dropping request\n", pXAct->hszItem); | |
| FreeDDElParam(WM_DDE_UNADVISE, pXAct->lParam); | |
| return WDML_QS_ERROR; | |
| } | |
| if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES)) | |
| { | |
| WDML_InvokeCallback(pConv->instance, XTYP_ADVSTOP, pXAct->wFmt, (HCONV)pConv, | |
| pConv->hszTopic, pXAct->hszItem, 0, 0, 0); | |
| } | |
| WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, | |
| pXAct->hszItem, pXAct->wFmt); | |
| /* send back ack */ | |
| WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, TRUE, pXAct->atom, | |
| pXAct->lParam, WM_DDE_UNADVISE); | |
| WDML_DecHSZ(pConv->instance, pXAct->hszItem); | |
| return WDML_QS_HANDLED; | |
| } | |
| /****************************************************************** | |
| * WDML_QueueExecute | |
| * | |
| * | |
| */ | |
| static WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam) | |
| { | |
| WDML_XACT* pXAct; | |
| pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0); | |
| if (pXAct) | |
| { | |
| pXAct->hMem = (HGLOBAL)lParam; | |
| } | |
| return pXAct; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandleExecute | |
| * | |
| * | |
| */ | |
| static WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct) | |
| { | |
| HDDEDATA hDdeData = DDE_FNOTPROCESSED; | |
| BOOL fAck = FALSE, fBusy = FALSE; | |
| if (!(pConv->instance->CBFflags & CBF_FAIL_EXECUTES)) | |
| { | |
| LPVOID ptr = GlobalLock(pXAct->hMem); | |
| if (ptr) | |
| { | |
| hDdeData = DdeCreateDataHandle(0, ptr, GlobalSize(pXAct->hMem), | |
| 0, 0, CF_TEXT, 0); | |
| GlobalUnlock(pXAct->hMem); | |
| } | |
| hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_EXECUTE, 0, (HCONV)pConv, | |
| pConv->hszTopic, 0, hDdeData, 0L, 0L); | |
| } | |
| switch ((ULONG_PTR)hDdeData) | |
| { | |
| case (ULONG_PTR)CBR_BLOCK: | |
| return WDML_QS_BLOCK; | |
| case DDE_FACK: | |
| fAck = TRUE; | |
| break; | |
| case DDE_FBUSY: | |
| fBusy = TRUE; | |
| break; | |
| default: | |
| FIXME("Unsupported returned value %p\n", hDdeData); | |
| /* fall through */ | |
| case DDE_FNOTPROCESSED: | |
| break; | |
| } | |
| WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, (UINT)pXAct->hMem, 0, 0); | |
| return WDML_QS_HANDLED; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerQueuePoke | |
| * | |
| * | |
| */ | |
| static WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam) | |
| { | |
| UINT_PTR uiLo, uiHi; | |
| WDML_XACT* pXAct; | |
| UnpackDDElParam(WM_DDE_POKE, lParam, &uiLo, &uiHi); | |
| pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, | |
| 0, WDML_MakeHszFromAtom(pConv->instance, uiHi)); | |
| if (pXAct) | |
| { | |
| pXAct->atom = uiHi; | |
| pXAct->hMem = (HGLOBAL)uiLo; | |
| } | |
| return pXAct; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandlePoke | |
| * | |
| * | |
| */ | |
| static WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct) | |
| { | |
| DDEPOKE* pDdePoke; | |
| HDDEDATA hDdeData; | |
| BOOL fBusy = FALSE, fAck = FALSE; | |
| pDdePoke = (DDEPOKE*)GlobalLock(pXAct->hMem); | |
| if (!pDdePoke) | |
| { | |
| return WDML_QS_ERROR; | |
| } | |
| if (!(pConv->instance->CBFflags & CBF_FAIL_POKES)) | |
| { | |
| hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, pDdePoke->Value, | |
| GlobalSize(pXAct->hMem) - sizeof(DDEPOKE) + 1, | |
| 0, 0, pDdePoke->cfFormat, 0); | |
| if (hDdeData) | |
| { | |
| HDDEDATA hDdeDataOut; | |
| hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_POKE, pDdePoke->cfFormat, | |
| (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, | |
| hDdeData, 0, 0); | |
| switch ((ULONG_PTR)hDdeDataOut) | |
| { | |
| case DDE_FACK: | |
| fAck = TRUE; | |
| break; | |
| case DDE_FBUSY: | |
| fBusy = TRUE; | |
| break; | |
| default: | |
| FIXME("Unsupported returned value %p\n", hDdeDataOut); | |
| /* fal through */ | |
| case DDE_FNOTPROCESSED: | |
| break; | |
| } | |
| DdeFreeDataHandle(hDdeData); | |
| } | |
| } | |
| GlobalUnlock(pXAct->hMem); | |
| 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; | |
| } | |
| /****************************************************************** | |
| * 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; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandleTerminate | |
| * | |
| * | |
| */ | |
| static WDML_QUEUE_STATE WDML_ServerHandleTerminate(WDML_CONV* pConv, WDML_XACT* pXAct) | |
| { | |
| /* 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); | |
| } | |
| PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0); | |
| WDML_RemoveConv(pConv, WDML_SERVER_SIDE); | |
| return WDML_QS_HANDLED; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerHandle | |
| * | |
| * | |
| */ | |
| 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; | |
| case WM_DDE_UNADVISE: | |
| qs = WDML_ServerHandleUnadvise(pConv, pXAct); | |
| break; | |
| case WM_DDE_EXECUTE: | |
| qs = WDML_ServerHandleExecute(pConv, pXAct); | |
| break; | |
| case WM_DDE_POKE: | |
| qs = WDML_ServerHandlePoke(pConv, pXAct); | |
| break; | |
| case WM_DDE_TERMINATE: | |
| qs = WDML_ServerHandleTerminate(pConv, pXAct); | |
| break; | |
| case WM_DDE_ACK: | |
| WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n"); | |
| break; | |
| default: | |
| FIXME("Unsupported message %d\n", pXAct->ddeMsg); | |
| } | |
| return qs; | |
| } | |
| /****************************************************************** | |
| * WDML_ServerConvProc | |
| * | |
| * | |
| */ | |
| static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| WDML_INSTANCE* pInstance; | |
| WDML_CONV* pConv; | |
| WDML_XACT* pXAct = NULL; | |
| TRACE("%p %04x %08x %08lx\n", hwndServer, iMsg, wParam , lParam); | |
| 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 DefWindowProcW(hwndServer, iMsg, wParam, lParam); | |
| } | |
| EnterCriticalSection(&WDML_CritSect); | |
| pInstance = WDML_GetInstanceFromWnd(hwndServer); | |
| pConv = WDML_GetConvFromWnd(hwndServer); | |
| if (!pConv) | |
| { | |
| ERR("Got a message (%x) on a not known conversation, dropping request\n", iMsg); | |
| goto theError; | |
| } | |
| if (pConv->hwndClient != WIN_GetFullHandle( (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; | |
| } | |
| switch (iMsg) | |
| { | |
| case WM_DDE_INITIATE: | |
| FIXME("WM_DDE_INITIATE message received!\n"); | |
| break; | |
| case WM_DDE_REQUEST: | |
| pXAct = WDML_ServerQueueRequest(pConv, lParam); | |
| break; | |
| case WM_DDE_ADVISE: | |
| pXAct = WDML_ServerQueueAdvise(pConv, lParam); | |
| break; | |
| case WM_DDE_UNADVISE: | |
| pXAct = WDML_ServerQueueUnadvise(pConv, lParam); | |
| break; | |
| case WM_DDE_EXECUTE: | |
| pXAct = WDML_ServerQueueExecute(pConv, lParam); | |
| break; | |
| case WM_DDE_POKE: | |
| pXAct = WDML_ServerQueuePoke(pConv, lParam); | |
| break; | |
| case WM_DDE_TERMINATE: | |
| pXAct = WDML_ServerQueueTerminate(pConv, lParam); | |
| break; | |
| case WM_DDE_ACK: | |
| WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n"); | |
| break; | |
| default: | |
| FIXME("Unsupported message %x\n", iMsg); | |
| } | |
| 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; | |
| } |