dde_client.c 38 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 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
#include <stdarg.h>
27 28
#include <string.h>
#include "windef.h"
29
#include "winbase.h"
30 31
#include "wingdi.h"
#include "winuser.h"
32
#include "winnls.h"
33 34
#include "dde.h"
#include "ddeml.h"
35
#include "win.h"
36
#include "wine/debug.h"
37
#include "dde_private.h"
38

39
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
40 41

static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM);	/* only for one client, not conv list */
42 43
const char WDML_szClientConvClassA[] = "WineDdeClientA";
const WCHAR WDML_szClientConvClassW[] = {'W','i','n','e','D','d','e','C','l','i','e','n','t','W',0};
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

/******************************************************************************
 * DdeConnectList [USER32.@]  Establishes conversation with DDE servers
 *
 * PARAMS
 *    idInst     [I] Instance identifier
 *    hszService [I] Handle to service name string
 *    hszTopic   [I] Handle to topic name string
 *    hConvList  [I] Handle to conversation list
 *    pCC        [I] Pointer to structure with context data
 *
 * RETURNS
 *    Success: Handle to new conversation list
 *    Failure: 0
 */
HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic,
60
				HCONVLIST hConvList, PCONVCONTEXT pCC)
61
{
62
    FIXME("(%d,%p,%p,%p,%p): stub\n", idInst, hszService, hszTopic, hConvList, pCC);
63 64 65 66 67 68 69 70
    return (HCONVLIST)1;
}

/*****************************************************************
 * DdeQueryNextServer [USER32.@]
 */
HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
{
71
    FIXME("(%p,%p): stub\n", hConvList, hConvPrev);
72 73 74 75 76 77
    return 0;
}

/******************************************************************************
 * DdeDisconnectList [USER32.@]  Destroys list and terminates conversations
 *
78 79 80 81
 *
 * PARAMS
 *    hConvList  [I] Handle to conversation list
 *
82 83 84 85
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
86
BOOL WINAPI DdeDisconnectList(HCONVLIST hConvList)
87
{
88
    FIXME("(%p): stub\n", hConvList);
89 90 91 92 93 94 95
    return TRUE;
}

/*****************************************************************
 *            DdeConnect   (USER32.@)
 */
HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic,
96
			PCONVCONTEXT pCC)
