dde_client.c 38.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 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
    SendMessageTimeoutW( HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, MAKELPARAM(aSrv, aTpc),
178
                         SMTO_ABORTIFHUNG, 0, 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
	pConv = NULL;
194
	pInstance->lastError = DMLERR_NO_CONV_ESTABLISHED;
195
	goto theEnd;
196
    }
197
    TRACE("Connected to Server window (%p)\n", pConv->hwndServer);
198 199
    pConv->wConvst = XST_CONNECTED;

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

212 213 214 215 216 217
 theEnd:

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

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

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

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

236 237
	/* to reestablish a connection, we have to make sure that:
	 * 1/ pConv is the conversation attached to the client window (it wouldn't be
238
	 *    if a call to DdeReconnect would have already been done...)
239 240 241 242 243 244 245 246 247
	 *    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;

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

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

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

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
	    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... */
275
			DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt,
276 277 278 279 280 281 282
					     pLink->transactionType, 1000, NULL);
		    }
		}
	    }
	    else
	    {
		/* reset the conversation as it was */
283
		SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
284 285 286
	    }
	}
    }
287

288 289 290 291 292 293
 theEnd:

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

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

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

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

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

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

322
    /* pack DdeAdvise	*/
323
    pDdeAdvise = GlobalLock(pXAct->hMem);
324 325
    pDdeAdvise->fAckReq   = (wType & XTYPF_ACKREQ) != 0;
    pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) != 0;
326
    pDdeAdvise->cfFormat  = wFmt;
327
    GlobalUnlock(pXAct->hMem);
328

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

    return pXAct;
}

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

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

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

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

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

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

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

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

388 389 390 391
    return WDML_QS_HANDLED;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
528
	    return WDML_QS_PASS;
529

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

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

    return WDML_QS_HANDLED;
552
}
553 554

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

566 567 568 569 570
    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);
571

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

    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, memSize);

    if (hMem)
    {
592
	LPSTR	pDst;
593

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

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

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

    TRACE("XTYP_EXECUTE transaction\n");
634

635 636 637 638 639 640 641 642 643
    if (pData == NULL)
    {
        if (cbData == (DWORD)-1)
            pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
        else
            pConv->instance->lastError = DMLERR_MEMORY_ERROR;
        return NULL;
    }

644 645 646
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
    if (!pXAct)
	return NULL;
647

648 649
    if (cbData == (DWORD)-1)
    {
650
	HDDEDATA	hDdeData = pData;
651

652 653
	pData = DdeAccessData(hDdeData, &cbData);
	if (pData)
654
	{
655 656
	    pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
	    DdeUnaccessData(hDdeData);
657 658
	}
    }
659
    else
660
    {
661
	pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
662
    }
663

664
    pXAct->lParam = (LPARAM)pXAct->hMem;
665

666 667 668 669 670 671 672 673
    return pXAct;
}

/******************************************************************
 *		WDML_HandleExecuteReply
 *
 *
 */
674
static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
675 676
{
    DDEACK	ddeAck;
677
    UINT_PTR	uiLo, uiHi;
678

679
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
680 681 682 683 684 685 686
    {
	return WDML_QS_PASS;
    }

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

687
    if ((HANDLE)uiHi != pXAct->hMem)
688
    {
689
        return WDML_QS_PASS;
690 691
    }

692
    if (ack) *ack = uiLo;
693
    WDML_ExtractAck(uiLo, &ddeAck);
694
    pXAct->hDdeData = (HDDEDATA)(UINT_PTR)ddeAck.fAck;
695

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

699 700 701 702
    return WDML_QS_HANDLED;
}

/******************************************************************
703
 *		WDML_ClientQueuePoke
704 705 706
 *
 *
 */
707
static WDML_XACT*	WDML_ClientQueuePoke(WDML_CONV* pConv, LPVOID pData, DWORD cbData,
708
					     UINT wFmt, HSZ hszItem)
709
{
710 711 712 713 714
    DDE_DATAHANDLE_HEAD *dh;
    WDML_XACT *pXAct;
    DDEPOKE *ddePoke;
    HGLOBAL hglobal;
    ATOM atom;
715 716

    TRACE("XTYP_POKE transaction\n");
717

718 719
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
720

721
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
722
    if (!pXAct)
723
    {
724 725
        GlobalDeleteAtom(atom);
        return NULL;
726
    }
727 728 729

    if (cbData == (DWORD)-1)
    {
730
        hglobal = pData;
731
        dh = GlobalLock(hglobal);
732
        cbData = GlobalSize(hglobal) - sizeof(DDE_DATAHANDLE_HEAD);
733
        pData = dh + 1;
734
        GlobalUnlock(hglobal);
735 736
    }

737
    pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, FIELD_OFFSET(DDEPOKE, Value[cbData]));
