dde_misc.c 62.4 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 38
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "dde.h"
#include "ddeml.h"
39
#include "win.h"
40
#include "dde_private.h"
41 42
#include "wine/unicode.h"
#include "wine/debug.h"
43

44
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
45

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

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

static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &WDML_CritSect,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
58
      0, 0, { (DWORD_PTR)(__FILE__ ": WDML_CritSect") }
59 60
};
CRITICAL_SECTION WDML_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
61

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


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

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

101 102 103 104
    case WM_DDE_EXECUTE:
        return uiHi;

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


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

122 123 124 125 126 127 128 129 130 131 132 133
    switch (msg)
    {
    case WM_DDE_ACK:
    case WM_DDE_ADVISE:
    case WM_DDE_DATA:
    case WM_DDE_POKE:
        if (!lParam) return FALSE;
        if (!(params = GlobalLock( (HGLOBAL)lParam )))
        {
            ERR("GlobalLock failed (%lx)\n", lParam);
            return FALSE;
        }
134
        TRACE("unpacked: low %08x, high %08x\n", params[0], params[1]);
135 136 137 138
        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 209
    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;
            TRACE("Reusing pack %08x %08x\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 244 245 246
     return FALSE;
}

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

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

251 252 253 254 255
/* ================================================================
 *
 * 			Instance management
 *
 * ================================================================ */
256 257 258 259 260 261

/******************************************************************************
 *		IncrementInstanceId
 *
 *	generic routine to increment the max instance Id and allocate a new application instance
 */
262
static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
263 264 265
{
    DWORD	id = InterlockedIncrement(&WDML_MaxInstanceID);

266
    pInstance->instanceID = id;
267
    TRACE("New instance id %d allocated\n", id);
268 269
}

270 271 272 273
/******************************************************************
 *		WDML_EventProc
 *
 *
274 275 276
 */
static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
277 278
    WDML_INSTANCE*	pInstance;
    HSZ			hsz1, hsz2;
279 280 281 282

    switch (uMsg)
    {
    case WM_WDML_REGISTER:
283
	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
284
        /* try calling the Callback */
285
	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
286
	{
287 288 289 290 291
	    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);
292 293 294 295
	}
	break;

    case WM_WDML_UNREGISTER:
296 297 298 299 300 301 302 303 304 305 306 307 308 309
	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
	{
	    hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
	    hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
	    WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
	    WDML_DecHSZ(pInstance, hsz1);
	    WDML_DecHSZ(pInstance, hsz2);
	}
	break;

    case WM_WDML_CONNECT_CONFIRM:
	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
310
	{
311 312 313 314
	    WDML_CONV*	pConv;
	    /* confirm connection...
	     * lookup for this conv handle
	     */
315 316
            HWND client = WIN_GetFullHandle( (HWND)wParam );
            HWND server = WIN_GetFullHandle( (HWND)lParam );
317
	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
318
	    {
319
		if (pConv->hwndClient == client && pConv->hwndServer == server)
320
		    break;
321
	    }
322
	    if (pConv)
323
	    {
324 325
		pConv->wStatus |= ST_ISLOCAL;

326 327
		WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
				    pConv->hszTopic, pConv->hszService, 0, 0,
328
				    (pConv->wStatus & ST_ISSELF) ? 1 : 0);
329 330
	    }
	}
331 332
	break;
    default:
333
	return DefWindowProcW(hwndEvent, uMsg, wParam, lParam);
334
    }
335
    return 0;
336 337
}

338 339
/******************************************************************
 *		WDML_Initialize
340 341 342
 *
 *
 */
343
UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
344
		     DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16)
345
{
346
    WDML_INSTANCE*		pInstance;
347 348
    WDML_INSTANCE*		reference_inst;
    UINT			ret;
349
    WNDCLASSEXW			wndclass;
350

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

354 355 356 357 358 359
    if (ulRes)
    {
	ERR("Reserved value not zero?  What does this mean?\n");
	/* trap this and no more until we know more */
	return DMLERR_NO_ERROR;
    }
360

361 362
    /* grab enough heap for one control struct - not really necessary for re-initialise
     *	but allows us to use same validation routines */
363
    pInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
364
    if (pInstance == NULL)
365 366 367 368 369
    {
	/* catastrophe !! warn user & abort */
	ERR("Instance create failed - out of memory\n");
	return DMLERR_SYS_ERROR;
    }
370 371
    pInstance->next = NULL;
    pInstance->monitor = (afCmd | APPCLASS_MONITOR);
372

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

375 376 377 378
    pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
    pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
    pInstance->threadID = GetCurrentThreadId();
    pInstance->callback = *pfnCallback;
379
    pInstance->unicode = bUnicode;
380 381 382 383 384 385 386 387
    pInstance->win16 = b16;
    pInstance->nodeList = NULL; /* node will be added later */
    pInstance->monitorFlags = afCmd & MF_MASK;
    pInstance->servers = NULL;
    pInstance->convs[0] = NULL;
    pInstance->convs[1] = NULL;
    pInstance->links[0] = NULL;
    pInstance->links[1] = NULL;
388 389

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

391
    pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
392

393
    if (!pInstance->clientOnly)
394 395
    {
	/* Check for other way of setting Client-only !! */
396
	pInstance->clientOnly =
397
	    (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
398
    }
399

400
    TRACE("instance created - checking validity\n");
401 402

    if (*pidInst == 0)
403 404
    {
	/*  Initialisation of new Instance Identifier */
405
	TRACE("new instance, callback %p flags %X\n",pfnCallback,afCmd);
406 407

	EnterCriticalSection(&WDML_CritSect);
408 409

	if (WDML_InstanceList == NULL)
410 411
	{
	    /* can't be another instance in this case, assign to the base pointer */
412
	    WDML_InstanceList = pInstance;
413

414
	    /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
415
	     *		present
416
	     *	-------------------------------      NOTE NOTE NOTE    --------------------------
417
	     *
418
	     *	the manual is not clear if this condition
419
	     *	applies to the first call to DdeInitialize from an application, or the
420 421
	     *	first call for a given callback !!!
	     */
422

423
	    pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
424 425
	    TRACE("First application instance detected OK\n");
	    /*	allocate new instance ID */
426
	    WDML_IncrementInstanceId(pInstance);
427 428
	}
	else
429 430 431 432 433
	{
	    /* 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");
434
	    while (reference_inst->next != NULL)
435 436 437 438
	    {
		/*
		 *	This set of tests will work if application uses same instance Id
		 *	at application level once allocated - which is what manual implies
439
		 *	should happen. If someone tries to be
440 441 442
		 *	clever (lazy ?) it will fail to pick up that later calls are for
		 *	the same application - should we trust them ?
		 */
443
		if (pInstance->instanceID == reference_inst->instanceID)
444
		{
445
		    /* Check 1 - must be same Client-only state */
446

447
		    if (pInstance->clientOnly != reference_inst->clientOnly)
448 449 450 451
		    {
			ret = DMLERR_DLL_USAGE;
			goto theError;
		    }
452

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

		    if (pInstance->monitor != reference_inst->monitor)
456 457 458 459
		    {
			ret = DMLERR_INVALIDPARAMETER;
			goto theError;
		    }
460

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

463
		    if (pInstance->callback == reference_inst->callback)
464 465 466 467 468 469 470 471
		    {
			ret = DMLERR_DLL_USAGE;
			goto theError;
		    }
		}
		reference_inst = reference_inst->next;
	    }
	    /*  All cleared, add to chain */
472

473
	    TRACE("Application Instance checks finished\n");
474 475
	    WDML_IncrementInstanceId(pInstance);
	    reference_inst->next = pInstance;
476
	}
477 478 479 480 481 482 483 484 485
	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;
486
	wndclass.cbWndExtra    = sizeof(ULONG_PTR);
487 488 489 490 491 492 493
	wndclass.hInstance     = 0;
	wndclass.hIcon         = 0;
	wndclass.hCursor       = 0;
	wndclass.hbrBackground = 0;
	wndclass.lpszMenuName  = NULL;
	wndclass.lpszClassName = WDML_szEventClass;
	wndclass.hIconSm       = 0;
494

495
	RegisterClassExW(&wndclass);
496

497
	pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL,
498 499
						WS_POPUP, 0, 0, 0, 0,
						0, 0, 0, 0);
500

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

503
	TRACE("New application instance processing finished OK\n");
504 505
    }
    else