97 98
{
    HWND		hwndClient;
99 100 101
    WDML_INSTANCE*	pInstance;
    WDML_CONV*		pConv = NULL;
    ATOM		aSrv = 0, aTpc = 0;
102

103
    TRACE("(0x%x,%p,%p,%p)\n", idInst, hszService, hszTopic, pCC);
104 105 106

    pInstance = WDML_GetInstance(idInst);
    if (!pInstance)
107
        return NULL;
108

109
    /* make sure this conv is never created */
110
    pConv = WDML_FindConv(pInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
111 112
    if (pConv != NULL)
    {
113
	ERR("This Conv already exists: (%p)\n", pConv);
114
	return NULL;
115
    }
116

117 118
    /* we need to establish a conversation with
       server, so create a window for it       */
119

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    if (pInstance->unicode)
    {
        WNDCLASSEXW wndclass;

        wndclass.cbSize        = sizeof(wndclass);
        wndclass.style         = 0;
        wndclass.lpfnWndProc   = WDML_ClientProc;
        wndclass.cbClsExtra    = 0;
        wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
        wndclass.hInstance     = 0;
        wndclass.hIcon         = 0;
        wndclass.hCursor       = 0;
        wndclass.hbrBackground = 0;
        wndclass.lpszMenuName  = NULL;
        wndclass.lpszClassName = WDML_szClientConvClassW;
        wndclass.hIconSm       = 0;

        RegisterClassExW(&wndclass);

        hwndClient = CreateWindowW(WDML_szClientConvClassW, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
    }
    else
    {
        WNDCLASSEXA wndclass;

        wndclass.cbSize        = sizeof(wndclass);
        wndclass.style         = 0;
        wndclass.lpfnWndProc   = WDML_ClientProc;
        wndclass.cbClsExtra    = 0;
        wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
        wndclass.hInstance     = 0;
        wndclass.hIcon         = 0;
        wndclass.hCursor       = 0;
        wndclass.hbrBackground = 0;
        wndclass.lpszMenuName  = NULL;
        wndclass.lpszClassName = WDML_szClientConvClassA;
        wndclass.hIconSm       = 0;

        RegisterClassExA(&wndclass);

        hwndClient = CreateWindowA(WDML_szClientConvClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
    }
162

163
    SetWindowLongPtrW(hwndClient, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
164

165 166 167 168 169 170 171 172 173 174 175
    if (hszService)
    {
	aSrv = WDML_MakeAtomFromHsz(hszService);
	if (!aSrv) goto theEnd;
    }
    if (hszTopic)
    {
	aTpc = WDML_MakeAtomFromHsz(hszTopic);
	if (!aTpc) goto theEnd;
    }

176
    /* note: sent messages shall not use packing */
177 178
    SendMessageTimeoutW( HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, MAKELPARAM(aSrv, aTpc),
                         SMTO_ABORTIFHUNG, 2000, NULL );
179

180 181 182 183 184
    pInstance = WDML_GetInstance(idInst);
    if (!pInstance)
    {
	goto theEnd;
    }
185

186 187 188
    /* At this point, Client WM_DDE_ACK should have saved hwndServer
       for this instance id and hwndClient if server responds.
       So get HCONV and return it. And add it to conv list */
189
    pConv = WDML_GetConvFromWnd(hwndClient);
190 191
    if (pConv == NULL || pConv->hwndServer == 0)
    {
192
	WARN("Done with INITIATE, but no Server window available\n");
193 194
	pConv = NULL;
	goto theEnd;
195
    }
196
    TRACE("Connected to Server window (%p)\n", pConv->hwndServer);
197 198
    pConv->wConvst = XST_CONNECTED;

199 200 201 202 203
    /* finish init of pConv */
    if (pCC != NULL)
    {
	pConv->convContext = *pCC;
    }
204
    else
205
    {
206 207
	memset(&pConv->convContext, 0, sizeof(pConv->convContext));
	pConv->convContext.cb = sizeof(pConv->convContext);
208
	pConv->convContext.iCodePage = (pInstance->unicode) ? CP_WINUNICODE : CP_WINANSI;
209 210
    }

211 212 213 214 215 216
 theEnd:

    if (aSrv) GlobalDeleteAtom(aSrv);
    if (aTpc) GlobalDeleteAtom(aTpc);
    return (HCONV)pConv;
}
217 218 219 220 221 222 223

/*****************************************************************
 *            DdeReconnect   (DDEML.37)
 *            DdeReconnect   (USER32.@)
 */
HCONV WINAPI DdeReconnect(HCONV hConv)
{
224 225 226 227
    WDML_CONV*	pConv;
    WDML_CONV*	pNewConv = NULL;
    ATOM	aSrv = 0, aTpc = 0;

228 229
    TRACE("(%p)\n", hConv);

230 231 232 233 234 235 236
    pConv = WDML_GetConv(hConv, FALSE);
    if (pConv != NULL && (pConv->wStatus & ST_CLIENT))
    {
	BOOL	ret;

	/* to reestablist a connection, we have to make sure that:
	 * 1/ pConv is the converstation attached to the client window (it wouldn't be
237
	 *    if a call to DdeReconnect would have already been done...)
238 239 240 241 242 243 244 245 246
	 *    FIXME: is this really an error ???
	 * 2/ the pConv conversation had really been deconnected
	 */
	if (pConv == WDML_GetConvFromWnd(pConv->hwndClient) &&
	    (pConv->wStatus & ST_TERMINATED) && !(pConv->wStatus & ST_CONNECTED))
	{
	    HWND	hwndClient = pConv->hwndClient;
	    HWND	hwndServer = pConv->hwndServer;

247
	    SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, 0);
248 249 250 251 252

	    aSrv = WDML_MakeAtomFromHsz(pConv->hszService);
	    aTpc = WDML_MakeAtomFromHsz(pConv->hszTopic);
	    if (!aSrv || !aTpc)	goto theEnd;

253
            /* note: sent messages shall not use packing */
254
	    ret = SendMessageW(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient,
255
                               MAKELPARAM(aSrv, aTpc));
256

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
	    pConv = WDML_GetConv(hConv, FALSE);
	    if (pConv == NULL)
	    {
		FIXME("Should fail reconnection\n");
		goto theEnd;
	    }

	    if (ret && (pNewConv = WDML_GetConvFromWnd(pConv->hwndClient)) != NULL)
	    {
		/* re-establish all links... */
		WDML_LINK* pLink;

		for (pLink = pConv->instance->links[WDML_CLIENT_SIDE]; pLink; pLink = pLink->next)
		{
		    if (pLink->hConv == hConv)
		    {
			/* try to reestablish the links... */
274
			DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt,
275 276 277 278 279 280 281
					     pLink->transactionType, 1000, NULL);
		    }
		}
	    }
	    else
	    {
		/* reset the conversation as it was */
282
		SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
283 284 285
	    }
	}
    }
286

287 288 289 290 291 292
 theEnd:

    if (aSrv) GlobalDeleteAtom(aSrv);
    if (aTpc) GlobalDeleteAtom(aTpc);
    return (HCONV)pNewConv;
}
293 294

/******************************************************************
295
 *		WDML_ClientQueueAdvise
296 297 298
 *
 * Creates and queue an WM_DDE_ADVISE transaction
 */
299
static WDML_XACT*	WDML_ClientQueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
300 301 302
{
    DDEADVISE*		pDdeAdvise;
    WDML_XACT*		pXAct;
303
    ATOM		atom;
304 305 306

    TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : "");

307 308
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
309

310
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, wFmt, hszItem);
311
    if (!pXAct)
312 313
    {
	GlobalDeleteAtom(atom);
314
	return NULL;
315
    }
316

317 318
    pXAct->wType = wType & ~0x0F;
    pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
319 320
    /* FIXME: hMem is unfreed for now... should be deleted in server */

321
    /* pack DdeAdvise	*/
322
    pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
323 324 325
    pDdeAdvise->fAckReq   = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
    pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
    pDdeAdvise->cfFormat  = wFmt;
326
    GlobalUnlock(pXAct->hMem);
327

328
    pXAct->lParam = PackDDElParam(WM_DDE_ADVISE, (UINT_PTR)pXAct->hMem, atom);
329 330 331 332 333 334 335 336 337

    return pXAct;
}

/******************************************************************
 *		WDML_HandleAdviseReply
 *
 * handles the reply to an advise request
 */