738 739 740 741 742
    ddePoke = GlobalLock(pXAct->hMem);
    if (!ddePoke)
    {
        pConv->instance->lastError = DMLERR_MEMORY_ERROR;
        return NULL;
743 744
    }

745 746 747 748 749 750
    ddePoke->unused = 0;
    ddePoke->fRelease = TRUE;
    ddePoke->cfFormat = wFmt;
    memcpy(ddePoke->Value, pData, cbData);
    GlobalUnlock(pXAct->hMem);

751
    pXAct->lParam = PackDDElParam(WM_DDE_POKE, (UINT_PTR)pXAct->hMem, atom);
752 753 754 755 756 757 758 759 760

    return pXAct;
}

/******************************************************************
 *		WDML_HandlePokeReply
 *
 *
 */
761
static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
762
{
763
    UINT_PTR	uiLo, uiHi;
764
    HSZ		hsz;
765

766
    if (msg->message != WM_DDE_ACK && WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
767 768 769 770 771
    {
	return WDML_QS_PASS;
    }

    UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
772 773
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
774 775 776 777
    {
	return WDML_QS_PASS;
    }
    FreeDDElParam(WM_DDE_ACK, msg->lParam);
778 779
    GlobalDeleteAtom(uiHi);

780
    if (ack) *ack = uiLo;
781
    GlobalFree(pXAct->hMem);
782 783 784 785 786

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

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
/******************************************************************
 *		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
 */
811
static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg)
812 813 814 815 816 817 818
{
    if (msg->message != WM_DDE_TERMINATE)
    {
	/* FIXME: should delete data passed here */
	return WDML_QS_SWALLOWED;
    }

819
    if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
820 821 822 823
    {
	FIXME("hmmm shouldn't happen\n");
	return WDML_QS_PASS;
    }
824
    if (!(pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS))
825
    {
826
	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
827 828 829 830 831 832
			    0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
    }
    WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
    return WDML_QS_HANDLED;
}

833
/******************************************************************
834
 *		WDML_HandleIncomingData
835 836 837
 *
 *
 */
838
static WDML_QUEUE_STATE WDML_HandleIncomingData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
839
{
840
    UINT_PTR		uiLo, uiHi;
841 842 843 844
    HDDEDATA		hDdeDataIn, hDdeDataOut;
    WDML_LINK*		pLink;
    WINE_DDEHEAD	wdh;
    HSZ			hsz;
845 846 847 848

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

850
    UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
851 852
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);

853
    hDdeDataIn = WDML_Global2DataHandle(pConv, (HGLOBAL)uiLo, &wdh);
854

855
    /* billx:
856 857 858
     *  For hot link, data should be passed to its callback with
     * XTYP_ADVDATA and callback should return the proper status.
     */
859
    pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz,
860
                          uiLo != 0, wdh.cfFormat);
861
    if (!pLink)
862 863
    {
	WDML_DecHSZ(pConv->instance, hsz);
864
        DdeFreeDataHandle(hDdeDataIn);
865 866
	return WDML_QS_PASS;
    }
867

868
    if (hDdeDataIn != 0 && wdh.fAckReq)
869
    {
870
	WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, msg->lParam, WM_DDE_DATA);
871 872 873 874 875 876 877
	if (msg->lParam)
	    msg->lParam = 0;
    }
    else
    {
	GlobalDeleteAtom(uiHi);
    }
878 879

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

882
    if (hDdeDataOut != (HDDEDATA)DDE_FACK || wdh.fRelease)
883
    {
884
        if (uiLo) GlobalFree((HANDLE)uiLo);
885
    }
886 887

    DdeFreeDataHandle(hDdeDataIn);
888

889
    WDML_DecHSZ(pConv->instance, hsz);
890 891
    if (msg->lParam)
	FreeDDElParam(WM_DDE_DATA, msg->lParam);
892

893 894 895 896
    return WDML_QS_HANDLED;
}

/******************************************************************
897
 *		WDML_HandleIncomingTerminate
898 899 900
 *
 *
 */
