1 /* 2 * DDEML library 3 * 4 * Copyright 1997 Alexandre Julliard 5 * Copyright 1997 Len White 6 * Copyright 1999 Keith Matthews 7 * Copyright 2000 Corel 8 * Copyright 2001 Eric Pouech 9 * Copyright 2003, 2004, 2005 Dmitry Timoshkov 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24 */ 25 26 #include <user32.h> 27 28 #include "dde_private.h" 29 30 WINE_DEFAULT_DEBUG_CHANNEL(ddeml); 31 32 /* convert between ATOM and HSZ avoiding compiler warnings */ 33 #define ATOM2HSZ(atom) ((HSZ) (ULONG_PTR)(atom)) 34 #define HSZ2ATOM(hsz) ((ATOM) (ULONG_PTR)(hsz)) 35 36 static WDML_INSTANCE* WDML_InstanceList = NULL; 37 static LONG WDML_MaxInstanceID = 0; /* OK for present, have to worry about wrap-around later */ 38 const WCHAR WDML_szEventClass[] = L"DDEMLEvent"; 39 40 /* protection for instance list */ 41 static CRITICAL_SECTION WDML_CritSect; 42 static CRITICAL_SECTION_DEBUG critsect_debug = 43 { 44 0, 0, &WDML_CritSect, 45 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 46 0, 0, { (DWORD_PTR)(__FILE__ ": WDML_CritSect") } 47 }; 48 static CRITICAL_SECTION WDML_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 }; 49 50 /* ================================================================ 51 * 52 * Pure DDE (non DDEML) management 53 * 54 * ================================================================ */ 55 56 57 /***************************************************************** 58 * PackDDElParam (USER32.@) 59 * 60 * RETURNS 61 * the packed lParam 62 */ 63 LPARAM WINAPI PackDDElParam(UINT msg, UINT_PTR uiLo, UINT_PTR uiHi) 64 { 65 HGLOBAL hMem; 66 UINT_PTR *params; 67 68 switch (msg) 69 { 70 case WM_DDE_ACK: 71 case WM_DDE_ADVISE: 72 case WM_DDE_DATA: 73 case WM_DDE_POKE: 74 if (!(hMem = GlobalAlloc(GMEM_DDESHARE, sizeof(UINT_PTR) * 2))) 75 { 76 ERR("GlobalAlloc failed\n"); 77 return 0; 78 } 79 if (!(params = GlobalLock(hMem))) 80 { 81 ERR("GlobalLock failed (%p)\n", hMem); 82 return 0; 83 } 84 params[0] = uiLo; 85 params[1] = uiHi; 86 GlobalUnlock(hMem); 87 return (LPARAM)hMem; 88 89 case WM_DDE_EXECUTE: 90 return uiHi; 91 92 default: 93 return MAKELONG(uiLo, uiHi); 94 } 95 } 96 97 98 /***************************************************************** 99 * UnpackDDElParam (USER32.@) 100 * 101 * RETURNS 102 * success: nonzero 103 * failure: zero 104 */ 105 BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam, 106 PUINT_PTR uiLo, PUINT_PTR uiHi) 107 { 108 UINT_PTR *params; 109 110 switch (msg) 111 { 112 case WM_DDE_ACK: 113 case WM_DDE_ADVISE: 114 case WM_DDE_DATA: 115 case WM_DDE_POKE: 116 if (!lParam || !(params = GlobalLock((HGLOBAL)lParam))) 117 { 118 if (uiLo) *uiLo = 0; 119 if (uiHi) *uiHi = 0; 120 return FALSE; 121 } 122 if (uiLo) *uiLo = params[0]; 123 if (uiHi) *uiHi = params[1]; 124 GlobalUnlock( (HGLOBAL)lParam ); 125 return TRUE; 126 127 case WM_DDE_EXECUTE: 128 if (uiLo) *uiLo = 0; 129 if (uiHi) *uiHi = lParam; 130 return TRUE; 131 132 default: 133 if (uiLo) *uiLo = LOWORD(lParam); 134 if (uiHi) *uiHi = HIWORD(lParam); 135 return TRUE; 136 } 137 } 138 139 140 /***************************************************************** 141 * FreeDDElParam (USER32.@) 142 * 143 * RETURNS 144 * success: nonzero 145 * failure: zero 146 */ 147 BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam) 148 { 149 switch (msg) 150 { 151 case WM_DDE_ACK: 152 case WM_DDE_ADVISE: 153 case WM_DDE_DATA: 154 case WM_DDE_POKE: 155 /* first check if it's a global handle */ 156 if (!GlobalHandle( (LPVOID)lParam )) return TRUE; 157 return !GlobalFree( (HGLOBAL)lParam ); 158 159 default: 160 return TRUE; 161 } 162 } 163 164 165 /***************************************************************** 166 * ReuseDDElParam (USER32.@) 167 * 168 * RETURNS 169 * the packed lParam 170 */ 171 LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut, 172 UINT_PTR uiLo, UINT_PTR uiHi) 173 { 174 UINT_PTR *params; 175 176 switch (msgIn) 177 { 178 case WM_DDE_ACK: 179 case WM_DDE_ADVISE: 180 case WM_DDE_DATA: 181 case WM_DDE_POKE: 182 switch(msgOut) 183 { 184 case WM_DDE_ACK: 185 case WM_DDE_ADVISE: 186 case WM_DDE_DATA: 187 case WM_DDE_POKE: 188 if (!lParam) return 0; 189 if (!(params = GlobalLock( (HGLOBAL)lParam ))) 190 { 191 ERR("GlobalLock failed\n"); 192 return 0; 193 } 194 params[0] = uiLo; 195 params[1] = uiHi; 196 TRACE("Reusing pack %08lx %08lx\n", uiLo, uiHi); 197 GlobalUnlock( (HGLOBAL)lParam ); 198 return lParam; 199 200 case WM_DDE_EXECUTE: 201 FreeDDElParam( msgIn, lParam ); 202 return uiHi; 203 204 default: 205 FreeDDElParam( msgIn, lParam ); 206 return MAKELPARAM(uiLo, uiHi); 207 } 208 209 default: 210 return PackDDElParam( msgOut, uiLo, uiHi ); 211 } 212 } 213 214 /***************************************************************** 215 * ImpersonateDdeClientWindow (USER32.@) 216 * 217 * PARAMS 218 * hWndClient [I] handle to DDE client window 219 * hWndServer [I] handle to DDE server window 220 */ 221 BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer) 222 { 223 FIXME("(%p %p): stub\n", hWndClient, hWndServer); 224 return FALSE; 225 } 226 227 /***************************************************************** 228 * DdeSetQualityOfService (USER32.@) 229 */ 230 231 BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, const SECURITY_QUALITY_OF_SERVICE *pqosNew, 232 PSECURITY_QUALITY_OF_SERVICE pqosPrev) 233 { 234 FIXME("(%p %p %p): stub\n", hwndClient, pqosNew, pqosPrev); 235 return TRUE; 236 } 237 238 /* ================================================================ 239 * 240 * WDML Error management 241 * 242 * ================================================================ */ 243 244 /****************************************************************************** 245 * DdeGetLastError [USER32.@] Gets most recent error code 246 * 247 * PARAMS 248 * idInst [I] Instance identifier 249 * 250 * RETURNS 251 * Last error code 252 */ 253 UINT WINAPI DdeGetLastError(DWORD idInst) 254 { 255 DWORD error_code; 256 WDML_INSTANCE* pInstance; 257 258 /* First check instance 259 */ 260 pInstance = WDML_GetInstance(idInst); 261 if (pInstance == NULL) 262 { 263 error_code = DMLERR_INVALIDPARAMETER; 264 } 265 else 266 { 267 error_code = pInstance->lastError; 268 pInstance->lastError = 0; 269 } 270 271 return error_code; 272 } 273 274 /****************************************************************** 275 * WDML_SetAllLastError 276 * 277 * 278 */ 279 static void WDML_SetAllLastError(DWORD lastError) 280 { 281 DWORD threadID; 282 WDML_INSTANCE* pInstance; 283 threadID = GetCurrentThreadId(); 284 pInstance = WDML_InstanceList; 285 while (pInstance) 286 { 287 if (pInstance->threadID == threadID) 288 pInstance->lastError = lastError; 289 pInstance = pInstance->next; 290 } 291 } 292 293 /* ================================================================ 294 * 295 * String management 296 * 297 * ================================================================ */ 298 299 300 /****************************************************************** 301 * WDML_FindNode 302 * 303 * 304 */ 305 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz) 306 { 307 HSZNode* pNode; 308 309 if (pInstance == NULL) return NULL; 310 311 for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next) 312 { 313 if (pNode->hsz == hsz) break; 314 } 315 if (!pNode) WARN("HSZ %p not found\n", hsz); 316 return pNode; 317 } 318 319 /****************************************************************** 320 * WDML_MakeAtomFromHsz 321 * 322 * Creates a global atom from an existing HSZ 323 * Generally used before sending an HSZ as an atom to a remote app 324 */ 325 ATOM WDML_MakeAtomFromHsz(HSZ hsz) 326 { 327 WCHAR nameBuffer[MAX_BUFFER_LEN]; 328 329 if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN)) 330 return GlobalAddAtomW(nameBuffer); 331 WARN("HSZ %p not found\n", hsz); 332 return 0; 333 } 334 335 /****************************************************************** 336 * WDML_MakeHszFromAtom 337 * 338 * Creates a HSZ from an existing global atom 339 * Generally used while receiving a global atom and transforming it 340 * into an HSZ 341 */ 342 HSZ WDML_MakeHszFromAtom(const WDML_INSTANCE* pInstance, ATOM atom) 343 { 344 WCHAR nameBuffer[MAX_BUFFER_LEN]; 345 346 if (!atom) return NULL; 347 348 if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN)) 349 { 350 TRACE("%x => %s\n", atom, debugstr_w(nameBuffer)); 351 return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE); 352 } 353 WARN("ATOM 0x%x not found\n", atom); 354 return 0; 355 } 356 357 /****************************************************************** 358 * WDML_IncHSZ 359 * 360 * 361 */ 362 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz) 363 { 364 HSZNode* pNode; 365 366 pNode = WDML_FindNode(pInstance, hsz); 367 if (!pNode) return FALSE; 368 369 pNode->refCount++; 370 return TRUE; 371 } 372 373 /****************************************************************************** 374 * WDML_DecHSZ (INTERNAL) 375 * 376 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list 377 * of HSZ nodes 378 * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count 379 */ 380 BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz) 381 { 382 HSZNode* pPrev = NULL; 383 HSZNode* pCurrent; 384 385 for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next) 386 { 387 /* If we found the node we were looking for and its ref count is one, 388 * we can remove it 389 */ 390 if (pCurrent->hsz == hsz) 391 { 392 if (--pCurrent->refCount == 0) 393 { 394 if (pCurrent == pInstance->nodeList) 395 { 396 pInstance->nodeList = pCurrent->next; 397 } 398 else 399 { 400 pPrev->next = pCurrent->next; 401 } 402 HeapFree(GetProcessHeap(), 0, pCurrent); 403 DeleteAtom(HSZ2ATOM(hsz)); 404 } 405 return TRUE; 406 } 407 } 408 WARN("HSZ %p not found\n", hsz); 409 410 return FALSE; 411 } 412 413 /****************************************************************************** 414 * WDML_FreeAllHSZ (INTERNAL) 415 * 416 * Frees up all the strings still allocated in the list and 417 * remove all the nodes from the list of HSZ nodes. 418 */ 419 static void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance) 420 { 421 /* Free any strings created in this instance. 422 */ 423 while (pInstance->nodeList != NULL) 424 { 425 DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz); 426 } 427 } 428 429 /****************************************************************************** 430 * InsertHSZNode (INTERNAL) 431 * 432 * Insert a node to the head of the list. 433 */ 434 static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz) 435 { 436 if (hsz != 0) 437 { 438 HSZNode* pNew = NULL; 439 /* Create a new node for this HSZ. 440 */ 441 pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode)); 442 if (pNew != NULL) 443 { 444 pNew->hsz = hsz; 445 pNew->next = pInstance->nodeList; 446 pNew->refCount = 1; 447 pInstance->nodeList = pNew; 448 } 449 else 450 { 451 ERR("Primary HSZ Node allocation failed - out of memory\n"); 452 } 453 } 454 } 455 456 /****************************************************************** 457 * WDML_QueryString 458 * 459 * 460 */ 461 static int WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax, 462 int codepage) 463 { 464 WCHAR pString[MAX_BUFFER_LEN]; 465 int ret; 466 /* If psz is null, we have to return only the length 467 * of the string. 468 */ 469 if (ptr == NULL) 470 { 471 ptr = pString; 472 cchMax = MAX_BUFFER_LEN; 473 } 474 475 /* if there is no input windows returns a NULL string */ 476 if (hsz == NULL) 477 { 478 CHAR *t_ptr = ptr; 479 *t_ptr = '\0'; 480 return 1; 481 } 482 483 switch (codepage) 484 { 485 case CP_WINANSI: 486 ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax); 487 break; 488 case CP_WINUNICODE: 489 ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax); 490 break; 491 default: 492 ERR("Unknown code page %d\n", codepage); 493 ret = 0; 494 } 495 return ret; 496 } 497 498 /***************************************************************** 499 * DdeQueryStringA [USER32.@] 500 */ 501 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage) 502 { 503 DWORD ret = 0; 504 WDML_INSTANCE* pInstance; 505 506 TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage); 507 508 /* First check instance 509 */ 510 pInstance = WDML_GetInstance(idInst); 511 if (pInstance != NULL) 512 { 513 if (iCodePage == 0) iCodePage = CP_WINANSI; 514 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage); 515 } 516 517 TRACE("returning %d (%s)\n", ret, debugstr_a(psz)); 518 return ret; 519 } 520 521 /***************************************************************** 522 * DdeQueryStringW [USER32.@] 523 */ 524 525 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage) 526 { 527 DWORD ret = 0; 528 WDML_INSTANCE* pInstance; 529 530 TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage); 531 532 /* First check instance 533 */ 534 pInstance = WDML_GetInstance(idInst); 535 if (pInstance != NULL) 536 { 537 if (iCodePage == 0) iCodePage = CP_WINUNICODE; 538 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage); 539 } 540 541 TRACE("returning %d (%s)\n", ret, debugstr_w(psz)); 542 return ret; 543 } 544 545 /****************************************************************** 546 * DML_CreateString 547 * 548 * 549 */ 550 static HSZ WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage) 551 { 552 HSZ hsz; 553 554 switch (codepage) 555 { 556 case CP_WINANSI: 557 hsz = ATOM2HSZ(AddAtomA(ptr)); 558 TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz); 559 break; 560 case CP_WINUNICODE: 561 hsz = ATOM2HSZ(AddAtomW(ptr)); 562 TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz); 563 break; 564 default: 565 ERR("Unknown code page %d\n", codepage); 566 return 0; 567 } 568 WDML_InsertHSZNode(pInstance, hsz); 569 return hsz; 570 } 571 572 /***************************************************************** 573 * DdeCreateStringHandleA [USER32.@] 574 * 575 * See DdeCreateStringHandleW. 576 */ 577 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage) 578 { 579 HSZ hsz = 0; 580 WDML_INSTANCE* pInstance; 581 582 TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage); 583 584 pInstance = WDML_GetInstance(idInst); 585 if (pInstance == NULL) 586 WDML_SetAllLastError(DMLERR_INVALIDPARAMETER); 587 else 588 { 589 if (codepage == 0) codepage = CP_WINANSI; 590 hsz = WDML_CreateString(pInstance, psz, codepage); 591 } 592 593 return hsz; 594 } 595 596 597 /****************************************************************************** 598 * DdeCreateStringHandleW [USER32.@] Creates handle to identify string 599 * 600 * PARAMS 601 * idInst [I] Instance identifier 602 * psz [I] Pointer to string 603 * codepage [I] Code page identifier 604 * RETURNS 605 * Success: String handle 606 * Failure: 0 607 */ 608 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage) 609 { 610 WDML_INSTANCE* pInstance; 611 HSZ hsz = 0; 612 613 pInstance = WDML_GetInstance(idInst); 614 if (pInstance == NULL) 615 WDML_SetAllLastError(DMLERR_INVALIDPARAMETER); 616 else 617 { 618 if (codepage == 0) codepage = CP_WINUNICODE; 619 hsz = WDML_CreateString(pInstance, psz, codepage); 620 } 621 622 return hsz; 623 } 624 625 /***************************************************************** 626 * DdeFreeStringHandle (USER32.@) 627 * RETURNS 628 * success: nonzero 629 * fail: zero 630 */ 631 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz) 632 { 633 WDML_INSTANCE* pInstance; 634 BOOL ret = FALSE; 635 636 TRACE("(%d,%p):\n", idInst, hsz); 637 638 /* First check instance 639 */ 640 pInstance = WDML_GetInstance(idInst); 641 if (pInstance) 642 ret = WDML_DecHSZ(pInstance, hsz); 643 644 return ret; 645 } 646 647 /***************************************************************** 648 * DdeKeepStringHandle (USER32.@) 649 * 650 * RETURNS 651 * success: nonzero 652 * fail: zero 653 */ 654 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz) 655 { 656 WDML_INSTANCE* pInstance; 657 BOOL ret = FALSE; 658 659 TRACE("(%d,%p):\n", idInst, hsz); 660 661 /* First check instance 662 */ 663 pInstance = WDML_GetInstance(idInst); 664 if (pInstance) 665 ret = WDML_IncHSZ(pInstance, hsz); 666 667 return ret; 668 } 669 670 /***************************************************************** 671 * DdeCmpStringHandles (USER32.@) 672 * 673 * Compares the value of two string handles. This comparison is 674 * not case sensitive. 675 * 676 * PARAMS 677 * hsz1 [I] Handle to the first string 678 * hsz2 [I] Handle to the second string 679 * 680 * RETURNS 681 * -1 The value of hsz1 is zero or less than hsz2 682 * 0 The values of hsz 1 and 2 are the same or both zero. 683 * 1 The value of hsz2 is zero of less than hsz1 684 */ 685 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2) 686 { 687 WCHAR psz1[MAX_BUFFER_LEN]; 688 WCHAR psz2[MAX_BUFFER_LEN]; 689 int ret = 0; 690 int ret1, ret2; 691 692 ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN); 693 ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN); 694 695 TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2)); 696 697 /* Make sure we found both strings. */ 698 if (ret1 == 0 && ret2 == 0) 699 { 700 /* If both are not found, return both "zero strings". */ 701 ret = 0; 702 } 703 else if (ret1 == 0) 704 { 705 /* If hsz1 is a not found, return hsz1 is "zero string". */ 706 ret = -1; 707 } 708 else if (ret2 == 0) 709 { 710 /* If hsz2 is a not found, return hsz2 is "zero string". */ 711 ret = 1; 712 } 713 else 714 { 715 /* Compare the two strings we got (case insensitive). */ 716 ret = lstrcmpiW(psz1, psz2); 717 /* Since strcmp returns any number smaller than 718 * 0 when the first string is found to be less than 719 * the second one we must make sure we are returning 720 * the proper values. 721 */ 722 if (ret < 0) 723 { 724 ret = -1; 725 } 726 else if (ret > 0) 727 { 728 ret = 1; 729 } 730 } 731 732 return ret; 733 } 734 735 /* ================================================================ 736 * 737 * Instance management 738 * 739 * ================================================================ */ 740 741 /****************************************************************************** 742 * IncrementInstanceId 743 * 744 * generic routine to increment the max instance Id and allocate a new application instance 745 */ 746 static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance) 747 { 748 DWORD id = InterlockedIncrement(&WDML_MaxInstanceID); 749 750 pInstance->instanceID = id; 751 TRACE("New instance id %d allocated\n", id); 752 } 753 754 /****************************************************************** 755 * WDML_EventProc 756 * 757 * 758 */ 759 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam) 760 { 761 WDML_INSTANCE* pInstance; 762 HSZ hsz1, hsz2; 763 764 switch (uMsg) 765 { 766 case WM_WDML_REGISTER: 767 pInstance = WDML_GetInstanceFromWnd(hwndEvent); 768 /* try calling the Callback */ 769 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS)) 770 { 771 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam); 772 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam); 773 WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0); 774 WDML_DecHSZ(pInstance, hsz1); 775 WDML_DecHSZ(pInstance, hsz2); 776 } 777 break; 778 779 case WM_WDML_UNREGISTER: 780 pInstance = WDML_GetInstanceFromWnd(hwndEvent); 781 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS)) 782 { 783 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam); 784 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam); 785 WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0); 786 WDML_DecHSZ(pInstance, hsz1); 787 WDML_DecHSZ(pInstance, hsz2); 788 } 789 break; 790 791 case WM_WDML_CONNECT_CONFIRM: 792 pInstance = WDML_GetInstanceFromWnd(hwndEvent); 793 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS)) 794 { 795 WDML_CONV* pConv; 796 /* confirm connection... 797 * lookup for this conv handle 798 */ 799 HWND client = WIN_GetFullHandle( (HWND)wParam ); 800 HWND server = WIN_GetFullHandle( (HWND)lParam ); 801 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next) 802 { 803 if (pConv->hwndClient == client && pConv->hwndServer == server) 804 break; 805 } 806 if (pConv) 807 { 808 pConv->wStatus |= ST_ISLOCAL; 809 810 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv, 811 pConv->hszTopic, pConv->hszService, 0, 0, 812 (pConv->wStatus & ST_ISSELF) ? 1 : 0); 813 } 814 } 815 break; 816 default: 817 return DefWindowProcW(hwndEvent, uMsg, wParam, lParam); 818 } 819 return 0; 820 } 821 822 /****************************************************************** 823 * WDML_Initialize 824 * 825 * 826 */ 827 static UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback, 828 DWORD afCmd, DWORD ulRes, BOOL bUnicode) 829 { 830 WDML_INSTANCE* pInstance; 831 WDML_INSTANCE* reference_inst; 832 UINT ret; 833 WNDCLASSEXW wndclass; 834 835 TRACE("(%p,%p,0x%x,%d,0x%x)\n", 836 pidInst, pfnCallback, afCmd, ulRes, bUnicode); 837 838 if (ulRes) 839 { 840 ERR("Reserved value not zero? What does this mean?\n"); 841 /* trap this and no more until we know more */ 842 return DMLERR_NO_ERROR; 843 } 844 845 /* grab enough heap for one control struct - not really necessary for re-initialise 846 * but allows us to use same validation routines */ 847 pInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE)); 848 if (pInstance == NULL) 849 { 850 /* catastrophe !! warn user & abort */ 851 ERR("Instance create failed - out of memory\n"); 852 return DMLERR_SYS_ERROR; 853 } 854 pInstance->next = NULL; 855 pInstance->monitor = (afCmd | APPCLASS_MONITOR); 856 857 /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */ 858 859 pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY; 860 pInstance->instanceID = *pidInst; /* May need to add calling proc Id */ 861 pInstance->threadID = GetCurrentThreadId(); 862 pInstance->callback = *pfnCallback; 863 pInstance->unicode = bUnicode; 864 pInstance->nodeList = NULL; /* node will be added later */ 865 pInstance->monitorFlags = afCmd & MF_MASK; 866 pInstance->wStatus = 0; 867 pInstance->lastError = DMLERR_NO_ERROR; 868 pInstance->servers = NULL; 869 pInstance->convs[0] = NULL; 870 pInstance->convs[1] = NULL; 871 pInstance->links[0] = NULL; 872 pInstance->links[1] = NULL; 873 874 /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */ 875 876 pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK))); 877 878 if (!pInstance->clientOnly) 879 { 880 /* Check for other way of setting Client-only !! */ 881 pInstance->clientOnly = 882 (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS; 883 } 884 885 TRACE("instance created - checking validity\n"); 886 887 if (*pidInst == 0) 888 { 889 /* Initialisation of new Instance Identifier */ 890 TRACE("new instance, callback %p flags %X\n",pfnCallback,afCmd); 891 892 EnterCriticalSection(&WDML_CritSect); 893 894 if (WDML_InstanceList == NULL) 895 { 896 /* can't be another instance in this case, assign to the base pointer */ 897 WDML_InstanceList = pInstance; 898 899 /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for 900 * present 901 * ------------------------------- NOTE NOTE NOTE -------------------------- 902 * 903 * the manual is not clear if this condition 904 * applies to the first call to DdeInitialize from an application, or the 905 * first call for a given callback !!! 906 */ 907 908 pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS; 909 TRACE("First application instance detected OK\n"); 910 /* allocate new instance ID */ 911 WDML_IncrementInstanceId(pInstance); 912 } 913 else 914 { 915 /* really need to chain the new one in to the latest here, but after checking conditions 916 * such as trying to start a conversation from an application trying to monitor */ 917 reference_inst = WDML_InstanceList; 918 TRACE("Subsequent application instance - starting checks\n"); 919 while (reference_inst->next != NULL) 920 { 921 /* 922 * This set of tests will work if application uses same instance Id 923 * at application level once allocated - which is what manual implies 924 * should happen. If someone tries to be 925 * clever (lazy ?) it will fail to pick up that later calls are for 926 * the same application - should we trust them ? 927 */ 928 if (pInstance->instanceID == reference_inst->instanceID) 929 { 930 /* Check 1 - must be same Client-only state */ 931 932 if (pInstance->clientOnly != reference_inst->clientOnly) 933 { 934 ret = DMLERR_DLL_USAGE; 935 goto theError; 936 } 937 938 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */ 939 940 if (pInstance->monitor != reference_inst->monitor) 941 { 942 ret = DMLERR_INVALIDPARAMETER; 943 goto theError; 944 } 945 946 /* Check 3 - must supply different callback address */ 947 948 if (pInstance->callback == reference_inst->callback) 949 { 950 ret = DMLERR_DLL_USAGE; 951 goto theError; 952 } 953 } 954 reference_inst = reference_inst->next; 955 } 956 /* All cleared, add to chain */ 957 958 TRACE("Application Instance checks finished\n"); 959 WDML_IncrementInstanceId(pInstance); 960 reference_inst->next = pInstance; 961 } 962 LeaveCriticalSection(&WDML_CritSect); 963 964 *pidInst = pInstance->instanceID; 965 966 /* for deadlock issues, windows must always be created when outside the critical section */ 967 wndclass.cbSize = sizeof(wndclass); 968 wndclass.style = 0; 969 wndclass.lpfnWndProc = WDML_EventProc; 970 wndclass.cbClsExtra = 0; 971 wndclass.cbWndExtra = sizeof(ULONG_PTR); 972 wndclass.hInstance = 0; 973 wndclass.hIcon = 0; 974 wndclass.hCursor = 0; 975 wndclass.hbrBackground = 0; 976 wndclass.lpszMenuName = NULL; 977 wndclass.lpszClassName = WDML_szEventClass; 978 wndclass.hIconSm = 0; 979 980 RegisterClassExW(&wndclass); 981 982 pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL, 983 WS_POPUP, 0, 0, 0, 0, 984 0, 0, 0, 0); 985 986 SetWindowLongPtrW(pInstance->hwndEvent, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance); 987 988 TRACE("New application instance processing finished OK\n"); 989 } 990 else 991 { 992 /* Reinitialisation situation --- FIX */ 993 TRACE("reinitialisation of (%p,%p,0x%x,%d): stub\n", pidInst, pfnCallback, afCmd, ulRes); 994 995 EnterCriticalSection(&WDML_CritSect); 996 997 if (WDML_InstanceList == NULL) 998 { 999 ret = DMLERR_INVALIDPARAMETER; 1000 goto theError; 1001 } 1002 /* can't reinitialise if we have initialised nothing !! */ 1003 reference_inst = WDML_InstanceList; 1004 /* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */ 1005 /* 1006 * MS allows initialisation without specifying a callback, should we allow addition of the 1007 * callback by a later call to initialise ? - if so this lot will have to change 1008 */ 1009 while (reference_inst->next != NULL) 1010 { 1011 if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback) 1012 { 1013 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */ 1014 1015 if (reference_inst->clientOnly) 1016 { 1017 if ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS) 1018 { 1019 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */ 1020 1021 if (!(afCmd & APPCMD_CLIENTONLY)) 1022 { 1023 ret = DMLERR_INVALIDPARAMETER; 1024 goto theError; 1025 } 1026 } 1027 } 1028 /* Check 2 - cannot change monitor modes */ 1029 1030 if (pInstance->monitor != reference_inst->monitor) 1031 { 1032 ret = DMLERR_INVALIDPARAMETER; 1033 goto theError; 1034 } 1035 1036 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */ 1037 1038 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly) 1039 { 1040 ret = DMLERR_INVALIDPARAMETER; 1041 goto theError; 1042 } 1043 break; 1044 } 1045 reference_inst = reference_inst->next; 1046 } 1047 if (reference_inst->next == NULL) 1048 { 1049 ret = DMLERR_INVALIDPARAMETER; 1050 goto theError; 1051 } 1052 /* All checked - change relevant flags */ 1053 1054 reference_inst->CBFflags = pInstance->CBFflags; 1055 reference_inst->clientOnly = pInstance->clientOnly; 1056 reference_inst->monitorFlags = pInstance->monitorFlags; 1057 1058 HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */ 1059 1060 LeaveCriticalSection(&WDML_CritSect); 1061 } 1062 1063 return DMLERR_NO_ERROR; 1064 theError: 1065 HeapFree(GetProcessHeap(), 0, pInstance); 1066 LeaveCriticalSection(&WDML_CritSect); 1067 return ret; 1068 } 1069 1070 /****************************************************************************** 1071 * DdeInitializeA (USER32.@) 1072 * 1073 * See DdeInitializeW. 1074 */ 1075 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback, 1076 DWORD afCmd, DWORD ulRes) 1077 { 1078 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE); 1079 } 1080 1081 /****************************************************************************** 1082 * DdeInitializeW [USER32.@] 1083 * Registers an application with the DDEML 1084 * 1085 * PARAMS 1086 * pidInst [I] Pointer to instance identifier 1087 * pfnCallback [I] Pointer to callback function 1088 * afCmd [I] Set of command and filter flags 1089 * ulRes [I] Reserved 1090 * 1091 * RETURNS 1092 * Success: DMLERR_NO_ERROR 1093 * Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR 1094 */ 1095 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback, 1096 DWORD afCmd, DWORD ulRes) 1097 { 1098 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE); 1099 } 1100 1101 /***************************************************************** 1102 * DdeUninitialize [USER32.@] Frees DDEML resources 1103 * 1104 * PARAMS 1105 * idInst [I] Instance identifier 1106 * 1107 * RETURNS 1108 * Success: TRUE 1109 * Failure: FALSE 1110 */ 1111 1112 BOOL WINAPI DdeUninitialize(DWORD idInst) 1113 { 1114 /* Stage one - check if we have a handle for this instance 1115 */ 1116 WDML_INSTANCE* pInstance; 1117 WDML_CONV* pConv; 1118 WDML_CONV* pConvNext; 1119 1120 TRACE("(%d)\n", idInst); 1121 1122 /* First check instance 1123 */ 1124 pInstance = WDML_GetInstance(idInst); 1125 if (pInstance == NULL) 1126 { 1127 /* 1128 * Needs something here to record NOT_INITIALIZED ready for DdeGetLastError 1129 */ 1130 return FALSE; 1131 } 1132 1133 /* first terminate all conversations client side 1134 * this shall close existing links... 1135 */ 1136 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext) 1137 { 1138 pConvNext = pConv->next; 1139 DdeDisconnect((HCONV)pConv); 1140 } 1141 if (pInstance->convs[WDML_CLIENT_SIDE]) 1142 FIXME("still pending conversations\n"); 1143 1144 /* then unregister all known service names */ 1145 DdeNameService(idInst, 0, 0, DNS_UNREGISTER); 1146 1147 /* Free the nodes that were not freed by this instance 1148 * and remove the nodes from the list of HSZ nodes. 1149 */ 1150 WDML_FreeAllHSZ(pInstance); 1151 1152 DestroyWindow(pInstance->hwndEvent); 1153 1154 /* OK now delete the instance handle itself */ 1155 1156 if (WDML_InstanceList == pInstance) 1157 { 1158 /* special case - the first/only entry */ 1159 WDML_InstanceList = pInstance->next; 1160 } 1161 else 1162 { 1163 /* general case, remove entry */ 1164 WDML_INSTANCE* inst; 1165 1166 for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next); 1167 inst->next = pInstance->next; 1168 } 1169 /* release the heap entry 1170 */ 1171 HeapFree(GetProcessHeap(), 0, pInstance); 1172 1173 return TRUE; 1174 } 1175 1176 /****************************************************************** 1177 * WDML_NotifyThreadExit 1178 * 1179 * 1180 */ 1181 void WDML_NotifyThreadDetach(void) 1182 { 1183 WDML_INSTANCE* pInstance; 1184 WDML_INSTANCE* next; 1185 DWORD tid = GetCurrentThreadId(); 1186 1187 EnterCriticalSection(&WDML_CritSect); 1188 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next) 1189 { 1190 next = pInstance->next; 1191 if (pInstance->threadID == tid) 1192 { 1193 LeaveCriticalSection(&WDML_CritSect); 1194 DdeUninitialize(pInstance->instanceID); 1195 EnterCriticalSection(&WDML_CritSect); 1196 } 1197 } 1198 LeaveCriticalSection(&WDML_CritSect); 1199 } 1200 1201 /****************************************************************** 1202 * WDML_InvokeCallback 1203 * 1204 * 1205 */ 1206 HDDEDATA WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv, 1207 HSZ hsz1, HSZ hsz2, HDDEDATA hdata, 1208 ULONG_PTR dwData1, ULONG_PTR dwData2) 1209 { 1210 HDDEDATA ret; 1211 1212 if (pInstance == NULL) 1213 return NULL; 1214 1215 TRACE("invoking CB[%p] (%x %x %p %p %p %p %lx %lx)\n", 1216 pInstance->callback, uType, uFmt, 1217 hConv, hsz1, hsz2, hdata, dwData1, dwData2); 1218 ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2); 1219 TRACE("done => %p\n", ret); 1220 return ret; 1221 } 1222 1223 /***************************************************************************** 1224 * WDML_GetInstance 1225 * 1226 * generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY 1227 * for an instance Id, or NULL if the entry does not exist 1228 * 1229 */ 1230 WDML_INSTANCE* WDML_GetInstance(DWORD instId) 1231 { 1232 WDML_INSTANCE* pInstance; 1233 1234 EnterCriticalSection(&WDML_CritSect); 1235 1236 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next) 1237 { 1238 if (pInstance->instanceID == instId) 1239 { 1240 if (GetCurrentThreadId() != pInstance->threadID) 1241 { 1242 FIXME("Tried to get instance from wrong thread\n"); 1243 continue; 1244 } 1245 break; 1246 } 1247 } 1248 1249 LeaveCriticalSection(&WDML_CritSect); 1250 1251 if (!pInstance) 1252 WARN("Instance entry missing for id %04x\n", instId); 1253 return pInstance; 1254 } 1255 1256 /****************************************************************** 1257 * WDML_GetInstanceFromWnd 1258 * 1259 * 1260 */ 1261 WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd) 1262 { 1263 return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE); 1264 } 1265 1266 /* ================================================================ 1267 * 1268 * Data handle management 1269 * 1270 * ================================================================ */ 1271 1272 /***************************************************************** 1273 * DdeCreateDataHandle (USER32.@) 1274 */ 1275 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff, 1276 HSZ hszItem, UINT wFmt, UINT afCmd) 1277 { 1278 1279 /* Other than check for validity we will ignore for now idInst, hszItem. 1280 * The purpose of these arguments still need to be investigated. 1281 */ 1282 1283 WDML_INSTANCE* pInstance; 1284 HGLOBAL hMem; 1285 LPBYTE pByte; 1286 DDE_DATAHANDLE_HEAD* pDdh; 1287 WCHAR psz[MAX_BUFFER_LEN]; 1288 1289 pInstance = WDML_GetInstance(idInst); 1290 if (pInstance == NULL) 1291 { 1292 WDML_SetAllLastError(DMLERR_INVALIDPARAMETER); 1293 return NULL; 1294 } 1295 1296 if (!GetAtomNameW(HSZ2ATOM(hszItem), psz, MAX_BUFFER_LEN)) 1297 { 1298 psz[0] = HSZ2ATOM(hszItem); 1299 psz[1] = 0; 1300 } 1301 1302 TRACE("(%d,%p,cb %d, cbOff %d,%p <%s>,fmt %04x,%x)\n", 1303 idInst, pSrc, cb, cbOff, hszItem, debugstr_w(psz), wFmt, afCmd); 1304 1305 if (afCmd != 0 && afCmd != HDATA_APPOWNED) 1306 return 0; 1307 1308 /* we use the first 4 bytes to store the size */ 1309 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff + sizeof(DDE_DATAHANDLE_HEAD)))) 1310 { 1311 ERR("GlobalAlloc failed\n"); 1312 return 0; 1313 } 1314 1315 pDdh = GlobalLock(hMem); 1316 if (!pDdh) 1317 { 1318 GlobalFree(hMem); 1319 return 0; 1320 } 1321 1322 pDdh->cfFormat = wFmt; 1323 pDdh->bAppOwned = (afCmd == HDATA_APPOWNED); 1324 1325 pByte = (LPBYTE)(pDdh + 1); 1326 if (pSrc) 1327 { 1328 memcpy(pByte, pSrc + cbOff, cb); 1329 } 1330 GlobalUnlock(hMem); 1331 1332 TRACE("=> %p\n", hMem); 1333 return hMem; 1334 } 1335 1336 /***************************************************************** 1337 * 1338 * DdeAddData (USER32.@) 1339 */ 1340 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff) 1341 { 1342 DWORD old_sz, new_sz; 1343 LPBYTE pDst; 1344 1345 TRACE("(%p,%p,cb %d, cbOff %d)\n", hData, pSrc, cb, cbOff); 1346 1347 pDst = DdeAccessData(hData, &old_sz); 1348 if (!pDst) return 0; 1349 1350 new_sz = cb + cbOff; 1351 if (new_sz > old_sz) 1352 { 1353 DdeUnaccessData(hData); 1354 hData = GlobalReAlloc(hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD), 1355 GMEM_MOVEABLE | GMEM_DDESHARE); 1356 pDst = DdeAccessData(hData, &old_sz); 1357 } 1358 1359 if (!pDst) return 0; 1360 1361 memcpy(pDst + cbOff, pSrc, cb); 1362 DdeUnaccessData(hData); 1363 return hData; 1364 } 1365 1366 /****************************************************************************** 1367 * DdeGetData [USER32.@] Copies data from DDE object to local buffer 1368 * 1369 * 1370 * PARAMS 1371 * hData [I] Handle to DDE object 1372 * pDst [I] Pointer to destination buffer 1373 * cbMax [I] Amount of data to copy 1374 * cbOff [I] Offset to beginning of data 1375 * 1376 * RETURNS 1377 * Size of memory object associated with handle 1378 */ 1379 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff) 1380 { 1381 DWORD dwSize, dwRet; 1382 LPBYTE pByte; 1383 1384 TRACE("(%p,%p,%d,%d)\n", hData, pDst, cbMax, cbOff); 1385 1386 pByte = DdeAccessData(hData, &dwSize); 1387 1388 if (pByte) 1389 { 1390 if (!pDst) 1391 { 1392 dwRet = dwSize; 1393 } 1394 else if (cbOff + cbMax < dwSize) 1395 { 1396 dwRet = cbMax; 1397 } 1398 else if (cbOff < dwSize) 1399 { 1400 dwRet = dwSize - cbOff; 1401 } 1402 else 1403 { 1404 dwRet = 0; 1405 } 1406 if (pDst && dwRet != 0) 1407 { 1408 memcpy(pDst, pByte + cbOff, dwRet); 1409 } 1410 DdeUnaccessData(hData); 1411 } 1412 else 1413 { 1414 dwRet = 0; 1415 } 1416 return dwRet; 1417 } 1418 1419 /***************************************************************** 1420 * DdeAccessData (USER32.@) 1421 */ 1422 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize) 1423 { 1424 HGLOBAL hMem = hData; 1425 DDE_DATAHANDLE_HEAD* pDdh; 1426 1427 TRACE("(%p,%p)\n", hData, pcbDataSize); 1428 1429 pDdh = GlobalLock(hMem); 1430 if (pDdh == NULL) 1431 { 1432 ERR("Failed on GlobalLock(%p)\n", hMem); 1433 return 0; 1434 } 1435 1436 if (pcbDataSize != NULL) 1437 { 1438 *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD); 1439 } 1440 TRACE("=> %p (%lu) fmt %04x\n", pDdh + 1, GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD), pDdh->cfFormat); 1441 return (LPBYTE)(pDdh + 1); 1442 } 1443 1444 /***************************************************************** 1445 * DdeUnaccessData (USER32.@) 1446 */ 1447 BOOL WINAPI DdeUnaccessData(HDDEDATA hData) 1448 { 1449 HGLOBAL hMem = hData; 1450 1451 TRACE("(%p)\n", hData); 1452 1453 GlobalUnlock(hMem); 1454 1455 return TRUE; 1456 } 1457 1458 /***************************************************************** 1459 * DdeFreeDataHandle (USER32.@) 1460 */ 1461 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData) 1462 { 1463 TRACE("(%p)\n", hData); 1464 1465 /* 1 is the handle value returned by an asynchronous operation. */ 1466 if (hData == (HDDEDATA)1) 1467 return TRUE; 1468 1469 return GlobalFree(hData) == 0; 1470 } 1471 1472 /****************************************************************** 1473 * WDML_IsAppOwned 1474 * 1475 * 1476 */ 1477 BOOL WDML_IsAppOwned(HDDEDATA hData) 1478 { 1479 DDE_DATAHANDLE_HEAD* pDdh; 1480 BOOL ret = FALSE; 1481 1482 pDdh = GlobalLock(hData); 1483 if (pDdh != NULL) 1484 { 1485 ret = pDdh->bAppOwned; 1486 GlobalUnlock(hData); 1487 } 1488 return ret; 1489 } 1490 1491 /* ================================================================ 1492 * 1493 * Global <=> Data handle management 1494 * 1495 * ================================================================ */ 1496 1497 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar: 1498 * offset size 1499 * (bytes) (bits) comment 1500 * 0 16 bit fields for options (release, ackreq, response...) 1501 * 2 16 clipboard format 1502 * 4 ? data to be used 1503 */ 1504 HDDEDATA WDML_Global2DataHandle(WDML_CONV* pConv, HGLOBAL hMem, WINE_DDEHEAD* p) 1505 { 1506 DDEDATA* pDd; 1507 HDDEDATA ret = 0; 1508 DWORD size; 1509 1510 if (hMem) 1511 { 1512 pDd = GlobalLock(hMem); 1513 size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD); 1514 if (pDd) 1515 { 1516 if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD)); 1517 switch (pDd->cfFormat) 1518 { 1519 default: 1520 FIXME("Unsupported format (%04x) for data %p, passing raw information\n", 1521 pDd->cfFormat, hMem); 1522 /* fall through */ 1523 case 0: 1524 case CF_TEXT: 1525 ret = DdeCreateDataHandle(pConv->instance->instanceID, pDd->Value, size, 0, 0, pDd->cfFormat, 0); 1526 break; 1527 case CF_BITMAP: 1528 if (size >= sizeof(BITMAP)) 1529 { 1530 BITMAP* bmp = (BITMAP*)pDd->Value; 1531 int count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes; 1532 if (size >= sizeof(BITMAP) + count) 1533 { 1534 HBITMAP hbmp; 1535 1536 if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight, 1537 bmp->bmPlanes, bmp->bmBitsPixel, 1538 pDd->Value + sizeof(BITMAP)))) 1539 { 1540 ret = DdeCreateDataHandle(pConv->instance->instanceID, (LPBYTE)&hbmp, sizeof(hbmp), 1541 0, 0, CF_BITMAP, 0); 1542 } 1543 else ERR("Can't create bmp\n"); 1544 } 1545 else 1546 { 1547 ERR("Wrong count: %u / %d\n", size, count); 1548 } 1549 } else ERR("No bitmap header\n"); 1550 break; 1551 } 1552 GlobalUnlock(hMem); 1553 } 1554 } 1555 return ret; 1556 } 1557 1558 /****************************************************************** 1559 * WDML_DataHandle2Global 1560 * 1561 * 1562 */ 1563 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease, 1564 BOOL fDeferUpd, BOOL fAckReq) 1565 { 1566 DDE_DATAHANDLE_HEAD* pDdh; 1567 DWORD dwSize; 1568 HGLOBAL hMem = 0; 1569 1570 dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD); 1571 pDdh = GlobalLock(hDdeData); 1572 if (dwSize && pDdh) 1573 { 1574 WINE_DDEHEAD* wdh = NULL; 1575 1576 switch (pDdh->cfFormat) 1577 { 1578 default: 1579 FIXME("Unsupported format (%04x) for data %p, passing raw information\n", 1580 pDdh->cfFormat, hDdeData); 1581 /* fall through */ 1582 case 0: 1583 case CF_TEXT: 1584 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize); 1585 if (hMem && (wdh = GlobalLock(hMem))) 1586 { 1587 memcpy(wdh + 1, pDdh + 1, dwSize); 1588 } 1589 break; 1590 case CF_BITMAP: 1591 if (dwSize >= sizeof(HBITMAP)) 1592 { 1593 BITMAP bmp; 1594 DWORD count; 1595 HBITMAP hbmp = *(HBITMAP*)(pDdh + 1); 1596 1597 if (GetObjectW(hbmp, sizeof(bmp), &bmp)) 1598 { 1599 count = bmp.bmWidthBytes * bmp.bmHeight; 1600 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1601 sizeof(WINE_DDEHEAD) + sizeof(bmp) + count); 1602 if (hMem && (wdh = GlobalLock(hMem))) 1603 { 1604 memcpy(wdh + 1, &bmp, sizeof(bmp)); 1605 GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp)); 1606 } 1607 } 1608 } 1609 break; 1610 } 1611 if (wdh) 1612 { 1613 wdh->unused = 0; 1614 wdh->fResponse = fResponse; 1615 wdh->fRelease = fRelease; 1616 wdh->fDeferUpd = fDeferUpd; 1617 wdh->fAckReq = fAckReq; 1618 wdh->cfFormat = pDdh->cfFormat; 1619 GlobalUnlock(hMem); 1620 } 1621 GlobalUnlock(hDdeData); 1622 } 1623 1624 return hMem; 1625 } 1626 1627 /* ================================================================ 1628 * 1629 * Server management 1630 * 1631 * ================================================================ */ 1632 1633 /****************************************************************** 1634 * WDML_AddServer 1635 * 1636 * 1637 */ 1638 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic) 1639 { 1640 static const WCHAR fmtW[] = {'%','s','(','0','x','%','*','x',')',0}; 1641 WDML_SERVER* pServer; 1642 WCHAR buf1[256]; 1643 WCHAR buf2[256]; 1644 1645 pServer = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER)); 1646 if (pServer == NULL) return NULL; 1647 1648 pServer->hszService = hszService; 1649 WDML_IncHSZ(pInstance, hszService); 1650 1651 DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE); 1652 snprintfW(buf2, 256, fmtW, buf1, 2*sizeof(ULONG_PTR), GetCurrentProcessId()); 1653 pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE); 1654 1655 pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService); 1656 pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec); 1657 1658 pServer->filterOn = TRUE; 1659 1660 pServer->next = pInstance->servers; 1661 pInstance->servers = pServer; 1662 return pServer; 1663 } 1664 1665 /****************************************************************** 1666 * WDML_RemoveServer 1667 * 1668 * 1669 */ 1670 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic) 1671 { 1672 WDML_SERVER* pPrev = NULL; 1673 WDML_SERVER* pServer = NULL; 1674 WDML_CONV* pConv; 1675 WDML_CONV* pConvNext; 1676 1677 pServer = pInstance->servers; 1678 1679 while (pServer != NULL) 1680 { 1681 if (DdeCmpStringHandles(pServer->hszService, hszService) == 0) 1682 { 1683 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER, 1684 pServer->atomService, pServer->atomServiceSpec); 1685 /* terminate all conversations for given topic */ 1686 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext) 1687 { 1688 pConvNext = pConv->next; 1689 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0) 1690 { 1691 HWND client = pConv->hwndClient, server = pConv->hwndServer; 1692 WDML_RemoveConv(pConv, WDML_SERVER_SIDE); 1693 /* don't care about return code (whether client window is present or not) */ 1694 PostMessageW(client, WM_DDE_TERMINATE, (WPARAM)server, 0); 1695 } 1696 } 1697 if (pServer == pInstance->servers) 1698 { 1699 pInstance->servers = pServer->next; 1700 } 1701 else 1702 { 1703 pPrev->next = pServer->next; 1704 } 1705 1706 DestroyWindow(pServer->hwndServer); 1707 WDML_DecHSZ(pInstance, pServer->hszServiceSpec); 1708 WDML_DecHSZ(pInstance, pServer->hszService); 1709 1710 GlobalDeleteAtom(pServer->atomService); 1711 GlobalDeleteAtom(pServer->atomServiceSpec); 1712 1713 HeapFree(GetProcessHeap(), 0, pServer); 1714 break; 1715 } 1716 1717 pPrev = pServer; 1718 pServer = pServer->next; 1719 } 1720 } 1721 1722 /***************************************************************************** 1723 * WDML_FindServer 1724 * 1725 * generic routine to return a pointer to the relevant ServiceNode 1726 * for a given service name, or NULL if the entry does not exist 1727 * 1728 */ 1729 WDML_SERVER* WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic) 1730 { 1731 WDML_SERVER* pServer; 1732 1733 for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next) 1734 { 1735 if (hszService == pServer->hszService) 1736 { 1737 return pServer; 1738 } 1739 } 1740 TRACE("Service name missing\n"); 1741 return NULL; 1742 } 1743 1744 /* ================================================================ 1745 * 1746 * Link (hot & warm) management 1747 * 1748 * ================================================================ */ 1749 1750 /****************************************************************** 1751 * WDML_AddLink 1752 * 1753 * 1754 */ 1755 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 1756 UINT wType, HSZ hszItem, UINT wFmt) 1757 { 1758 WDML_LINK* pLink; 1759 1760 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK)); 1761 if (pLink == NULL) 1762 { 1763 ERR("OOM\n"); 1764 return; 1765 } 1766 1767 pLink->hConv = hConv; 1768 pLink->transactionType = wType; 1769 WDML_IncHSZ(pInstance, pLink->hszItem = hszItem); 1770 pLink->uFmt = wFmt; 1771 pLink->next = pInstance->links[side]; 1772 pInstance->links[side] = pLink; 1773 } 1774 1775 /****************************************************************** 1776 * WDML_RemoveLink 1777 * 1778 * 1779 */ 1780 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 1781 HSZ hszItem, UINT uFmt) 1782 { 1783 WDML_LINK* pPrev = NULL; 1784 WDML_LINK* pCurrent = NULL; 1785 1786 pCurrent = pInstance->links[side]; 1787 1788 while (pCurrent != NULL) 1789 { 1790 if (pCurrent->hConv == hConv && 1791 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 && 1792 pCurrent->uFmt == uFmt) 1793 { 1794 if (pCurrent == pInstance->links[side]) 1795 { 1796 pInstance->links[side] = pCurrent->next; 1797 } 1798 else 1799 { 1800 pPrev->next = pCurrent->next; 1801 } 1802 1803 WDML_DecHSZ(pInstance, pCurrent->hszItem); 1804 HeapFree(GetProcessHeap(), 0, pCurrent); 1805 break; 1806 } 1807 1808 pPrev = pCurrent; 1809 pCurrent = pCurrent->next; 1810 } 1811 } 1812 1813 /* this function is called to remove all links related to the conv. 1814 It should be called from both client and server when terminating 1815 the conversation. 1816 */ 1817 /****************************************************************** 1818 * WDML_RemoveAllLinks 1819 * 1820 * 1821 */ 1822 static void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side) 1823 { 1824 WDML_LINK* pPrev = NULL; 1825 WDML_LINK* pCurrent = NULL; 1826 WDML_LINK* pNext = NULL; 1827 1828 pCurrent = pInstance->links[side]; 1829 1830 while (pCurrent != NULL) 1831 { 1832 if (pCurrent->hConv == (HCONV)pConv) 1833 { 1834 if (pCurrent == pInstance->links[side]) 1835 { 1836 pInstance->links[side] = pCurrent->next; 1837 pNext = pCurrent->next; 1838 } 1839 else 1840 { 1841 pPrev->next = pCurrent->next; 1842 pNext = pCurrent->next; 1843 } 1844 1845 WDML_DecHSZ(pInstance, pCurrent->hszItem); 1846 1847 HeapFree(GetProcessHeap(), 0, pCurrent); 1848 pCurrent = NULL; 1849 } 1850 1851 if (pCurrent) 1852 { 1853 pPrev = pCurrent; 1854 pCurrent = pCurrent->next; 1855 } 1856 else 1857 { 1858 pCurrent = pNext; 1859 } 1860 } 1861 } 1862 1863 /****************************************************************** 1864 * WDML_FindLink 1865 * 1866 * 1867 */ 1868 WDML_LINK* WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 1869 HSZ hszItem, BOOL use_fmt, UINT uFmt) 1870 { 1871 WDML_LINK* pCurrent; 1872 1873 for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next) 1874 { 1875 /* we don't need to check for transaction type as it can be altered */ 1876 1877 if (pCurrent->hConv == hConv && 1878 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 && 1879 (!use_fmt || pCurrent->uFmt == uFmt)) 1880 { 1881 break; 1882 } 1883 1884 } 1885 1886 return pCurrent; 1887 } 1888 1889 /* ================================================================ 1890 * 1891 * Transaction management 1892 * 1893 * ================================================================ */ 1894 1895 /****************************************************************** 1896 * WDML_AllocTransaction 1897 * 1898 * Alloc a transaction structure for handling the message ddeMsg 1899 */ 1900 WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg, 1901 UINT wFmt, HSZ hszItem) 1902 { 1903 WDML_XACT* pXAct; 1904 static WORD tid = 1; /* FIXME: wrap around */ 1905 1906 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT)); 1907 if (!pXAct) 1908 { 1909 pInstance->lastError = DMLERR_MEMORY_ERROR; 1910 return NULL; 1911 } 1912 1913 pXAct->xActID = tid++; 1914 pXAct->ddeMsg = ddeMsg; 1915 pXAct->hDdeData = 0; 1916 pXAct->hUser = 0; 1917 pXAct->next = NULL; 1918 pXAct->wType = 0; 1919 pXAct->wFmt = wFmt; 1920 if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem); 1921 pXAct->atom = 0; 1922 pXAct->hMem = 0; 1923 pXAct->lParam = 0; 1924 1925 return pXAct; 1926 } 1927 1928 /****************************************************************** 1929 * WDML_QueueTransaction 1930 * 1931 * Adds a transaction to the list of transaction 1932 */ 1933 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct) 1934 { 1935 WDML_XACT** pt; 1936 1937 /* advance to last in queue */ 1938 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next); 1939 *pt = pXAct; 1940 } 1941 1942 /****************************************************************** 1943 * WDML_UnQueueTransaction 1944 * 1945 * 1946 */ 1947 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct) 1948 { 1949 WDML_XACT** pt; 1950 1951 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next) 1952 { 1953 if (*pt == pXAct) 1954 { 1955 *pt = pXAct->next; 1956 return TRUE; 1957 } 1958 } 1959 return FALSE; 1960 } 1961 1962 /****************************************************************** 1963 * WDML_FreeTransaction 1964 * 1965 * 1966 */ 1967 void WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt) 1968 { 1969 /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */ 1970 if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1) 1971 { 1972 GlobalFree(pXAct->hMem); 1973 } 1974 if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem); 1975 1976 HeapFree(GetProcessHeap(), 0, pXAct); 1977 } 1978 1979 /****************************************************************** 1980 * WDML_FindTransaction 1981 * 1982 * 1983 */ 1984 static WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid) 1985 { 1986 WDML_XACT* pXAct; 1987 1988 tid = HIWORD(tid); 1989 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next) 1990 { 1991 if (pXAct->xActID == tid) 1992 break; 1993 } 1994 return pXAct; 1995 } 1996 1997 /* ================================================================ 1998 * 1999 * Conversation management 2000 * 2001 * ================================================================ */ 2002 2003 /****************************************************************** 2004 * WDML_AddConv 2005 * 2006 * 2007 */ 2008 WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side, 2009 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer) 2010 { 2011 WDML_CONV* pConv; 2012 2013 /* no conversation yet, add it */ 2014 pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV)); 2015 if (!pConv) return NULL; 2016 2017 pConv->instance = pInstance; 2018 WDML_IncHSZ(pInstance, pConv->hszService = hszService); 2019 WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic); 2020 pConv->magic = WDML_CONV_MAGIC; 2021 pConv->hwndServer = hwndServer; 2022 pConv->hwndClient = hwndClient; 2023 pConv->transactions = NULL; 2024 pConv->hUser = 0; 2025 pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L; 2026 pConv->wStatus |= pInstance->wStatus; 2027 /* check if both side of the conversation are of the same instance */ 2028 if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) && 2029 WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer)) 2030 { 2031 pConv->wStatus |= ST_ISSELF; 2032 } 2033 pConv->wConvst = XST_NULL; 2034 2035 pConv->next = pInstance->convs[side]; 2036 pInstance->convs[side] = pConv; 2037 2038 TRACE("pConv->wStatus %04x pInstance(%p)\n", pConv->wStatus, pInstance); 2039 2040 return pConv; 2041 } 2042 2043 /****************************************************************** 2044 * WDML_FindConv 2045 * 2046 * 2047 */ 2048 WDML_CONV* WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side, 2049 HSZ hszService, HSZ hszTopic) 2050 { 2051 WDML_CONV* pCurrent; 2052 2053 for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next) 2054 { 2055 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 && 2056 DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0) 2057 { 2058 return pCurrent; 2059 } 2060 2061 } 2062 return NULL; 2063 } 2064 2065 /****************************************************************** 2066 * WDML_RemoveConv 2067 * 2068 * 2069 */ 2070 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side) 2071 { 2072 WDML_CONV* pPrev = NULL; 2073 WDML_CONV* pCurrent; 2074 WDML_XACT* pXAct; 2075 WDML_XACT* pXActNext; 2076 HWND hWnd; 2077 2078 if (!pRef) 2079 return; 2080 2081 /* remove any pending transaction */ 2082 for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext) 2083 { 2084 pXActNext = pXAct->next; 2085 WDML_FreeTransaction(pRef->instance, pXAct, TRUE); 2086 } 2087 2088 WDML_RemoveAllLinks(pRef->instance, pRef, side); 2089 2090 /* FIXME: should we keep the window around ? it seems so (at least on client side 2091 * to let QueryConvInfo work after conv termination, but also to implement 2092 * DdeReconnect... 2093 */ 2094 /* destroy conversation window, but first remove pConv from hWnd. 2095 * this would help the wndProc do appropriate handling upon a WM_DESTROY message 2096 */ 2097 hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer; 2098 SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0); 2099 2100 DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer); 2101 2102 WDML_DecHSZ(pRef->instance, pRef->hszService); 2103 WDML_DecHSZ(pRef->instance, pRef->hszTopic); 2104 2105 for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next) 2106 { 2107 if (pCurrent == pRef) 2108 { 2109 if (pCurrent == pRef->instance->convs[side]) 2110 { 2111 pRef->instance->convs[side] = pCurrent->next; 2112 } 2113 else 2114 { 2115 pPrev->next = pCurrent->next; 2116 } 2117 pCurrent->magic = 0; 2118 HeapFree(GetProcessHeap(), 0, pCurrent); 2119 break; 2120 } 2121 } 2122 } 2123 2124 /****************************************************************** 2125 * WDML_EnableCallback 2126 */ 2127 static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd) 2128 { 2129 if (wCmd == EC_DISABLE) 2130 { 2131 pConv->wStatus |= ST_BLOCKED; 2132 TRACE("EC_DISABLE: conv %p status flags %04x\n", pConv, pConv->wStatus); 2133 return TRUE; 2134 } 2135 2136 if (wCmd == EC_QUERYWAITING) 2137 return pConv->transactions != NULL; 2138 2139 if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE) 2140 { 2141 FIXME("Unknown command code %04x\n", wCmd); 2142 return FALSE; 2143 } 2144 2145 if (wCmd == EC_ENABLEALL) 2146 { 2147 pConv->wStatus &= ~ST_BLOCKED; 2148 TRACE("EC_ENABLEALL: conv %p status flags %04x\n", pConv, pConv->wStatus); 2149 } 2150 2151 while (pConv->transactions) 2152 { 2153 WDML_XACT *pXAct = pConv->transactions; 2154 2155 if (pConv->wStatus & ST_CLIENT) 2156 { 2157 /* transaction should be in the queue until handled */ 2158 WDML_ClientHandle(pConv, pXAct, 0, NULL); 2159 WDML_UnQueueTransaction(pConv, pXAct); 2160 } 2161 else 2162 { 2163 /* transaction should be removed from the queue before handling */ 2164 WDML_UnQueueTransaction(pConv, pXAct); 2165 WDML_ServerHandle(pConv, pXAct); 2166 } 2167 2168 WDML_FreeTransaction(pConv->instance, pXAct, TRUE); 2169 2170 if (wCmd == EC_ENABLEONE) break; 2171 } 2172 return TRUE; 2173 } 2174 2175 /***************************************************************** 2176 * DdeEnableCallback (USER32.@) 2177 */ 2178 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd) 2179 { 2180 BOOL ret = FALSE; 2181 WDML_CONV *pConv; 2182 2183 TRACE("(%d, %p, %04x)\n", idInst, hConv, wCmd); 2184 2185 if (hConv) 2186 { 2187 pConv = WDML_GetConv(hConv, TRUE); 2188 2189 if (pConv && pConv->instance->instanceID == idInst) 2190 ret = WDML_EnableCallback(pConv, wCmd); 2191 } 2192 else 2193 { 2194 WDML_INSTANCE *pInstance = WDML_GetInstance(idInst); 2195 2196 if (!pInstance) 2197 return FALSE; 2198 2199 TRACE("adding flags %04x to instance %p\n", wCmd, pInstance); 2200 pInstance->wStatus |= wCmd; 2201 2202 if (wCmd == EC_DISABLE) 2203 { 2204 pInstance->wStatus |= ST_BLOCKED; 2205 TRACE("EC_DISABLE: inst %p status flags %04x\n", pInstance, pInstance->wStatus); 2206 } 2207 else if (wCmd == EC_ENABLEALL) 2208 { 2209 pInstance->wStatus &= ~ST_BLOCKED; 2210 TRACE("EC_ENABLEALL: inst %p status flags %04x\n", pInstance, pInstance->wStatus); 2211 } 2212 2213 ret = TRUE; 2214 2215 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConv->next) 2216 { 2217 ret = WDML_EnableCallback(pConv, wCmd); 2218 if (ret && wCmd == EC_QUERYWAITING) break; 2219 } 2220 } 2221 2222 return ret; 2223 } 2224 2225 /****************************************************************** 2226 * WDML_GetConv 2227 * 2228 * 2229 */ 2230 WDML_CONV* WDML_GetConv(HCONV hConv, BOOL checkConnected) 2231 { 2232 WDML_CONV* pConv = (WDML_CONV*)hConv; 2233 2234 /* FIXME: should do better checking */ 2235 if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL; 2236 2237 if (!pConv->instance) 2238 { 2239 WARN("wrong thread ID, no instance\n"); 2240 return NULL; 2241 } 2242 2243 if (pConv->instance->threadID != GetCurrentThreadId()) 2244 { 2245 WARN("wrong thread ID\n"); 2246 pConv->instance->lastError = DMLERR_INVALIDPARAMETER; /* FIXME: check */ 2247 return NULL; 2248 } 2249 2250 if (checkConnected && !(pConv->wStatus & ST_CONNECTED)) 2251 { 2252 WARN("found conv but ain't connected\n"); 2253 pConv->instance->lastError = DMLERR_NO_CONV_ESTABLISHED; 2254 return NULL; 2255 } 2256 2257 return pConv; 2258 } 2259 2260 /****************************************************************** 2261 * WDML_GetConvFromWnd 2262 * 2263 * 2264 */ 2265 WDML_CONV* WDML_GetConvFromWnd(HWND hWnd) 2266 { 2267 return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION); 2268 } 2269 2270 /****************************************************************** 2271 * WDML_PostAck 2272 * 2273 * 2274 */ 2275 BOOL WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode, 2276 BOOL fBusy, BOOL fAck, UINT_PTR pmt, LPARAM lParam, UINT oldMsg) 2277 { 2278 DDEACK ddeAck; 2279 HWND from, to; 2280 2281 if (side == WDML_SERVER_SIDE) 2282 { 2283 from = pConv->hwndServer; 2284 to = pConv->hwndClient; 2285 } 2286 else 2287 { 2288 to = pConv->hwndServer; 2289 from = pConv->hwndClient; 2290 } 2291 2292 ddeAck.bAppReturnCode = appRetCode; 2293 ddeAck.reserved = 0; 2294 ddeAck.fBusy = fBusy; 2295 ddeAck.fAck = fAck; 2296 2297 TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative"); 2298 2299 lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) : 2300 PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt); 2301 if (!PostMessageW(to, WM_DDE_ACK, (WPARAM)from, lParam)) 2302 { 2303 pConv->wStatus &= ~ST_CONNECTED; 2304 pConv->instance->lastError = DMLERR_POSTMSG_FAILED; 2305 FreeDDElParam(WM_DDE_ACK, lParam); 2306 return FALSE; 2307 } 2308 return TRUE; 2309 } 2310 2311 /***************************************************************** 2312 * DdeSetUserHandle (USER32.@) 2313 */ 2314 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser) 2315 { 2316 WDML_CONV* pConv; 2317 2318 pConv = WDML_GetConv(hConv, FALSE); 2319 if (pConv == NULL) 2320 return FALSE; 2321 2322 if (id == QID_SYNC) 2323 { 2324 pConv->hUser = hUser; 2325 } 2326 else 2327 { 2328 WDML_XACT* pXAct; 2329 2330 pXAct = WDML_FindTransaction(pConv, id); 2331 if (pXAct) 2332 { 2333 pXAct->hUser = hUser; 2334 } 2335 else 2336 { 2337 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID; 2338 return FALSE; 2339 } 2340 } 2341 return TRUE; 2342 } 2343 2344 /****************************************************************** 2345 * WDML_GetLocalConvInfo 2346 * 2347 * 2348 */ 2349 static BOOL WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id) 2350 { 2351 BOOL ret = TRUE; 2352 WDML_LINK* pLink; 2353 WDML_SIDE side; 2354 2355 ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((ULONG_PTR)pConv | 1) : 0; 2356 ci->hszSvcPartner = pConv->hszService; 2357 ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */ 2358 ci->hszTopic = pConv->hszTopic; 2359 ci->wStatus = pConv->wStatus; 2360 2361 side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE; 2362 2363 for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next) 2364 { 2365 if (pLink->hConv == (HCONV)pConv) 2366 { 2367 ci->wStatus |= ST_ADVISE; 2368 break; 2369 } 2370 } 2371 2372 /* FIXME: non handled status flags: 2373 ST_BLOCKED 2374 ST_BLOCKNEXT 2375 ST_INLIST 2376 */ 2377 2378 ci->wConvst = pConv->wConvst; /* FIXME */ 2379 2380 ci->wLastError = 0; /* FIXME: note it's not the instance last error */ 2381 ci->hConvList = 0; 2382 ci->ConvCtxt = pConv->convContext; 2383 if (ci->wStatus & ST_CLIENT) 2384 { 2385 ci->hwnd = pConv->hwndClient; 2386 ci->hwndPartner = pConv->hwndServer; 2387 } 2388 else 2389 { 2390 ci->hwnd = pConv->hwndServer; 2391 ci->hwndPartner = pConv->hwndClient; 2392 } 2393 if (id == QID_SYNC) 2394 { 2395 ci->hUser = pConv->hUser; 2396 ci->hszItem = 0; 2397 ci->wFmt = 0; 2398 ci->wType = 0; 2399 } 2400 else 2401 { 2402 WDML_XACT* pXAct; 2403 2404 pXAct = WDML_FindTransaction(pConv, id); 2405 if (pXAct) 2406 { 2407 ci->hUser = pXAct->hUser; 2408 ci->hszItem = pXAct->hszItem; 2409 ci->wFmt = pXAct->wFmt; 2410 ci->wType = pXAct->wType; 2411 } 2412 else 2413 { 2414 ret = 0; 2415 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID; 2416 } 2417 } 2418 return ret; 2419 } 2420 2421 /****************************************************************** 2422 * DdeQueryConvInfo (USER32.@) 2423 * 2424 * FIXME: Set last DDE error on failure. 2425 */ 2426 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo) 2427 { 2428 UINT ret = lpConvInfo->cb; 2429 CONVINFO ci; 2430 WDML_CONV* pConv; 2431 2432 TRACE("(%p,%x,%p)\n", hConv, id, lpConvInfo); 2433 2434 if (!hConv) 2435 { 2436 FIXME("hConv is NULL\n"); 2437 return 0; 2438 } 2439 2440 pConv = WDML_GetConv(hConv, FALSE); 2441 if (pConv != NULL) 2442 { 2443 if (!WDML_GetLocalConvInfo(pConv, &ci, id)) 2444 ret = 0; 2445 } 2446 else 2447 { 2448 if ((ULONG_PTR)hConv & 1) 2449 { 2450 pConv = WDML_GetConv((HCONV)((ULONG_PTR)hConv & ~1), FALSE); 2451 if (pConv != NULL) 2452 FIXME("Request on remote conversation information is not implemented yet\n"); 2453 } 2454 ret = 0; 2455 } 2456 2457 if (ret != 0) 2458 { 2459 ci.cb = lpConvInfo->cb; 2460 memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci))); 2461 } 2462 return ret; 2463 } 2464 2465 /* ================================================================ 2466 * 2467 * Information broadcast across DDEML implementations 2468 * 2469 * ================================================================ */ 2470 2471 struct tagWDML_BroadcastPmt 2472 { 2473 LPCWSTR clsName; 2474 UINT uMsg; 2475 WPARAM wParam; 2476 LPARAM lParam; 2477 }; 2478 2479 /****************************************************************** 2480 * WDML_BroadcastEnumProc 2481 * 2482 * 2483 */ 2484 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam) 2485 { 2486 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam; 2487 WCHAR buffer[128]; 2488 2489 if (GetClassNameW(hWnd, buffer, 128) > 0 && 2490 lstrcmpiW(buffer, s->clsName) == 0) 2491 { 2492 PostMessageW(hWnd, s->uMsg, s->wParam, s->lParam); 2493 } 2494 return TRUE; 2495 } 2496 2497 /****************************************************************** 2498 * WDML_BroadcastDDEWindows 2499 * 2500 * 2501 */ 2502 void WDML_BroadcastDDEWindows(LPCWSTR clsName, UINT uMsg, WPARAM wParam, LPARAM lParam) 2503 { 2504 struct tagWDML_BroadcastPmt s; 2505 2506 s.clsName = clsName; 2507 s.uMsg = uMsg; 2508 s.wParam = wParam; 2509 s.lParam = lParam; 2510 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s); 2511 } 2512