338
static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
339 340
{
    DDEACK		ddeAck;
341
    UINT_PTR		uiLo, uiHi;
342 343
    HSZ			hsz;

344
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
345 346 347 348 349
    {
	return WDML_QS_PASS;
    }

    UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
350
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
351

352
    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
353 354 355
	return WDML_QS_PASS;

    GlobalDeleteAtom(uiHi);
356
    FreeDDElParam(WM_DDE_ACK, msg->lParam);
357

358
    if (ack) *ack = uiLo;
359
    WDML_ExtractAck(uiLo, &ddeAck);
360

361 362 363
    if (ddeAck.fAck)
    {
	WDML_LINK*	pLink;
364

365
	/* billx: first to see if the link is already created. */
366
	pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
367
			      pXAct->hszItem, TRUE, pXAct->wFmt);
368 369
	if (pLink != NULL)
	{
370
	    /* we found a link, and only need to modify it in case it changes */
371
	    pLink->transactionType = pXAct->wType;
372 373 374
	}
	else
	{
375
	    WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
376
			 pXAct->wType, pXAct->hszItem, pXAct->wFmt);
377
	}
378
        pXAct->hDdeData = (HDDEDATA)1;
379 380 381
    }
    else
    {
382
	TRACE("Returning FALSE on XTYP_ADVSTART - fAck was FALSE\n");
383
	GlobalFree(pXAct->hMem);
384
        pXAct->hDdeData = NULL;
385
    }
386

387 388 389 390
    return WDML_QS_HANDLED;
}

/******************************************************************
391
 *		WDML_ClientQueueUnadvise
392 393 394
 *
 * queues an unadvise transaction
 */
395
static WDML_XACT*	WDML_ClientQueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
396 397
{
    WDML_XACT*	pXAct;
398
    ATOM	atom;
399

400 401
    TRACE("XTYP_ADVSTOP transaction\n");

402 403
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
404

405 406
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem);
    if (!pXAct)
407
    {
408
	GlobalDeleteAtom(atom);
409 410
	return NULL;
    }
411 412

    /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
413
     * on the specified item.
414 415
     */
    pXAct->lParam = PackDDElParam(WM_DDE_UNADVISE, wFmt, atom);
416 417
    return pXAct;
}
418

419 420 421 422 423
/******************************************************************
 *		WDML_HandleUnadviseReply
 *
 *
 */
424
static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
425 426
{
    DDEACK	ddeAck;
427
    UINT_PTR	uiLo, uiHi;
428
    HSZ		hsz;
429

430
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
431 432 433 434 435
    {
	return WDML_QS_PASS;
    }

    UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
436
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
437

438
    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
439 440
	return WDML_QS_PASS;

441
    FreeDDElParam(WM_DDE_ACK, msg->lParam);
442
    GlobalDeleteAtom(uiHi);
443

444
    if (ack) *ack = uiLo;
445
    WDML_ExtractAck(uiLo, &ddeAck);
446

447
    TRACE("WM_DDE_ACK received while waiting for a timeout\n");
448

449 450
    if (!ddeAck.fAck)
    {
451
	TRACE("Returning FALSE on XTYP_ADVSTOP - fAck was FALSE\n");
452
        pXAct->hDdeData = NULL;
453 454 455 456
    }
    else
    {
	/* billx: remove the link */
457
	WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
458
			pXAct->hszItem, pXAct->wFmt);
459
        pXAct->hDdeData = (HDDEDATA)1;
460 461 462 463 464
    }
    return WDML_QS_HANDLED;
}

/******************************************************************
465
 *		WDML_ClientQueueRequest
466 467 468
 *
 *
 */
469
static WDML_XACT*	WDML_ClientQueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
470 471
{
    WDML_XACT*	pXAct;
472
    ATOM	atom;
473

474
    TRACE("XTYP_REQUEST transaction\n");
475

476 477
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
478

479 480
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, wFmt, hszItem);
    if (!pXAct)
481
    {
482
	GlobalDeleteAtom(atom);
483 484
	return NULL;
    }
485 486 487

    pXAct->lParam = PackDDElParam(WM_DDE_REQUEST, wFmt, atom);

488 489 490 491 492 493 494 495
    return pXAct;
}

/******************************************************************
 *		WDML_HandleRequestReply
 *
 *
 */
496
static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
497
{
498 499
    DDEACK		ddeAck;
    WINE_DDEHEAD	wdh;
500
    UINT_PTR		uiLo, uiHi;
501 502
    HSZ			hsz;

503
    if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
504
	return WDML_QS_PASS;
505 506 507 508

    switch (msg->message)
    {
    case WM_DDE_ACK:
509 510
        UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
        FreeDDElParam(WM_DDE_ACK, msg->lParam);
511
	GlobalDeleteAtom(uiHi);
512
       if (ack) *ack = uiLo;
513
	WDML_ExtractAck(uiLo, &ddeAck);
514
	pXAct->hDdeData = 0;
515 516
	if (ddeAck.fAck)
	    ERR("Positive answer should appear in NACK for a request, assuming negative\n");
517 518 519 520
	TRACE("Negative answer...\n");
	break;

    case WM_DDE_DATA:
521
        UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
522
	TRACE("Got the result (%08lx)\n", uiLo);
523 524 525 526

	hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);

	if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
527
	    return WDML_QS_PASS;
528 529 530 531 532 533 534 535

	pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
	if (wdh.fRelease)
	{
	    GlobalFree((HGLOBAL)uiLo);
	}
	if (wdh.fAckReq)
	{
536
	    pConv->instance->lastError = DMLERR_MEMORY_ERROR;
537 538 539 540
	}
	else
	{
	    GlobalDeleteAtom(uiHi);
541
            FreeDDElParam(WM_DDE_ACK, msg->lParam);
542
	}
543 544 545
	break;

    default:
546
        FreeDDElParam(msg->message, msg->lParam);
547 548 549 550
	return WDML_QS_PASS;
    }

    return WDML_QS_HANDLED;
551
}
552 553

/******************************************************************
554
 *		WDML_BuildExecuteCommand
555
 *
556 557
 * Creates a DDE block suitable for sending in WM_DDE_COMMAND
 * It also takes care of string conversion between the two window procedures
558
 */