506 507
    {
	/* Reinitialisation situation   --- FIX  */
508
	TRACE("reinitialisation of (%p,%p,0x%x,%d): stub\n", pidInst, pfnCallback, afCmd, ulRes);
509

510
	EnterCriticalSection(&WDML_CritSect);
511 512

	if (WDML_InstanceList == NULL)
513
	{
514
	    ret = DMLERR_INVALIDPARAMETER;
515 516
	    goto theError;
	}
517
	HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
518 519 520 521 522 523 524 525 526 527 528 529
	/* 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 */
530

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

537 538
			if (!(afCmd & APPCMD_CLIENTONLY))
			{
539
			    ret = DMLERR_INVALIDPARAMETER;
540 541 542 543 544
			    goto theError;
			}
		    }
		}
		/* Check 2 - cannot change monitor modes */
545 546

		if (pInstance->monitor != reference_inst->monitor)
547
		{
548
		    ret = DMLERR_INVALIDPARAMETER;
549 550
		    goto theError;
		}
551

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

554 555
		if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
		{
556
		    ret = DMLERR_INVALIDPARAMETER;
557 558 559 560 561 562 563 564 565 566 567
		    goto theError;
		}
		break;
	    }
	    reference_inst = reference_inst->next;
	}
	if (reference_inst->next == NULL)
	{
	    ret = DMLERR_INVALIDPARAMETER;
	    goto theError;
	}
568
	/* All checked - change relevant flags */
569

570 571 572 573
	reference_inst->CBFflags = pInstance->CBFflags;
	reference_inst->clientOnly = pInstance->clientOnly;
	reference_inst->monitorFlags = pInstance->monitorFlags;
	LeaveCriticalSection(&WDML_CritSect);
574
    }
575

576 577
    return DMLERR_NO_ERROR;
 theError:
578 579
    HeapFree(GetProcessHeap(), 0, pInstance);
    LeaveCriticalSection(&WDML_CritSect);
580 581 582
    return ret;
}

583 584
/******************************************************************************
 *            DdeInitializeA   (USER32.@)
585 586
 *
 * See DdeInitializeW.
587 588 589 590
 */
UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
			   DWORD afCmd, DWORD ulRes)
{
591
    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, FALSE);
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
}

/******************************************************************************
 * DdeInitializeW [USER32.@]
 * Registers an application with the DDEML
 *
 * PARAMS
 *    pidInst     [I] Pointer to instance identifier
 *    pfnCallback [I] Pointer to callback function
 *    afCmd       [I] Set of command and filter flags
 *    ulRes       [I] Reserved
 *
 * RETURNS
 *    Success: DMLERR_NO_ERROR
 *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
 */
UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
			   DWORD afCmd, DWORD ulRes)
{
611
    return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
612 613
}

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
/*****************************************************************
 * DdeUninitialize [USER32.@]  Frees DDEML resources
 *
 * PARAMS
 *    idInst [I] Instance identifier
 *
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */

BOOL WINAPI DdeUninitialize(DWORD idInst)
{
    /*  Stage one - check if we have a handle for this instance
     */
629 630 631 632
    WDML_INSTANCE*		pInstance;
    WDML_CONV*			pConv;
    WDML_CONV*			pConvNext;

633
    TRACE("(%d)\n", idInst);
634

635
    EnterCriticalSection(&WDML_CritSect);
636

637
    /*  First check instance
638
     */
639 640
    pInstance = WDML_GetInstance(idInst);
    if (pInstance == NULL)
641
    {
642
	LeaveCriticalSection(&WDML_CritSect);
643 644 645 646 647
	/*
	 *	Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
	 */
	return FALSE;
    }
648 649 650

    /* first terminate all conversations client side
     * this shall close existing links...
651
     */
652 653 654 655 656 657 658 659 660 661
    for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
    {
	pConvNext = pConv->next;
	DdeDisconnect((HCONV)pConv);
    }
    if (pInstance->convs[WDML_CLIENT_SIDE])
	FIXME("still pending conversations\n");

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

663 664 665
    /* Free the nodes that were not freed by this instance
     * and remove the nodes from the list of HSZ nodes.
     */
666
    WDML_FreeAllHSZ(pInstance);
667

668
    DestroyWindow(pInstance->hwndEvent);
669

670
    /* OK now delete the instance handle itself */
671

672
    if (WDML_InstanceList == pInstance)
673
    {
674
	/* special case - the first/only entry */
675
	WDML_InstanceList = pInstance->next;
676 677 678
    }
    else
    {
679 680
	/* general case, remove entry */
	WDML_INSTANCE*	inst;
681

682 683
	for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
	inst->next = pInstance->next;
684
    }
685
    /* leave crit sect and release the heap entry
686
     */
687 688
    HeapFree(GetProcessHeap(), 0, pInstance);
    LeaveCriticalSection(&WDML_CritSect);
689 690 691
    return TRUE;
}

692 693 694
/******************************************************************
 *		WDML_NotifyThreadExit
 *
695 696
 *
 */
