dde_misc.c 64.2 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * DDEML library
 *
 * Copyright 1997 Alexandre Julliard
 * Copyright 1997 Len White
 * Copyright 1999 Keith Matthews
 * Copyright 2000 Corel
 * Copyright 2001 Eric Pouech
9
 * Copyright 2003, 2004, 2005 Dmitry Timoshkov
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 25
 */

26 27 28
#include "config.h"
#include "wine/port.h"

29
#include <string.h>
30
#include <stdarg.h>
31
#include <stdio.h>
32
#include "windef.h"
33
#include "winbase.h"
34 35 36 37
#include "wingdi.h"
#include "winuser.h"
#include "dde.h"
#include "ddeml.h"
38
#include "win.h"
39
#include "dde_private.h"
40 41
#include "wine/unicode.h"
#include "wine/debug.h"
42

43
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
44

45 46 47 48
/* convert between ATOM and HSZ avoiding compiler warnings */
#define ATOM2HSZ(atom)	((HSZ)	(ULONG_PTR)(atom))
#define HSZ2ATOM(hsz)	((ATOM)	(ULONG_PTR)(hsz))

49
static WDML_INSTANCE*	WDML_InstanceList = NULL;
50
static LONG		WDML_MaxInstanceID = 0;  /* OK for present, have to worry about wrap-around later */
51
const WCHAR		WDML_szEventClass[] = {'W','i','n','e','D','d','e','E','v','e','n','t','C','l','a','s','s',0};
52

53 54
/* protection for instance list */
static CRITICAL_SECTION WDML_CritSect;
55 56 57 58
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &WDML_CritSect,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
59
      0, 0, { (DWORD_PTR)(__FILE__ ": WDML_CritSect") }
60
};
61
static CRITICAL_SECTION WDML_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
62

63 64 65 66 67
/* ================================================================
 *
 * 			Pure DDE (non DDEML) management
 *
 * ================================================================ */
68 69 70 71 72 73 74 75


/*****************************************************************
 *            PackDDElParam (USER32.@)
 *
 * RETURNS
 *   the packed lParam
 */
76
LPARAM WINAPI PackDDElParam(UINT msg, UINT_PTR uiLo, UINT_PTR uiHi)
77
{
78
    HGLOBAL hMem;
79
    UINT_PTR *params;
80 81 82 83 84 85 86

    switch (msg)
    {
    case WM_DDE_ACK:
    case WM_DDE_ADVISE:
    case WM_DDE_DATA:
    case WM_DDE_POKE:
87
        if (!(hMem = GlobalAlloc(GMEM_DDESHARE, sizeof(UINT_PTR) * 2)))
88 89 90 91 92 93
        {
            ERR("GlobalAlloc failed\n");
            return 0;
        }
        if (!(params = GlobalLock(hMem)))
        {
94
            ERR("GlobalLock failed (%p)\n", hMem);
95 96 97 98 99 100
            return 0;
        }
        params[0] = uiLo;
        params[1] = uiHi;
        GlobalUnlock(hMem);
        return (LPARAM)hMem;
101

102 103 104 105
    case WM_DDE_EXECUTE:
        return uiHi;

    default:
106
        return MAKELONG(uiLo, uiHi);
107
    }
108 109 110 111 112 113 114 115 116 117 118
}


/*****************************************************************
 *            UnpackDDElParam (USER32.@)
 *
 * RETURNS
 *   success: nonzero
 *   failure: zero
 */
BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
119
			    PUINT_PTR uiLo, PUINT_PTR uiHi)
120
{
121
    UINT_PTR *params;
122

123 124 125 126 127 128
    switch (msg)
    {
    case WM_DDE_ACK:
    case WM_DDE_ADVISE:
    case WM_DDE_DATA:
    case WM_DDE_POKE:
129
        if (!lParam || !(params = GlobalLock((HGLOBAL)lParam)))
130
        {
131 132
            if (uiLo) *uiLo = 0;
            if (uiHi) *uiHi = 0;
133 134 135 136 137 138
            return FALSE;
        }
        if (uiLo) *uiLo = params[0];
        if (uiHi) *uiHi = params[1];
        GlobalUnlock( (HGLOBAL)lParam );
        return TRUE;
139

140 141 142 143
    case WM_DDE_EXECUTE:
        if (uiLo) *uiLo = 0;
        if (uiHi) *uiHi = lParam;
        return TRUE;
144

145 146 147 148 149
    default:
        if (uiLo) *uiLo = LOWORD(lParam);
        if (uiHi) *uiHi = HIWORD(lParam);
        return TRUE;
    }
150 151 152 153 154 155 156 157 158 159 160 161
}


/*****************************************************************
 *            FreeDDElParam (USER32.@)
 *
 * RETURNS
 *   success: nonzero
 *   failure: zero
 */
BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
{
162 163 164 165 166 167 168 169 170
    switch (msg)
    {
    case WM_DDE_ACK:
    case WM_DDE_ADVISE:
    case WM_DDE_DATA:
    case WM_DDE_POKE:
        /* first check if it's a global handle */
        if (!GlobalHandle( (LPVOID)lParam )) return TRUE;
        return !GlobalFree( (HGLOBAL)lParam );
171

172 173
    default:
        return TRUE;
174 175 176 177 178 179 180 181 182 183 184
     }
}


/*****************************************************************
 *            ReuseDDElParam (USER32.@)
 *
 * RETURNS
 *   the packed lParam
 */
LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
185
                             UINT_PTR uiLo, UINT_PTR uiHi)
186
{
187
    UINT_PTR *params;
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    switch (msgIn)
    {
    case WM_DDE_ACK:
    case WM_DDE_ADVISE:
    case WM_DDE_DATA:
    case WM_DDE_POKE:
        switch(msgOut)
        {
        case WM_DDE_ACK:
        case WM_DDE_ADVISE:
        case WM_DDE_DATA:
        case WM_DDE_POKE:
            if (!lParam) return 0;
            if (!(params = GlobalLock( (HGLOBAL)lParam )))
            {
                ERR("GlobalLock failed\n");
                return 0;
            }
            params[0] = uiLo;
            params[1] = uiHi;
209
            TRACE("Reusing pack %08lx %08lx\n", uiLo, uiHi);
210
            GlobalUnlock( (HGLOBAL)lParam );
211
            return lParam;
212

213 214 215
        case WM_DDE_EXECUTE:
            FreeDDElParam( msgIn, lParam );
            return uiHi;
216

217 218
        default:
            FreeDDElParam( msgIn, lParam );
219
            return MAKELPARAM(uiLo, uiHi);
220
        }
221

222 223 224
    default:
        return PackDDElParam( msgOut, uiLo, uiHi );
    }
225 226 227 228 229
}

/*****************************************************************
 *            ImpersonateDdeClientWindow (USER32.@)
 *
230 231 232
 * PARAMS
 * hWndClient	  [I] handle to DDE client window
 * hWndServer	  [I] handle to DDE server window
233
 */
234
BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
235
{
236
     FIXME("(%p %p): stub\n", hWndClient, hWndServer);
237 238 239 240 241 242 243
     return FALSE;
}

/*****************************************************************
 *            DdeSetQualityOfService (USER32.@)
 */