559
static	HGLOBAL	WDML_BuildExecuteCommand(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
560
{
561 562 563
    HGLOBAL	hMem;
    BOOL	clientUnicode, serverUnicode;
    DWORD	memSize;
564

565 566 567 568 569
    clientUnicode = pConv->instance->unicode;
    TRACE("client %p uses unicode = %d\n", pConv->hwndClient, clientUnicode);
    /* FIXME: how exactly Windows determines what to use for the server side? */
    serverUnicode = IsWindowUnicode(pConv->hwndServer) && IsWindowUnicode(pConv->hwndClient);
    TRACE("server %p uses unicode = %d\n", pConv->hwndServer, serverUnicode);
570

571
    if (clientUnicode == serverUnicode)
572
    {
573 574 575 576 577
	memSize = cbData;
    }
    else
    {
	if (clientUnicode)
578
	{
579 580 581 582
	    memSize = WideCharToMultiByte( CP_ACP, 0, pData, cbData, NULL, 0, NULL, NULL);
	}
	else
	{
583
	    memSize = MultiByteToWideChar( CP_ACP, 0, pData, cbData, NULL, 0) * sizeof(WCHAR);
584 585 586 587 588 589 590
	}
    }

    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, memSize);

    if (hMem)
    {
591
	LPSTR	pDst;
592

593 594 595 596 597 598 599 600 601 602
	pDst = GlobalLock(hMem);
	if (pDst)
	{
	    if (clientUnicode == serverUnicode)
	    {
		memcpy(pDst, pData, cbData);
	    }
	    else
	    {
		if (clientUnicode)
603
		{
604
		    WideCharToMultiByte( CP_ACP, 0, pData, cbData, pDst, memSize, NULL, NULL);
605 606 607
		}
		else
		{
608
		    MultiByteToWideChar( CP_ACP, 0, pData, cbData, (LPWSTR)pDst, memSize/sizeof(WCHAR));
609 610
		}
	    }
611 612

	    GlobalUnlock(hMem);
613 614 615
	}
	else
	{
616 617
	    GlobalFree(hMem);
	    hMem = 0;
618 619
	}
    }
620 621 622 623 624 625 626 627
    return hMem;
}

/******************************************************************
 *		WDML_ClientQueueExecute
 *
 *
 */
628
static WDML_XACT*	WDML_ClientQueueExecute(WDML_CONV* pConv, LPVOID pData, DWORD cbData)
629 630 631 632
{
    WDML_XACT*	pXAct;

    TRACE("XTYP_EXECUTE transaction\n");
633

634 635 636
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
    if (!pXAct)
	return NULL;
637

638 639
    if (cbData == (DWORD)-1)
    {
640
	HDDEDATA	hDdeData = pData;
641

642 643
	pData = DdeAccessData(hDdeData, &cbData);
	if (pData)
644
	{
645 646
	    pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
	    DdeUnaccessData(hDdeData);
647 648
	}
    }
649
    else
650
    {
651
	pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
652
    }
653

654
    pXAct->lParam = (LPARAM)pXAct->hMem;
655

656 657 658 659 660 661 662 663
    return pXAct;
}

/******************************************************************
 *		WDML_HandleExecuteReply
 *
 *
 */
664
static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
665 666
{
    DDEACK	ddeAck;
667
    UINT_PTR	uiLo, uiHi;
668

669
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
670 671 672 673 674 675 676
    {
	return WDML_QS_PASS;
    }

    UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
    FreeDDElParam(WM_DDE_ACK, msg->lParam);

677
    if ((HANDLE)uiHi != pXAct->hMem)
678
    {
679
        return WDML_QS_PASS;
680 681
    }

682
    if (ack) *ack = uiLo;
683
    WDML_ExtractAck(uiLo, &ddeAck);
684
    pXAct->hDdeData = (HDDEDATA)(UINT_PTR)ddeAck.fAck;
685

686 687 688
    TRACE("hDdeData = %p\n", pXAct->hDdeData);
    pConv->instance->lastError = (pXAct->hDdeData != 0) ? DMLERR_NO_ERROR : DMLERR_NOTPROCESSED;

689 690 691 692
    return WDML_QS_HANDLED;
}

/******************************************************************
693
 *		WDML_ClientQueuePoke
694 695 696
 *
 *
 */
697
static WDML_XACT*	WDML_ClientQueuePoke(WDML_CONV* pConv, LPVOID pData, DWORD cbData,
698
					     UINT wFmt, HSZ hszItem)
699
{
700 701 702 703 704
    DDE_DATAHANDLE_HEAD *dh;
    WDML_XACT *pXAct;
    DDEPOKE *ddePoke;
    HGLOBAL hglobal;
    ATOM atom;
705 706

    TRACE("XTYP_POKE transaction\n");
707

708 709
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
710

711
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
712
    if (!pXAct)
713
    {
714 715
        GlobalDeleteAtom(atom);
        return NULL;
716
    }
717 718 719

    if (cbData == (DWORD)-1)
    {
720 721 722 723 724
        hglobal = (HGLOBAL)pData;
        dh = (DDE_DATAHANDLE_HEAD *)GlobalLock(hglobal);
        cbData = GlobalSize(hglobal) - sizeof(DDE_DATAHANDLE_HEAD);
        pData = (LPVOID)(dh + 1);
        GlobalUnlock(hglobal);
725 726
    }

727 728 729 730 731 732
    pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
    ddePoke = GlobalLock(pXAct->hMem);
    if (!ddePoke)
    {
        pConv->instance->lastError = DMLERR_MEMORY_ERROR;
        return NULL;
733 734
    }

735 736 737 738 739 740
    ddePoke->unused = 0;
    ddePoke->fRelease = TRUE;
    ddePoke->cfFormat = wFmt;
    memcpy(ddePoke->Value, pData, cbData);
    GlobalUnlock(pXAct->hMem);

741
    pXAct->lParam = PackDDElParam(WM_DDE_POKE, (UINT_PTR)pXAct->hMem, atom);
742 743 744 745 746 747 748 749 750

    return pXAct;
}