697
void WDML_NotifyThreadDetach(void)
698
{
699 700 701 702 703 704
    WDML_INSTANCE*	pInstance;
    WDML_INSTANCE*	next;
    DWORD		tid = GetCurrentThreadId();

    EnterCriticalSection(&WDML_CritSect);
    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
705
    {
706 707
	next = pInstance->next;
	if (pInstance->threadID == tid)
708
	{
709
	    DdeUninitialize(pInstance->instanceID);
710 711
	}
    }
712
    LeaveCriticalSection(&WDML_CritSect);
713 714
}

715 716
/******************************************************************
 *		WDML_InvokeCallback
717 718 719
 *
 *
 */
720
HDDEDATA 	WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
721
				    HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
722
				    ULONG_PTR dwData1, ULONG_PTR dwData2)
723
{
724 725 726
    HDDEDATA	ret;

    if (pInstance == NULL)
727
	return NULL;
728
    TRACE("invoking CB%d[%p] (%x %x %p %p %p %p %lx %lx)\n",
729 730
	  pInstance->win16 ? 16 : 32, pInstance->callback, uType, uFmt,
	  hConv, hsz1, hsz2, hdata, dwData1, dwData2);
731
    if (pInstance->win16)
732
    {
733
	ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv,
734
				    hsz1, hsz2, hdata, dwData1, dwData2);
735
    }
736
    else
737
    {
738
	ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
739
    }
740
    TRACE("done => %p\n", ret);
741
    return ret;
742 743 744
}

/*****************************************************************************
745
 *	WDML_GetInstance
746 747 748 749 750
 *
 *	generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
 *	for an instance Id, or NULL if the entry does not exist
 *
 */
751
WDML_INSTANCE*	WDML_GetInstance(DWORD instId)
752
{
753
    WDML_INSTANCE*	pInstance;
754

755
    for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
756
    {
757
	if (pInstance->instanceID == instId)
758
	{
759 760 761 762 763 764
	    if (GetCurrentThreadId() != pInstance->threadID)
	    {
		FIXME("Tried to get instance from wrong thread\n");
		continue;
	    }
	    return pInstance;
765 766 767 768 769 770
	}
    }
    TRACE("Instance entry missing\n");
    return NULL;
}

771 772 773
/******************************************************************
 *		WDML_GetInstanceFromWnd
 *
774 775
 *
 */
776
WDML_INSTANCE*	WDML_GetInstanceFromWnd(HWND hWnd)
777
{
778
    return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE);
779 780 781
}

/******************************************************************************
782 783 784 785
 * DdeGetLastError [USER32.@]  Gets most recent error code
 *
 * PARAMS
 *    idInst [I] Instance identifier
786
 *
787 788
 * RETURNS
 *    Last error code
789
 */
790
UINT WINAPI DdeGetLastError(DWORD idInst)
791
{
792 793
    DWORD		error_code;
    WDML_INSTANCE*	pInstance;
794

795
    EnterCriticalSection(&WDML_CritSect);
796

797 798 799
    /*  First check instance
     */
    pInstance = WDML_GetInstance(idInst);
800
    if  (pInstance == NULL)
801
    {
802
	error_code = DMLERR_INVALIDPARAMETER;
803
    }
804
    else
805
    {
806 807
	error_code = pInstance->lastError;
	pInstance->lastError = 0;
808
    }
809

810 811
    LeaveCriticalSection(&WDML_CritSect);
    return error_code;
812 813
}

814
/* ================================================================
815
 *
816 817 818
 * 			String management
 *
 * ================================================================ */
819

820 821 822 823 824 825 826

/******************************************************************
 *		WDML_FindNode
 *
 *
 */
static HSZNode*	WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
827
{
828 829 830 831 832
    HSZNode*	pNode;

    if (pInstance == NULL) return NULL;

    for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
833
    {
834
	if (pNode->hsz == hsz) break;
835
    }
836
    if (!pNode) WARN("HSZ %p not found\n", hsz);
837
    return pNode;
838 839
}

840 841
/******************************************************************
 *		WDML_MakeAtomFromHsz
842
 *
843 844
 * Creates a global atom from an existing HSZ
 * Generally used before sending an HSZ as an atom to a remote app
845
 */
846
ATOM	WDML_MakeAtomFromHsz(HSZ hsz)
847
{
848
    WCHAR nameBuffer[MAX_BUFFER_LEN];
849

850
    if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
851
	return GlobalAddAtomW(nameBuffer);
852
    WARN("HSZ %p not found\n", hsz);
853 854
    return 0;
}
855

856 857 858 859 860 861
/******************************************************************
 *		WDML_MakeHszFromAtom
 *
 * Creates a HSZ from an existing global atom
 * Generally used while receiving a global atom and transforming it
 * into an HSZ
862
 */
863
HSZ	WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom)
864
{
865 866
    WCHAR nameBuffer[MAX_BUFFER_LEN];

867
    if (!atom) return NULL;
868 869 870 871 872 873 874 875

    if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
    {
	TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
	return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
    }
    WARN("ATOM 0x%x not found\n", atom);
    return 0;
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
}

/******************************************************************
 *		WDML_IncHSZ
 *
 *
 */
BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
{
    HSZNode*	pNode;

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

    pNode->refCount++;
    return TRUE;
}

/******************************************************************************
 *           WDML_DecHSZ    (INTERNAL)
 *
 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
 * of HSZ nodes
 * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
 */
BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
{
    HSZNode* 	pPrev = NULL;
    HSZNode* 	pCurrent;

    for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
907
    {
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
	/* If we found the node we were looking for and its ref count is one,
	 * we can remove it
	 */
	if (pCurrent->hsz == hsz)
	{
	    if (--pCurrent->refCount == 0)
	    {
		if (pCurrent == pInstance->nodeList)
		{
		    pInstance->nodeList = pCurrent->next;
		}
		else
		{
		    pPrev->next = pCurrent->next;
		}
		HeapFree(GetProcessHeap(), 0, pCurrent);
924
		DeleteAtom(HSZ2ATOM(hsz));
925 926 927
	    }
	    return TRUE;
	}
928
    }
929
    WARN("HSZ %p not found\n", hsz);
930

931 932 933 934 935 936 937 938 939 940 941 942
    return FALSE;
}

/******************************************************************************
 *            WDML_FreeAllHSZ    (INTERNAL)
 *
 * Frees up all the strings still allocated in the list and
 * remove all the nodes from the list of HSZ nodes.
 */
void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
{
    /* Free any strings created in this instance.
943
     */
944
    while (pInstance->nodeList != NULL)
945
    {
946
	DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
947
    }
948 949 950 951 952 953 954 955 956 957
}

/******************************************************************************
 *            InsertHSZNode    (INTERNAL)
 *
 * Insert a node to the head of the list.
 */