244
BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, const SECURITY_QUALITY_OF_SERVICE *pqosNew,
245 246
				   PSECURITY_QUALITY_OF_SERVICE pqosPrev)
{
247
     FIXME("(%p %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
248 249 250
     return TRUE;
}

251 252
/* ================================================================
 *
253
 * 			WDML Error management
254 255
 *
 * ================================================================ */
256 257

/******************************************************************************
258
 * DdeGetLastError [USER32.@]  Gets most recent error code
259
 *
260 261 262 263 264
 * PARAMS
 *    idInst [I] Instance identifier
 *
 * RETURNS
 *    Last error code
265
 */
266
UINT WINAPI DdeGetLastError(DWORD idInst)
267
{
268 269
    DWORD		error_code;
    WDML_INSTANCE*	pInstance;
270

271 272 273 274 275 276 277 278 279 280 281 282 283 284
    /*  First check instance
     */
    pInstance = WDML_GetInstance(idInst);
    if  (pInstance == NULL)
    {
	error_code = DMLERR_INVALIDPARAMETER;
    }
    else
    {
	error_code = pInstance->lastError;
	pInstance->lastError = 0;
    }

    return error_code;
285 286
}

287
/******************************************************************
288
 *		WDML_SetAllLastError
289 290
 *
 *
291
 */
292
static void	WDML_SetAllLastError(DWORD lastError)
293
{
294
    DWORD		threadID;
295
    WDML_INSTANCE*	pInstance;
296 297 298
    threadID = GetCurrentThreadId();
    pInstance = WDML_InstanceList;
    while (pInstance)
299
    {
300 301 302
	if (pInstance->threadID == threadID)
	    pInstance->lastError = lastError;
	pInstance = pInstance->next;
303 304 305
    }
}

306 307 308 309 310 311 312
/* ================================================================
 *
 * 			String management
 *
 * ================================================================ */


313
/******************************************************************
314
 *		WDML_FindNode
315 316 317
 *
 *
 */
318
static HSZNode*	WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
319
{
320
    HSZNode*	pNode;
321

322
    if (pInstance == NULL) return NULL;
323

324
    for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
325
    {
326
	if (pNode->hsz == hsz) break;
327
    }
328 329 330
    if (!pNode) WARN("HSZ %p not found\n", hsz);
    return pNode;
}
331

332 333 334 335 336 337 338 339 340
/******************************************************************
 *		WDML_MakeAtomFromHsz
 *
 * Creates a global atom from an existing HSZ
 * Generally used before sending an HSZ as an atom to a remote app
 */
ATOM	WDML_MakeAtomFromHsz(HSZ hsz)
{
    WCHAR nameBuffer[MAX_BUFFER_LEN];
341

342 343 344 345 346
    if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
	return GlobalAddAtomW(nameBuffer);
    WARN("HSZ %p not found\n", hsz);
    return 0;
}
347

348 349 350 351 352 353 354 355 356 357
/******************************************************************
 *		WDML_MakeHszFromAtom
 *
 * Creates a HSZ from an existing global atom
 * Generally used while receiving a global atom and transforming it
 * into an HSZ
 */
HSZ	WDML_MakeHszFromAtom(const WDML_INSTANCE* pInstance, ATOM atom)
{
    WCHAR nameBuffer[MAX_BUFFER_LEN];
358

359
    if (!atom) return NULL;
360

361
    if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
362
    {
363 364
	TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
	return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
365
    }
366 367 368
    WARN("ATOM 0x%x not found\n", atom);
    return 0;
}
369

370 371 372 373 374 375 376 377
/******************************************************************
 *		WDML_IncHSZ
 *
 *
 */
BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
{
    HSZNode*	pNode;
378

379 380
    pNode = WDML_FindNode(pInstance, hsz);
    if (!pNode) return FALSE;
381

382 383 384
    pNode->refCount++;
    return TRUE;
}
385

386 387 388 389 390 391 392 393 394 395 396
/******************************************************************************
 *           WDML_DecHSZ    (INTERNAL)
 *
 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
 * of HSZ nodes
 * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
 */
BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
{
    HSZNode* 	pPrev = NULL;
    HSZNode* 	pCurrent;
397

398 399 400 401 402 403
    for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
    {
	/* If we found the node we were looking for and its ref count is one,
	 * we can remove it
	 */
	if (pCurrent->hsz == hsz)
404
	{
405
	    if (--pCurrent->refCount == 0)
406
	    {
407
		if (pCurrent == pInstance->nodeList)
408
		{
409
		    pInstance->nodeList = pCurrent->next;
410
		}
411
		else
412
		{
413
		    pPrev->next = pCurrent->next;
414
		}
415 416
		HeapFree(GetProcessHeap(), 0, pCurrent);
		DeleteAtom(HSZ2ATOM(hsz));
417
	    }
418
	    return TRUE;
419 420
	}
    }
421
    WARN("HSZ %p not found\n", hsz);
422

423
    return FALSE;
424 425
}

426
/******************************************************************************
427
 *            WDML_FreeAllHSZ    (INTERNAL)
428
 *
429 430
 * Frees up all the strings still allocated in the list and
 * remove all the nodes from the list of HSZ nodes.
431
 */
432
static void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
433
{
434 435 436 437 438 439
    /* Free any strings created in this instance.
     */
    while (pInstance->nodeList != NULL)
    {
	DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
    }
440 441 442
}

/******************************************************************************
443
 *            InsertHSZNode    (INTERNAL)
444
 *
445
 * Insert a node to the head of the list.
446
 */
447
static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
448
{
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    if (hsz != 0)
    {
	HSZNode* pNew = NULL;
	/* Create a new node for this HSZ.
	 */
	pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
	if (pNew != NULL)
	{
	    pNew->hsz      = hsz;
	    pNew->next     = pInstance->nodeList;
	    pNew->refCount = 1;
	    pInstance->nodeList = pNew;
	}
	else
	{
	    ERR("Primary HSZ Node allocation failed - out of memory\n");
	}
    }
467 468
}

469 470
/******************************************************************
 *		WDML_QueryString
471 472 473
 *
 *
 */
474 475
static int	WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
				 int codepage)
476
{
477 478 479 480
    WCHAR	pString[MAX_BUFFER_LEN];
    int		ret;
    /* If psz is null, we have to return only the length
     * of the string.
481
     */
482
    if (ptr == NULL)
483
    {
484 485
	ptr = pString;
	cchMax = MAX_BUFFER_LEN;
486
    }
487

488 489
    /* if there is no input windows returns a NULL string */
    if (hsz == NULL)
490
    {
491 492 493
	CHAR *t_ptr = ptr;
	*t_ptr = '\0';
	return 1;
494
    }
495

496
    switch (codepage)
497
    {
498 499 500 501 502 503 504 505 506
    case CP_WINANSI:
	ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
	break;
    case CP_WINUNICODE:
	ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
        break;
    default:
	ERR("Unknown code page %d\n", codepage);
	ret = 0;
507
    }
508 509
    return ret;
}
510

511 512 513 514 515 516 517 518 519 520 521
/*****************************************************************
 * DdeQueryStringA [USER32.@]
 */
DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
{
    DWORD		ret = 0;
    WDML_INSTANCE*	pInstance;

    TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);

    /*  First check instance
522
     */
523 524 525 526 527 528
    pInstance = WDML_GetInstance(idInst);
    if (pInstance != NULL)
    {
	if (iCodePage == 0) iCodePage = CP_WINANSI;
	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
    }
529

530 531
    TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
    return ret;
532 533
}

534 535
/*****************************************************************
 * DdeQueryStringW [USER32.@]
536
 */
537 538

DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
539
{
540
    DWORD		ret = 0;
541 542
    WDML_INSTANCE*	pInstance;

543 544 545 546 547 548
    TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);

    /*  First check instance
     */
    pInstance = WDML_GetInstance(idInst);
    if (pInstance != NULL)
549
    {
550 551
	if (iCodePage == 0) iCodePage = CP_WINUNICODE;
	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
552
    }
553 554 555

    TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
    return ret;
556 557
}

558
/******************************************************************
559
 *		DML_CreateString
560 561 562
 *
 *
 */
563
static	HSZ	WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
564
{
565
    HSZ		hsz;
566

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
    switch (codepage)
    {
    case CP_WINANSI:
	hsz = ATOM2HSZ(AddAtomA(ptr));
	TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz);
	break;
    case CP_WINUNICODE:
	hsz = ATOM2HSZ(AddAtomW(ptr));
	TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz);
	break;
    default:
	ERR("Unknown code page %d\n", codepage);
	return 0;
    }
    WDML_InsertHSZNode(pInstance, hsz);
    return hsz;
583 584
}

585 586
/*****************************************************************
 * DdeCreateStringHandleA [USER32.@]
587
 *
588
 * See DdeCreateStringHandleW.
589
 */
590
HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
591
{
592
    HSZ			hsz = 0;
593
    WDML_INSTANCE*	pInstance;
594

595
    TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage);
596

597 598 599 600
    pInstance = WDML_GetInstance(idInst);
    if (pInstance == NULL)
	WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
    else
601
    {
602 603
	if (codepage == 0) codepage = CP_WINANSI;
	hsz = WDML_CreateString(pInstance, psz, codepage);
604
    }
605

606
    return hsz;
607 608 609 610
}


/******************************************************************************
611
 * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
612 613
 *
 * PARAMS
614 615 616
 * 	idInst   [I] Instance identifier
 * 	psz      [I] Pointer to string
 *	codepage [I] Code page identifier
617
 * RETURNS
618 619
 *    Success: String handle
 *    Failure: 0
620
 */
621
HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
622
{
623
    WDML_INSTANCE*	pInstance;
624
    HSZ			hsz = 0;
625

626
    pInstance = WDML_GetInstance(idInst);
627 628
    if (pInstance == NULL)
	WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
629
    else
630
    {
631 632
	if (codepage == 0) codepage = CP_WINUNICODE;
	hsz = WDML_CreateString(pInstance, psz, codepage);
633
    }
634

635
    return hsz;
636 637
}

638 639 640 641 642
/*****************************************************************
 *            DdeFreeStringHandle   (USER32.@)
 * RETURNS
 *  success: nonzero
 *  fail:    zero
643
 */
644
BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
645 646
{
    WDML_INSTANCE*	pInstance;
647
    BOOL		ret = FALSE;
648

649 650 651 652 653 654 655
    TRACE("(%d,%p):\n", idInst, hsz);

    /*  First check instance
     */
    pInstance = WDML_GetInstance(idInst);
    if (pInstance)
	ret = WDML_DecHSZ(pInstance, hsz);
656

657 658
    return ret;
}
659