/******************************************************************
 *		WDML_HandlePokeReply
 *
 *
 */
751
static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
752
{
753
    UINT_PTR	uiLo, uiHi;
754
    HSZ		hsz;
755

756
    if (msg->message != WM_DDE_ACK && WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
757 758 759 760 761
    {
	return WDML_QS_PASS;
    }

    UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
762 763
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
764 765 766 767
    {
	return WDML_QS_PASS;
    }
    FreeDDElParam(WM_DDE_ACK, msg->lParam);
768 769
    GlobalDeleteAtom(uiHi);

770
    if (ack) *ack = uiLo;
771
    GlobalFree(pXAct->hMem);
772 773 774 775 776

    pXAct->hDdeData = (HDDEDATA)TRUE;
    return TRUE;
}

777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
/******************************************************************
 *		WDML_ClientQueueTerminate
 *
 * Creates and queue an WM_DDE_TERMINATE transaction
 */
static WDML_XACT*	WDML_ClientQueueTerminate(WDML_CONV* pConv)
{
    WDML_XACT*		pXAct;

    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
    if (!pXAct)
	return NULL;

    pXAct->lParam = 0;
    pConv->wStatus &= ~ST_CONNECTED;

    return pXAct;
}

/******************************************************************
 *		WDML_HandleTerminateReply
 *
 * handles the reply to a terminate request
 */
801
static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg)
802 803 804 805 806 807 808
{
    if (msg->message != WM_DDE_TERMINATE)
    {
	/* FIXME: should delete data passed here */
	return WDML_QS_SWALLOWED;
    }

809
    if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
810 811 812 813 814 815
    {
	FIXME("hmmm shouldn't happen\n");
	return WDML_QS_PASS;
    }
    if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
    {
816
	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
817 818 819 820 821 822
			    0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
    }
    WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
    return WDML_QS_HANDLED;
}

823
/******************************************************************
824
 *		WDML_HandleIncomingData
825 826 827
 *
 *
 */
828
static WDML_QUEUE_STATE WDML_HandleIncomingData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
829
{
830
    UINT_PTR		uiLo, uiHi;
831 832 833 834
    HDDEDATA		hDdeDataIn, hDdeDataOut;
    WDML_LINK*		pLink;
    WINE_DDEHEAD	wdh;
    HSZ			hsz;
835 836 837 838

    TRACE("WM_DDE_DATA message received in the Client Proc!\n");
    /* wParam -- sending window handle	*/
    /* lParam -- hDdeData & item HSZ	*/
839

840
    UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
841 842 843
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);

    hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
844

845
    /* billx:
846 847 848
     *  For hot link, data should be passed to its callback with
     * XTYP_ADVDATA and callback should return the proper status.
     */
849
    pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz,
850
                          uiLo ? TRUE : FALSE, wdh.cfFormat);
851
    if (!pLink)
852 853
    {
	WDML_DecHSZ(pConv->instance, hsz);
854
        DdeFreeDataHandle(hDdeDataIn);
855 856
	return WDML_QS_PASS;
    }
857

858
    if (hDdeDataIn != 0 && wdh.fAckReq)
859
    {
860
	WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, msg->lParam, WM_DDE_DATA);
861 862 863 864 865 866 867
	if (msg->lParam)
	    msg->lParam = 0;
    }
    else
    {
	GlobalDeleteAtom(uiHi);
    }
868 869

    hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_ADVDATA, pLink->uFmt, pLink->hConv,
870
				      pConv->hszTopic, pLink->hszItem, hDdeDataIn, 0, 0);
871

872
    if (hDdeDataOut != (HDDEDATA)DDE_FACK || wdh.fRelease)
873
    {
874
        if (uiLo) GlobalFree((HANDLE)uiLo);
875
    }
876 877

    DdeFreeDataHandle(hDdeDataIn);
878

879
    WDML_DecHSZ(pConv->instance, hsz);
880 881
    if (msg->lParam)
	FreeDDElParam(WM_DDE_DATA, msg->lParam);
882

883 884 885 886
    return WDML_QS_HANDLED;
}

/******************************************************************
887
 *		WDML_HandleIncomingTerminate
888 889 890
 *
 *
 */