static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
{
    if (hsz != 0)
958
    {
959 960
	HSZNode* pNew = NULL;
	/* Create a new node for this HSZ.
961
	 */
962
	pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
963
	if (pNew != NULL)
964
	{
965 966 967 968 969 970 971 972
	    pNew->hsz      = hsz;
	    pNew->next     = pInstance->nodeList;
	    pNew->refCount = 1;
	    pInstance->nodeList = pNew;
	}
	else
	{
	    ERR("Primary HSZ Node allocation failed - out of memory\n");
973 974
	}
    }
975 976 977 978 979 980 981
}

/******************************************************************
 *		WDML_QueryString
 *
 *
 */
982
static int	WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
				 int codepage)
{
    WCHAR	pString[MAX_BUFFER_LEN];
    int		ret;
    /* If psz is null, we have to return only the length
     * of the string.
     */
    if (ptr == NULL)
    {
	ptr = pString;
	cchMax = MAX_BUFFER_LEN;
    }

    switch (codepage)
    {
    case CP_WINANSI:
999
	ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
1000 1001
	break;
    case CP_WINUNICODE:
1002
	ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
1003
        break;
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
    default:
	ERR("Unknown code page %d\n", codepage);
	ret = 0;
    }
    return ret;
}

/*****************************************************************
 * DdeQueryStringA [USER32.@]
 */
DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
{
    DWORD		ret = 0;
    WDML_INSTANCE*	pInstance;
1018

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

1021
    EnterCriticalSection(&WDML_CritSect);
1022 1023

    /*  First check instance
1024 1025 1026 1027 1028 1029 1030 1031
     */
    pInstance = WDML_GetInstance(idInst);
    if (pInstance != NULL)
    {
	if (iCodePage == 0) iCodePage = CP_WINANSI;
	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
    }
    LeaveCriticalSection(&WDML_CritSect);
1032

1033
    TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
1034 1035 1036 1037 1038 1039 1040 1041 1042
    return ret;
}

/*****************************************************************
 * DdeQueryStringW [USER32.@]
 */

DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
{
1043 1044 1045
    DWORD		ret = 0;
    WDML_INSTANCE*	pInstance;

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

1048
    EnterCriticalSection(&WDML_CritSect);
1049 1050

    /*  First check instance
1051 1052 1053
     */
    pInstance = WDML_GetInstance(idInst);
    if (pInstance != NULL)
1054
    {
1055 1056
	if (iCodePage == 0) iCodePage = CP_WINUNICODE;
	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1057
    }
1058 1059
    LeaveCriticalSection(&WDML_CritSect);

1060
    TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
1061 1062 1063
    return ret;
}

1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
/******************************************************************
 *		DML_CreateString
 *
 *
 */
static	HSZ	WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
{
    HSZ		hsz;

    switch (codepage)
    {
    case CP_WINANSI:
1076
	hsz = ATOM2HSZ(AddAtomA(ptr));
1077
	TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz);
1078 1079
	break;
    case CP_WINUNICODE:
1080
	hsz = ATOM2HSZ(AddAtomW(ptr));
1081
	TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz);
1082 1083 1084 1085 1086 1087 1088 1089 1090
	break;
    default:
	ERR("Unknown code page %d\n", codepage);
	return 0;
    }
    WDML_InsertHSZNode(pInstance, hsz);
    return hsz;
}

1091 1092 1093
/*****************************************************************
 * DdeCreateStringHandleA [USER32.@]
 *
1094
 * See DdeCreateStringHandleW.
1095 1096 1097 1098
 */
HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
{
    HSZ			hsz = 0;
1099
    WDML_INSTANCE*	pInstance;
1100

1101
    TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage);
1102

1103
    EnterCriticalSection(&WDML_CritSect);
1104

1105 1106
    pInstance = WDML_GetInstance(idInst);
    if (pInstance)
1107
    {
1108 1109
	if (codepage == 0) codepage = CP_WINANSI;
	hsz = WDML_CreateString(pInstance, psz, codepage);
1110
    }
1111 1112

    LeaveCriticalSection(&WDML_CritSect);
1113
    return hsz;
1114 1115 1116 1117 1118 1119
}


/******************************************************************************
 * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
 *
1120 1121 1122 1123
 * PARAMS
 * 	idInst   [I] Instance identifier
 * 	psz      [I] Pointer to string
 *	codepage [I] Code page identifier
1124 1125 1126 1127
 * RETURNS
 *    Success: String handle
 *    Failure: 0
 */
1128
HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
1129
{
1130
    WDML_INSTANCE*	pInstance;
1131
    HSZ			hsz = 0;
1132

1133
    TRACE("(%d,%s,%d)\n", idInst, debugstr_w(psz), codepage);
1134

1135
    EnterCriticalSection(&WDML_CritSect);
1136

1137 1138
    pInstance = WDML_GetInstance(idInst);
    if (pInstance)
1139 1140 1141 1142
    {
	if (codepage == 0) codepage = CP_WINUNICODE;
	hsz = WDML_CreateString(pInstance, psz, codepage);
    }
1143 1144 1145
    LeaveCriticalSection(&WDML_CritSect);

    return hsz;
1146 1147 1148 1149
}

/*****************************************************************
 *            DdeFreeStringHandle   (USER32.@)
1150 1151 1152
 * RETURNS
 *  success: nonzero
 *  fail:    zero
1153 1154 1155
 */
BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
{
1156 1157 1158
    WDML_INSTANCE*	pInstance;
    BOOL		ret = FALSE;

1159
    TRACE("(%d,%p):\n", idInst, hsz);
1160

1161
    EnterCriticalSection(&WDML_CritSect);
1162 1163

    /*  First check instance
1164
     */
1165 1166 1167 1168 1169 1170 1171
    pInstance = WDML_GetInstance(idInst);
    if (pInstance)
	ret = WDML_DecHSZ(pInstance, hsz);

    LeaveCriticalSection(&WDML_CritSect);

    return ret;
1172 1173 1174 1175 1176
}

/*****************************************************************
 *            DdeKeepStringHandle  (USER32.@)
 *
1177 1178 1179
 * RETURNS
 *  success: nonzero
 *  fail:    zero
1180 1181 1182
 */
BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
{
1183 1184 1185
    WDML_INSTANCE*	pInstance;
    BOOL		ret = FALSE;

1186
    TRACE("(%d,%p):\n", idInst, hsz);
1187

1188
    EnterCriticalSection(&WDML_CritSect);
1189

1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
    /*  First check instance
     */
    pInstance = WDML_GetInstance(idInst);
    if (pInstance)
	ret = WDML_IncHSZ(pInstance, hsz);

    LeaveCriticalSection(&WDML_CritSect);
    return ret;
}

/*****************************************************************
 *            DdeCmpStringHandles (USER32.@)
 *
 * Compares the value of two string handles.  This comparison is
 * not case sensitive.
 *
1206 1207 1208 1209 1210 1211 1212 1213
 * PARAMS
 *  hsz1    [I] Handle to the first string
 *  hsz2    [I] Handle to the second string
 *
 * 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
1214 1215 1216 1217 1218 1219 1220
 */
INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
{
    WCHAR	psz1[MAX_BUFFER_LEN];
    WCHAR	psz2[MAX_BUFFER_LEN];
    int		ret = 0;
    int		ret1, ret2;
1221

1222 1223
    ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
    ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
1224

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

1227 1228
    /* Make sure we found both strings. */
    if (ret1 == 0 && ret2 == 0)
1229
    {
1230 1231
	/* If both are not found, return both  "zero strings". */
	ret = 0;
1232
    }
1233
    else if (ret1 == 0)
1234
    {
1235 1236
	/* If hsz1 is a not found, return hsz1 is "zero string". */
	ret = -1;
1237
    }
1238
    else if (ret2 == 0)
1239
    {
1240 1241
	/* If hsz2 is a not found, return hsz2 is "zero string". */
	ret = 1;
1242
    }
1243 1244 1245 1246
    else
    {
	/* Compare the two strings we got (case insensitive). */
	ret = lstrcmpiW(psz1, psz2);
1247
	/* Since strcmp returns any number smaller than
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
	 * 0 when the first string is found to be less than
	 * the second one we must make sure we are returning
	 * the proper values.
	 */
	if (ret < 0)
	{
	    ret = -1;
	}
	else if (ret > 0)
	{
	    ret = 1;
	}
    }
1261

1262
    return ret;
1263 1264
}

1265 1266 1267 1268 1269 1270
/* ================================================================
 *
 * 			Data handle management
 *
 * ================================================================ */

1271 1272 1273
/*****************************************************************
 *            DdeCreateDataHandle (USER32.@)
 */
1274
HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff,
1275
                                    HSZ hszItem, UINT wFmt, UINT afCmd)
1276
{
1277 1278 1279
    /* For now, we ignore idInst, hszItem.
     * The purpose of these arguments still need to be investigated.
     */
1280

1281 1282 1283
    HGLOBAL     		hMem;
    LPBYTE      		pByte;
    DDE_DATAHANDLE_HEAD*	pDdh;
1284 1285
    WCHAR psz[MAX_BUFFER_LEN];

1286 1287 1288 1289 1290
    if (!GetAtomNameW(HSZ2ATOM(hszItem), psz, MAX_BUFFER_LEN))
    {
        psz[0] = HSZ2ATOM(hszItem);
        psz[1] = 0;
    }
1291

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

1295 1296 1297
    if (afCmd != 0 && afCmd != HDATA_APPOWNED)
        return 0;

1298
    /* we use the first 4 bytes to store the size */
1299
    if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff + sizeof(DDE_DATAHANDLE_HEAD))))
1300 1301 1302
    {
	ERR("GlobalAlloc failed\n");
	return 0;
1303 1304
    }

1305
    pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1306 1307 1308 1309 1310 1311
    if (!pDdh)
    {
        GlobalFree(hMem);
        return 0;
    }

1312
    pDdh->cfFormat = wFmt;
1313 1314
    pDdh->bAppOwned = (afCmd == HDATA_APPOWNED);

1315 1316 1317 1318 1319 1320
    pByte = (LPBYTE)(pDdh + 1);
    if (pSrc)
    {
	memcpy(pByte, pSrc + cbOff, cb);
    }
    GlobalUnlock(hMem);
1321

1322
    TRACE("=> %p\n", hMem);
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
    return (HDDEDATA)hMem;
}

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

1335
    TRACE("(%p,%p,cb %d, cbOff %d)\n", hData, pSrc, cb, cbOff);
1336

1337 1338
    pDst = DdeAccessData(hData, &old_sz);
    if (!pDst) return 0;
1339

1340 1341 1342 1343
    new_sz = cb + cbOff;
    if (new_sz > old_sz)
    {
	DdeUnaccessData(hData);
1344
	hData = GlobalReAlloc((HGLOBAL)hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1345 1346 1347
			      GMEM_MOVEABLE | GMEM_DDESHARE);
	pDst = DdeAccessData(hData, &old_sz);
    }
1348

1349
    if (!pDst) return 0;
1350

1351 1352 1353 1354 1355 1356 1357 1358
    memcpy(pDst + cbOff, pSrc, cb);
    DdeUnaccessData(hData);
    return hData;
}

/******************************************************************************
 * DdeGetData [USER32.@]  Copies data from DDE object to local buffer
 *
1359 1360 1361 1362 1363 1364 1365
 *
 * 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
 *
1366 1367 1368
 * RETURNS
 *    Size of memory object associated with handle
 */
1369
DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1370 1371 1372
{
    DWORD   dwSize, dwRet;
    LPBYTE  pByte;
1373

1374
    TRACE("(%p,%p,%d,%d)\n", hData, pDst, cbMax, cbOff);
1375

1376
    pByte = DdeAccessData(hData, &dwSize);
1377 1378

    if (pByte)
1379
    {
1380 1381 1382 1383 1384
        if (!pDst)
        {
            dwRet = dwSize;
        }
        else if (cbOff + cbMax < dwSize)
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
	{
	    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)
{
    HGLOBAL			hMem = (HGLOBAL)hData;
    DDE_DATAHANDLE_HEAD*	pDdh;
1416

1417
    TRACE("(%p,%p)\n", hData, pcbDataSize);
1418

1419 1420 1421
    pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
    if (pDdh == NULL)
    {
1422
	ERR("Failed on GlobalLock(%p)\n", hMem);
1423 1424
	return 0;
    }
1425

1426 1427 1428 1429
    if (pcbDataSize != NULL)
    {
	*pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
    }
1430
    TRACE("=> %p (%lu) fmt %04x\n", pDdh + 1, GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD), pDdh->cfFormat);
1431 1432 1433 1434 1435 1436 1437 1438 1439
    return (LPBYTE)(pDdh + 1);
}

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

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

1443
    GlobalUnlock(hMem);
1444

1445 1446 1447 1448 1449 1450 1451
    return TRUE;
}

/*****************************************************************
 *            DdeFreeDataHandle   (USER32.@)
 */
BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1452
{
1453
    TRACE("(%p)\n", hData);
1454 1455 1456
    return GlobalFree((HGLOBAL)hData) == 0;
}

1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
/******************************************************************
 *		WDML_IsAppOwned
 *
 *
 */
BOOL WDML_IsAppOwned(HDDEDATA hData)
{
    DDE_DATAHANDLE_HEAD*	pDdh;
    BOOL                        ret = FALSE;

    pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock((HGLOBAL)hData);
    if (pDdh != NULL)
    {
        ret = pDdh->bAppOwned;
        GlobalUnlock((HGLOBAL)hData);
    }
    return ret;
}