660 661
/*****************************************************************
 *            DdeKeepStringHandle  (USER32.@)
662
 *
663 664 665
 * RETURNS
 *  success: nonzero
 *  fail:    zero
666
 */
667
BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
668
{
669 670
    WDML_INSTANCE*	pInstance;
    BOOL		ret = FALSE;
671

672
    TRACE("(%d,%p):\n", idInst, hsz);
673

674 675 676 677 678 679 680
    /*  First check instance
     */
    pInstance = WDML_GetInstance(idInst);
    if (pInstance)
	ret = WDML_IncHSZ(pInstance, hsz);

    return ret;
681 682
}

683 684
/*****************************************************************
 *            DdeCmpStringHandles (USER32.@)
685
 *
686 687
 * Compares the value of two string handles.  This comparison is
 * not case sensitive.
688
 *
689 690 691
 * PARAMS
 *  hsz1    [I] Handle to the first string
 *  hsz2    [I] Handle to the second string
692
 *
693 694 695 696
 * RETURNS
 *  -1 The value of hsz1 is zero or less than hsz2
 *  0  The values of hsz 1 and 2 are the same or both zero.
 *  1  The value of hsz2 is zero of less than hsz1
697
 */
698
INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
699
{
700 701 702 703
    WCHAR	psz1[MAX_BUFFER_LEN];
    WCHAR	psz2[MAX_BUFFER_LEN];
    int		ret = 0;
    int		ret1, ret2;
704

705 706
    ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
    ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
707

708
    TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
709

710 711
    /* Make sure we found both strings. */
    if (ret1 == 0 && ret2 == 0)
712
    {
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
	/* If both are not found, return both  "zero strings". */
	ret = 0;
    }
    else if (ret1 == 0)
    {
	/* If hsz1 is a not found, return hsz1 is "zero string". */
	ret = -1;
    }
    else if (ret2 == 0)
    {
	/* If hsz2 is a not found, return hsz2 is "zero string". */
	ret = 1;
    }
    else
    {
	/* Compare the two strings we got (case insensitive). */
	ret = lstrcmpiW(psz1, psz2);
	/* Since strcmp returns any number smaller than
	 * 0 when the first string is found to be less than
	 * the second one we must make sure we are returning
	 * the proper values.
734
	 */
735
	if (ret < 0)
736
	{
737 738 739 740 741
	    ret = -1;
	}
	else if (ret > 0)
	{
	    ret = 1;
742
	}
743
    }
744

745
    return ret;
746 747
}

748 749 750 751 752 753
/* ================================================================
 *
 * 			Instance management
 *
 * ================================================================ */

754
/******************************************************************************
755
 *		IncrementInstanceId
756
 *
757
 *	generic routine to increment the max instance Id and allocate a new application instance
758
 */
759
static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
760
{
761 762 763 764
    DWORD	id = InterlockedIncrement(&WDML_MaxInstanceID);

    pInstance->instanceID = id;
    TRACE("New instance id %d allocated\n", id);
765 766
}

767 768 769
/******************************************************************
 *		WDML_EventProc
 *
770 771
 *
 */
772
static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
773
{
774 775 776 777
    WDML_INSTANCE*	pInstance;
    HSZ			hsz1, hsz2;

    switch (uMsg)
778
    {
779 780 781 782
    case WM_WDML_REGISTER:
	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
        /* try calling the Callback */
	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
783
	{
784 785 786 787 788
	    hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
	    hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
	    WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
	    WDML_DecHSZ(pInstance, hsz1);
	    WDML_DecHSZ(pInstance, hsz2);
789
	}
790 791 792 793 794
	break;

    case WM_WDML_UNREGISTER:
	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
795
	{
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
	    hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
	    hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
	    WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
	    WDML_DecHSZ(pInstance, hsz1);
	    WDML_DecHSZ(pInstance, hsz2);
	}
	break;

    case WM_WDML_CONNECT_CONFIRM:
	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
	{
	    WDML_CONV*	pConv;
	    /* confirm connection...
	     * lookup for this conv handle
	     */
            HWND client = WIN_GetFullHandle( (HWND)wParam );
            HWND server = WIN_GetFullHandle( (HWND)lParam );
	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
	    {
		if (pConv->hwndClient == client && pConv->hwndServer == server)
		    break;
	    }
	    if (pConv)
	    {
		pConv->wStatus |= ST_ISLOCAL;

		WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
				    pConv->hszTopic, pConv->hszService, 0, 0,
				    (pConv->wStatus & ST_ISSELF) ? 1 : 0);
	    }
827
	}
828 829 830
	break;
    default:
	return DefWindowProcW(hwndEvent, uMsg, wParam, lParam);
831
    }
832
    return 0;
833 834 835
}

/******************************************************************
836
 *		WDML_Initialize
837 838 839
 *
 *
 */
840 841
static UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
                            DWORD afCmd, DWORD ulRes, BOOL bUnicode)
842
{
843 844 845 846 847 848 849 850 851
    WDML_INSTANCE*		pInstance;
    WDML_INSTANCE*		reference_inst;
    UINT			ret;
    WNDCLASSEXW			wndclass;

    TRACE("(%p,%p,0x%x,%d,0x%x)\n",
	  pidInst, pfnCallback, afCmd, ulRes, bUnicode);

    if (ulRes)
852
    {
853 854 855
	ERR("Reserved value not zero?  What does this mean?\n");
	/* trap this and no more until we know more */
	return DMLERR_NO_ERROR;
856 857
    }

858 859 860 861
    /* grab enough heap for one control struct - not really necessary for re-initialise
     *	but allows us to use same validation routines */
    pInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
    if (pInstance == NULL)
862
    {
863 864 865
	/* catastrophe !! warn user & abort */
	ERR("Instance create failed - out of memory\n");
	return DMLERR_SYS_ERROR;
866
    }
867 868
    pInstance->next = NULL;
    pInstance->monitor = (afCmd | APPCLASS_MONITOR);
869

870
    /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
871

872 873 874 875 876 877 878 879 880 881 882 883 884 885
    pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
    pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
    pInstance->threadID = GetCurrentThreadId();
    pInstance->callback = *pfnCallback;
    pInstance->unicode = bUnicode;
    pInstance->nodeList = NULL; /* node will be added later */
    pInstance->monitorFlags = afCmd & MF_MASK;
    pInstance->wStatus = 0;
    pInstance->lastError = DMLERR_NO_ERROR;
    pInstance->servers = NULL;
    pInstance->convs[0] = NULL;
    pInstance->convs[1] = NULL;
    pInstance->links[0] = NULL;
    pInstance->links[1] = NULL;
886

887
    /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
888

889 890 891
    pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));

    if (!pInstance->clientOnly)
892
    {
893 894 895
	/* Check for other way of setting Client-only !! */
	pInstance->clientOnly =
	    (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
896
    }
897

898
    TRACE("instance created - checking validity\n");
899

900 901 902 903
    if (*pidInst == 0)
    {
	/*  Initialisation of new Instance Identifier */
	TRACE("new instance, callback %p flags %X\n",pfnCallback,afCmd);
904

905
	EnterCriticalSection(&WDML_CritSect);
906

907 908 909 910
	if (WDML_InstanceList == NULL)
	{
	    /* can't be another instance in this case, assign to the base pointer */
	    WDML_InstanceList = pInstance;
911

912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
	    /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
	     *		present
	     *	-------------------------------      NOTE NOTE NOTE    --------------------------
	     *
	     *	the manual is not clear if this condition
	     *	applies to the first call to DdeInitialize from an application, or the
	     *	first call for a given callback !!!
	     */

	    pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
	    TRACE("First application instance detected OK\n");
	    /*	allocate new instance ID */
	    WDML_IncrementInstanceId(pInstance);
	}
	else
	{
	    /* really need to chain the new one in to the latest here, but after checking conditions
	     *	such as trying to start a conversation from an application trying to monitor */
	    reference_inst = WDML_InstanceList;
	    TRACE("Subsequent application instance - starting checks\n");
	    while (reference_inst->next != NULL)
	    {
		/*
		 *	This set of tests will work if application uses same instance Id
		 *	at application level once allocated - which is what manual implies
		 *	should happen. If someone tries to be
		 *	clever (lazy ?) it will fail to pick up that later calls are for
		 *	the same application - should we trust them ?
		 */
		if (pInstance->instanceID == reference_inst->instanceID)
		{
		    /* Check 1 - must be same Client-only state */

		    if (pInstance->clientOnly != reference_inst->clientOnly)
		    {
			ret = DMLERR_DLL_USAGE;
			goto theError;
		    }

		    /* Check 2 - cannot use 'Monitor' with any non-monitor modes */

		    if (pInstance->monitor != reference_inst->monitor)
		    {
			ret = DMLERR_INVALIDPARAMETER;
			goto theError;
		    }

		    /* Check 3 - must supply different callback address */

		    if (pInstance->callback == reference_inst->callback)
		    {
			ret = DMLERR_DLL_USAGE;
			goto theError;
		    }
		}
		reference_inst = reference_inst->next;
	    }
	    /*  All cleared, add to chain */

	    TRACE("Application Instance checks finished\n");
	    WDML_IncrementInstanceId(pInstance);
	    reference_inst->next = pInstance;
	}
	LeaveCriticalSection(&WDML_CritSect);

	*pidInst = pInstance->instanceID;

	/* for deadlock issues, windows must always be created when outside the critical section */
	wndclass.cbSize        = sizeof(wndclass);
	wndclass.style         = 0;
	wndclass.lpfnWndProc   = WDML_EventProc;
	wndclass.cbClsExtra    = 0;
	wndclass.cbWndExtra    = sizeof(ULONG_PTR);
	wndclass.hInstance     = 0;
	wndclass.hIcon         = 0;
	wndclass.hCursor       = 0;
	wndclass.hbrBackground = 0;
	wndclass.lpszMenuName  = NULL;
	wndclass.lpszClassName = WDML_szEventClass;
	wndclass.hIconSm       = 0;

	RegisterClassExW(&wndclass);

	pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL,
						WS_POPUP, 0, 0, 0, 0,
						0, 0, 0, 0);

	SetWindowLongPtrW(pInstance->hwndEvent, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);

	TRACE("New application instance processing finished OK\n");
    }
    else
