/****************************** Module Header ******************************\ * Module Name: ddeml.C * * DDE Manager main module - Contains all exported ddeml functions. * * Created: 12/12/88 Sanford Staab * * Copyright (c) 1988, 1989 Microsoft Corporation * 4/5/89 sanfords removed need for hwndFrame registration parameter * 6/5/90 sanfords Fixed callbacks so they are blocked during * timeouts. * Fixed SendDDEInit allocation bug. * Added hApp to ConvInfo structure. * Allowed QueryConvInfo() to work on server hConvs. * 11/29/90 sanfords eliminated SendDDEInit() * \***************************************************************************/ #include "ddemlp.h" #include "verddeml.h" /****** Globals *******/ HANDLE hInstance = 0; // initialized by LoadProc HANDLE hheapDmg = 0; // main DLL heap PAPPINFO pAppInfoList = NULL; // registered app/thread data list PPILE pDataInfoPile = NULL; // Data handle tracking pile PPILE pLostAckPile = NULL; // Ack tracking pile WORD hwInst = 1; // used to validate stuff. CONVCONTEXT CCDef = { sizeof(CONVCONTEXT), 0, 0, CP_WINANSI, 0L, 0L }; // default context. char szNull[] = ""; char szT[20]; WORD cMonitor = 0; // number of registered monitors FARPROC prevMsgHook = NULL; // used for hook links FARPROC prevCallHook = NULL; // used for hook links ATOM gatomDDEMLMom = 0; ATOM gatomDMGClass = 0; DWORD ShutdownTimeout; DWORD ShutdownRetryTimeout; LPMQL gMessageQueueList = NULL; // see PostDdeMessage(); #ifdef DEBUG int bDbgFlags = 0; #endif /****** class strings ******/ char SZFRAMECLASS[] = "DMGFrame"; char SZDMGCLASS[] = "DMGClass"; char SZCLIENTCLASS[] = "DMGClientClass"; char SZSERVERCLASS[] = "DMGServerClass"; char SZMONITORCLASS[] = "DMGMonitorClass"; char SZCONVLISTCLASS[] = "DMGHoldingClass"; char SZHEAPWATCHCLASS[] = "DMGHeapWatchClass"; #ifdef DEBUG WORD cAtoms = 0; // for debugging hszs! #endif // PROGMAN HACK!!!! // This is here so DDEML works properly with PROGMAN 3.0 which incorrectly // deletes its initiate-ack atoms after sending its ack. ATOM aProgmanHack = 0; /* * maps XTYP_CONSTANTS to filter flags */ DWORD aulmapType[] = { 0L, // nothing 0L, // XTYP_ADVDATA 0L, // XTYP_ADVREQ CBF_FAIL_ADVISES, // XTYP_ADVSTART 0L, // XTYP_ADVSTOP CBF_FAIL_EXECUTES, // XTYP_EXECUTE CBF_FAIL_CONNECTIONS, // XTYP_CONNECT CBF_SKIP_CONNECT_CONFIRMS, // XTYP_CONNECT_CONFIRM 0L, // XTYP_MONITOR CBF_FAIL_POKES, // XTYP_POKE CBF_SKIP_REGISTRATIONS, // XTYP_REGISTER CBF_FAIL_REQUESTS, // XTYP_REQUEST CBF_SKIP_DISCONNECTS, // XTYP_DISCONNECT CBF_SKIP_UNREGISTRATIONS, // XTYP_UNREGISTER CBF_FAIL_CONNECTIONS, // XTYP_WILDCONNECT 0L, // XTYP_XACT_COMPLETE }; UINT EXPENTRY DdeInitialize( LPDWORD pidInst, PFNCALLBACK pfnCallback, DWORD afCmd, DWORD ulRes) { WORD wRet; #ifdef DEBUG if (!hheapDmg) { bDbgFlags = GetProfileInt("DDEML", "DebugFlags", 0); } #endif TRACEAPIIN((szT, "DdeInitialize(%lx(->%lx), %lx, %lx, %lx)\n", pidInst, *pidInst, pfnCallback, afCmd, ulRes)); if (ulRes != 0L) { wRet = DMLERR_INVALIDPARAMETER; } else { wRet = Register(pidInst, pfnCallback, afCmd); } TRACEAPIOUT((szT, "DdeInitialize:%x\n", wRet)); return(wRet); } DWORD Myatodw(LPCSTR psz) { DWORD dwRet = 0; if (psz == NULL) { return(0); } while (*psz) { dwRet = (dwRet << 1) + (dwRet << 3) + (*psz - '0'); psz++; } return(dwRet); } WORD Register( LPDWORD pidInst, PFNCALLBACK pfnCallback, DWORD afCmd) { PAPPINFO pai = 0L; SEMENTER(); if (afCmd & APPCLASS_MONITOR) { if (cMonitor == MAX_MONITORS) { return(DMLERR_DLL_USAGE); } // ensure monitors only get monitor callbacks. afCmd |= CBF_MONMASK; } if ((pai = (PAPPINFO)(*pidInst)) != NULL) { if (pai->instCheck != HIWORD(*pidInst)) { return(DMLERR_INVALIDPARAMETER); } /* * re-registration - only allow CBF_ and MF_ flags to be altered */ pai->afCmd = (pai->afCmd & ~(CBF_MASK | MF_MASK)) | (afCmd & (CBF_MASK | MF_MASK)); return(DMLERR_NO_ERROR); } if (!hheapDmg) { // Read in any alterations to the zombie terminate timeouts GetProfileString("DDEML", "ShutdownTimeout", "3000", szT, 20); ShutdownTimeout = Myatodw(szT); if (!ShutdownTimeout) { ShutdownTimeout = 3000; } GetProfileString("DDEML", "ShutdownRetryTimeout", "30000", szT, 20); ShutdownRetryTimeout = Myatodw(szT); if (!ShutdownRetryTimeout) { ShutdownRetryTimeout = 30000; } // PROGMAN HACK!!!! aProgmanHack = GlobalAddAtom("Progman"); /* UTTER GREASE to fool the pile routines into making a local pile */ hheapDmg = HIWORD((LPVOID)(&pDataInfoPile)); RegisterClasses(); } if (!pDataInfoPile) { if (!(pDataInfoPile = CreatePile(hheapDmg, sizeof(DIP), 8))) { goto Abort; } } if (!pLostAckPile) { if (!(pLostAckPile = CreatePile(hheapDmg, sizeof(LAP), 8))) { goto Abort; } } pai = (PAPPINFO)(DWORD)FarAllocMem(hheapDmg, sizeof(APPINFO)); if (pai == NULL) { goto Abort; } if (!(pai->hheapApp = DmgCreateHeap(4096))) { FarFreeMem((LPSTR)pai); pai = 0L; goto Abort; } /* * We NEVER expect a memory allocation failure here because we just * allocated the heap. */ pai->next = pAppInfoList; pai->pfnCallback = pfnCallback; // pai->pAppNamePile = NULL; LMEM_ZEROINIT pai->pHDataPile = CreatePile(pai->hheapApp, sizeof(HDDEDATA), 32); pai->pHszPile = CreatePile(pai->hheapApp, sizeof(ATOM), 16); // pai->plstCBExceptions = NULL; LMEM_ZEROINIT // pai->hwndSvrRoot = 0; may never need it LMEM_ZEROINIT pai->plstCB = CreateLst(pai->hheapApp, sizeof(CBLI)); pai->afCmd = afCmd | APPCMD_FILTERINITS; pai->hTask = GetCurrentTask(); // pai->hwndDmg = LMEM_ZEROINIT // pai->hwndFrame = LMEM_ZEROINIT // pai->hwndMonitor = LMEM_ZEROINIT // pai->hwndTimer = 0; LMEM_ZEROINIT // pai->LastError = DMLERR_NO_ERROR; LMEM_ZEROINIT // pai->wFlags = 0; // pai->fEnableOneCB = FALSE; LMEM_ZEROINIT // pai->cZombies = 0; LMEM_ZEROINIT // pai->cInProcess = 0; LMEM_ZEROINIT pai->instCheck = ++hwInst; pai->pServerAdvList = CreateLst(pai->hheapApp, sizeof(ADVLI)); pai->lpMemReserve = FarAllocMem(pai->hheapApp, CB_RESERVE); pAppInfoList = pai; *pidInst = (DWORD)MAKELONG((WORD)pai, pai->instCheck); // NB We pass a pointer to pai in this CreateWindow because // 32bit MFC has a habit of subclassing our dde windows so this // param ends up getting thunked and since it's not really // a pointer things get a bit broken by the thunks. if ((pai->hwndDmg = CreateWindow( SZDMGCLASS, szNull, WS_OVERLAPPED, 0, 0, 0, 0, (HWND)NULL, (HMENU)NULL, hInstance, &pai)) == 0L) { goto Abort; } if (pai->afCmd & APPCLASS_MONITOR) { pai->afCmd |= CBF_MONMASK; // monitors only get MONITOR and REGISTER callbacks! if ((pai->hwndMonitor = CreateWindow( SZMONITORCLASS, szNull, WS_OVERLAPPED, 0, 0, 0, 0, (HWND)NULL, (HMENU)NULL, hInstance, &pai)) == 0L) { goto Abort; } if (++cMonitor == 1) { prevMsgHook = SetWindowsHook(WH_GETMESSAGE, (FARPROC)DdePostHookProc); prevCallHook = SetWindowsHook(WH_CALLWNDPROC, (FARPROC)DdeSendHookProc); } } else if (afCmd & APPCMD_CLIENTONLY) { /* * create an invisible top-level frame for initiates. (if server ok) */ afCmd |= CBF_FAIL_ALLSVRXACTIONS; } else { if ((pai->hwndFrame = CreateWindow( SZFRAMECLASS, szNull, WS_POPUP, 0, 0, 0, 0, (HWND)NULL, (HMENU)NULL, hInstance, &pai)) == 0L) { goto Abort; } } // SetMessageQueue(200); SEMLEAVE(); return(DMLERR_NO_ERROR); Abort: SEMLEAVE(); if (pai) { DdeUninitialize((DWORD)(LPSTR)pai); } return(DMLERR_SYS_ERROR); } LRESULT FAR PASCAL TermDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: return(TRUE); case WM_COMMAND: switch (wParam) { case IDABORT: case IDRETRY: case IDIGNORE: EndDialog(hwnd, wParam); return(0); } break; } return(0); } /***************************** Public Function ****************************\ * PUBDOC START * BOOL EXPENTRY DdeUninitialize(void); * This unregisters the application from the DDEMGR. All DLL resources * associated with the application are destroyed. * * PUBDOC END * * History: * Created 12/14/88 Sanfords \***************************************************************************/ BOOL EXPENTRY DdeUninitialize( DWORD idInst) { register PAPPINFO pai; PAPPINFO paiT; ATOM a; DWORD hData; MSG msg; extern VOID DumpGlobalLogs(VOID); TRACEAPIIN((szT, "DdeUninitialize(%lx)\n", idInst)); pai = (PAPPINFO)LOWORD(idInst); if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeUninitialize:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; /* * This is a hack to catch apps that call DdeUninitialize while within * a synchronous transaction modal loop. */ pai->wFlags |= AWF_UNINITCALLED; if (pai->wFlags & AWF_INSYNCTRANSACTION) { TRACEAPIOUT((szT, "DdeUninitialize:1\n")); return(TRUE); } /* * inform others of DeRegistration */ if (pai->pAppNamePile != NULL) { DdeNameService(idInst, (HSZ)NULL, (HSZ)NULL, DNS_UNREGISTER); } /* * Let any lagging dde activity die down. */ while (EmptyDDEPostQ()) { Yield(); while (PeekMessage((MSG FAR *)&msg, (HWND)NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) { DispatchMessage((MSG FAR *)&msg); Yield(); } for (paiT = pAppInfoList; paiT != NULL; paiT = paiT->next) { if (paiT->hTask == pai->hTask) { CheckCBQ(paiT); } } } // Let all windows left begin to self destruct. ChildMsg(pai->hwndDmg, UM_DISCONNECT, ST_PERM2DIE, 0L, FALSE); if (ShutdownTimeout && pai->cZombies) { WORD wRet; WORD hiTimeout; /* * This ugly mess is here to prevent DDEML from closing down and * destroying windows that are not properly terminated. Any * windows waiting on WM_DDE_TERMINATE messages set the cZombies * count. If there are any left we go into a modal loop till * things clean up. This should, in most cases happen fairly * quickly. */ hiTimeout = HIWORD(ShutdownTimeout); SetTimer(pai->hwndDmg, TID_SHUTDOWN, LOWORD(ShutdownTimeout), NULL); TRACETERM((szT, "DdeUninitialize: Entering terminate modal loop. cZombies=%d[%x:%x]\n", ((LPAPPINFO)pai)->cZombies, HIWORD(&((LPAPPINFO)pai)->cZombies), LOWORD(&((LPAPPINFO)pai)->cZombies))); while (pai->cZombies > 0) { Yield(); // give other apps a chance to post terminates. GetMessage(&msg, (HWND)NULL, 0, 0xffff); if (msg.message == WM_TIMER && msg.wParam == TID_SHUTDOWN && msg.hwnd == pai->hwndDmg) { if (hiTimeout--) { SetTimer(pai->hwndDmg, TID_SHUTDOWN, 0xFFFF, NULL); } else { FARPROC lpfn; KillTimer(pai->hwndDmg, TID_SHUTDOWN); if (!pai->cZombies) { break; } TRACETERM((szT, "DdeUninitialize Zombie hangup: pai=%x:%x\n", HIWORD((LPAPPINFO)pai), (WORD)(pai))); /* * If the partner window died in any remaining zombie * windows, get them shut down. */ ChildMsg(pai->hwndDmg, UM_DISCONNECT, ST_CHECKPARTNER, 0L, FALSE); if (pai->cZombies > 0) { lpfn = MakeProcInstance((FARPROC)TermDlgProc, hInstance); wRet = DialogBox(hInstance, "TermDialog", (HWND)NULL, lpfn); FreeProcInstance(lpfn); if (wRet == IDABORT || wRet == -1) { pai->cZombies = 0; break; // ignore zombies! } if (wRet == IDRETRY) { hiTimeout = HIWORD(ShutdownRetryTimeout); SetTimer(pai->hwndDmg, TID_SHUTDOWN, LOWORD(ShutdownRetryTimeout), NULL); } // IDIGNORE - loop forever! } } } // app should already be shut-down so we don't bother with // accelerator or menu translations. DispatchMessage(&msg); /* * tell all instances in this task to process their * callbacks so we can clear our queue. */ EmptyDDEPostQ(); for (paiT = pAppInfoList; paiT != NULL; paiT = paiT->next) { if (paiT->hTask == pai->hTask) { CheckCBQ(paiT); } } } } #if 0 // don't need this anymore if (pai->hwndTimer) { pai->wTimeoutStatus |= TOS_ABORT; PostMessage(pai->hwndTimer, WM_TIMER, TID_TIMEOUT, 0); // if this fails, no big deal because it means the queue is full // and the modal loop will catch our TOS_ABORT quickly. // We need to do this in case no activity is happening in the // modal loop. } #endif if (pai->hwndMonitor) { DmgDestroyWindow(pai->hwndMonitor); if (!--cMonitor) { UnhookWindowsHook(WH_GETMESSAGE, (FARPROC)DdePostHookProc); UnhookWindowsHook(WH_CALLWNDPROC, (FARPROC)DdeSendHookProc); } } UnlinkAppInfo(pai); DmgDestroyWindow(pai->hwndDmg); DmgDestroyWindow(pai->hwndFrame); while (PopPileSubitem(pai->pHDataPile, (LPBYTE)&hData)) FreeDataHandle(pai, hData, FALSE); DestroyPile(pai->pHDataPile); while (PopPileSubitem(pai->pHszPile, (LPBYTE)&a)) { MONHSZ(a, MH_CLEANUP, pai->hTask); FreeHsz(a); } DestroyPile(pai->pHszPile); DestroyPile(pai->pAppNamePile); DestroyLst(pai->pServerAdvList); DmgDestroyHeap(pai->hheapApp); pai->instCheck--; // make invalid on later attempts to reinit. FarFreeMem((LPSTR)pai); /* last one out.... trash the data info heap */ if (!pAppInfoList) { #ifdef DEBUG DIP dip; AssertF(!PopPileSubitem(pDataInfoPile, (LPBYTE)&dip), "leftover APPOWNED handles"); #endif DestroyPile(pDataInfoPile); DestroyPile(pLostAckPile); pDataInfoPile = NULL; pLostAckPile = NULL; AssertFW(cAtoms == 0, "DdeUninitialize() - leftover atoms"); // PROGMAN HACK!!!! GlobalDeleteAtom(aProgmanHack); // CLOSEHEAPWATCH(); } #ifdef DEBUG DumpGlobalLogs(); #endif TRACEAPIOUT((szT, "DdeUninitialize:1\n")); return(TRUE); } HCONVLIST EXPENTRY DdeConnectList( DWORD idInst, HSZ hszSvcName, HSZ hszTopic, HCONVLIST hConvList, PCONVCONTEXT pCC) { PAPPINFO pai; HWND hConv, hConvNext, hConvNew, hConvLast; HWND hConvListNew; PCLIENTINFO pciOld, pciNew; TRACEAPIIN((szT, "DdeConnectList(%lx, %lx, %lx, %lx, %lx)\n", idInst, hszSvcName, hszTopic, hConvList, pCC)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeConnectList:0\n")); return(0L); } pai->LastError = DMLERR_NO_ERROR; if (hConvList && !ValidateHConv(hConvList)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeConnectList:0\n")); return(0L); } /* * destroy any dead old clients */ if ((HWND)hConvList && (hConv = GetWindow((HWND)hConvList, GW_CHILD))) { do { hConvNext = GetWindow((HWND)hConv, GW_HWNDNEXT); pciOld = (PCLIENTINFO)GetWindowLong(hConv, GWL_PCI); if (!(pciOld->ci.fs & ST_CONNECTED)) { SetParent(hConv, pai->hwndDmg); Disconnect(hConv, ST_PERM2DIE, pciOld); } } while (hConv = hConvNext); } // create a new list window if ((hConvListNew = CreateWindow( SZCONVLISTCLASS, szNull, WS_CHILD, 0, 0, 0, 0, pai->hwndDmg, (HMENU)NULL, hInstance, &pai)) == NULL) { SETLASTERROR(pai, DMLERR_SYS_ERROR); TRACEAPIOUT((szT, "DdeConnectList:0\n")); return(0L); } // Make all possible connections to new list window hConvNew = GetDDEClientWindow(pai, hConvListNew, HIWORD(hszSvcName), LOWORD(hszSvcName), LOWORD(hszTopic), pCC); /* * If no new hConvs created, return old list. */ if (hConvNew == NULL) { // if no old hConvs as well, destroy all and return NULL if ((HWND)hConvList && GetWindow((HWND)hConvList, GW_CHILD) == NULL) { SendMessage((HWND)hConvList, UM_DISCONNECT, ST_PERM2DIE, 0L); SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); TRACEAPIOUT((szT, "DdeConnectList:0\n")); return(NULL); } // else just return old list (- dead convs) if (hConvList == NULL) { DestroyWindow(hConvListNew); SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); } TRACEAPIOUT((szT, "DdeConnectList:%lx\n", hConvList)); return(hConvList); } /* * remove duplicates from the new list */ if ((HWND)hConvList && (hConv = GetWindow((HWND)hConvList, GW_CHILD))) { // go throuch old list... do { pciOld = (PCLIENTINFO)GetWindowLong(hConv, GWL_PCI); /* * destroy any new clients that are duplicates of the old ones. */ hConvNew = GetWindow(hConvListNew, GW_CHILD); hConvLast = GetWindow(hConvNew, GW_HWNDLAST); while (hConvNew) { if (hConvNew == hConvLast) { hConvNext = NULL; } else { hConvNext = GetWindow(hConvNew, GW_HWNDNEXT); } pciNew = (PCLIENTINFO)GetWindowLong(hConvNew, GWL_PCI); if (pciOld->ci.aServerApp == pciNew->ci.aServerApp && pciOld->ci.aTopic == pciNew->ci.aTopic && pciOld->ci.hwndFrame == pciNew->ci.hwndFrame) { /* * assume same app, same topic, same hwndFrame is a duplicate. * * Move dieing window out of the list since it * dies asynchronously and will still be around * after this API exits. */ SetParent(hConvNew, pai->hwndDmg); Disconnect(hConvNew, ST_PERM2DIE, (PCLIENTINFO)GetWindowLong(hConvNew, GWL_PCI)); } hConvNew = hConvNext; } hConvNext = GetWindow(hConv, GW_HWNDNEXT); if (hConvNext && (GetParent(hConvNext) != (HWND)hConvList)) { hConvNext = NULL; } /* * move the unique old client to the new list */ SetParent(hConv, hConvListNew); } while (hConv = hConvNext); // get rid of the old list SendMessage((HWND)hConvList, UM_DISCONNECT, ST_PERM2DIE, 0L); } /* * If none are left, fail because no conversations were established. */ if (GetWindow(hConvListNew, GW_CHILD) == NULL) { SendMessage(hConvListNew, UM_DISCONNECT, ST_PERM2DIE, 0L); SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); TRACEAPIOUT((szT, "DdeConnectList:0\n")); return(NULL); } else { TRACEAPIOUT((szT, "DdeConnectList:%lx\n", MAKEHCONV(hConvListNew))); return(MAKEHCONV(hConvListNew)); } } HCONV EXPENTRY DdeQueryNextServer( HCONVLIST hConvList, HCONV hConvPrev) { HWND hwndMaybe; PAPPINFO pai; TRACEAPIIN((szT, "DdeQueryNextServer(%lx, %lx)\n", hConvList, hConvPrev)); if (!ValidateHConv(hConvList)) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); } TRACEAPIOUT((szT, "DdeQueryNextServer:0\n")); return NULL; } pai = EXTRACTHCONVLISTPAI(hConvList); pai->LastError = DMLERR_NO_ERROR; if (hConvPrev == NULL) { TRACEAPIOUT((szT, "DdeQueryNextServer:%lx\n", MAKEHCONV(GetWindow((HWND)hConvList, GW_CHILD)))); return MAKEHCONV(GetWindow((HWND)hConvList, GW_CHILD)); } else { if (!ValidateHConv(hConvPrev)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeQueryNextServer:0\n")); return NULL; } hwndMaybe = GetWindow((HWND)hConvPrev, GW_HWNDNEXT); if (!hwndMaybe) { TRACEAPIOUT((szT, "DdeQueryNextServer:0\n")); return NULL; } // make sure it's got the same parent and isn't the first child // ### maybe this code can go - I'm not sure how GW_HWNDNEXT acts. SS if (GetParent(hwndMaybe) == (HWND)hConvList && hwndMaybe != GetWindow((HWND)hConvList, GW_CHILD)) { TRACEAPIOUT((szT, "DdeQueryNextServer:%lx\n", MAKEHCONV(hwndMaybe))); return MAKEHCONV(hwndMaybe); } TRACEAPIOUT((szT, "DdeQueryNextServer:0\n")); return NULL; } } BOOL EXPENTRY DdeDisconnectList( HCONVLIST hConvList) { PAPPINFO pai; TRACEAPIIN((szT, "DdeDisconnectList(%lx)\n", hConvList)); if (!ValidateHConv(hConvList)) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); } TRACEAPIOUT((szT, "DdeDisconnectList:0\n")); return(FALSE); } pai = EXTRACTHCONVLISTPAI(hConvList); pai->LastError = DMLERR_NO_ERROR; SendMessage((HWND)hConvList, UM_DISCONNECT, ST_PERM2DIE, 0L); TRACEAPIOUT((szT, "DdeDisconnectList:1\n")); return(TRUE); } HCONV EXPENTRY DdeConnect( DWORD idInst, HSZ hszSvcName, HSZ hszTopic, PCONVCONTEXT pCC) { PAPPINFO pai; HWND hwnd; TRACEAPIIN((szT, "DdeConnect(%lx, %lx, %lx, %lx)\n", idInst, hszSvcName, hszTopic, pCC)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeConnect:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; if (pCC && pCC->cb != sizeof(CONVCONTEXT)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeConnect:0\n")); return(0); } hwnd = GetDDEClientWindow(pai, pai->hwndDmg, (HWND)HIWORD(hszSvcName), LOWORD(hszSvcName), LOWORD(hszTopic), pCC); if (hwnd == 0) { SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); } TRACEAPIOUT((szT, "DdeConnect:%lx\n", MAKEHCONV(hwnd))); return(MAKEHCONV(hwnd)); } BOOL EXPENTRY DdeDisconnect( HCONV hConv) { PAPPINFO pai; PCLIENTINFO pci; TRACEAPIIN((szT, "DdeDisconnect(%lx)\n", hConv)); if (!ValidateHConv(hConv)) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); } TRACEAPIOUT((szT, "DdeDisconnect:0\n")); return(FALSE); } pai = EXTRACTHCONVPAI(hConv); pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI); if (pai->cInProcess) { // do asynchronously if this is called within a callback if (!PostMessage((HWND)hConv, UM_DISCONNECT, ST_PERM2DIE, (LONG)pci)) { SETLASTERROR(pai, DMLERR_SYS_ERROR); TRACEAPIOUT((szT, "DdeDisconnect:0\n")); return(FALSE); } } else { Disconnect((HWND)hConv, ST_PERM2DIE, pci); } TRACEAPIOUT((szT, "DdeDisconnect:1\n")); return(TRUE); } HCONV EXPENTRY DdeReconnect( HCONV hConv) { HWND hwnd; PAPPINFO pai; PCLIENTINFO pci; TRACEAPIIN((szT, "DdeReconnect(%lx)\n", hConv)); if (!ValidateHConv(hConv)) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); } TRACEAPIOUT((szT, "DdeReconnect:0\n")); return(FALSE); } pai = EXTRACTHCONVPAI(hConv); pai->LastError = DMLERR_NO_ERROR; pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI); // The dyeing window MUST be a client to reconnect. if (!(pci->ci.fs & ST_CLIENT)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeReconnect:0\n")); return(FALSE); } hwnd = GetDDEClientWindow(pai, pai->hwndDmg, pci->ci.hwndFrame, pci->ci.aServerApp, pci->ci.aTopic, &pci->ci.CC); if (hwnd == 0) { SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); TRACEAPIOUT((szT, "DdeReconnect:0\n")); return(FALSE); } if (pci->ci.fs & ST_INLIST) { SetParent(hwnd, GetParent((HWND)hConv)); } if (pci->ci.fs & ST_ADVISE) { DWORD result; PADVLI pali, paliNext; // recover advise loops here for (pali = (PADVLI)pci->pClientAdvList->pItemFirst; pali; pali = paliNext) { paliNext = (PADVLI)pali->next; if (pali->hwnd == (HWND)hConv) { XFERINFO xi; xi.pulResult = &result; xi.ulTimeout = (DWORD)TIMEOUT_ASYNC; xi.wType = XTYP_ADVSTART | (pali->fsStatus & (XTYPF_NODATA | XTYPF_ACKREQ)); xi.wFmt = pali->wFmt; xi.hszItem = (HSZ)pali->aItem; xi.hConvClient = MAKEHCONV(hwnd); xi.cbData = 0; xi.hDataClient = NULL; ClientXferReq(&xi, hwnd, (PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI)); } } } TRACEAPIOUT((szT, "DdeReconnect:%lx\n", MAKEHCONV(hwnd))); return(MAKEHCONV(hwnd)); } UINT EXPENTRY DdeQueryConvInfo( HCONV hConv, DWORD idTransaction, PCONVINFO pConvInfo) { PCLIENTINFO pci; PAPPINFO pai; PXADATA pxad; PCQDATA pqd; BOOL fClient; WORD cb; CONVINFO ci; SEMCHECKOUT(); TRACEAPIIN((szT, "DdeQueryConvInfo(%lx, %lx, %lx(->cb=%lx))\n", hConv, idTransaction, pConvInfo, pConvInfo->cb)); if (!ValidateHConv(hConv) || !(pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI))) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); } TRACEAPIOUT((szT, "DdeQueryConvInfo:0\n")); return(FALSE); } pai = pci->ci.pai; pai->LastError = DMLERR_NO_ERROR; /* * This check attempts to prevent improperly coded apps from * crashing due to having not initialized the cb field. */ if (pConvInfo->cb > sizeof(CONVINFO) || pConvInfo->cb == 0) { pConvInfo->cb = sizeof(CONVINFO) - sizeof(HWND) - // for new hwnd field sizeof(HWND); // for new hwndPartner field } fClient = (BOOL)SendMessage((HWND)hConv, UM_QUERY, Q_CLIENT, 0L); if (idTransaction == QID_SYNC || !fClient) { pxad = &pci->ci.xad; } else { if (pci->pQ != NULL && (pqd = (PCQDATA)Findqi(pci->pQ, idTransaction))) { pxad = &pqd->xad; } else { SETLASTERROR(pai, DMLERR_UNFOUND_QUEUE_ID); TRACEAPIOUT((szT, "DdeQueryConvInfo:0\n")); return(FALSE); } } SEMENTER(); ci.cb = sizeof(CONVINFO); ci.hConvPartner = (IsWindow((HWND)pci->ci.hConvPartner) && ((pci->ci.fs & (ST_ISLOCAL | ST_CONNECTED)) == (ST_ISLOCAL | ST_CONNECTED))) ? pci->ci.hConvPartner : NULL; ci.hszSvcPartner = fClient ? pci->ci.aServerApp : 0; ci.hszServiceReq = pci->ci.hszSvcReq; ci.hszTopic = pci->ci.aTopic; ci.wStatus = pci->ci.fs; ci.ConvCtxt = pci->ci.CC; if (fClient) { ci.hUser = pxad->hUser; ci.hszItem = pxad->pXferInfo->hszItem; ci.wFmt = pxad->pXferInfo->wFmt; ci.wType = pxad->pXferInfo->wType; ci.wConvst = pxad->state; ci.wLastError = pxad->LastError; } else { ci.hUser = pci->ci.xad.hUser; ci.hszItem = NULL; ci.wFmt = 0; ci.wType = 0; ci.wConvst = pci->ci.xad.state; ci.wLastError = pci->ci.pai->LastError; } ci.hConvList = (pci->ci.fs & ST_INLIST) ? MAKEHCONV(GetParent((HWND)hConv)) : 0; cb = min(sizeof(CONVINFO), (WORD)pConvInfo->cb); ci.hwnd = (HWND)hConv; ci.hwndPartner = (HWND)pci->ci.hConvPartner; hmemcpy((LPBYTE)pConvInfo, (LPBYTE)&ci, cb); pConvInfo->cb = cb; SEMLEAVE(); TRACEAPIOUT((szT, "DdeQueryConvInfo:%x\n", cb)); return(cb); } BOOL EXPENTRY DdeSetUserHandle( HCONV hConv, DWORD id, DWORD hUser) { PAPPINFO pai; PCLIENTINFO pci; PXADATA pxad; PCQDATA pqd; TRACEAPIIN((szT, "DdeSetUserHandle(%lx, %lx, %lx)\n", hConv, id, hUser)); if (!ValidateHConv(hConv)) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); } TRACEAPIOUT((szT, "DdeSetUserHandle:0\n")); return(FALSE); } pai = EXTRACTHCONVPAI(hConv); pai->LastError = DMLERR_NO_ERROR; SEMCHECKOUT(); pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI); if (!pci) { Error: SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeSetUserHandle:0\n")); return(FALSE); } pxad = &pci->ci.xad; if (id != QID_SYNC) { if (!SendMessage((HWND)hConv, UM_QUERY, Q_CLIENT, 0)) { goto Error; } if (pci->pQ != NULL && (pqd = (PCQDATA)Findqi(pci->pQ, id))) { pxad = &pqd->xad; } else { SETLASTERROR(pai, DMLERR_UNFOUND_QUEUE_ID); TRACEAPIOUT((szT, "DdeSetUserHandle:0\n")); return(FALSE); } } pxad->hUser = hUser; TRACEAPIOUT((szT, "DdeSetUserHandle:1\n")); return(TRUE); } BOOL EXPENTRY DdePostAdvise( DWORD idInst, HSZ hszTopic, HSZ hszItem) { PAPPINFO pai; PSERVERINFO psi = NULL; register PADVLI pali; PADVLI paliPrev, paliEnd, paliMove; TRACEAPIIN((szT, "DdePostAdvise(%lx, %lx, %lx)\n", idInst, hszTopic, hszItem)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdePostAdvise:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; if (pai->afCmd & APPCMD_CLIENTONLY) { SETLASTERROR(pai, DMLERR_DLL_USAGE); TRACEAPIOUT((szT, "DdePostAdvise:0\n")); return(FALSE); } paliPrev = NULL; paliEnd = NULL; paliMove = NULL; pali = (PADVLI)pai->pServerAdvList->pItemFirst; while (pali && pali != paliMove) { if ((!hszItem || pali->aItem == (ATOM)hszItem) && (!hszTopic || pali->aTopic == (ATOM)hszTopic)) { /* * Advise loops are tricky because of the desireable FACKREQ feature * of DDE. The advise loop list holds information in its fsStatus * field to maintain the state of the advise loop. * * if the ADVST_WAITING bit is set, the server is still waiting for * the client to give it the go-ahead for more data with an * ACK message on this item. (FACKREQ is set) Without a go-ahead, * the server will not send any more advise data to the client but * will instead set the ADVST_CHANGED bit which will cause another * WM_DDE_DATA message to be sent to the client as soon as the * go-ahead ACK is received. This keeps the client up to date * but never overloads it. */ if (pali->fsStatus & ADVST_WAITING) { /* * if the client has not yet finished with the last data * we gave him, just update the advise loop status * instead of sending data now. */ pali->fsStatus |= ADVST_CHANGED; goto NextLink; } psi = (PSERVERINFO)GetWindowLong(pali->hwnd, GWL_PCI); if (pali->fsStatus & DDE_FDEFERUPD) { /* * In the nodata case, we don't bother the server. Just * pass the client an apropriate DATA message. */ IncHszCount(pali->aItem); // message copy #ifdef DEBUG cAtoms--; // don't count this add #endif PostDdeMessage(&psi->ci, WM_DDE_DATA, pali->hwnd, MAKELONG(0, pali->aItem), 0, 0); } else { PostServerAdvise(pali->hwnd, psi, pali, CountAdvReqLeft(pali)); } if (pali->fsStatus & DDE_FACKREQ && pali->next) { /* * In order to know what ack goes with what data sent out, we * place any updated advise loops at the end of the list so * that acks associated with them are found last. ie First ack * back goes with oldest data out. */ // Unlink if (paliPrev) { paliPrev->next = pali->next; } else { pai->pServerAdvList->pItemFirst = (PLITEM)pali->next; } // put on the end if (paliEnd) { paliEnd->next = (PLITEM)pali; paliEnd = pali; } else { for (paliEnd = pali; paliEnd->next; paliEnd = (PADVLI)paliEnd->next) { } paliEnd->next = (PLITEM)pali; paliMove = paliEnd = pali; } pali->next = NULL; if (paliPrev) { pali = (PADVLI)paliPrev->next; } else { pali = (PADVLI)pai->pServerAdvList->pItemFirst; } continue; } } NextLink: paliPrev = pali; pali = (PADVLI)pali->next; } TRACEAPIOUT((szT, "DdePostAdvise:1\n")); return(TRUE); } /* * History: 4/18/91 sanfords - now always frees any incomming data handle * thats not APPOWNED regardless of error case. */ HDDEDATA EXPENTRY DdeClientTransaction( LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt, UINT wType, DWORD ulTimeout, LPDWORD pulResult) { PAPPINFO pai; PCLIENTINFO pci; HDDEDATA hData, hDataBack, hRet = 0; SEMCHECKOUT(); TRACEAPIIN((szT, "DdeClientTransaction(%lx, %lx, %lx, %lx, %x, %x, %lx, %lx)\n", pData, cbData, hConv, hszItem, wFmt, wType, ulTimeout, pulResult)); if (!ValidateHConv(hConv)) { pai = NULL; while (pai = GetCurrentAppInfo(pai)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); } goto FreeErrExit; } pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI); pai = pci->ci.pai; /* * Don't let transactions happen if we are shutting down * or are already doing a sync transaction. */ if ((ulTimeout != TIMEOUT_ASYNC && pai->wFlags & AWF_INSYNCTRANSACTION) || pai->wFlags & AWF_UNINITCALLED) { SETLASTERROR(pai, DMLERR_REENTRANCY); goto FreeErrExit; } pci->ci.pai->LastError = DMLERR_NO_ERROR; if (!(pci->ci.fs & ST_CONNECTED)) { SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); goto FreeErrExit; } // If local, check filters first if (pci->ci.fs & ST_ISLOCAL) { PAPPINFO paiServer; PSERVERINFO psi; // we can do this because the app heaps are in global shared memory psi = (PSERVERINFO)GetWindowLong((HWND)pci->ci.hConvPartner, GWL_PCI); if (!psi) { // SERVER DIED! - simulate a terminate received. Terminate((HWND)hConv, (HWND)pci->ci.hConvPartner, pci); SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED); goto FreeErrExit; } paiServer = psi->ci.pai; if (paiServer->afCmd & aulmapType[(wType & XTYP_MASK) >> XTYP_SHIFT]) { SETLASTERROR(pai, DMLERR_NOTPROCESSED); FreeErrExit: if ((wType == XTYP_POKE || wType == XTYP_EXECUTE) && cbData == -1 && !(LOWORD((DWORD)pData) & HDATA_APPOWNED)) { FREEEXTHDATA(pData); } TRACEAPIOUT((szT, "DdeClientTransaction:0\n")); return(0); } } pai = pci->ci.pai; switch (wType) { case XTYP_POKE: case XTYP_EXECUTE: // prepair the outgoing handle if (cbData == -1L) { // handle given, not pointer hData = ((LPEXTDATAINFO)pData)->hData; if (!(LOWORD(hData) & HDATA_APPOWNED)) { FREEEXTHDATA(pData); } if (!(hData = DllEntry(&pci->ci, hData))) { TRACEAPIOUT((szT, "DdeClientTransaction:0\n")); return(0); } pData = (LPBYTE)hData; // place onto stack for pass on to ClientXferReq. } else { // pointer given, create handle from it. if (!(pData = (LPBYTE)PutData(pData, cbData, 0, LOWORD(hszItem), wFmt, 0, pai))) { SETLASTERROR(pai, DMLERR_MEMORY_ERROR); TRACEAPIOUT((szT, "DdeClientTransaction:0\n")); return(0); } } hData = (HDDEDATA)pData; // used to prevent compiler over-optimization. case XTYP_REQUEST: case XTYP_ADVSTART: case XTYP_ADVSTART | XTYPF_NODATA: case XTYP_ADVSTART | XTYPF_ACKREQ: if (wType != XTYP_EXECUTE && !hszItem) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeClientTransaction:0\n")); return(0); } case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ: if (wType != XTYP_EXECUTE && !wFmt) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeClientTransaction:0\n")); return(0); } case XTYP_ADVSTOP: pai->LastError = DMLERR_NO_ERROR; // reset before start. if (ulTimeout == TIMEOUT_ASYNC) { hRet = (HDDEDATA)ClientXferReq((PXFERINFO)&pulResult, (HWND)hConv, pci); } else { pai->wFlags |= AWF_INSYNCTRANSACTION; hDataBack = (HDDEDATA)ClientXferReq((PXFERINFO)&pulResult, (HWND)hConv, pci); pai->wFlags &= ~AWF_INSYNCTRANSACTION; if ((wType & XCLASS_DATA) && hDataBack) { LPEXTDATAINFO pedi; //if (AddPileItem(pai->pHDataPile, (LPBYTE)&hDataBack, CmpHIWORD) == API_ERROR) { // SETLASTERROR(pai, DMLERR_MEMORY_ERROR); // goto ReturnPoint; //} // use app heap so any leftovers at Uninitialize time go away. pedi = (LPEXTDATAINFO)FarAllocMem(pai->hheapApp, sizeof(EXTDATAINFO)); if (pedi) { pedi->pai = pai; pedi->hData = hDataBack; } else { SETLASTERROR(pai, DMLERR_MEMORY_ERROR); } hRet = (HDDEDATA)pedi; goto ReturnPoint; } else if (hDataBack) { hRet = TRUE; } } goto ReturnPoint; } SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); ReturnPoint: if (pai->wFlags & AWF_UNINITCALLED) { pai->wFlags &= ~AWF_UNINITCALLED; DdeUninitialize(MAKELONG((WORD)pai, pai->instCheck)); } TRACEAPIOUT((szT, "DdeClientTransaction:%lx\n", hRet)); return(hRet); } /***************************** Public Function ****************************\ * PUBDOC START * WORD EXPENTRY DdeGetLastError(void) * * This API returns the most recent error registered by the DDE manager for * the current thread. This should be called anytime a DDE manager API * returns in a failed state. * * returns an error code which corresponds to a DMLERR_ constant found in * ddeml.h. This error code may be passed on to DdePostError() to * show the user the reason for the error. * * PUBDOC END * * History: * Created 12/14/88 Sanfords \***************************************************************************/ UINT EXPENTRY DdeGetLastError( DWORD idInst) { register PAPPINFO pai; register WORD err = DMLERR_DLL_NOT_INITIALIZED; TRACEAPIIN((szT, "DdeGetLastError(%lx)\n", idInst)); pai = (PAPPINFO)idInst; if (pai) { if (pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeGetLastError:%x [bad instance]\n", DMLERR_INVALIDPARAMETER)); return(DMLERR_INVALIDPARAMETER); } err = pai->LastError; pai->LastError = DMLERR_NO_ERROR; } TRACEAPIOUT((szT, "DdeGetLastError:%x\n", err)); return(err); } /*\ * Data Handles: * * Control flags: * * HDCF_APPOWNED * Only the app can free this in the apps PID/TID context. * SET - when DdeCreateDataHandle is called with this flag given. * The hData is Logged at this time. * * HDCF_READONLY - set by ClientXfer and callback return. * The app cannot add data to handles in this state. * SET - when ClientXfer is entered * SET - when callback is left * * The DLL can free: * any hData EXCEPT those hDatas which are * APPOWNED where PIDcurrent == PIDowner. * * any unfreed logged hDatas are freed at unregistration time. * * The APP can free: * any logged hData. * * Logging points: ClientXfer return, CheckQueue return, PutData(APPOWNED). * * WARNING: * * Apps with multiple thread registration that talk to themselves * must not free hDatas until all threads are done with them. * \*/ /***************************** Public Function ****************************\ * PUBDOC START * HDDEDATA EXPENTRY DdeCreateDataHandle(pSrc, cb, cbOff, hszItem, wFmt, afCmd) * LPBYTE pSrc; * DWORD cb; * DWORD cbOff; * HSZ hszItem; * WORD wFmt; * WORD afCmd; * * This api allows a server application to create a hData apropriate * for return from its call-back function. * The passed in data is stored into the hData which is * returned on success. Any portions of the data handle not filled are * undefined. afCmd contains any of the HDATA_ constants described below: * * HDATA_APPOWNED * This declares the created data handle to be the responsability of * the application to free it. Application owned data handles may * be returned from the callback function multiple times. This allows * a server app to be able to support many clients without having to * recopy the data for each request. * * NOTES: * If an application expects this data handle to hold >64K of data via * DdeAddData(), it should specify a cb + cbOff to be as large as * the object is expected to get to avoid unnecessary data copying * or reallocation by the DLL. * * if psrc==NULL, no actual data copying takes place. * * Data handles given to an application via the DdeMgrClientXfer() or * DdeMgrCheckQueue() functions are the responsability of the client * application to free and MUST NOT be returned from the callback * function as server data! * * PUBDOC END * * History: * Created 12/14/88 Sanfords \***************************************************************************/ HDDEDATA EXPENTRY DdeCreateDataHandle( DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff, HSZ hszItem, UINT wFmt, UINT afCmd) { PAPPINFO pai; HDDEDATA hData; TRACEAPIIN((szT, "DdeCreateDataHandle(%lx, %lx, %lx, %lx, %lx, %x, %x)\n", idInst, pSrc, cb, cbOff, hszItem, wFmt, afCmd)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeCreateDataHandle:0\n")); return(0); } pai->LastError = DMLERR_NO_ERROR; if (afCmd & ~(HDATA_APPOWNED)) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeCreateDataHandle:0\n")); return(0L); } hData = PutData(pSrc, cb, cbOff, LOWORD(hszItem), wFmt, afCmd, pai); if (hData) { LPEXTDATAINFO pedi; // use app heap so any leftovers at Uninitialize time go away. pedi = (LPEXTDATAINFO)FarAllocMem(pai->hheapApp, sizeof(EXTDATAINFO)); if (pedi) { pedi->pai = pai; pedi->hData = hData; } hData = (HDDEDATA)(DWORD)pedi; } TRACEAPIOUT((szT, "DdeCreateDataHandle:%lx\n", hData)); return(hData); } HDDEDATA EXPENTRY DdeAddData( HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff) { PAPPINFO pai; HDDEDATA FAR * phData; DIP newDip; HANDLE hd, hNewData; LPEXTDATAINFO pedi; TRACEAPIIN((szT, "DdeAddData(%lx, %lx, %lx, %lx)\n", hData, pSrc, cb, cbOff)); if (!hData) goto DdeAddDataError; pedi = (LPEXTDATAINFO)hData; pai = pedi->pai; pai->LastError = DMLERR_NO_ERROR; hData = pedi->hData; /* if the datahandle is bogus, abort */ hd = hNewData = HIWORD(hData); if (!hd || (LOWORD(hData) & HDATA_READONLY)) { DdeAddDataError: SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeAddData:0\n")); return(0L); } /* * we need this check in case the owning app is trying to reallocate * after giving the hData away. (his copy of the handle would not have * the READONLY flag set) */ phData = (HDDEDATA FAR *)FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, 0); if (!phData || LOWORD(*phData) & HDATA_READONLY) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeAddData:0\n")); return(0L); } /* HACK ALERT! * make sure the first two words req'd by windows dde is there, * that is if the data isn't from an execute */ if (!(LOWORD(hData) & HDATA_EXEC)) { cbOff += 4L; } if (GlobalSize(hd) < cb + cbOff) { /* * need to grow the block before putting new data in... */ if (!(hNewData = GLOBALREALLOC(hd, cb + cbOff, GMEM_MOVEABLE))) { /* * We can't grow the seg. Try allocating a new one. */ if (!(hNewData = GLOBALALLOC(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff))) { /* failed.... die */ SETLASTERROR(pai, DMLERR_MEMORY_ERROR); TRACEAPIOUT((szT, "DdeAddData:0\n")); return(0); } else { /* * got a new block, now copy data and trash old one */ CopyHugeBlock(GLOBALPTR(hd), GLOBALPTR(hNewData), GlobalSize(hd)); GLOBALFREE(hd); // objects flow through - no need to free. } } if (hNewData != hd) { /* if the handle is different and in piles, update data piles */ if (FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, FPI_DELETE)) { DIP *pDip; HDDEDATA hdT; // replace entry in global data info pile. if (pDip = (DIP *)(DWORD)FindPileItem(pDataInfoPile, CmpWORD, (LPBYTE)&hd, 0)) { newDip.hData = hNewData; newDip.hTask = pDip->hTask; newDip.cCount = pDip->cCount; newDip.fFlags = pDip->fFlags; FindPileItem(pDataInfoPile, CmpWORD, (LPBYTE)&hd, FPI_DELETE); /* following assumes addpileitem will not fail...!!! */ AddPileItem(pDataInfoPile, (LPBYTE)&newDip, CmpWORD); } hdT = (HDDEDATA)MAKELONG(newDip.fFlags, hNewData); AddPileItem(pai->pHDataPile, (LPBYTE)&hdT, CmpHIWORD); } hData = MAKELONG(LOWORD(hData), hNewData); } } if (pSrc) { CopyHugeBlock(pSrc, HugeOffset(GLOBALLOCK(HIWORD(hData)), cbOff), cb); } pedi->hData = hData; TRACEAPIOUT((szT, "DdeAddData:%lx\n", pedi)); return((HDDEDATA)pedi); } DWORD EXPENTRY DdeGetData(hData, pDst, cbMax, cbOff) HDDEDATA hData; LPBYTE pDst; DWORD cbMax; DWORD cbOff; { PAPPINFO pai; DWORD cbSize; BOOL fExec = TRUE; TRACEAPIIN((szT, "DdeGetData(%lx, %lx, %lx, %lx)\n", hData, pDst, cbMax, cbOff)); // // Check for NULL. // Packard Bell Navigator passes NULL at startup. In 3.1 we'd // maybe trash our local heap using ds:0. But now touching pai will // fault since it's a far pointer and 0:0 is bad. // // Also makes your system stabler. // if (!hData) goto DdeGetDataError; pai = EXTRACTHDATAPAI(hData); pai->LastError = DMLERR_NO_ERROR; hData = ((LPEXTDATAINFO)hData)->hData; cbSize = GlobalSize(HIWORD(hData)); /* HACK ALERT! * make sure the first two words req'd by windows dde is there, * as long as it's not execute data */ if (!(LOWORD(hData) & HDATA_EXEC)) { cbOff += 4; fExec = FALSE; } if (cbOff >= cbSize) { DdeGetDataError: SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeGetData:0\n")); return(0L); } cbMax = min(cbMax, cbSize - cbOff); if (pDst == NULL) { TRACEAPIOUT((szT, "DdeGetData:%lx\n", fExec ? cbSize : cbSize - 4)); return(fExec ? cbSize : cbSize - 4); } else { CopyHugeBlock(HugeOffset(GLOBALLOCK(HIWORD(hData)), cbOff), pDst, cbMax); TRACEAPIOUT((szT, "DdeGetData:%lx\n", cbMax)); return(cbMax); } } LPBYTE EXPENTRY DdeAccessData( HDDEDATA hData, LPDWORD pcbDataSize) { PAPPINFO pai; DWORD offset; LPBYTE lpRet; TRACEAPIIN((szT, "DdeAccessData(%lx, %lx)\n", hData, pcbDataSize)); if (!hData) goto DdeAccessDataError; pai = EXTRACTHDATAPAI(hData); pai->LastError = DMLERR_NO_ERROR; hData = ((LPEXTDATAINFO)hData)->hData; if (HIWORD(hData) && (HIWORD(hData) != 0xFFFF) ) { /* messed around here getting past the first two words, which * aren't even there if this is execute data */ offset = (LOWORD(hData) & HDATA_EXEC) ? 0L : 4L; if (pcbDataSize) { *pcbDataSize = GlobalSize(HIWORD(hData)) - offset; } lpRet = (LPBYTE)GLOBALLOCK(HIWORD(hData)) + offset; TRACEAPIOUT((szT, "DdeAccessData:%lx\n", lpRet)); return(lpRet); } DdeAccessDataError: SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeAccessData:0\n")); return(0L); } BOOL EXPENTRY DdeUnaccessData( HDDEDATA hData) { PAPPINFO pai; TRACEAPIIN((szT, "DdeUnaccessData(%lx)\n", hData)); // // BOGUS -- we should set last error and RIP also. // if (hData) { pai = EXTRACTHDATAPAI(hData); pai->LastError = DMLERR_NO_ERROR; } TRACEAPIOUT((szT, "DdeUnaccessData:1\n")); return(TRUE); } // Diamond Multimedia Kit 5000 creates a non-app-owned data handle, // uses it in a client transaction (which free's it) and then // calls DDEFreeDataHandle which can fault (depending on what junk // gets left behind). To handle this we validate the data handle // before doing anything else. BOOL HDdeData_Validate(HDDEDATA hData) { WORD wSaveDS; UINT nRet; // we better check HIWORD(hData) before we try to stuff it into ds if(IsBadReadPtr((LPCSTR)hData, 1)) { #ifdef DEBUG OutputDebugString("DDEML: Invalid HDDEDATA.\n\r"); #endif return(FALSE); } wSaveDS = SwitchDS(HIWORD(hData)); // Use the validation layer to check the handle // We can call LocalSize with the near ptr as the handle because: // 1. The HDDEDATA was allocated with LPTR (LMEM_FIXED | LMEM_ZEROINIT) // 2. Local mem that is alloc'd LMEM_FIXED, the offset is the handle // 3. We don't want to call LocalHandle to get the handle because it has // no parameter vailidation & blows up for bad handles nRet = LocalSize((HANDLE)LOWORD(hData)); SwitchDS(wSaveDS); #ifdef DEBUG if (!nRet) { OutputDebugString("DDEML: Invalid HDDEDATA.\n\r"); } #endif return nRet; } BOOL EXPENTRY DdeFreeDataHandle( HDDEDATA hData) { PAPPINFO pai; LPEXTDATAINFO pedi; TRACEAPIIN((szT, "DdeFreeDataHandle(%lx)\n", hData)); pedi = (LPEXTDATAINFO)hData; if ( !pedi || !HDdeData_Validate(hData) ) { TRACEAPIOUT((szT, "DdeFreeDataHandle:1\n")); return(TRUE); } pai = EXTRACTHDATAPAI(hData); pai->LastError = DMLERR_NO_ERROR; if (!(LOWORD(pedi->hData) & HDATA_NOAPPFREE)) { FreeDataHandle(pedi->pai, pedi->hData, FALSE); FarFreeMem((LPSTR)pedi); } TRACEAPIOUT((szT, "DdeFreeDataHandle:2\n")); return(TRUE); } /***************************************************************************\ * PUBDOC START * HSZ management notes: * * HSZs are used in this DLL to simplify string handling for applications * and for inter-process communication. Since many applications use a * fixed set of Application/Topic/Item names, it is convenient to convert * them to HSZs and allow quick comparisons for lookups. This also frees * the DLL up from having to constantly provide string buffers for copying * strings between itself and its clients. * * HSZs are the same as atoms except they have no restrictions on length or * number and are 32 bit values. They are case preserving and can be * compared directly for case sensitive comparisons or via DdeCmpStringHandles() * for case insensitive comparisons. * * When an application creates an HSZ via DdeCreateStringHandle() or increments its * count via DdeKeepStringHandle() it is essentially claiming the HSZ for * its own use. On the other hand, when an application is given an * HSZ from the DLL via a callback, it is using another application's HSZ * and should not free that HSZ via DdeFreeStringHandle(). * * The DLL insures that during the callback any HSZs given will remain * valid for the duration of the callback. * * If an application wishes to keep that HSZ to use for itself as a * standard for future comparisons, it should increment its count so that, * should the owning application free it, the HSZ will not become invalid. * This also prevents an HSZ from changing its value. (ie, app A frees it * and then app B creates a new one that happens to use the same HSZ code, * then app C, which had the HSZ stored all along (but forgot to increment * its count) now is holding a handle to a different string.) * * Applications may free HSZs they have created or incremented at any time * by calling DdeFreeStringHandle(). * * The DLL internally increments HSZ counts while in use so that they will * not be destroyed until both the DLL and all applications concerned are * through with them. * * IT IS THE APPLICATIONS RESPONSIBILITY TO PROPERLY CREATE AND FREE HSZs!! * * PUBDOC END \***************************************************************************/ HSZ EXPENTRY DdeCreateStringHandle( DWORD idInst, LPCSTR psz, int iCodePage) { #define pai ((PAPPINFO)idInst) ATOM a; TRACEAPIIN((szT, "DdeCreateStringHandle(%lx, %s, %x)\n", idInst, psz, iCodePage)); if (pai == NULL | pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeCreateStringHandle:0\n")); return(0); } pai->LastError = DMLERR_NO_ERROR; if (psz == NULL || *psz == '\0') { TRACEAPIOUT((szT, "DdeCreateStringHandle:0\n")); return(0); } if (iCodePage == 0 || iCodePage == CP_WINANSI || iCodePage == GetKBCodePage()) { SEMENTER(); a = FindAddHsz((LPSTR)psz, TRUE); SEMLEAVE(); MONHSZ(a, MH_CREATE, pai->hTask); if (AddPileItem(pai->pHszPile, (LPBYTE)&a, NULL) == API_ERROR) { SETLASTERROR(pai, DMLERR_MEMORY_ERROR); a = 0; } TRACEAPIOUT((szT, "DdeCreateStringHandle:%x\n", a)); return((HSZ)a); } else { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeCreateStringHandle:0\n")); return(0); } #undef pai } BOOL EXPENTRY DdeFreeStringHandle( DWORD idInst, HSZ hsz) { PAPPINFO pai; ATOM a = LOWORD(hsz); BOOL fRet; TRACEAPIIN((szT, "DdeFreeStringHandle(%lx, %lx)\n", idInst, hsz)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeFreeStringHandle:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; MONHSZ(a, MH_DELETE, pai->hTask); FindPileItem(pai->pHszPile, CmpWORD, (LPBYTE)&a, FPI_DELETE); fRet = FreeHsz(a); TRACEAPIOUT((szT, "DdeFreeStringHandle:%x\n", fRet)); return(fRet); } BOOL EXPENTRY DdeKeepStringHandle( DWORD idInst, HSZ hsz) { PAPPINFO pai; ATOM a = LOWORD(hsz); BOOL fRet; TRACEAPIIN((szT, "DdeKeepStringHandle(%lx, %lx)\n", idInst, hsz)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeKeepStringHandle:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; MONHSZ(a, MH_KEEP, pai->hTask); AddPileItem(pai->pHszPile, (LPBYTE)&a, NULL); fRet = IncHszCount(a); TRACEAPIOUT((szT, "DdeKeepStringHandle:%x\n", fRet)); return(fRet); } DWORD EXPENTRY DdeQueryString( DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, int iCodePage) { PAPPINFO pai; DWORD dwRet; TRACEAPIIN((szT, "DdeQueryString(%lx, %lx, %lx, %lx, %x)\n", idInst, hsz, psz, cchMax, iCodePage)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeQueryString:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; if (iCodePage == 0 || iCodePage == CP_WINANSI || iCodePage == GetKBCodePage()) { if (psz) { if (hsz) { dwRet = QueryHszName(hsz, psz, (WORD)cchMax); TRACEAPIOUT((szT, "DdeQueryString:%lx(%s)\n", dwRet, psz)); return(dwRet); } else { *psz = '\0'; TRACEAPIOUT((szT, "DdeQueryString:0\n")); return(0); } } else if (hsz) { dwRet = QueryHszLength(hsz); TRACEAPIOUT((szT, "DdeQueryString:%lx\n", dwRet)); return(dwRet); } else { TRACEAPIOUT((szT, "DdeQueryString:0\n")); return(0); } } else { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeQueryString:0\n")); return(0); } } int EXPENTRY DdeCmpStringHandles( HSZ hsz1, HSZ hsz2) { int iRet; TRACEAPIIN((szT, "DdeCmpStringHandles(%lx, %lx)\n", hsz1, hsz2)); if (hsz2 > hsz1) { iRet = -1; } else if (hsz2 < hsz1) { iRet = 1; } else { iRet = 0; } TRACEAPIOUT((szT, "DdeCmpStringHandles:%x\n", iRet)); return(iRet); } BOOL EXPENTRY DdeAbandonTransaction( DWORD idInst, HCONV hConv, DWORD idTransaction) { PAPPINFO pai; TRACEAPIIN((szT, "DdeAbandonTransaction(%lx, %lx, %lx)\n", idInst, hConv, idTransaction)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeAbandonTransaction:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; if ((hConv && !ValidateHConv(hConv)) || idTransaction == QID_SYNC) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeAbandonTransaction:0\n")); return(FALSE); } if (hConv == NULL) { // do all conversations! register HWND hwnd; register HWND hwndLast; if (!(hwnd = GetWindow(pai->hwndDmg, GW_CHILD))) { TRACEAPIOUT((szT, "DdeAbandonTransaction:1\n")); return(TRUE); } hwndLast = GetWindow(hwnd, GW_HWNDLAST); do { AbandonTransaction(hwnd, pai, idTransaction, TRUE); if (hwnd == hwndLast) { break; } hwnd = GetWindow(hwnd, GW_HWNDNEXT); } while (TRUE); } else { BOOL fRet; fRet = AbandonTransaction((HWND)hConv, pai, idTransaction, TRUE); TRACEAPIOUT((szT, "DdeAbandonTransaction:%x\n", fRet)); return(fRet); } TRACEAPIOUT((szT, "DdeAbandonTransaction:1\n")); return(TRUE); } BOOL AbandonTransaction( HWND hwnd, PAPPINFO pai, DWORD id, BOOL fMarkOnly) { PCLIENTINFO pci; PCQDATA pcqd; WORD err; SEMCHECKOUT(); SEMENTER(); pci = (PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI); if (!pci->ci.fs & ST_CLIENT) { err = DMLERR_INVALIDPARAMETER; failExit: SETLASTERROR(pai, err); SEMLEAVE(); SEMCHECKOUT(); return(FALSE); } do { /* * HACK: id == 0 -> all ids so we cycle */ pcqd = (PCQDATA)Findqi(pci->pQ, id); if (!pcqd) { if (id) { err = DMLERR_UNFOUND_QUEUE_ID; goto failExit; } break; } if (fMarkOnly) { pcqd->xad.fAbandoned = TRUE; if (!id) { while (pcqd = (PCQDATA)FindNextQi(pci->pQ, (PQUEUEITEM)pcqd, FALSE)) { pcqd->xad.fAbandoned = TRUE; } break; } } else { if (pcqd->xad.pdata && pcqd->xad.pdata != 1 && !FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&pcqd->xad.pdata, 0)) { FreeDDEData(LOWORD(pcqd->xad.pdata), pcqd->xad.pXferInfo->wFmt); } /* * Decrement the use count we incremented when the client started * this transaction. */ FreeHsz(LOWORD(pcqd->XferInfo.hszItem)); Deleteqi(pci->pQ, MAKEID(pcqd)); } } while (!id); SEMLEAVE(); SEMCHECKOUT(); return(TRUE); } BOOL EXPENTRY DdeEnableCallback( DWORD idInst, HCONV hConv, UINT wCmd) { PAPPINFO pai; BOOL fRet; TRACEAPIIN((szT, "DdeEnableCallback(%lx, %lx, %x)\n", idInst, hConv, wCmd)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeEnableCallback:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; if ((hConv && !ValidateHConv(hConv)) || (wCmd & ~(EC_ENABLEONE | EC_ENABLEALL | EC_DISABLE | EC_QUERYWAITING))) { SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeEnableCallback:0\n")); return(FALSE); } SEMCHECKOUT(); if (wCmd & EC_QUERYWAITING) { PCBLI pli; int cWaiting = 0; SEMENTER(); for (pli = (PCBLI)pai->plstCB->pItemFirst; pli && cWaiting < 2; pli = (PCBLI)pli->next) { if (hConv || pli->hConv == hConv) { cWaiting++; } } SEMLEAVE(); fRet = cWaiting > 1 || (cWaiting == 1 && pai->cInProcess == 0); TRACEAPIOUT((szT, "DdeEnableCallback:%x\n", fRet)); return(fRet); } /* * We depend on the fact that EC_ constants relate to ST_ constants. */ if (hConv == NULL) { if (wCmd & EC_DISABLE) { pai->wFlags |= AWF_DEFCREATESTATE; } else { pai->wFlags &= ~AWF_DEFCREATESTATE; } ChildMsg(pai->hwndDmg, UM_SETBLOCK, wCmd, 0, FALSE); } else { SendMessage((HWND)hConv, UM_SETBLOCK, wCmd, 0); } if (!(wCmd & EC_DISABLE)) { // This is synchronous! Fail if we made this from within a callback. if (pai->cInProcess) { SETLASTERROR(pai, DMLERR_REENTRANCY); TRACEAPIOUT((szT, "DdeEnableCallback:0\n")); return(FALSE); } SendMessage(pai->hwndDmg, UM_CHECKCBQ, 0, (DWORD)(LPSTR)pai); } TRACEAPIOUT((szT, "DdeEnableCallback:1\n")); return(TRUE); // TRUE implies the callback queue is free of unblocked calls. } HDDEDATA EXPENTRY DdeNameService( DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd) { PAPPINFO pai; PPILE panp; TRACEAPIIN((szT, "DdeNameService(%lx, %lx, %lx, %x)\n", idInst, hsz1, hsz2, afCmd)); pai = (PAPPINFO)idInst; if (pai == NULL || pai->instCheck != HIWORD(idInst)) { TRACEAPIOUT((szT, "DdeNameService:0\n")); return(FALSE); } pai->LastError = DMLERR_NO_ERROR; if (afCmd & DNS_FILTERON) { pai->afCmd |= APPCMD_FILTERINITS; } if (afCmd & DNS_FILTEROFF) { pai->afCmd &= ~APPCMD_FILTERINITS; } if (afCmd & (DNS_REGISTER | DNS_UNREGISTER)) { if (pai->afCmd & APPCMD_CLIENTONLY) { SETLASTERROR(pai, DMLERR_DLL_USAGE); TRACEAPIOUT((szT, "DdeNameService:0\n")); return(FALSE); } panp = pai->pAppNamePile; if (hsz1 == NULL) { if (afCmd & DNS_REGISTER) { /* * registering NULL is not allowed! */ SETLASTERROR(pai, DMLERR_INVALIDPARAMETER); TRACEAPIOUT((szT, "DdeNameService:0\n")); return(FALSE); } /* * unregistering NULL is just like unregistering each * registered name. * * 10/19/90 - made this a synchronous event so that hsz * can be freed by calling app after this call completes * without us having to keep a copy around forever. */ while (PopPileSubitem(panp, (LPBYTE)&hsz1)) { RegisterService(FALSE, (GATOM)hsz1, pai->hwndFrame); FreeHsz(LOWORD(hsz1)); } TRACEAPIOUT((szT, "DdeNameService:1\n")); return(TRUE); } if (afCmd & DNS_REGISTER) { if (panp == NULL) { panp = pai->pAppNamePile = CreatePile(pai->hheapApp, sizeof(HSZ), 8); } IncHszCount(LOWORD(hsz1)); AddPileItem(panp, (LPBYTE)&hsz1, NULL); } else { // DNS_UNREGISTER FindPileItem(panp, CmpDWORD, (LPBYTE)&hsz1, FPI_DELETE); } // see 10/19/90 note above. RegisterService(afCmd & DNS_REGISTER ? TRUE : FALSE, (GATOM)hsz1, pai->hwndFrame); if (afCmd & DNS_UNREGISTER) { FreeHsz(LOWORD(hsz1)); } TRACEAPIOUT((szT, "DdeNameService:1\n")); return(TRUE); } TRACEAPIOUT((szT, "DdeNameService:0\n")); return(0L); }