901
static WDML_QUEUE_STATE WDML_HandleIncomingTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
902
{
903
    if (pConv->hwndServer != WIN_GetFullHandle((HWND)msg->wParam))
904
	return WDML_QS_PASS;
905

906 907 908 909
    pConv->wStatus |= ST_TERMINATED;
    if (pConv->wStatus & ST_CONNECTED)
    {
	/* don't care about result code (if server exists or not) */
910
	PostMessageW(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
911 912 913
	pConv->wStatus &= ~ST_CONNECTED;
    }
    /* have to keep connection around to allow reconnection */
914 915 916 917 918 919 920 921
    return WDML_QS_HANDLED;
}

/******************************************************************
 *		WDML_HandleReply
 *
 * handles any incoming reply, and try to match to an already sent request
 */
922
static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd, DWORD *ack)
923
{
924
    WDML_XACT*		pXAct = pConv->transactions;
925 926
    WDML_QUEUE_STATE	qs;

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

    /* now check the results */
962
    switch (qs)
963 964
    {
    case WDML_QS_ERROR:
965
    case WDML_QS_SWALLOWED:
966 967 968 969
	*hdd = 0;
	break;
    case WDML_QS_HANDLED:
	/* ok, we have resolved a pending transaction
970
	 * notify callback if asynchronous.
971
	 */
972
	if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
973
	{
974 975 976 977
	    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;
978
	}
979 980 981 982 983 984
	else
	{
	    *hdd = pXAct->hDdeData;
	}
	break;
    case WDML_QS_PASS:
985
	/* no pending transaction found, try a warm/hot link or a termination request */
986 987 988
	switch (msg->message)
	{
	case WM_DDE_DATA:
989
	    qs = WDML_HandleIncomingData(pConv, msg, hdd);
990 991
	    break;
	case WM_DDE_TERMINATE:
992
	    qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
993
	    break;
994 995 996 997 998 999 1000 1001
        case WM_DDE_ACK:
            /* This happens at end of DdeClientTransaction XTYP_EXECUTE
             * Without this assignment, DdeClientTransaction's return value is undefined
             */
            *hdd = (HDDEDATA)TRUE;
            if (ack)
                *ack = DDE_FACK;
	    break;
1002 1003
	}
	break;
1004 1005 1006
    case WDML_QS_BLOCK:
	FIXME("shouldn't be used on client side\n");
	break;
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
    }

    return qs;
}

/******************************************************************
 *		WDML_SyncWaitTransactionReply
 *
 * waits until an answer for a sent request is received
 * time out is also handled. only used for synchronous transactions
 */
1018
static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, const WDML_XACT* pXAct, DWORD *ack)
1019
{
1020 1021 1022
    DWORD	dwTime;
    DWORD	err;
    WDML_CONV*	pConv;
1023

1024
    TRACE("Starting wait for a timeout of %d ms\n", dwTimeout);
1025 1026 1027

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

1029 1030
    while ((dwTime = GetCurrentTime()) < dwTimeout)
    {
1031
	/* we cannot be in the crit sect all the time because when client and server run in a
1032 1033
	 * single process they need to share the access to the internal data
	 */
1034
	if (MsgWaitForMultipleObjects(0, NULL, FALSE,
1035
				      dwTimeout - dwTime, QS_POSTMESSAGE) == WAIT_OBJECT_0)
1036 1037
	{
	    MSG		msg;
1038

1039
	    while (PeekMessageW(&msg, 0, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
1040
	    {
1041
                HDDEDATA hdd = NULL;
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071

                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);
                }
            }
1072 1073 1074 1075 1076
	}
    }

    TRACE("Timeout !!\n");

1077 1078 1079 1080
    pConv = WDML_GetConv(hConv, FALSE);
    if (pConv != NULL)
    {
	if (pConv->transactions)
1081
	{
1082 1083 1084 1085 1086 1087 1088 1089 1090
	    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;
	    }
1091

1092 1093
	    pConv->instance->lastError = err;
	}
1094
    }
1095

1096 1097 1098
    return 0;
}

1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131

/*****************************************************************
 *            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;
}


1132 1133 1134 1135 1136 1137 1138 1139 1140
/*****************************************************************
 *            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;
1141

1142
    TRACE("(%p,%d,%p,%p,%x,%x,%d,%p)\n",
1143
	  pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
1144

1145 1146
    if (hConv == 0)
    {
1147
	WARN("Invalid conversation handle NULL\n");
1148 1149
	return 0;
    }
1150

1151
    pConv = WDML_GetConv(hConv, TRUE);
1152 1153 1154
    if (pConv == NULL)
    {
	/* cannot set error... cannot get back to DDE instance */