1004
    {
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
	/* Reinitialisation situation   --- FIX  */
	TRACE("reinitialisation of (%p,%p,0x%x,%d): stub\n", pidInst, pfnCallback, afCmd, ulRes);

	EnterCriticalSection(&WDML_CritSect);

	if (WDML_InstanceList == NULL)
	{
	    ret = DMLERR_INVALIDPARAMETER;
	    goto theError;
	}
	/* can't reinitialise if we have initialised nothing !! */
	reference_inst = WDML_InstanceList;
	/* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
	/*
	 *	MS allows initialisation without specifying a callback, should we allow addition of the
	 *	callback by a later call to initialise ? - if so this lot will have to change
	 */
	while (reference_inst->next != NULL)
	{
	    if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
	    {
		/* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */

		if (reference_inst->clientOnly)
		{
		    if  ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
		    {
				/* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */

			if (!(afCmd & APPCMD_CLIENTONLY))
			{
			    ret = DMLERR_INVALIDPARAMETER;
			    goto theError;
			}
		    }
		}
		/* Check 2 - cannot change monitor modes */

		if (pInstance->monitor != reference_inst->monitor)
		{
		    ret = DMLERR_INVALIDPARAMETER;
		    goto theError;
		}

		/* Check 3 - trying to set Client-only via APPCMD when not set so previously */

		if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
		{
		    ret = DMLERR_INVALIDPARAMETER;
		    goto theError;
		}
		break;
	    }
	    reference_inst = reference_inst->next;
	}
	if (reference_inst->next == NULL)
	{
	    ret = DMLERR_INVALIDPARAMETER;
	    goto theError;
	}
	/* All checked - change relevant flags */

	reference_inst->CBFflags = pInstance->CBFflags;
	reference_inst->clientOnly = pInstance->clientOnly;
	reference_inst->monitorFlags = pInstance->monitorFlags;

	HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */

	LeaveCriticalSection(&WDML_CritSect);
1074
    }
1075

1076 1077 1078 1079
    return DMLERR_NO_ERROR;
 theError:
    HeapFree(GetProcessHeap(), 0, pInstance);
    LeaveCriticalSection(&WDML_CritSect);
1080 1081 1082
    return ret;
}

1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
/******************************************************************************
 *            DdeInitializeA   (USER32.@)
 *
 * See DdeInitializeW.
 */
UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
			   DWORD afCmd, DWORD ulRes)
{
    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE);
}

/******************************************************************************
 * DdeInitializeW [USER32.@]
 * Registers an application with the DDEML
1097
 *
1098 1099 1100 1101 1102
 * PARAMS
 *    pidInst     [I] Pointer to instance identifier
 *    pfnCallback [I] Pointer to callback function
 *    afCmd       [I] Set of command and filter flags
 *    ulRes       [I] Reserved
1103
 *
1104 1105 1106
 * RETURNS
 *    Success: DMLERR_NO_ERROR
 *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
1107
 */
1108 1109
UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
			   DWORD afCmd, DWORD ulRes)
1110
{
1111
    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE);
1112 1113
}

1114
/*****************************************************************
1115
 * DdeUninitialize [USER32.@]  Frees DDEML resources
1116
 *
1117 1118 1119 1120 1121 1122
 * PARAMS
 *    idInst [I] Instance identifier
 *
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
1123
 */
1124 1125

BOOL WINAPI DdeUninitialize(DWORD idInst)
1126
{
1127 1128 1129 1130 1131
    /*  Stage one - check if we have a handle for this instance
     */
    WDML_INSTANCE*		pInstance;
    WDML_CONV*			pConv;
    WDML_CONV*			pConvNext;
1132

1133
    TRACE("(%d)\n", idInst);
1134

1135 1136
    /*  First check instance
     */
1137
    pInstance = WDML_GetInstance(idInst);
1138
    if (pInstance == NULL)
1139
    {
1140 1141 1142 1143
	/*
	 *	Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
	 */
	return FALSE;
1144
    }
1145

1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
    /* first terminate all conversations client side
     * this shall close existing links...
     */
    for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
    {
	pConvNext = pConv->next;
	DdeDisconnect((HCONV)pConv);
    }
    if (pInstance->convs[WDML_CLIENT_SIDE])
	FIXME("still pending conversations\n");
1156

1157 1158
    /* then unregister all known service names */
    DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
1159

1160 1161 1162 1163
    /* Free the nodes that were not freed by this instance
     * and remove the nodes from the list of HSZ nodes.
     */
    WDML_FreeAllHSZ(pInstance);
1164

1165 1166 1167 1168 1169 1170 1171 1172 1173
    DestroyWindow(pInstance->hwndEvent);

    /* OK now delete the instance handle itself */

    if (WDML_InstanceList == pInstance)
    {
	/* special case - the first/only entry */
	WDML_InstanceList = pInstance->next;
    }
1174
    else
1175
    {
1176 1177 1178 1179 1180
	/* general case, remove entry */
	WDML_INSTANCE*	inst;

	for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
	inst->next = pInstance->next;
1181
    }
1182 1183 1184
    /* release the heap entry
     */
    HeapFree(GetProcessHeap(), 0, pInstance);
1185

1186
    return TRUE;
1187 1188
}

1189 1190 1191 1192
/******************************************************************
 *		WDML_NotifyThreadExit
 *
 *
1193
 */
1194
void WDML_NotifyThreadDetach(void)
1195
{
1196
    WDML_INSTANCE*	pInstance;
1197 1198
    WDML_INSTANCE*	next;
    DWORD		tid = GetCurrentThreadId();
1199

1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
    EnterCriticalSection(&WDML_CritSect);
    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
    {
	next = pInstance->next;
	if (pInstance->threadID == tid)
	{
            LeaveCriticalSection(&WDML_CritSect);
	    DdeUninitialize(pInstance->instanceID);
            EnterCriticalSection(&WDML_CritSect);
	}
    }
    LeaveCriticalSection(&WDML_CritSect);
1212 1213
}

1214 1215 1216
/******************************************************************
 *		WDML_InvokeCallback
 *
1217 1218
 *
 */
1219 1220 1221
HDDEDATA 	WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
				    HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
				    ULONG_PTR dwData1, ULONG_PTR dwData2)
1222
{
1223
    HDDEDATA	ret;
1224

1225 1226
    if (pInstance == NULL)
	return NULL;
1227

1228 1229 1230 1231 1232
    TRACE("invoking CB[%p] (%x %x %p %p %p %p %lx %lx)\n",
	  pInstance->callback, uType, uFmt,
	  hConv, hsz1, hsz2, hdata, dwData1, dwData2);
    ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
    TRACE("done => %p\n", ret);
1233 1234 1235
    return ret;
}

1236 1237
/*****************************************************************************
 *	WDML_GetInstance
1238
 *
1239 1240
 *	generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
 *	for an instance Id, or NULL if the entry does not exist
1241
 *
1242
 */