1476 1477 1478 1479 1480
/* ================================================================
 *
 *                  Global <=> Data handle management
 *
 * ================================================================ */
1481

1482
/* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1483
 *    offset	  size
1484 1485 1486 1487
 *    (bytes)	 (bits)	comment
 *	0	   16	bit fields for options (release, ackreq, response...)
 *	2	   16	clipboard format
 *	4	   ?	data to be used
1488 1489 1490 1491 1492
 */
HDDEDATA        WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
{
    DDEDATA*    pDd;
    HDDEDATA	ret = 0;
1493
    DWORD       size;
1494 1495 1496 1497

    if (hMem)
    {
        pDd = GlobalLock(hMem);
1498
        size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
1499 1500 1501
        if (pDd)
        {
	    if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1502 1503 1504
            switch (pDd->cfFormat)
            {
            default:
1505 1506
                FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
                      pDd->cfFormat, hMem);
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520
                /* fall thru */
            case 0:
            case CF_TEXT:
                ret = DdeCreateDataHandle(0, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
                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;

1521
                        if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
1522 1523 1524
                                                 bmp->bmPlanes, bmp->bmBitsPixel,
                                                 pDd->Value + sizeof(BITMAP))))
                        {
1525
                            ret = DdeCreateDataHandle(0, (LPBYTE)&hbmp, sizeof(hbmp),
1526 1527 1528 1529 1530 1531
                                                      0, 0, CF_BITMAP, 0);
                        }
                        else ERR("Can't create bmp\n");
                    }
                    else
                    {
1532
                        ERR("Wrong count: %u / %d\n", size, count);
1533 1534 1535 1536 1537
                    }
                } else ERR("No bitmap header\n");
                break;
            }
            GlobalUnlock(hMem);
1538
        }
1539 1540 1541
    }
    return ret;
}
1542

1543
/******************************************************************
1544 1545
 *		WDML_DataHandle2Global
 *
1546 1547
 *
 */
1548
HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1549
			       BOOL fDeferUpd, BOOL fAckReq)
1550
{
1551 1552 1553
    DDE_DATAHANDLE_HEAD*	pDdh;
    DWORD                       dwSize;
    HGLOBAL                     hMem = 0;
1554

1555 1556
    dwSize = GlobalSize((HGLOBAL)hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
    pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock((HGLOBAL)hDdeData);
1557
    if (dwSize && pDdh)
1558
    {
1559
        WINE_DDEHEAD*    wdh = NULL;
1560

1561 1562 1563
        switch (pDdh->cfFormat)
        {
        default:
1564 1565
            FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
                   pDdh->cfFormat, hDdeData);
1566 1567 1568 1569
            /* fall thru */
        case 0:
        case CF_TEXT:
            hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
1570
            if (hMem && (wdh = GlobalLock(hMem)))
1571 1572 1573
            {
                memcpy(wdh + 1, pDdh + 1, dwSize);
            }
1574 1575 1576 1577 1578 1579 1580 1581
            break;
        case CF_BITMAP:
            if (dwSize >= sizeof(HBITMAP))
            {
                BITMAP  bmp;
                DWORD   count;
                HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);

1582
                if (GetObjectW(hbmp, sizeof(bmp), &bmp))
1583 1584
                {
                    count = bmp.bmWidthBytes * bmp.bmHeight;
1585
                    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1586
                                       sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
1587
                    if (hMem && (wdh = GlobalLock(hMem)))
1588 1589 1590 1591 1592 1593 1594
                    {
                        memcpy(wdh + 1, &bmp, sizeof(bmp));
                        GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
                    }
                }
            }
            break;
1595
        }
1596 1597
        if (wdh)
        {
1598
            wdh->unused = 0;
1599 1600 1601 1602 1603 1604 1605 1606
            wdh->fResponse = fResponse;
            wdh->fRelease = fRelease;
            wdh->fDeferUpd = fDeferUpd;
            wdh->fAckReq = fAckReq;
            wdh->cfFormat = pDdh->cfFormat;
            GlobalUnlock(hMem);
        }
        GlobalUnlock((HGLOBAL)hDdeData);
1607
    }
1608

1609
    return hMem;
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
}

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

/******************************************************************
 *		WDML_AddServer
 *
 *
 */
1623
WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1624
{
1625
    static const WCHAR fmtW[] = {'%','s','(','0','x','%','0','8','l','x',')',0};
1626
    WDML_SERVER* 	pServer;
1627 1628
    WCHAR		buf1[256];
    WCHAR		buf2[256];
1629

1630
    pServer = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1631
    if (pServer == NULL) return NULL;
1632

1633 1634
    pServer->hszService = hszService;
    WDML_IncHSZ(pInstance, hszService);
1635

1636 1637 1638
    DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE);
    snprintfW(buf2, 256, fmtW, buf1, GetCurrentProcessId());
    pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE);
1639 1640 1641 1642

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

1643
    pServer->filterOn = TRUE;
1644

1645 1646
    pServer->next = pInstance->servers;
    pInstance->servers = pServer;
1647 1648 1649 1650 1651 1652 1653 1654
    return pServer;
}

/******************************************************************
 *		WDML_RemoveServer
 *
 *
 */
1655
void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1656
{
1657 1658 1659 1660 1661 1662
    WDML_SERVER*	pPrev = NULL;
    WDML_SERVER*	pServer = NULL;
    WDML_CONV*		pConv;
    WDML_CONV*		pConvNext;

    pServer = pInstance->servers;
1663

1664
    while (pServer != NULL)
1665
    {
1666
	if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1667
	{
1668
	    WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1669 1670 1671 1672 1673 1674 1675 1676 1677
				     pServer->atomService, pServer->atomServiceSpec);
	    /* terminate all conversations for given topic */
	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
	    {
		pConvNext = pConv->next;
		if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
		{
		    WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
		    /* don't care about return code (whether client window is present or not) */
1678
		    PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
1679 1680 1681
		}
	    }
	    if (pServer == pInstance->servers)
1682
	    {
1683
		pInstance->servers = pServer->next;
1684 1685 1686
	    }
	    else
	    {
1687
		pPrev->next = pServer->next;
1688
	    }
1689

1690 1691 1692 1693 1694 1695 1696 1697
	    DestroyWindow(pServer->hwndServer);
	    WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
	    WDML_DecHSZ(pInstance, pServer->hszService);

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

	    HeapFree(GetProcessHeap(), 0, pServer);
1698 1699
	    break;
	}
1700

1701 1702
	pPrev = pServer;
	pServer = pServer->next;
1703 1704 1705 1706 1707 1708 1709 1710 1711 1712
    }
}

/*****************************************************************************
 *	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
 *
 */