891
static WDML_QUEUE_STATE WDML_HandleIncomingTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
892
{
893
    if (pConv->hwndServer != WIN_GetFullHandle((HWND)msg->wParam))
894
	return WDML_QS_PASS;
895

896 897 898
    pConv->wStatus |= ST_TERMINATED;
    if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
    {
899
	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
900 901 902 903 904
			    0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
    }
    if (pConv->wStatus & ST_CONNECTED)
    {
	/* don't care about result code (if server exists or not) */
905
	PostMessageW(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
906 907 908
	pConv->wStatus &= ~ST_CONNECTED;
    }
    /* have to keep connection around to allow reconnection */
909 910 911 912 913 914 915 916
    return WDML_QS_HANDLED;
}

/******************************************************************
 *		WDML_HandleReply
 *
 * handles any incoming reply, and try to match to an already sent request
 */
917
static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd, DWORD *ack)
918
{
919
    WDML_XACT*		pXAct = pConv->transactions;
920 921
    WDML_QUEUE_STATE	qs;

922
    if (pConv->transactions)
923 924 925 926 927
    {
	/* first check message against a pending transaction, if any */
	switch (pXAct->ddeMsg)
	{
	case WM_DDE_ADVISE:
928
	    qs = WDML_HandleAdviseReply(pConv, msg, pXAct, ack);
929 930
	    break;
	case WM_DDE_UNADVISE:
931
	    qs = WDML_HandleUnadviseReply(pConv, msg, pXAct, ack);
932 933
	    break;
	case WM_DDE_EXECUTE:
934
	    qs = WDML_HandleExecuteReply(pConv, msg, pXAct, ack);
935 936
	    break;
	case WM_DDE_REQUEST:
937
	    qs = WDML_HandleRequestReply(pConv, msg, pXAct, ack);
938 939
	    break;
	case WM_DDE_POKE:
940
	    qs = WDML_HandlePokeReply(pConv, msg, pXAct, ack);
941
	    break;
942
	case WM_DDE_TERMINATE:
943
	    qs = WDML_HandleTerminateReply(pConv, msg);
944
	    break;
945 946 947 948 949 950 951 952 953 954 955
	default:
	    qs = WDML_QS_ERROR;
	    FIXME("oooch\n");
	}
    }
    else
    {
	qs = WDML_QS_PASS;
    }

    /* now check the results */
956
    switch (qs)
957 958
    {
    case WDML_QS_ERROR:
959
    case WDML_QS_SWALLOWED:
960 961 962 963
	*hdd = 0;
	break;
    case WDML_QS_HANDLED:
	/* ok, we have resolved a pending transaction
964
	 * notify callback if asynchronous.
965
	 */
966
	if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
967
	{
968 969 970 971
	    WDML_InvokeCallback(pConv->instance, XTYP_XACT_COMPLETE, pXAct->wFmt,
				(HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
				pXAct->hDdeData, MAKELONG(0, pXAct->xActID), 0 /* FIXME */);
	    qs = WDML_QS_PASS;
972
	}
973 974 975 976 977 978
	else
	{
	    *hdd = pXAct->hDdeData;
	}
	break;
    case WDML_QS_PASS:
979
	/* no pending transaction found, try a warm/hot link or a termination request */
980 981 982
	switch (msg->message)
	{
	case WM_DDE_DATA:
983
	    qs = WDML_HandleIncomingData(pConv, msg, hdd);
984 985
	    break;
	case WM_DDE_TERMINATE:
986
	    qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
987 988 989
	    break;
	}
	break;
990 991 992
    case WDML_QS_BLOCK:
	FIXME("shouldn't be used on client side\n");
	break;
993 994 995 996 997 998 999 1000 1001 1002 1003
    }

    return qs;
}

/******************************************************************
 *		WDML_SyncWaitTransactionReply
 *
 * waits until an answer for a sent request is received
 * time out is also handled. only used for synchronous transactions
 */
1004
static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct, DWORD *ack)
1005
{
1006 1007 1008
    DWORD	dwTime;
    DWORD	err;
    WDML_CONV*	pConv;
1009

1010
    TRACE("Starting wait for a timeout of %d ms\n", dwTimeout);
1011 1012 1013

    /* FIXME: time 32 bit wrap around */
    dwTimeout += GetCurrentTime();
1014

1015 1016
    while ((dwTime = GetCurrentTime()) < dwTimeout)
    {
1017
	/* we cannot be in the crit sect all the time because when client and server run in a
1018 1019
	 * single process they need to share the access to the internal data
	 */
1020
	if (MsgWaitForMultipleObjects(0, NULL, FALSE,
1021
				      dwTimeout - dwTime, QS_POSTMESSAGE) == WAIT_OBJECT_0)
1022 1023
	{
	    MSG		msg;
1024

1025
	    while (PeekMessageW(&msg, 0, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
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
                HDDEDATA hdd;

                pConv = WDML_GetConv(hConv, FALSE);
                if (pConv == NULL)
                {
                    /* conversation no longer available... return failure */
                    return 0;
                }
                if (msg.hwnd == pConv->hwndClient)
                {
                    /* check that either pXAct has been processed or no more xActions are pending */
                    BOOL ret = (pConv->transactions == pXAct);
                    if (WDML_HandleReply(pConv, &msg, &hdd, ack) == WDML_QS_HANDLED)
                    {
                        TRACE("WDML_HandleReply returned WDML_QS_HANDLED\n");
                        ret = TRUE;
                    }
                    else
                        ret = (pConv->transactions == NULL || ret);

                    if (ret)
                    {
                        pConv->instance->lastError = hdd ? DMLERR_NO_ERROR : DMLERR_NOTPROCESSED;
                        return hdd;
                    }
                }
                else
                {
                    DispatchMessageW(&msg);
                }
            }
1058 1059 1060 1061 1062
	}
    }

    TRACE("Timeout !!\n");

1063 1064 1065 1066
    pConv = WDML_GetConv(hConv, FALSE);
    if (pConv != NULL)
    {
	if (pConv->transactions)
1067
	{
1068 1069 1070 1071 1072 1073 1074 1075 1076
	    switch (pConv->transactions->ddeMsg)
	    {
	    case WM_DDE_ADVISE:		err = DMLERR_ADVACKTIMEOUT;	break;
	    case WM_DDE_REQUEST:	err = DMLERR_DATAACKTIMEOUT; 	break;
	    case WM_DDE_EXECUTE:	err = DMLERR_EXECACKTIMEOUT;	break;
	    case WM_DDE_POKE:		err = DMLERR_POKEACKTIMEOUT;	break;
	    case WM_DDE_UNADVISE:	err = DMLERR_UNADVACKTIMEOUT;	break;
	    default:			err = DMLERR_INVALIDPARAMETER;	break;
	    }
1077

1078 1079
	    pConv->instance->lastError = err;
	}
1080
    }
1081

1082 1083 1084
    return 0;
}

1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117

/*****************************************************************
 *            WDML_ClientHandle
 */
HDDEDATA WDML_ClientHandle(WDML_CONV *pConv, WDML_XACT *pXAct, DWORD dwTimeout, LPDWORD pdwResult)
{
    HDDEDATA hDdeData;

    if (!PostMessageW(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam))
    {
        WARN("Failed posting message %x to %p (error=0x%x)\n",
              pXAct->ddeMsg, pConv->hwndServer, GetLastError());
        pConv->wStatus &= ~ST_CONNECTED;
        pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
        return 0;
    }
    pXAct->dwTimeout = dwTimeout;
    /* FIXME: should set the app bits on *pdwResult */

    if (dwTimeout == TIMEOUT_ASYNC)
    {
        if (pdwResult)
            *pdwResult = MAKELONG(0, pXAct->xActID);

        hDdeData = (HDDEDATA)1;
    }
    else
        hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct, pdwResult);

    return hDdeData;
}