1243
WDML_INSTANCE*	WDML_GetInstance(DWORD instId)
1244
{
1245
    WDML_INSTANCE*	pInstance;
1246

1247
    EnterCriticalSection(&WDML_CritSect);
1248

1249
    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
1250
    {
1251
	if (pInstance->instanceID == instId)
1252
	{
1253 1254 1255 1256 1257 1258
	    if (GetCurrentThreadId() != pInstance->threadID)
	    {
		FIXME("Tried to get instance from wrong thread\n");
		continue;
	    }
	    break;
1259 1260
	}
    }
1261

1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
    LeaveCriticalSection(&WDML_CritSect);

    if (!pInstance)
        WARN("Instance entry missing for id %04x\n", instId);
    return pInstance;
}

/******************************************************************
 *		WDML_GetInstanceFromWnd
 *
 *
 */
WDML_INSTANCE*	WDML_GetInstanceFromWnd(HWND hWnd)
{
    return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE);
1277 1278
}

1279 1280 1281 1282 1283 1284
/* ================================================================
 *
 * 			Data handle management
 *
 * ================================================================ */

1285 1286 1287
/*****************************************************************
 *            DdeCreateDataHandle (USER32.@)
 */
1288
HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff,
1289
                                    HSZ hszItem, UINT wFmt, UINT afCmd)
1290
{
1291 1292

    /* Other than check for validity we will ignore for now idInst, hszItem.
1293 1294
     * The purpose of these arguments still need to be investigated.
     */
1295

1296
    WDML_INSTANCE*		pInstance;
1297 1298 1299
    HGLOBAL     		hMem;
    LPBYTE      		pByte;
    DDE_DATAHANDLE_HEAD*	pDdh;
1300 1301
    WCHAR psz[MAX_BUFFER_LEN];

1302 1303 1304 1305 1306 1307 1308
    pInstance = WDML_GetInstance(idInst);
    if (pInstance == NULL)
    {
        WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
        return NULL;
    }

1309 1310 1311 1312 1313
    if (!GetAtomNameW(HSZ2ATOM(hszItem), psz, MAX_BUFFER_LEN))
    {
        psz[0] = HSZ2ATOM(hszItem);
        psz[1] = 0;
    }
1314

1315
    TRACE("(%d,%p,cb %d, cbOff %d,%p <%s>,fmt %04x,%x)\n",
1316
	  idInst, pSrc, cb, cbOff, hszItem, debugstr_w(psz), wFmt, afCmd);
1317

1318 1319 1320
    if (afCmd != 0 && afCmd != HDATA_APPOWNED)
        return 0;

1321
    /* we use the first 4 bytes to store the size */
1322
    if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff + sizeof(DDE_DATAHANDLE_HEAD))))
1323 1324 1325
    {
	ERR("GlobalAlloc failed\n");
	return 0;
1326 1327
    }

1328
    pDdh = GlobalLock(hMem);
1329 1330 1331 1332 1333 1334
    if (!pDdh)
    {
        GlobalFree(hMem);
        return 0;
    }

1335
    pDdh->cfFormat = wFmt;
1336 1337
    pDdh->bAppOwned = (afCmd == HDATA_APPOWNED);

1338 1339 1340 1341 1342 1343
    pByte = (LPBYTE)(pDdh + 1);
    if (pSrc)
    {
	memcpy(pByte, pSrc + cbOff, cb);
    }
    GlobalUnlock(hMem);
1344

1345
    TRACE("=> %p\n", hMem);
1346
    return hMem;
1347 1348 1349 1350 1351 1352 1353 1354 1355
}

/*****************************************************************
 *
 *            DdeAddData (USER32.@)
 */
HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
{
    DWORD	old_sz, new_sz;
1356 1357
    LPBYTE	pDst;

1358
    TRACE("(%p,%p,cb %d, cbOff %d)\n", hData, pSrc, cb, cbOff);
1359

1360 1361
    pDst = DdeAccessData(hData, &old_sz);
    if (!pDst) return 0;
1362

1363 1364 1365 1366
    new_sz = cb + cbOff;
    if (new_sz > old_sz)
    {
	DdeUnaccessData(hData);
1367
	hData = GlobalReAlloc(hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1368 1369 1370
			      GMEM_MOVEABLE | GMEM_DDESHARE);
	pDst = DdeAccessData(hData, &old_sz);
    }
1371

1372
    if (!pDst) return 0;
1373

1374 1375 1376 1377 1378 1379 1380 1381
    memcpy(pDst + cbOff, pSrc, cb);
    DdeUnaccessData(hData);
    return hData;
}

/******************************************************************************
 * DdeGetData [USER32.@]  Copies data from DDE object to local buffer
 *
1382 1383 1384 1385 1386 1387 1388
 *
 * PARAMS
 * hData	[I] Handle to DDE object
 * pDst		[I] Pointer to destination buffer
 * cbMax	[I] Amount of data to copy
 * cbOff	[I] Offset to beginning of data
 *
1389 1390 1391
 * RETURNS
 *    Size of memory object associated with handle
 */
1392
DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1393 1394 1395
{
    DWORD   dwSize, dwRet;
    LPBYTE  pByte;
1396

1397
    TRACE("(%p,%p,%d,%d)\n", hData, pDst, cbMax, cbOff);
1398

1399
    pByte = DdeAccessData(hData, &dwSize);
1400 1401

    if (pByte)
1402
    {
1403 1404 1405 1406 1407
        if (!pDst)
        {
            dwRet = dwSize;
        }
        else if (cbOff + cbMax < dwSize)
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436
	{
	    dwRet = cbMax;
	}
	else if (cbOff < dwSize)
	{
	    dwRet = dwSize - cbOff;
	}
	else
	{
	    dwRet = 0;
	}
	if (pDst && dwRet != 0)
	{
	    memcpy(pDst, pByte + cbOff, dwRet);
	}
	DdeUnaccessData(hData);
    }
    else
    {
	dwRet = 0;
    }
    return dwRet;
}

/*****************************************************************
 *            DdeAccessData (USER32.@)
 */
LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
{
1437
    HGLOBAL			hMem = hData;
1438
    DDE_DATAHANDLE_HEAD*	pDdh;
1439

1440
    TRACE("(%p,%p)\n", hData, pcbDataSize);
1441

1442
    pDdh = GlobalLock(hMem);
1443 1444
    if (pDdh == NULL)
    {
1445
	ERR("Failed on GlobalLock(%p)\n", hMem);
1446 1447
	return 0;
    }
1448

1449 1450 1451 1452
    if (pcbDataSize != NULL)
    {
	*pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
    }
1453
    TRACE("=> %p (%lu) fmt %04x\n", pDdh + 1, GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD), pDdh->cfFormat);
1454 1455 1456 1457 1458 1459 1460 1461
    return (LPBYTE)(pDdh + 1);
}

/*****************************************************************
 *            DdeUnaccessData (USER32.@)
 */
BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
{
1462
    HGLOBAL hMem = hData;
1463

1464
    TRACE("(%p)\n", hData);
1465

1466
    GlobalUnlock(hMem);
1467

1468 1469 1470 1471 1472 1473 1474
    return TRUE;
}

/*****************************************************************
 *            DdeFreeDataHandle   (USER32.@)
 */
BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1475
{
1476
    TRACE("(%p)\n", hData);
1477 1478 1479 1480 1481

    /* 1 is the handle value returned by an asynchronous operation. */
    if (hData == (HDDEDATA)1)
        return TRUE;

1482
    return GlobalFree(hData) == 0;
1483 1484
}

1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
/******************************************************************
 *		WDML_IsAppOwned
 *
 *
 */
BOOL WDML_IsAppOwned(HDDEDATA hData)
{
    DDE_DATAHANDLE_HEAD*	pDdh;
    BOOL                        ret = FALSE;

1495
    pDdh = GlobalLock(hData);
1496 1497 1498
    if (pDdh != NULL)
    {
        ret = pDdh->bAppOwned;
1499
        GlobalUnlock(hData);
1500 1501 1502 1503
    }
    return ret;
}

1504 1505 1506 1507 1508
/* ================================================================
 *
 *                  Global <=> Data handle management
 *
 * ================================================================ */
1509

1510
/* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1511
 *    offset	  size
1512 1513 1514 1515
 *    (bytes)	 (bits)	comment
 *	0	   16	bit fields for options (release, ackreq, response...)
 *	2	   16	clipboard format
 *	4	   ?	data to be used
1516
 */