1713
WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1714 1715
{
    WDML_SERVER*	pServer;
1716

1717
    for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
    {
	if (hszService == pServer->hszService)
	{
	    return pServer;
	}
    }
    TRACE("Service name missing\n");
    return NULL;
}

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

/******************************************************************
 *		WDML_AddConv
 *
 *
 */
1739
WDML_CONV*	WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1740 1741 1742
			     HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
{
    WDML_CONV*	pConv;
1743

1744 1745 1746
    /* no converstation yet, add it */
    pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
    if (!pConv) return NULL;
1747 1748 1749 1750

    pConv->instance = pInstance;
    WDML_IncHSZ(pInstance, pConv->hszService = hszService);
    WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1751
    pConv->magic = WDML_CONV_MAGIC;
1752 1753 1754 1755
    pConv->hwndServer = hwndServer;
    pConv->hwndClient = hwndClient;
    pConv->transactions = NULL;
    pConv->hUser = 0;
1756 1757 1758 1759 1760 1761 1762 1763
    pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
    /* check if both side of the conversation are of the same instance */
    if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
	WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
    {
	pConv->wStatus |= ST_ISSELF;
    }
    pConv->wConvst = XST_NULL;
1764

1765 1766
    pConv->next = pInstance->convs[side];
    pInstance->convs[side] = pConv;
1767

1768 1769 1770 1771 1772 1773 1774 1775
    return pConv;
}

/******************************************************************
 *		WDML_FindConv
 *
 *
 */
1776
WDML_CONV*	WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1777 1778 1779
			      HSZ hszService, HSZ hszTopic)
{
    WDML_CONV*	pCurrent = NULL;
1780

1781
    for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1782 1783 1784 1785 1786 1787
    {
	if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
	    DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
	{
	    return pCurrent;
	}
1788

1789 1790 1791 1792 1793 1794 1795 1796 1797
    }
    return NULL;
}

/******************************************************************
 *		WDML_RemoveConv
 *
 *
 */
1798
void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1799
{
1800 1801 1802 1803 1804
    WDML_CONV*	pPrev = NULL;
    WDML_CONV* 	pCurrent;
    WDML_XACT*	pXAct;
    WDML_XACT*	pXActNext;
    HWND	hWnd;
1805 1806 1807

    if (!pRef)
	return;
1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825

    /* 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;
1826
    SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
1827 1828 1829 1830 1831 1832 1833

    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)
1834 1835 1836
    {
	if (pCurrent == pRef)
	{
1837
	    if (pCurrent == pRef->instance->convs[side])
1838
	    {
1839
		pRef->instance->convs[side] = pCurrent->next;
1840 1841 1842 1843 1844
	    }
	    else
	    {
		pPrev->next = pCurrent->next;
	    }
1845
	    pCurrent->magic = 0;
1846 1847 1848 1849 1850 1851
	    HeapFree(GetProcessHeap(), 0, pCurrent);
	    break;
	}
    }
}

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 1884 1885 1886 1887 1888 1889 1890 1891
/******************************************************************
 *              WDML_EnableCallback
 */
static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
{
    if (wCmd == EC_DISABLE)
    {
        FIXME("EC_DISABLE is not implemented\n");
        return TRUE;
    }

    if (wCmd == EC_QUERYWAITING)
        return pConv->transactions ? TRUE : FALSE;

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

    while (pConv->transactions)
    {
        WDML_XACT *pXAct = pConv->transactions;
        WDML_UnQueueTransaction(pConv, pXAct);

        if (pConv->wStatus & ST_CLIENT)
        {
            /*WDML_ClientHandle(pConv, pXAct);*/
            FIXME("Client delayed transaction queue handling is not supported\n");
        }
        else
            WDML_ServerHandle(pConv, pXAct);

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

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

1892 1893 1894 1895 1896
/*****************************************************************
 *            DdeEnableCallback (USER32.@)
 */
BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
{
1897 1898
    BOOL ret = FALSE;
    WDML_CONV *pConv;
1899

1900
    TRACE("(%d, %p, %04x)\n", idInst, hConv, wCmd);
1901 1902 1903 1904 1905 1906 1907 1908 1909 1910

    EnterCriticalSection(&WDML_CritSect);

    pConv = WDML_GetConv(hConv, TRUE);

    if (pConv && pConv->instance->instanceID == idInst)
        ret = WDML_EnableCallback(pConv, wCmd);

    LeaveCriticalSection(&WDML_CritSect);
    return ret;
1911 1912
}

1913 1914 1915
/******************************************************************
 *		WDML_GetConv
 *
1916
 *
1917
 */
1918
WDML_CONV*	WDML_GetConv(HCONV hConv, BOOL checkConnected)
1919
{
1920 1921
    WDML_CONV*	pConv = (WDML_CONV*)hConv;

1922
    /* FIXME: should do better checking */
1923
    if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL;
1924 1925 1926 1927 1928 1929

    if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
    {
	FIXME("found conv but ain't connected\n");
	return NULL;
    }
1930
    if (!pConv->instance || GetCurrentThreadId() != pConv->instance->threadID)
1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945
    {
	FIXME("wrong thread ID\n");
	return NULL;
    }

    return pConv;
}

/******************************************************************
 *		WDML_GetConvFromWnd
 *
 *
 */
WDML_CONV*	WDML_GetConvFromWnd(HWND hWnd)
{
1946
    return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION);
1947 1948 1949 1950 1951 1952 1953
}

/******************************************************************
 *		WDML_PostAck
 *
 *
 */
1954
BOOL		WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
1955
			     BOOL fBusy, BOOL fAck, UINT pmt, LPARAM lParam, UINT oldMsg)
1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976
{
    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");
1977

1978 1979
    lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) :
        PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt);
1980
    if (!PostMessageW(to, WM_DDE_ACK, (WPARAM)from, lParam))
1981
    {
1982 1983 1984
	pConv->wStatus &= ~ST_CONNECTED;
        FreeDDElParam(WM_DDE_ACK, lParam);
        return FALSE;
1985
    }
1986
    return TRUE;
1987 1988 1989 1990 1991 1992 1993 1994 1995 1996
}

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

1997
    TRACE("(%p,%x,%x)\n", hConv, id, hUser);
1998

1999 2000 2001 2002 2003 2004 2005 2006
    EnterCriticalSection(&WDML_CritSect);

    pConv = WDML_GetConv(hConv, FALSE);
    if (pConv == NULL)
    {
	ret = FALSE;
	goto theError;
    }
2007
    if (id == QID_SYNC)
2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041
    {
	pConv->hUser = hUser;
    }
    else
    {
	WDML_XACT*	pXAct;

	pXAct = WDML_FindTransaction(pConv, id);
	if (pXAct)
	{
	    pXAct->hUser = hUser;
	}
	else
	{
	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
	    ret = FALSE;
	}
    }
 theError:
    LeaveCriticalSection(&WDML_CritSect);
    return ret;
}

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