1118 1119 1120 1121 1122 1123 1124 1125 1126
/*****************************************************************
 *            DdeClientTransaction  (USER32.@)
 */
HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt,
				     UINT wType, DWORD dwTimeout, LPDWORD pdwResult)
{
    WDML_CONV*		pConv;
    WDML_XACT*		pXAct;
    HDDEDATA		hDdeData = 0;
1127

1128
    TRACE("(%p,%d,%p,%p,%x,%x,%d,%p)\n",
1129
	  pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
1130

1131 1132
    if (hConv == 0)
    {
1133
	WARN("Invalid conversation handle NULL\n");
1134 1135
	return 0;
    }
1136

1137
    pConv = WDML_GetConv(hConv, TRUE);
1138 1139 1140
    if (pConv == NULL)
    {
	/* cannot set error... cannot get back to DDE instance */
1141
        return 0;
1142 1143 1144 1145 1146
    }

    switch (wType)
    {
    case XTYP_EXECUTE:
1147
        /* Windows simply ignores hszItem and wFmt in this case */
1148
	pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
1149 1150
	break;
    case XTYP_POKE:
1151 1152 1153 1154 1155 1156 1157
        if (!hszItem)
        {
            pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
            return 0;
        }
        pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
        break;
1158 1159 1160 1161 1162 1163
    case XTYP_ADVSTART|XTYPF_NODATA:
    case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
    case XTYP_ADVSTART:
    case XTYP_ADVSTART|XTYPF_ACKREQ:
	if (pData)
	{
1164
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1165
            return 0;
1166
	}
1167
	pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
1168 1169 1170 1171
	break;
    case XTYP_ADVSTOP:
	if (pData)
	{
1172
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1173
            return 0;
1174
	}
1175
	pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
1176 1177
	break;
    case XTYP_REQUEST:
1178
	if (pData || !hszItem)
1179
	{
1180
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1181
            return 0;
1182
	}
1183
	pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
1184 1185
	break;
    default:
1186
        FIXME("Unknown transaction type %04x\n", wType);
1187
	/* unknown transaction type */
1188
	pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1189
        return 0;
1190 1191
    }

1192 1193 1194
    if (pXAct == NULL)
    {
	pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1195
        return 0;
1196 1197 1198 1199
    }

    WDML_QueueTransaction(pConv, pXAct);

1200 1201 1202
    TRACE("pConv->wStatus %04x\n", pConv->wStatus);

    if (pConv->wStatus & ST_BLOCKED)
1203
    {
1204 1205
        TRACE("Transactions are blocked, add to the queue and exit\n");
        return (HDDEDATA)1;
1206
    }
1207

1208 1209
    hDdeData = WDML_ClientHandle(pConv, pXAct, dwTimeout, pdwResult);
    if (dwTimeout != TIMEOUT_ASYNC)
1210
    {
1211 1212
        WDML_UnQueueTransaction(pConv, pXAct);
        WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1213 1214
    }

1215 1216 1217
    return hDdeData;
}

1218 1219 1220 1221 1222 1223 1224 1225 1226
/*****************************************************************
 *            DdeAbandonTransaction (USER32.@)
 */
BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
{
    WDML_INSTANCE*	pInstance;
    WDML_CONV*		pConv;
    WDML_XACT*          pXAct;

1227
    TRACE("(%08x,%p,%08x);\n", idInst, hConv, idTransaction);
1228 1229 1230 1231 1232 1233 1234 1235 1236

    if ((pInstance = WDML_GetInstance(idInst)))
    {
        if (hConv)
        {
            if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance)
            {
                for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
                {
1237
                    if (pXAct->dwTimeout == TIMEOUT_ASYNC &&
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
                        (idTransaction == 0 || pXAct->xActID == idTransaction))
                    {
                        WDML_UnQueueTransaction(pConv, pXAct);
                        WDML_FreeTransaction(pInstance, pXAct, TRUE);
                    }
                }
            }
        }
        else
        {
            for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv; pConv = pConv->next)
            {
1250
                if (!(pConv->wStatus & ST_CONNECTED)) continue;
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
                for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
                {
                    if (pXAct->dwTimeout == TIMEOUT_ASYNC)
                    {
                        WDML_UnQueueTransaction(pConv, pXAct);
                        WDML_FreeTransaction(pInstance, pXAct, TRUE);
                    }
                }
            }
        }
    }

    return TRUE;
}