1517
HDDEDATA        WDML_Global2DataHandle(WDML_CONV* pConv, HGLOBAL hMem, WINE_DDEHEAD* p)
1518 1519 1520
{
    DDEDATA*    pDd;
    HDDEDATA	ret = 0;
1521
    DWORD       size;
1522 1523 1524 1525

    if (hMem)
    {
        pDd = GlobalLock(hMem);
1526
        size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
1527 1528 1529
        if (pDd)
        {
	    if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1530 1531 1532
            switch (pDd->cfFormat)
            {
            default:
1533 1534
                FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
                      pDd->cfFormat, hMem);
1535
                /* fall through */
1536 1537
            case 0:
            case CF_TEXT:
1538
                ret = DdeCreateDataHandle(pConv->instance->instanceID, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
                break;
            case CF_BITMAP:
                if (size >= sizeof(BITMAP))
                {
                    BITMAP*     bmp = (BITMAP*)pDd->Value;
                    int         count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes;
                    if (size >= sizeof(BITMAP) + count)
                    {
                        HBITMAP hbmp;

1549
                        if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
1550 1551 1552
                                                 bmp->bmPlanes, bmp->bmBitsPixel,
                                                 pDd->Value + sizeof(BITMAP))))
                        {
1553
                            ret = DdeCreateDataHandle(pConv->instance->instanceID, (LPBYTE)&hbmp, sizeof(hbmp),
1554 1555 1556 1557 1558 1559
                                                      0, 0, CF_BITMAP, 0);
                        }
                        else ERR("Can't create bmp\n");
                    }
                    else
                    {
1560
                        ERR("Wrong count: %u / %d\n", size, count);
1561 1562 1563 1564 1565
                    }
                } else ERR("No bitmap header\n");
                break;
            }
            GlobalUnlock(hMem);
1566
        }
1567 1568 1569
    }
    return ret;
}
1570

1571
/******************************************************************
1572 1573
 *		WDML_DataHandle2Global
 *
1574 1575
 *
 */
1576
HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1577
			       BOOL fDeferUpd, BOOL fAckReq)
1578
{
1579 1580 1581
    DDE_DATAHANDLE_HEAD*	pDdh;
    DWORD                       dwSize;
    HGLOBAL                     hMem = 0;
1582

1583 1584
    dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
    pDdh = GlobalLock(hDdeData);
1585
    if (dwSize && pDdh)
1586
    {
1587
        WINE_DDEHEAD*    wdh = NULL;
1588

1589 1590 1591
        switch (pDdh->cfFormat)
        {
        default:
1592 1593
            FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
                   pDdh->cfFormat, hDdeData);
1594
            /* fall through */
1595 1596 1597
        case 0:
        case CF_TEXT:
            hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
1598
            if (hMem && (wdh = GlobalLock(hMem)))
1599 1600 1601
            {
                memcpy(wdh + 1, pDdh + 1, dwSize);
            }
1602 1603 1604 1605 1606 1607 1608 1609
            break;
        case CF_BITMAP:
            if (dwSize >= sizeof(HBITMAP))
            {
                BITMAP  bmp;
                DWORD   count;
                HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);

1610
                if (GetObjectW(hbmp, sizeof(bmp), &bmp))
1611 1612
                {
                    count = bmp.bmWidthBytes * bmp.bmHeight;
1613
                    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1614
                                       sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
1615
                    if (hMem && (wdh = GlobalLock(hMem)))
1616 1617 1618 1619 1620 1621 1622
                    {
                        memcpy(wdh + 1, &bmp, sizeof(bmp));
                        GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
                    }
                }
            }
            break;
1623
        }
1624 1625
        if (wdh)
        {
1626
            wdh->unused = 0;
1627 1628 1629 1630 1631 1632 1633
            wdh->fResponse = fResponse;
            wdh->fRelease = fRelease;
            wdh->fDeferUpd = fDeferUpd;
            wdh->fAckReq = fAckReq;
            wdh->cfFormat = pDdh->cfFormat;
            GlobalUnlock(hMem);
        }
1634
        GlobalUnlock(hDdeData);
1635
    }
1636

1637
    return hMem;
1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650
}

/* ================================================================
 *
 * 			Server management
 *
 * ================================================================ */

/******************************************************************
 *		WDML_AddServer
 *
 *
 */
1651
WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1652
{
1653
    static const WCHAR fmtW[] = {'%','s','(','0','x','%','*','x',')',0};
1654
    WDML_SERVER* 	pServer;
1655 1656
    WCHAR		buf1[256];
    WCHAR		buf2[256];
1657

1658
    pServer = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1659
    if (pServer == NULL) return NULL;
1660

1661 1662
    pServer->hszService = hszService;
    WDML_IncHSZ(pInstance, hszService);
1663

1664
    DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE);
1665
    snprintfW(buf2, 256, fmtW, buf1, 2*sizeof(ULONG_PTR), GetCurrentProcessId());
1666
    pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE);
1667 1668 1669 1670

    pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
    pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);

1671
    pServer->filterOn = TRUE;
1672

1673 1674
    pServer->next = pInstance->servers;
    pInstance->servers = pServer;
1675 1676 1677 1678 1679 1680 1681 1682
    return pServer;
}

/******************************************************************
 *		WDML_RemoveServer
 *
 *
 */
1683
void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1684
{
1685 1686 1687 1688 1689 1690
    WDML_SERVER*	pPrev = NULL;
    WDML_SERVER*	pServer = NULL;
    WDML_CONV*		pConv;
    WDML_CONV*		pConvNext;

    pServer = pInstance->servers;
1691

1692
    while (pServer != NULL)
1693
    {
1694
	if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1695
	{
1696
	    WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1697 1698 1699 1700 1701 1702 1703
				     pServer->atomService, pServer->atomServiceSpec);
	    /* terminate all conversations for given topic */
	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
	    {
		pConvNext = pConv->next;
		if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
		{
1704
                    HWND client = pConv->hwndClient, server = pConv->hwndServer;
1705 1706
		    WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
		    /* don't care about return code (whether client window is present or not) */
1707
		    PostMessageW(client, WM_DDE_TERMINATE, (WPARAM)server, 0);
1708 1709 1710
		}
	    }
	    if (pServer == pInstance->servers)
1711
	    {
1712
		pInstance->servers = pServer->next;
1713 1714 1715
	    }
	    else
	    {
1716
		pPrev->next = pServer->next;
1717
	    }
1718

1719 1720 1721 1722 1723 1724 1725 1726
	    DestroyWindow(pServer->hwndServer);
	    WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
	    WDML_DecHSZ(pInstance, pServer->hszService);

	    GlobalDeleteAtom(pServer->atomService);
	    GlobalDeleteAtom(pServer->atomServiceSpec);

	    HeapFree(GetProcessHeap(), 0, pServer);
1727 1728
	    break;
	}
1729

1730 1731
	pPrev = pServer;
	pServer = pServer->next;
1732 1733 1734 1735 1736 1737 1738 1739 1740 1741
    }
}

/*****************************************************************************
 *	WDML_FindServer
 *
 *	generic routine to return a pointer to the relevant ServiceNode
 *	for a given service name, or NULL if the entry does not exist
 *
 */
1742
WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1743 1744
{
    WDML_SERVER*	pServer;
1745

1746
    for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1747 1748 1749 1750 1751 1752
    {
	if (hszService == pServer->hszService)
	{
	    return pServer;
	}
    }
1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834
    TRACE("Service name missing\n");
    return NULL;
}

/* ================================================================
 *
 * 			Link (hot & warm) management
 *
 * ================================================================ */

/******************************************************************
 *		WDML_AddLink
 *
 *
 */
void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
		  UINT wType, HSZ hszItem, UINT wFmt)
{
    WDML_LINK*	pLink;

    pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
    if (pLink == NULL)
    {
	ERR("OOM\n");
	return;
    }

    pLink->hConv = hConv;
    pLink->transactionType = wType;
    WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
    pLink->uFmt = wFmt;
    pLink->next = pInstance->links[side];
    pInstance->links[side] = pLink;
}

/******************************************************************
 *		WDML_RemoveLink
 *
 *
 */
void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
		     HSZ hszItem, UINT uFmt)
{
    WDML_LINK* pPrev = NULL;
    WDML_LINK* pCurrent = NULL;

    pCurrent = pInstance->links[side];

    while (pCurrent != NULL)
    {
	if (pCurrent->hConv == hConv &&
	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
	    pCurrent->uFmt == uFmt)
	{
	    if (pCurrent == pInstance->links[side])
	    {
		pInstance->links[side] = pCurrent->next;
	    }
	    else
	    {
		pPrev->next = pCurrent->next;
	    }

	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
	    HeapFree(GetProcessHeap(), 0, pCurrent);
	    break;
	}

	pPrev = pCurrent;
	pCurrent = pCurrent->next;
    }
}

/* this function is called to remove all links related to the conv.
   It should be called from both client and server when terminating
   the conversation.
*/
/******************************************************************
 *		WDML_RemoveAllLinks
 *
 *
 */
1835
static void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883
{
    WDML_LINK* pPrev = NULL;
    WDML_LINK* pCurrent = NULL;
    WDML_LINK* pNext = NULL;

    pCurrent = pInstance->links[side];

    while (pCurrent != NULL)
    {
	if (pCurrent->hConv == (HCONV)pConv)
	{
	    if (pCurrent == pInstance->links[side])
	    {
		pInstance->links[side] = pCurrent->next;
		pNext = pCurrent->next;
	    }
	    else
	    {
		pPrev->next = pCurrent->next;
		pNext = pCurrent->next;
	    }

	    WDML_DecHSZ(pInstance, pCurrent->hszItem);

	    HeapFree(GetProcessHeap(), 0, pCurrent);
	    pCurrent = NULL;
	}

	if (pCurrent)
	{
	    pPrev = pCurrent;
	    pCurrent = pCurrent->next;
	}
	else
	{
	    pCurrent = pNext;
	}
    }
}