2042
    ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((ULONG_PTR)pConv | 1) : 0;
2043 2044 2045 2046 2047 2048 2049 2050 2051
    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)
    {
2052
	if (pLink->hConv == (HCONV)pConv)
2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
	{
	    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.@)
 *
2111
 * FIXME: Set last DDE error on failure.
2112
 */
2113
UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
2114 2115 2116 2117 2118
{
    UINT	ret = lpConvInfo->cb;
    CONVINFO	ci;
    WDML_CONV*	pConv;

2119
    TRACE("(%p,%x,%p)\n", hConv, id, lpConvInfo);
2120 2121 2122 2123 2124 2125 2126

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

2127 2128 2129
    EnterCriticalSection(&WDML_CritSect);

    pConv = WDML_GetConv(hConv, FALSE);
2130
    if (pConv != NULL)
2131
    {
2132 2133
        if (!WDML_GetLocalConvInfo(pConv, &ci, id))
            ret = 0;
2134
    }
2135
    else
2136
    {
2137 2138 2139 2140 2141 2142 2143
        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;
2144 2145 2146
    }
    LeaveCriticalSection(&WDML_CritSect);
    if (ret != 0)
2147
	memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci)));
2148
    return ret;
2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161
}

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

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

2167 2168 2169 2170 2171 2172 2173 2174 2175
    pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
    if (pLink == NULL)
    {
	ERR("OOM\n");
	return;
    }

    pLink->hConv = hConv;
    pLink->transactionType = wType;
2176
    WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
2177
    pLink->uFmt = wFmt;
2178 2179
    pLink->next = pInstance->links[side];
    pInstance->links[side] = pLink;
2180 2181 2182 2183 2184 2185 2186
}

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

2193
    pCurrent = pInstance->links[side];
2194

2195 2196 2197 2198 2199 2200
    while (pCurrent != NULL)
    {
	if (pCurrent->hConv == hConv &&
	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
	    pCurrent->uFmt == uFmt)
	{
2201
	    if (pCurrent == pInstance->links[side])
2202
	    {
2203
		pInstance->links[side] = pCurrent->next;
2204 2205 2206 2207 2208
	    }
	    else
	    {
		pPrev->next = pCurrent->next;
	    }
2209

2210
	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
2211 2212 2213
	    HeapFree(GetProcessHeap(), 0, pCurrent);
	    break;
	}
2214

2215 2216 2217 2218 2219 2220
	pPrev = pCurrent;
	pCurrent = pCurrent->next;
    }
}

/* this function is called to remove all links related to the conv.
2221
   It should be called from both client and server when terminating
2222 2223 2224 2225 2226 2227 2228
   the conversation.
*/
/******************************************************************
 *		WDML_RemoveAllLinks
 *
 *
 */
2229
void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2230 2231 2232 2233
{
    WDML_LINK* pPrev = NULL;
    WDML_LINK* pCurrent = NULL;
    WDML_LINK* pNext = NULL;
2234

2235
    pCurrent = pInstance->links[side];
2236

2237 2238
    while (pCurrent != NULL)
    {
2239
	if (pCurrent->hConv == (HCONV)pConv)
2240
	{
2241
	    if (pCurrent == pInstance->links[side])
2242
	    {
2243
		pInstance->links[side] = pCurrent->next;
2244 2245 2246 2247 2248 2249 2250
		pNext = pCurrent->next;
	    }
	    else
	    {
		pPrev->next = pCurrent->next;
		pNext = pCurrent->next;
	    }
2251

2252
	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
2253

2254 2255 2256
	    HeapFree(GetProcessHeap(), 0, pCurrent);
	    pCurrent = NULL;
	}
2257

2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274
	if (pCurrent)
	{
	    pPrev = pCurrent;
	    pCurrent = pCurrent->next;
	}
	else
	{
	    pCurrent = pNext;
	}
    }
}

/******************************************************************
 *		WDML_FindLink
 *
 *
 */
2275
WDML_LINK* 	WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2276
			      HSZ hszItem, BOOL use_fmt, UINT uFmt)
2277 2278
{
    WDML_LINK*	pCurrent = NULL;
2279

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

2284 2285
	if (pCurrent->hConv == hConv &&
	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2286
	    (!use_fmt || pCurrent->uFmt == uFmt))
2287 2288 2289
	{
	    break;
	}
2290

2291
    }
2292

2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306
    return pCurrent;
}

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

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

    pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2314
    if (!pXAct)
2315
    {
2316
	pInstance->lastError = DMLERR_MEMORY_ERROR;
2317 2318 2319 2320 2321 2322 2323 2324
	return NULL;
    }

    pXAct->xActID = tid++;
    pXAct->ddeMsg = ddeMsg;
    pXAct->hDdeData = 0;
    pXAct->hUser = 0;
    pXAct->next = NULL;
2325 2326
    pXAct->wType = 0;
    pXAct->wFmt = wFmt;
2327
    if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
2328 2329 2330 2331
    pXAct->atom = 0;
    pXAct->hMem = 0;
    pXAct->lParam = 0;

2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342
    return pXAct;
}

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

2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373
    /* 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
 *
 *
 */
2374
void	WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2375
{
2376
    /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
2377
    if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
2378
    {
2379
	GlobalFree(pXAct->hMem);
2380 2381
    }
    if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
2382

2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393
    HeapFree(GetProcessHeap(), 0, pXAct);
}

/******************************************************************
 *		WDML_FindTransaction
 *
 *
 */
WDML_XACT*	WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
{
    WDML_XACT* pXAct;
2394

2395 2396 2397 2398 2399 2400 2401 2402 2403
    tid = HIWORD(tid);
    for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
    {
	if (pXAct->xActID == tid)
	    break;
    }
    return pXAct;
}

2404 2405 2406 2407 2408 2409
/* ================================================================
 *
 * 	   Information broadcast across DDEML implementations
 *
 * ================================================================ */

2410 2411
struct tagWDML_BroadcastPmt
{
2412
    LPCWSTR	clsName;
2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
    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;
2426
    WCHAR				buffer[128];
2427

2428 2429
    if (GetClassNameW(hWnd, buffer, 128) > 0 &&
	lstrcmpiW(buffer, s->clsName) == 0)
2430
    {
2431
	PostMessageW(hWnd, s->uMsg, s->wParam, s->lParam);
2432 2433 2434 2435 2436 2437 2438 2439 2440
    }
    return TRUE;
}

/******************************************************************
 *		WDML_BroadcastDDEWindows
 *
 *
 */
2441
void WDML_BroadcastDDEWindows(LPCWSTR clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2442 2443 2444 2445 2446 2447 2448 2449 2450
{
    struct tagWDML_BroadcastPmt	s;

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