1155
        return 0;
1156 1157 1158 1159 1160
    }

    switch (wType)
    {
    case XTYP_EXECUTE:
1161
    /* Windows simply ignores hszItem and wFmt in this case */
1162
	pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
1163 1164
	if (pXAct == NULL)
	    return 0;
1165 1166
	break;
    case XTYP_POKE:
1167 1168 1169 1170 1171 1172 1173
        if (!hszItem)
        {
            pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
            return 0;
        }
        pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
        break;
1174 1175 1176 1177 1178 1179
    case XTYP_ADVSTART|XTYPF_NODATA:
    case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
    case XTYP_ADVSTART:
    case XTYP_ADVSTART|XTYPF_ACKREQ:
	if (pData)
	{
1180
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1181
            return 0;
1182
	}
1183
	pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
1184 1185 1186 1187
	break;
    case XTYP_ADVSTOP:
	if (pData)
	{
1188
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1189
            return 0;
1190
	}
1191
	pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
1192 1193
	break;
    case XTYP_REQUEST:
1194
	if (pData || !hszItem)
1195
	{
1196
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1197
            return 0;
1198
	}
1199
	pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
1200 1201
	break;
    default:
1202
        FIXME("Unknown transaction type %04x\n", wType);
1203
	/* unknown transaction type */
1204
	pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1205
        return 0;
1206 1207
    }

1208 1209 1210
    if (pXAct == NULL)
    {
	pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1211
        return 0;
1212 1213 1214 1215
    }

    WDML_QueueTransaction(pConv, pXAct);

1216 1217 1218
    TRACE("pConv->wStatus %04x\n", pConv->wStatus);

    if (pConv->wStatus & ST_BLOCKED)
1219
    {
1220 1221
        TRACE("Transactions are blocked, add to the queue and exit\n");
        return (HDDEDATA)1;
1222
    }
1223

1224 1225
    hDdeData = WDML_ClientHandle(pConv, pXAct, dwTimeout, pdwResult);
    if (dwTimeout != TIMEOUT_ASYNC)
1226
    {
1227 1228
        WDML_UnQueueTransaction(pConv, pXAct);
        WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1229 1230
    }

1231 1232 1233
    return hDdeData;
}

1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
/*****************************************************************
 *            DdeAbandonTransaction (USER32.@)
 */
BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
{
    WDML_INSTANCE*	pInstance;
    WDML_CONV*		pConv;
    WDML_XACT*          pXAct;

    if ((pInstance = WDML_GetInstance(idInst)))
    {
        if (hConv)
        {
            if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance)
            {
1249 1250 1251 1252 1253

                pXAct = pConv->transactions;
                while (pXAct) {
                    WDML_XACT *nextXAct = pXAct->next;

1254
                    if (pXAct->dwTimeout == TIMEOUT_ASYNC &&
1255 1256 1257 1258 1259
                        (idTransaction == 0 || pXAct->xActID == idTransaction))
                    {
                        WDML_UnQueueTransaction(pConv, pXAct);
                        WDML_FreeTransaction(pInstance, pXAct, TRUE);
                    }
1260
                    pXAct = nextXAct;
1261 1262 1263 1264 1265 1266 1267
                }
            }
        }
        else
        {
            for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv; pConv = pConv->next)
            {
1268
                if (!(pConv->wStatus & ST_CONNECTED)) continue;
1269 1270 1271 1272
                pXAct = pConv->transactions;
                while (pXAct) {
                    WDML_XACT *nextXAct = pXAct->next;

1273 1274 1275 1276 1277
                    if (pXAct->dwTimeout == TIMEOUT_ASYNC)
                    {
                        WDML_UnQueueTransaction(pConv, pXAct);
                        WDML_FreeTransaction(pInstance, pXAct, TRUE);
                    }
1278
                    pXAct = nextXAct;
1279 1280 1281 1282 1283 1284 1285 1286
                }
            }
        }
    }

    return TRUE;
}

1287 1288 1289 1290 1291 1292 1293
/******************************************************************
 *		WDML_ClientProc
 *
 * Window Proc created on client side for each conversation
 */