1266 1267 1268 1269 1270 1271 1272
/******************************************************************
 *		WDML_ClientProc
 *
 * Window Proc created on client side for each conversation
 */
static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
1273 1274 1275 1276
    UINT	uiLo, uiHi;
    WDML_CONV*	pConv = NULL;
    HSZ		hszSrv, hszTpc;

1277
    TRACE("%p %04x %08lx %08lx\n", hwnd, iMsg, wParam , lParam);
1278

1279
    if (iMsg == WM_DDE_ACK &&
1280 1281
	/* in the initial WM_INITIATE sendmessage */
	((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
1282
    {
1283
	/* In response to WM_DDE_INITIATE, save server window  */
1284
	char		buf[256];
1285
	WDML_INSTANCE*	pInstance;
1286

1287 1288 1289
        /* note: sent messages do not need packing */
	uiLo = LOWORD(lParam);
        uiHi = HIWORD(lParam);
1290 1291 1292 1293 1294 1295 1296

	/* FIXME: convlist should be handled here */
	if (pConv)
	{
	    /* we already have started the conv with a server, drop other replies */
	    GlobalDeleteAtom(uiLo);
	    GlobalDeleteAtom(uiHi);
1297
            PostMessageW((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
1298 1299 1300 1301 1302 1303 1304 1305
	    return 0;
	}

	pInstance = WDML_GetInstanceFromWnd(hwnd);

	hszSrv = WDML_MakeHszFromAtom(pInstance, uiLo);
	hszTpc = WDML_MakeHszFromAtom(pInstance, uiHi);

1306
	pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, hwnd, (HWND)wParam);
1307

1308
	SetWindowLongPtrW(hwnd, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
1309 1310 1311 1312
	pConv->wStatus |= ST_CONNECTED;
	pConv->wConvst = XST_INIT1;

	/* check if server is handled by DDEML */
1313 1314 1315 1316
	if ((GetClassNameA((HWND)wParam, buf, sizeof(buf)) &&
	     lstrcmpiA(buf, WDML_szServerConvClassA) == 0) ||
	    (GetClassNameW((HWND)wParam, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) &&
	     lstrcmpiW((LPWSTR)buf, WDML_szServerConvClassW) == 0))
1317 1318 1319 1320
	{
	    pConv->wStatus |= ST_ISLOCAL;
	}

1321
	WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_CONNECT_CONFIRM, (WPARAM)hwnd, wParam);
1322 1323 1324 1325 1326 1327

	GlobalDeleteAtom(uiLo);
	GlobalDeleteAtom(uiHi);

	/* accept conversation */
	return 1;
1328
    }
1329 1330

    if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
1331
    {
1332
	pConv = WDML_GetConvFromWnd(hwnd);
1333

1334
	if (pConv)
1335 1336 1337 1338 1339 1340 1341 1342 1343
	{
	    MSG		msg;
	    HDDEDATA	hdd;

	    msg.hwnd = hwnd;
	    msg.message = iMsg;
	    msg.wParam = wParam;
	    msg.lParam = lParam;

1344
	    WDML_HandleReply(pConv, &msg, &hdd, NULL);
1345 1346 1347 1348
	}

	return 0;
    }
1349

1350 1351
    return IsWindowUnicode(hwnd) ? DefWindowProcW(hwnd, iMsg, wParam, lParam) :
                                   DefWindowProcA(hwnd, iMsg, wParam, lParam);
1352 1353
}

1354 1355 1356 1357 1358 1359 1360 1361 1362
/*****************************************************************
 *            DdeDisconnect   (USER32.@)
 */
BOOL WINAPI DdeDisconnect(HCONV hConv)
{
    WDML_CONV*	pConv = NULL;
    WDML_XACT*	pXAct;
    BOOL	ret = FALSE;

1363
    TRACE("(%p)\n", hConv);
1364

1365 1366
    if (hConv == 0)
    {
1367
	WARN("DdeDisconnect(): hConv = 0\n");
1368 1369
	return FALSE;
    }
1370

1371
    pConv = WDML_GetConv(hConv, TRUE);
1372 1373
    if (pConv != NULL)
    {
1374 1375 1376 1377 1378 1379
        if (pConv->wStatus & ST_CLIENT)
        {
            /* FIXME: should abandon all pending transactions */
            pXAct = WDML_ClientQueueTerminate(pConv);
            if (pXAct != NULL)
            {
1380
                if (PostMessageW(pConv->hwndServer, pXAct->ddeMsg,
1381
                                 (WPARAM)pConv->hwndClient, pXAct->lParam))
1382
                {
1383
                    WDML_SyncWaitTransactionReply(hConv, 10000, pXAct, NULL);
1384 1385
                    ret = TRUE;
                }
1386
                else
1387 1388
                    pConv->instance->lastError = DMLERR_POSTMSG_FAILED;

1389 1390 1391 1392 1393 1394 1395 1396 1397
                WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
                /* still have to destroy data assosiated with conversation */
                WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
            }
            else
            {
                FIXME("Not implemented yet for a server side conversation\n");
            }
        }
1398 1399 1400 1401
    }

    return ret;
}
1402 1403 1404 1405 1406 1407 1408 1409

/*****************************************************************
 *            DdeImpersonateClient (USER32.@)
 */
BOOL WINAPI DdeImpersonateClient(HCONV hConv)
{
    WDML_CONV*	pConv;
    BOOL	ret = FALSE;
1410

1411 1412
    TRACE("(%p)\n", hConv);

1413
    pConv = WDML_GetConv(hConv, TRUE);
1414 1415 1416 1417 1418 1419
    if (pConv)
    {
	ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
    }
    return ret;
}