/******************************************************************
 *		WDML_FindLink
 *
 *
 */
WDML_LINK* 	WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
			      HSZ hszItem, BOOL use_fmt, UINT uFmt)
{
1884
    WDML_LINK*	pCurrent;
1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996

    for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
    {
	/* we don't need to check for transaction type as it can be altered */

	if (pCurrent->hConv == hConv &&
	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
	    (!use_fmt || pCurrent->uFmt == uFmt))
	{
	    break;
	}

    }

    return pCurrent;
}

/* ================================================================
 *
 * 			Transaction management
 *
 * ================================================================ */

/******************************************************************
 *		WDML_AllocTransaction
 *
 * Alloc a transaction structure for handling the message ddeMsg
 */
WDML_XACT*	WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
				      UINT wFmt, HSZ hszItem)
{
    WDML_XACT*		pXAct;
    static WORD		tid = 1;	/* FIXME: wrap around */

    pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
    if (!pXAct)
    {
	pInstance->lastError = DMLERR_MEMORY_ERROR;
	return NULL;
    }

    pXAct->xActID = tid++;
    pXAct->ddeMsg = ddeMsg;
    pXAct->hDdeData = 0;
    pXAct->hUser = 0;
    pXAct->next = NULL;
    pXAct->wType = 0;
    pXAct->wFmt = wFmt;
    if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
    pXAct->atom = 0;
    pXAct->hMem = 0;
    pXAct->lParam = 0;

    return pXAct;
}

/******************************************************************
 *		WDML_QueueTransaction
 *
 * Adds a transaction to the list of transaction
 */
void	WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
{
    WDML_XACT**	pt;

    /* advance to last in queue */
    for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
    *pt = pXAct;
}

/******************************************************************
 *		WDML_UnQueueTransaction
 *
 *
 */
BOOL	WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct)
{
    WDML_XACT**	pt;

    for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
    {
	if (*pt == pXAct)
	{
	    *pt = pXAct->next;
	    return TRUE;
	}
    }
    return FALSE;
}

/******************************************************************
 *		WDML_FreeTransaction
 *
 *
 */
void	WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
{
    /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
    if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
    {
	GlobalFree(pXAct->hMem);
    }
    if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);

    HeapFree(GetProcessHeap(), 0, pXAct);
}

/******************************************************************
 *		WDML_FindTransaction
 *
 *
 */
1997
static WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
{
    WDML_XACT* pXAct;

    tid = HIWORD(tid);
    for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
    {
	if (pXAct->xActID == tid)
	    break;
    }
    return pXAct;
2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020
}

/* ================================================================
 *
 * 		Conversation management
 *
 * ================================================================ */

/******************************************************************
 *		WDML_AddConv
 *
 *
 */
2021
WDML_CONV*	WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
2022 2023 2024
			     HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
{
    WDML_CONV*	pConv;
2025

2026
    /* no conversation yet, add it */
2027 2028
    pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
    if (!pConv) return NULL;
2029 2030 2031 2032

    pConv->instance = pInstance;
    WDML_IncHSZ(pInstance, pConv->hszService = hszService);
    WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
2033
    pConv->magic = WDML_CONV_MAGIC;
2034 2035 2036 2037
    pConv->hwndServer = hwndServer;
    pConv->hwndClient = hwndClient;
    pConv->transactions = NULL;
    pConv->hUser = 0;
2038
    pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
2039
    pConv->wStatus |= pInstance->wStatus;
2040 2041 2042 2043 2044 2045 2046
    /* check if both side of the conversation are of the same instance */
    if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
	WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
    {
	pConv->wStatus |= ST_ISSELF;
    }
    pConv->wConvst = XST_NULL;
2047

2048 2049
    pConv->next = pInstance->convs[side];
    pInstance->convs[side] = pConv;
2050

2051
    TRACE("pConv->wStatus %04x pInstance(%p)\n", pConv->wStatus, pInstance);
2052

2053 2054 2055 2056 2057 2058 2059 2060
    return pConv;
}

/******************************************************************
 *		WDML_FindConv
 *
 *
 */
2061
WDML_CONV*	WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
2062 2063
			      HSZ hszService, HSZ hszTopic)
{
2064
    WDML_CONV*	pCurrent;
2065

2066
    for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2067 2068 2069 2070 2071 2072
    {
	if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
	    DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
	{
	    return pCurrent;
	}
2073

2074 2075 2076 2077 2078 2079 2080 2081 2082
    }
    return NULL;
}

/******************************************************************
 *		WDML_RemoveConv
 *
 *
 */
2083
void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
2084
{
2085 2086 2087 2088 2089
    WDML_CONV*	pPrev = NULL;
    WDML_CONV* 	pCurrent;
    WDML_XACT*	pXAct;
    WDML_XACT*	pXActNext;
    HWND	hWnd;
2090 2091 2092

    if (!pRef)
	return;
2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110

    /* remove any pending transaction */
    for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
    {
	pXActNext = pXAct->next;
	WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
    }

    WDML_RemoveAllLinks(pRef->instance, pRef, side);

    /* FIXME: should we keep the window around ? it seems so (at least on client side
     * to let QueryConvInfo work after conv termination, but also to implement
     * DdeReconnect...
     */
    /* destroy conversation window, but first remove pConv from hWnd.
     * this would help the wndProc do appropriate handling upon a WM_DESTROY message
     */
    hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
2111
    SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
2112 2113 2114 2115 2116 2117 2118

    DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);

    WDML_DecHSZ(pRef->instance, pRef->hszService);
    WDML_DecHSZ(pRef->instance, pRef->hszTopic);

    for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
2119 2120 2121
    {
	if (pCurrent == pRef)
	{
2122
	    if (pCurrent == pRef->instance->convs[side])
2123
	    {
2124
		pRef->instance->convs[side] = pCurrent->next;
2125 2126 2127 2128 2129
	    }
	    else
	    {
		pPrev->next = pCurrent->next;
	    }
2130
	    pCurrent->magic = 0;
2131 2132 2133 2134 2135 2136
	    HeapFree(GetProcessHeap(), 0, pCurrent);
	    break;
	}
    }
}

2137 2138 2139 2140 2141 2142 2143
/******************************************************************
 *              WDML_EnableCallback
 */
static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
{
    if (wCmd == EC_DISABLE)
    {
2144 2145
        pConv->wStatus |= ST_BLOCKED;
        TRACE("EC_DISABLE: conv %p status flags %04x\n", pConv, pConv->wStatus);
2146 2147 2148 2149
        return TRUE;
    }

    if (wCmd == EC_QUERYWAITING)
2150
        return pConv->transactions != NULL;
2151 2152 2153 2154 2155 2156 2157

    if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
    {
        FIXME("Unknown command code %04x\n", wCmd);
        return FALSE;
    }

2158 2159 2160 2161 2162 2163
    if (wCmd == EC_ENABLEALL)
    {
        pConv->wStatus &= ~ST_BLOCKED;
        TRACE("EC_ENABLEALL: conv %p status flags %04x\n", pConv, pConv->wStatus);
    }

2164 2165 2166 2167 2168 2169
    while (pConv->transactions)
    {
        WDML_XACT *pXAct = pConv->transactions;

        if (pConv->wStatus & ST_CLIENT)
        {
2170 2171 2172
            /* transaction should be in the queue until handled */
            WDML_ClientHandle(pConv, pXAct, 0, NULL);
            WDML_UnQueueTransaction(pConv, pXAct);
2173 2174
        }
        else
2175 2176 2177
        {
            /* transaction should be removed from the queue before handling */
            WDML_UnQueueTransaction(pConv, pXAct);
2178
            WDML_ServerHandle(pConv, pXAct);
2179
        }
2180 2181 2182 2183 2184 2185 2186 2187

        WDML_FreeTransaction(pConv->instance, pXAct, TRUE);

        if (wCmd == EC_ENABLEONE) break;
    }
    return TRUE;
}

2188 2189 2190 2191 2192
/*****************************************************************
 *            DdeEnableCallback (USER32.@)
 */
BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
{
2193 2194
    BOOL ret = FALSE;
    WDML_CONV *pConv;
2195

2196
    TRACE("(%d, %p, %04x)\n", idInst, hConv, wCmd);
2197

2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
    if (hConv)
    {
        pConv = WDML_GetConv(hConv, TRUE);

        if (pConv && pConv->instance->instanceID == idInst)
            ret = WDML_EnableCallback(pConv, wCmd);
    }
    else
    {
        WDML_INSTANCE *pInstance = WDML_GetInstance(idInst);
2208

2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233
        if (!pInstance)
            return FALSE;

        TRACE("adding flags %04x to instance %p\n", wCmd, pInstance);
        pInstance->wStatus |= wCmd;

        if (wCmd == EC_DISABLE)
        {
            pInstance->wStatus |= ST_BLOCKED;
            TRACE("EC_DISABLE: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
        }
        else if (wCmd == EC_ENABLEALL)
        {
            pInstance->wStatus &= ~ST_BLOCKED;
            TRACE("EC_ENABLEALL: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
        }

        ret = TRUE;

        for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConv->next)
        {
            ret = WDML_EnableCallback(pConv, wCmd);
            if (ret && wCmd == EC_QUERYWAITING) break;
        }
    }
2234 2235

    return ret;
2236 2237
}

2238 2239 2240
/******************************************************************
 *		WDML_GetConv
 *
2241
 *
2242
 */
2243
WDML_CONV*	WDML_GetConv(HCONV hConv, BOOL checkConnected)
2244
{
2245 2246
    WDML_CONV*	pConv = (WDML_CONV*)hConv;

2247
    /* FIXME: should do better checking */
2248
    if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL;
2249

2250 2251 2252 2253 2254 2255 2256
    if (!pConv->instance)
    {
        WARN("wrong thread ID, no instance\n");
	return NULL;
    }

    if (pConv->instance->threadID != GetCurrentThreadId())
2257
    {
2258 2259
        WARN("wrong thread ID\n");
        pConv->instance->lastError = DMLERR_INVALIDPARAMETER; /* FIXME: check */
2260 2261
	return NULL;
    }
2262 2263

    if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
2264
    {
2265 2266
        WARN("found conv but ain't connected\n");
        pConv->instance->lastError = DMLERR_NO_CONV_ESTABLISHED;
2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279
	return NULL;
    }

    return pConv;
}

/******************************************************************
 *		WDML_GetConvFromWnd
 *
 *
 */
WDML_CONV*	WDML_GetConvFromWnd(HWND hWnd)
{
2280
    return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION);
2281 2282 2283 2284 2285 2286 2287
}

/******************************************************************
 *		WDML_PostAck
 *
 *
 */
2288
BOOL		WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
2289
			     BOOL fBusy, BOOL fAck, UINT_PTR pmt, LPARAM lParam, UINT oldMsg)
2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
{
    DDEACK	ddeAck;
    HWND	from, to;

    if (side == WDML_SERVER_SIDE)
    {
	from = pConv->hwndServer;
	to   = pConv->hwndClient;
    }
    else
    {
	to   = pConv->hwndServer;
	from = pConv->hwndClient;
    }

    ddeAck.bAppReturnCode = appRetCode;
    ddeAck.reserved       = 0;
    ddeAck.fBusy          = fBusy;
    ddeAck.fAck           = fAck;

    TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
2311

2312 2313
    lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) :
        PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt);
2314
    if (!PostMessageW(to, WM_DDE_ACK, (WPARAM)from, lParam))
2315
    {
2316
	pConv->wStatus &= ~ST_CONNECTED;
2317
        pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
2318 2319
        FreeDDElParam(WM_DDE_ACK, lParam);
        return FALSE;
2320
    }
2321
    return TRUE;
2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332
}

/*****************************************************************
 *            DdeSetUserHandle (USER32.@)
 */
BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
{
    WDML_CONV*	pConv;

    pConv = WDML_GetConv(hConv, FALSE);
    if (pConv == NULL)
2333 2334
	return FALSE;

2335
    if (id == QID_SYNC)
2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350
    {
	pConv->hUser = hUser;
    }
    else
    {
	WDML_XACT*	pXAct;

	pXAct = WDML_FindTransaction(pConv, id);
	if (pXAct)
	{
	    pXAct->hUser = hUser;
	}
	else
	{
	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2351
	    return  FALSE;
2352 2353
	}
    }
2354
    return TRUE;
2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367
}

/******************************************************************
 *		WDML_GetLocalConvInfo
 *
 *
 */
static	BOOL	WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
{
    BOOL 	ret = TRUE;
    WDML_LINK*	pLink;
    WDML_SIDE	side;

2368
    ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((ULONG_PTR)pConv | 1) : 0;
2369 2370 2371 2372 2373 2374 2375 2376 2377
    ci->hszSvcPartner = pConv->hszService;
    ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
    ci->hszTopic = pConv->hszTopic;
    ci->wStatus = pConv->wStatus;

    side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;

    for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
    {
2378
	if (pLink->hConv == (HCONV)pConv)
2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436
	{
	    ci->wStatus |= ST_ADVISE;
	    break;
	}
    }

    /* FIXME: non handled status flags:
       ST_BLOCKED
       ST_BLOCKNEXT
       ST_INLIST
    */

    ci->wConvst = pConv->wConvst; /* FIXME */

    ci->wLastError = 0; /* FIXME: note it's not the instance last error */
    ci->hConvList = 0;
    ci->ConvCtxt = pConv->convContext;
    if (ci->wStatus & ST_CLIENT)
    {
	ci->hwnd = pConv->hwndClient;
	ci->hwndPartner = pConv->hwndServer;
    }
    else
    {
	ci->hwnd = pConv->hwndServer;
	ci->hwndPartner = pConv->hwndClient;
    }
    if (id == QID_SYNC)
    {
	ci->hUser = pConv->hUser;
	ci->hszItem = 0;
	ci->wFmt = 0;
	ci->wType = 0;
    }
    else
    {
	WDML_XACT*	pXAct;

	pXAct = WDML_FindTransaction(pConv, id);
	if (pXAct)
	{
	    ci->hUser = pXAct->hUser;
	    ci->hszItem = pXAct->hszItem;
	    ci->wFmt = pXAct->wFmt;
	    ci->wType = pXAct->wType;
	}
	else
	{
	    ret = 0;
	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
	}
    }
    return ret;
}

/******************************************************************
 *		DdeQueryConvInfo (USER32.@)
 *
2437
 * FIXME: Set last DDE error on failure.
2438
 */
2439
UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
2440 2441 2442 2443 2444
{
    UINT	ret = lpConvInfo->cb;
    CONVINFO	ci;
    WDML_CONV*	pConv;

2445
    TRACE("(%p,%x,%p)\n", hConv, id, lpConvInfo);
2446 2447 2448 2449 2450 2451 2452

    if (!hConv)
    {
        FIXME("hConv is NULL\n");
        return 0;
    }

2453
    pConv = WDML_GetConv(hConv, FALSE);
2454
    if (pConv != NULL)
2455
    {
2456 2457
        if (!WDML_GetLocalConvInfo(pConv, &ci, id))
            ret = 0;
2458
    }
2459
    else
2460
    {
2461 2462 2463 2464 2465 2466 2467
        if ((ULONG_PTR)hConv & 1)
        {
            pConv = WDML_GetConv((HCONV)((ULONG_PTR)hConv & ~1), FALSE);
            if (pConv != NULL)
                FIXME("Request on remote conversation information is not implemented yet\n");
        }
        ret = 0;
2468
    }
2469

2470
    if (ret != 0)
2471 2472
    {
	ci.cb = lpConvInfo->cb;
2473
	memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci)));
2474
    }
2475
    return ret;
2476 2477
}

2478 2479 2480 2481 2482 2483
/* ================================================================
 *
 * 	   Information broadcast across DDEML implementations
 *
 * ================================================================ */

2484 2485
struct tagWDML_BroadcastPmt
{
2486
    LPCWSTR	clsName;
2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499
    UINT	uMsg;
    WPARAM	wParam;
    LPARAM	lParam;
};

/******************************************************************
 *		WDML_BroadcastEnumProc
 *
 *
 */
static	BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
{
    struct tagWDML_BroadcastPmt*	s = (struct tagWDML_BroadcastPmt*)lParam;
2500
    WCHAR				buffer[128];
2501

2502 2503
    if (GetClassNameW(hWnd, buffer, 128) > 0 &&
	lstrcmpiW(buffer, s->clsName) == 0)
2504
    {
2505
	PostMessageW(hWnd, s->uMsg, s->wParam, s->lParam);
2506 2507 2508 2509 2510 2511 2512 2513 2514
    }
    return TRUE;
}

/******************************************************************
 *		WDML_BroadcastDDEWindows
 *
 *
 */
2515
void WDML_BroadcastDDEWindows(LPCWSTR clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2516 2517 2518 2519 2520 2521 2522 2523 2524
{
    struct tagWDML_BroadcastPmt	s;

    s.clsName = clsName;
    s.uMsg    = uMsg;
    s.wParam  = wParam;
    s.lParam  = lParam;
    EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
}