static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
1294 1295 1296 1297
    UINT	uiLo, uiHi;
    WDML_CONV*	pConv = NULL;
    HSZ		hszSrv, hszTpc;

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

1300
    if (iMsg == WM_DDE_ACK &&
1301 1302
	/* in the initial WM_INITIATE sendmessage */
	((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
1303
    {
1304
	/* In response to WM_DDE_INITIATE, save server window  */
1305
	char		buf[256];
1306
	WDML_INSTANCE*	pInstance;
1307

1308 1309 1310
        /* note: sent messages do not need packing */
	uiLo = LOWORD(lParam);
        uiHi = HIWORD(lParam);
1311 1312 1313 1314 1315 1316 1317

	/* FIXME: convlist should be handled here */
	if (pConv)
	{
	    /* we already have started the conv with a server, drop other replies */
	    GlobalDeleteAtom(uiLo);
	    GlobalDeleteAtom(uiHi);
1318
            PostMessageW((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
1319 1320 1321 1322 1323 1324 1325 1326
	    return 0;
	}

	pInstance = WDML_GetInstanceFromWnd(hwnd);

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

1327
	pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, hwnd, (HWND)wParam);
1328

1329
	SetWindowLongPtrW(hwnd, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
1330 1331 1332 1333
	pConv->wStatus |= ST_CONNECTED;
	pConv->wConvst = XST_INIT1;

	/* check if server is handled by DDEML */
1334 1335 1336 1337
	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))
1338 1339 1340 1341 1342 1343 1344 1345 1346
	{
	    pConv->wStatus |= ST_ISLOCAL;
	}

	GlobalDeleteAtom(uiLo);
	GlobalDeleteAtom(uiHi);

	/* accept conversation */
	return 1;
1347
    }
1348 1349

    if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
1350
    {
1351
	pConv = WDML_GetConvFromWnd(hwnd);
1352

1353
	if (pConv)
1354 1355 1356 1357 1358 1359 1360 1361 1362
	{
	    MSG		msg;
	    HDDEDATA	hdd;

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

1363
	    WDML_HandleReply(pConv, &msg, &hdd, NULL);
1364 1365 1366 1367
	}

	return 0;
    }
1368

1369 1370
    return IsWindowUnicode(hwnd) ? DefWindowProcW(hwnd, iMsg, wParam, lParam) :
                                   DefWindowProcA(hwnd, iMsg, wParam, lParam);
1371 1372
}

1373 1374 1375 1376 1377 1378 1379 1380 1381
/*****************************************************************
 *            DdeDisconnect   (USER32.@)
 */
BOOL WINAPI DdeDisconnect(HCONV hConv)
{
    WDML_CONV*	pConv = NULL;
    WDML_XACT*	pXAct;
    BOOL	ret = FALSE;

1382
    TRACE("(%p)\n", hConv);
1383

1384 1385
    if (hConv == 0)
    {
1386
	WARN("DdeDisconnect(): hConv = 0\n");
1387 1388
	return FALSE;
    }
1389

1390
    pConv = WDML_GetConv(hConv, TRUE);
1391 1392
    if (pConv != NULL)
    {
1393 1394 1395 1396 1397 1398
        if (pConv->wStatus & ST_CLIENT)
        {
            /* FIXME: should abandon all pending transactions */
            pXAct = WDML_ClientQueueTerminate(pConv);
            if (pXAct != NULL)
            {
1399
                if (PostMessageW(pConv->hwndServer, pXAct->ddeMsg,
1400
                                 (WPARAM)pConv->hwndClient, pXAct->lParam))
1401
                {
1402
                    WDML_SyncWaitTransactionReply(hConv, 10000, pXAct, NULL);
1403 1404
                    ret = TRUE;
                }
1405
                else
1406 1407
                    pConv->instance->lastError = DMLERR_POSTMSG_FAILED;

1408
                WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1409
                /* still have to destroy data associated with conversation */
1410 1411 1412 1413 1414 1415 1416
                WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
            }
            else
            {
                FIXME("Not implemented yet for a server side conversation\n");
            }
        }
1417 1418 1419 1420
    }

    return ret;
}
1421 1422 1423 1424 1425 1426 1427 1428

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

1430 1431
    TRACE("(%p)\n", hConv);

1432
    pConv = WDML_GetConv(hConv, TRUE);
1433 1434 1435 1436 1437 1438
    if (pConv)
    {
	ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
    }
    return ret;
}