dde_client.c 38.5 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 32
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
33
#include "winnls.h"
34 35
#include "dde.h"
#include "ddeml.h"
36
#include "win.h"
37
#include "wine/debug.h"
38
#include "dde_private.h"
39

40
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
41 42

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

/******************************************************************************
 * 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,
61
				HCONVLIST hConvList, PCONVCONTEXT pCC)
62
{
63
    FIXME("(%ld,%p,%p,%p,%p): stub\n", idInst, hszService, hszTopic, hConvList, pCC);
64 65 66 67 68 69 70 71
    return (HCONVLIST)1;
}

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

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

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

104
    TRACE("(0x%lx,%p,%p,%p)\n", idInst, hszService, hszTopic, pCC);
105 106 107 108 109

    EnterCriticalSection(&WDML_CritSect);

    pInstance = WDML_GetInstance(idInst);
    if (!pInstance)
110
    {
111
	goto theEnd;
112
    }
113

114
    /* make sure this conv is never created */
115
    pConv = WDML_FindConv(pInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
116 117
    if (pConv != NULL)
    {
118
	ERR("This Conv already exists: (%p)\n", pConv);
119
	goto theEnd;
120
    }
121

122 123
    /* we need to establish a conversation with
       server, so create a window for it       */
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 162 163 164 165 166
    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);
    }
167

168
    SetWindowLongPtrW(hwndClient, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
169

170 171 172 173 174 175 176 177 178 179 180 181 182
    if (hszService)
    {
	aSrv = WDML_MakeAtomFromHsz(hszService);
	if (!aSrv) goto theEnd;
    }
    if (hszTopic)
    {
	aTpc = WDML_MakeAtomFromHsz(hszTopic);
	if (!aTpc) goto theEnd;
    }

    LeaveCriticalSection(&WDML_CritSect);

183
    /* note: sent messages shall not use packing */
184 185
    SendMessageTimeoutW( HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, MAKELPARAM(aSrv, aTpc),
                         SMTO_ABORTIFHUNG, 2000, NULL );
186

187 188 189 190 191 192 193
    EnterCriticalSection(&WDML_CritSect);

    pInstance = WDML_GetInstance(idInst);
    if (!pInstance)
    {
	goto theEnd;
    }
194

195 196 197
    /* 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 */
198
    pConv = WDML_GetConvFromWnd(hwndClient);
199 200
    if (pConv == NULL || pConv->hwndServer == 0)
    {
201
	WARN("Done with INITIATE, but no Server window available\n");
202 203
	pConv = NULL;
	goto theEnd;
204
    }
205
    TRACE("Connected to Server window (%p)\n", pConv->hwndServer);
206 207
    pConv->wConvst = XST_CONNECTED;

208 209 210 211 212
    /* finish init of pConv */
    if (pCC != NULL)
    {
	pConv->convContext = *pCC;
    }
213
    else
214
    {
215 216
	memset(&pConv->convContext, 0, sizeof(pConv->convContext));
	pConv->convContext.cb = sizeof(pConv->convContext);
217
	pConv->convContext.iCodePage = (pInstance->unicode) ? CP_WINUNICODE : CP_WINANSI;
218 219
    }

220 221 222 223 224 225 226
 theEnd:
    LeaveCriticalSection(&WDML_CritSect);

    if (aSrv) GlobalDeleteAtom(aSrv);
    if (aTpc) GlobalDeleteAtom(aTpc);
    return (HCONV)pConv;
}
227 228 229 230 231 232 233

/*****************************************************************
 *            DdeReconnect   (DDEML.37)
 *            DdeReconnect   (USER32.@)
 */
HCONV WINAPI DdeReconnect(HCONV hConv)
{
234 235 236 237
    WDML_CONV*	pConv;
    WDML_CONV*	pNewConv = NULL;
    ATOM	aSrv = 0, aTpc = 0;

238 239
    TRACE("(%p)\n", hConv);

240 241 242 243 244 245 246 247
    EnterCriticalSection(&WDML_CritSect);
    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
248
	 *    if a call to DdeReconnect would have already been done...)
249 250 251 252 253 254 255 256 257 258
	 *    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;
	    ATOM	aSrv, aTpc;

259
	    SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, 0);
260 261 262 263 264 265 266

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

	    LeaveCriticalSection(&WDML_CritSect);

267
            /* note: sent messages shall not use packing */
268
	    ret = SendMessageW(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient,
269
                               MAKELPARAM(aSrv, aTpc));
270

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	    EnterCriticalSection(&WDML_CritSect);

	    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... */
290
			DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt,
291 292 293 294 295 296 297
					     pLink->transactionType, 1000, NULL);
		    }
		}
	    }
	    else
	    {
		/* reset the conversation as it was */
298
		SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
299 300 301
	    }
	}
    }
302

303 304 305 306 307 308 309
 theEnd:
    LeaveCriticalSection(&WDML_CritSect);

    if (aSrv) GlobalDeleteAtom(aSrv);
    if (aTpc) GlobalDeleteAtom(aTpc);
    return (HCONV)pNewConv;
}
310 311

/******************************************************************
312
 *		WDML_ClientQueueAdvise
313 314 315
 *
 * Creates and queue an WM_DDE_ADVISE transaction
 */
316
static WDML_XACT*	WDML_ClientQueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
317 318 319
{
    DDEADVISE*		pDdeAdvise;
    WDML_XACT*		pXAct;
320
    ATOM		atom;
321 322 323

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

324 325
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
326

327
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, wFmt, hszItem);
328
    if (!pXAct)
329 330
    {
	GlobalDeleteAtom(atom);
331
	return NULL;
332
    }
333

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

338
    /* pack DdeAdvise	*/
339
    pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
340 341 342
    pDdeAdvise->fAckReq   = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
    pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
    pDdeAdvise->cfFormat  = wFmt;
343
    GlobalUnlock(pXAct->hMem);
344

345
    pXAct->lParam = PackDDElParam(WM_DDE_ADVISE, (UINT_PTR)pXAct->hMem, atom);
346 347 348 349 350 351 352 353 354

    return pXAct;
}

/******************************************************************
 *		WDML_HandleAdviseReply
 *
 * handles the reply to an advise request
 */
355
static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
356 357
{
    DDEACK		ddeAck;
358
    UINT_PTR		uiLo, uiHi;
359 360
    HSZ			hsz;

361
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
362 363 364 365 366
    {
	return WDML_QS_PASS;
    }

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

369
    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
370 371 372
	return WDML_QS_PASS;

    GlobalDeleteAtom(uiHi);
373
    FreeDDElParam(WM_DDE_ACK, msg->lParam);
374

375
    if (ack) *ack = uiLo;
376
    WDML_ExtractAck(uiLo, &ddeAck);
377

378 379 380
    if (ddeAck.fAck)
    {
	WDML_LINK*	pLink;
381

382
	/* billx: first to see if the link is already created. */
383
	pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
384
			      pXAct->hszItem, TRUE, pXAct->wFmt);
385 386
	if (pLink != NULL)
	{
387
	    /* we found a link, and only need to modify it in case it changes */
388
	    pLink->transactionType = pXAct->wType;
389 390 391
	}
	else
	{
392
	    WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
393
			 pXAct->wType, pXAct->hszItem, pXAct->wFmt);
394
	}
395
        pXAct->hDdeData = (HDDEDATA)1;
396 397 398
    }
    else
    {
399
	TRACE("Returning FALSE on XTYP_ADVSTART - fAck was FALSE\n");
400
	GlobalFree(pXAct->hMem);
401
        pXAct->hDdeData = NULL;
402
    }
403

404 405 406 407
    return WDML_QS_HANDLED;
}

/******************************************************************
408
 *		WDML_ClientQueueUnadvise
409 410 411
 *
 * queues an unadvise transaction
 */
412
static WDML_XACT*	WDML_ClientQueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
413 414
{
    WDML_XACT*	pXAct;
415
    ATOM	atom;
416

417 418
    TRACE("XTYP_ADVSTOP transaction\n");

419 420
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
421

422 423
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem);
    if (!pXAct)
424
    {
425
	GlobalDeleteAtom(atom);
426 427
	return NULL;
    }
428 429

    /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
430
     * on the specified item.
431 432
     */
    pXAct->lParam = PackDDElParam(WM_DDE_UNADVISE, wFmt, atom);
433 434
    return pXAct;
}
435

436 437 438 439 440
/******************************************************************
 *		WDML_HandleUnadviseReply
 *
 *
 */
441
static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
442 443
{
    DDEACK	ddeAck;
444
    UINT_PTR	uiLo, uiHi;
445
    HSZ		hsz;
446

447
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
448 449 450 451 452
    {
	return WDML_QS_PASS;
    }

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

455
    if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
456 457
	return WDML_QS_PASS;

458
    FreeDDElParam(WM_DDE_ACK, msg->lParam);
459
    GlobalDeleteAtom(uiHi);
460

461
    if (ack) *ack = uiLo;
462
    WDML_ExtractAck(uiLo, &ddeAck);
463

464
    TRACE("WM_DDE_ACK received while waiting for a timeout\n");
465

466 467
    if (!ddeAck.fAck)
    {
468
	TRACE("Returning FALSE on XTYP_ADVSTOP - fAck was FALSE\n");
469
        pXAct->hDdeData = NULL;
470 471 472 473
    }
    else
    {
	/* billx: remove the link */
474
	WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
475
			pXAct->hszItem, pXAct->wFmt);
476
        pXAct->hDdeData = (HDDEDATA)1;
477 478 479 480 481
    }
    return WDML_QS_HANDLED;
}

/******************************************************************
482
 *		WDML_ClientQueueRequest
483 484 485
 *
 *
 */
486
static WDML_XACT*	WDML_ClientQueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
487 488
{
    WDML_XACT*	pXAct;
489
    ATOM	atom;
490

491
    TRACE("XTYP_REQUEST transaction\n");
492

493 494
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
495

496 497
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, wFmt, hszItem);
    if (!pXAct)
498
    {
499
	GlobalDeleteAtom(atom);
500 501
	return NULL;
    }
502 503 504

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

505 506 507 508 509 510 511 512
    return pXAct;
}

/******************************************************************
 *		WDML_HandleRequestReply
 *
 *
 */
513
static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
514
{
515 516
    DDEACK		ddeAck;
    WINE_DDEHEAD	wdh;
517
    UINT_PTR		uiLo, uiHi;
518 519
    HSZ			hsz;

520
    if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
521
	return WDML_QS_PASS;
522 523 524 525

    switch (msg->message)
    {
    case WM_DDE_ACK:
526 527
        UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
        FreeDDElParam(WM_DDE_ACK, msg->lParam);
528
	GlobalDeleteAtom(uiHi);
529
       if (ack) *ack = uiLo;
530
	WDML_ExtractAck(uiLo, &ddeAck);
531
	pXAct->hDdeData = 0;
532 533
	if (ddeAck.fAck)
	    ERR("Positive answer should appear in NACK for a request, assuming negative\n");
534 535 536 537
	TRACE("Negative answer...\n");
	break;

    case WM_DDE_DATA:
538
        UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
539
	TRACE("Got the result (%08x)\n", uiLo);
540 541 542 543

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

	if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
544
	    return WDML_QS_PASS;
545 546 547 548 549 550 551 552

	pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
	if (wdh.fRelease)
	{
	    GlobalFree((HGLOBAL)uiLo);
	}
	if (wdh.fAckReq)
	{
553
	    WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, msg->lParam, WM_DDE_DATA);
554 555 556 557
	}
	else
	{
	    GlobalDeleteAtom(uiHi);
558
            FreeDDElParam(WM_DDE_ACK, msg->lParam);
559
	}
560 561 562
	break;

    default:
563
        FreeDDElParam(msg->message, msg->lParam);
564 565 566 567
	return WDML_QS_PASS;
    }

    return WDML_QS_HANDLED;
568
}
569 570

/******************************************************************
571
 *		WDML_BuildExecuteCommand
572
 *
573 574
 * Creates a DDE block suitable for sending in WM_DDE_COMMAND
 * It also takes care of string conversion between the two window procedures
575
 */
576
static	HGLOBAL	WDML_BuildExecuteCommand(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
577
{
578 579 580
    HGLOBAL	hMem;
    BOOL	clientUnicode, serverUnicode;
    DWORD	memSize;
581

582 583 584 585 586
    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);
587

588
    if (clientUnicode == serverUnicode)
589
    {
590 591 592 593 594
	memSize = cbData;
    }
    else
    {
	if (clientUnicode)
595
	{
596 597 598 599
	    memSize = WideCharToMultiByte( CP_ACP, 0, pData, cbData, NULL, 0, NULL, NULL);
	}
	else
	{
600
	    memSize = MultiByteToWideChar( CP_ACP, 0, pData, cbData, NULL, 0) * sizeof(WCHAR);
601 602 603 604 605 606 607
	}
    }

    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, memSize);

    if (hMem)
    {
608
	LPSTR	pDst;
609

610 611 612 613 614 615 616 617 618 619
	pDst = GlobalLock(hMem);
	if (pDst)
	{
	    if (clientUnicode == serverUnicode)
	    {
		memcpy(pDst, pData, cbData);
	    }
	    else
	    {
		if (clientUnicode)
620
		{
621
		    WideCharToMultiByte( CP_ACP, 0, pData, cbData, pDst, memSize, NULL, NULL);
622 623 624
		}
		else
		{
625
		    MultiByteToWideChar( CP_ACP, 0, pData, cbData, (LPWSTR)pDst, memSize/sizeof(WCHAR));
626 627
		}
	    }
628 629

	    GlobalUnlock(hMem);
630 631 632
	}
	else
	{
633 634
	    GlobalFree(hMem);
	    hMem = 0;
635 636
	}
    }
637 638 639 640 641 642 643 644 645 646 647 648 649
    return hMem;
}

/******************************************************************
 *		WDML_ClientQueueExecute
 *
 *
 */
static WDML_XACT*	WDML_ClientQueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
{
    WDML_XACT*	pXAct;

    TRACE("XTYP_EXECUTE transaction\n");
650

651 652 653
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
    if (!pXAct)
	return NULL;
654

655 656 657
    if (cbData == (DWORD)-1)
    {
	HDDEDATA	hDdeData = (HDDEDATA)pData;
658

659 660
	pData = DdeAccessData(hDdeData, &cbData);
	if (pData)
661
	{
662 663
	    pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
	    DdeUnaccessData(hDdeData);
664 665
	}
    }
666
    else
667
    {
668
	pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
669
    }
670

671
    pXAct->lParam = (LPARAM)pXAct->hMem;
672

673 674 675 676 677 678 679 680
    return pXAct;
}

/******************************************************************
 *		WDML_HandleExecuteReply
 *
 *
 */
681
static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
682 683
{
    DDEACK	ddeAck;
684
    UINT_PTR	uiLo, uiHi;
685

686
    if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
687 688 689 690 691 692 693
    {
	return WDML_QS_PASS;
    }

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

694
    if ((HANDLE)uiHi != pXAct->hMem)
695
    {
696
        return WDML_QS_PASS;
697 698
    }

699
    if (ack) *ack = uiLo;
700
    WDML_ExtractAck(uiLo, &ddeAck);
701
    pXAct->hDdeData = (HDDEDATA)(UINT_PTR)ddeAck.fAck;
702

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

706 707 708 709
    return WDML_QS_HANDLED;
}

/******************************************************************
710
 *		WDML_ClientQueuePoke
711 712 713
 *
 *
 */
714
static WDML_XACT*	WDML_ClientQueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData,
715
					     UINT wFmt, HSZ hszItem)
716 717
{
    WDML_XACT*	pXAct;
718
    ATOM	atom;
719 720

    TRACE("XTYP_POKE transaction\n");
721

722 723
    atom = WDML_MakeAtomFromHsz(hszItem);
    if (!atom) return NULL;
724

725
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
726
    if (!pXAct)
727 728
    {
	GlobalDeleteAtom(atom);
729
	return NULL;
730
    }
731 732 733

    if (cbData == (DWORD)-1)
    {
734
	pXAct->hMem = (HDDEDATA)pData;
735 736 737 738 739
    }
    else
    {
	DDEPOKE*	ddePoke;

740 741
	pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
	ddePoke = GlobalLock(pXAct->hMem);
742
	if (ddePoke)
743 744 745 746
	{
	    memcpy(ddePoke->Value, pData, cbData);
	    ddePoke->fRelease = FALSE; /* FIXME: app owned ? */
	    ddePoke->cfFormat = wFmt;
747
	    GlobalUnlock(pXAct->hMem);
748 749 750
	}
    }

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 811 812 813 814 815 816 817 818
/******************************************************************
 *		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
 */
static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
{
    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 824 825
    {
	FIXME("hmmm shouldn't happen\n");
	return WDML_QS_PASS;
    }
    if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
    {
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 835 836 837
/******************************************************************
 *		WDML_HandleReplyData
 *
 *
 */
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 853
    hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);

    hDdeDataIn = WDML_Global2DataHandle((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 ? TRUE : FALSE, 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
    pConv->wStatus |= ST_TERMINATED;
    if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
    {
909
	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
910 911 912 913 914
			    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) */
915
	PostMessageW(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
916 917 918
	pConv->wStatus &= ~ST_CONNECTED;
    }
    /* have to keep connection around to allow reconnection */
919 920 921 922 923 924 925 926
    return WDML_QS_HANDLED;
}

/******************************************************************
 *		WDML_HandleReply
 *
 * handles any incoming reply, and try to match to an already sent request
 */
927
static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd, DWORD *ack)
928
{
929
    WDML_XACT*		pXAct = pConv->transactions;
930 931
    WDML_QUEUE_STATE	qs;

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

    /* now check the results */
966
    switch (qs)
967 968
    {
    case WDML_QS_ERROR:
969
    case WDML_QS_SWALLOWED:
970 971 972 973 974 975 976
	*hdd = 0;
	break;
    case WDML_QS_HANDLED:
	/* ok, we have resolved a pending transaction
	 * notify callback if asynchronous, and remove it in any case
	 */
	WDML_UnQueueTransaction(pConv, pXAct);
977
	if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
978
	{
979 980 981 982
	    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;
983
	}
984 985 986 987
	else
	{
	    *hdd = pXAct->hDdeData;
	}
988
	WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
989 990
	break;
    case WDML_QS_PASS:
991
	/* no pending transaction found, try a warm/hot link or a termination request */
992 993 994
	switch (msg->message)
	{
	case WM_DDE_DATA:
995
	    qs = WDML_HandleIncomingData(pConv, msg, hdd);
996 997
	    break;
	case WM_DDE_TERMINATE:
998
	    qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
999 1000 1001
	    break;
	}
	break;
1002 1003 1004
    case WDML_QS_BLOCK:
	FIXME("shouldn't be used on client side\n");
	break;
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    }

    return qs;
}

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

    TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout);

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

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

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

                EnterCriticalSection(&WDML_CritSect);

                pConv = WDML_GetConv(hConv, FALSE);
                if (pConv == NULL)
                {
                    LeaveCriticalSection(&WDML_CritSect);
                    /* 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;
                        LeaveCriticalSection(&WDML_CritSect);
                        return hdd;
                    }
                }
                else
                {
                    LeaveCriticalSection(&WDML_CritSect);
                    DispatchMessageW(&msg);
                }
            }
1076 1077 1078 1079 1080
	}
    }

    TRACE("Timeout !!\n");

1081 1082 1083 1084 1085 1086
    EnterCriticalSection(&WDML_CritSect);

    pConv = WDML_GetConv(hConv, FALSE);
    if (pConv != NULL)
    {
	if (pConv->transactions)
1087
	{
1088 1089 1090 1091 1092 1093 1094 1095 1096
	    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;
	    }
1097

1098 1099
	    pConv->instance->lastError = err;
	}
1100
    }
1101 1102
    LeaveCriticalSection(&WDML_CritSect);

1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
    return 0;
}

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

1116
    TRACE("(%p,%ld,%p,%p,%x,%x,%ld,%p)\n",
1117
	  pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
1118

1119 1120
    if (hConv == 0)
    {
1121
	WARN("Invalid conversation handle NULL\n");
1122 1123
	return 0;
    }
1124

1125
    EnterCriticalSection(&WDML_CritSect);
1126

1127
    pConv = WDML_GetConv(hConv, TRUE);
1128 1129 1130 1131 1132 1133 1134 1135 1136
    if (pConv == NULL)
    {
	/* cannot set error... cannot get back to DDE instance */
	goto theError;
    }

    switch (wType)
    {
    case XTYP_EXECUTE:
1137
        /* Windows simply ignores hszItem and wFmt in this case */
1138
	pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
1139 1140
	break;
    case XTYP_POKE:
1141
	pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
1142 1143 1144 1145 1146 1147 1148
	break;
    case XTYP_ADVSTART|XTYPF_NODATA:
    case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
    case XTYP_ADVSTART:
    case XTYP_ADVSTART|XTYPF_ACKREQ:
	if (pData)
	{
1149
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1150 1151
	    goto theError;
	}
1152
	pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
1153 1154 1155 1156
	break;
    case XTYP_ADVSTOP:
	if (pData)
	{
1157
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1158 1159
	    goto theError;
	}
1160
	pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
1161 1162 1163 1164
	break;
    case XTYP_REQUEST:
	if (pData)
	{
1165
	    pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1166 1167
	    goto theError;
	}
1168
	pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
1169 1170 1171 1172
	break;
    default:
	FIXME("Unknown transation\n");
	/* unknown transaction type */
1173
	pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1174 1175 1176
	goto theError;
    }

1177 1178 1179 1180 1181 1182 1183 1184
    if (pXAct == NULL)
    {
	pConv->instance->lastError = DMLERR_MEMORY_ERROR;
	goto theError;
    }

    WDML_QueueTransaction(pConv, pXAct);

1185
    if (!PostMessageW(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam))
1186
    {
1187
	WARN("Failed posting message %x to %p (error=0x%lx)\n",
1188 1189 1190 1191 1192 1193
	      pXAct->ddeMsg, pConv->hwndServer, GetLastError());
	pConv->wStatus &= ~ST_CONNECTED;
	WDML_UnQueueTransaction(pConv, pXAct);
	WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
	goto theError;
    }
1194 1195
    pXAct->dwTimeout = dwTimeout;
    /* FIXME: should set the app bits on *pdwResult */
1196

1197 1198 1199 1200 1201 1202 1203
    if (dwTimeout == TIMEOUT_ASYNC)
    {
	if (pdwResult)
	{
	    *pdwResult = MAKELONG(0, pXAct->xActID);
	}
	hDdeData = (HDDEDATA)1;
1204 1205
    }
    else
1206
    {
1207
	DWORD	count, i;
1208

1209 1210 1211
	count = WDML_CritSect.RecursionCount;
	for (i = 0; i < count; i++)
	    LeaveCriticalSection(&WDML_CritSect);
1212
	hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct, pdwResult);
1213 1214
	for (i = 0; i < count; i++)
	    EnterCriticalSection(&WDML_CritSect);
1215
    }
1216 1217
    LeaveCriticalSection(&WDML_CritSect);

1218 1219
    return hDdeData;
 theError:
1220
    LeaveCriticalSection(&WDML_CritSect);
1221 1222 1223
    return 0;
}

1224 1225 1226 1227 1228 1229 1230 1231 1232
/*****************************************************************
 *            DdeAbandonTransaction (USER32.@)
 */
BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
{
    WDML_INSTANCE*	pInstance;
    WDML_CONV*		pConv;
    WDML_XACT*          pXAct;

1233
    TRACE("(%08lx,%p,%08lx);\n", idInst, hConv, idTransaction);
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243

    EnterCriticalSection(&WDML_CritSect);
    if ((pInstance = WDML_GetInstance(idInst)))
    {
        if (hConv)
        {
            if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance)
            {
                for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
                {
1244
                    if (pXAct->dwTimeout == TIMEOUT_ASYNC &&
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
                        (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)
            {
1257
                if (!(pConv->wStatus & ST_CONNECTED)) continue;
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
                for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
                {
                    if (pXAct->dwTimeout == TIMEOUT_ASYNC)
                    {
                        WDML_UnQueueTransaction(pConv, pXAct);
                        WDML_FreeTransaction(pInstance, pXAct, TRUE);
                    }
                }
            }
        }
    }
    LeaveCriticalSection(&WDML_CritSect);

    return TRUE;
}

1274 1275 1276 1277 1278 1279 1280
/******************************************************************
 *		WDML_ClientProc
 *
 * Window Proc created on client side for each conversation
 */
static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
1281 1282 1283 1284
    UINT	uiLo, uiHi;
    WDML_CONV*	pConv = NULL;
    HSZ		hszSrv, hszTpc;

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

1287
    if (iMsg == WM_DDE_ACK &&
1288 1289
	/* in the initial WM_INITIATE sendmessage */
	((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
1290
    {
1291
	/* In response to WM_DDE_INITIATE, save server window  */
1292
	char		buf[256];
1293
	WDML_INSTANCE*	pInstance;
1294

1295 1296 1297
        /* note: sent messages do not need packing */
	uiLo = LOWORD(lParam);
        uiHi = HIWORD(lParam);
1298 1299 1300 1301 1302 1303 1304

	/* FIXME: convlist should be handled here */
	if (pConv)
	{
	    /* we already have started the conv with a server, drop other replies */
	    GlobalDeleteAtom(uiLo);
	    GlobalDeleteAtom(uiHi);
1305
            PostMessageW((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
1306 1307 1308 1309 1310 1311 1312 1313
	    return 0;
	}

	pInstance = WDML_GetInstanceFromWnd(hwnd);

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

1314
	pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, hwnd, (HWND)wParam);
1315

1316
	SetWindowLongPtrW(hwnd, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
1317 1318 1319 1320
	pConv->wStatus |= ST_CONNECTED;
	pConv->wConvst = XST_INIT1;

	/* check if server is handled by DDEML */
1321 1322 1323 1324
	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))
1325 1326 1327 1328
	{
	    pConv->wStatus |= ST_ISLOCAL;
	}

1329
	WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_CONNECT_CONFIRM, (WPARAM)hwnd, wParam);
1330 1331 1332 1333 1334 1335

	GlobalDeleteAtom(uiLo);
	GlobalDeleteAtom(uiHi);

	/* accept conversation */
	return 1;
1336
    }
1337 1338

    if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
1339
    {
1340 1341 1342
	EnterCriticalSection(&WDML_CritSect);

	pConv = WDML_GetConvFromWnd(hwnd);
1343

1344
	if (pConv)
1345 1346 1347 1348 1349 1350 1351 1352 1353
	{
	    MSG		msg;
	    HDDEDATA	hdd;

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

1354
	    WDML_HandleReply(pConv, &msg, &hdd, NULL);
1355 1356
	}

1357
	LeaveCriticalSection(&WDML_CritSect);
1358 1359
	return 0;
    }
1360

1361 1362
    return IsWindowUnicode(hwnd) ? DefWindowProcW(hwnd, iMsg, wParam, lParam) :
                                   DefWindowProcA(hwnd, iMsg, wParam, lParam);
1363 1364
}

1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
/*****************************************************************
 *            DdeDisconnect   (USER32.@)
 */
BOOL WINAPI DdeDisconnect(HCONV hConv)
{
    WDML_CONV*	pConv = NULL;
    WDML_XACT*	pXAct;
    DWORD	count, i;
    BOOL	ret = FALSE;

1375
    TRACE("(%p)\n", hConv);
1376

1377 1378
    if (hConv == 0)
    {
1379
	WARN("DdeDisconnect(): hConv = 0\n");
1380 1381
	return FALSE;
    }
1382

1383
    EnterCriticalSection(&WDML_CritSect);
1384
    pConv = WDML_GetConv(hConv, TRUE);
1385 1386
    if (pConv != NULL)
    {
1387 1388 1389 1390 1391 1392 1393 1394 1395
        if (pConv->wStatus & ST_CLIENT)
        {
            /* FIXME: should abandon all pending transactions */
            pXAct = WDML_ClientQueueTerminate(pConv);
            if (pXAct != NULL)
            {
                count = WDML_CritSect.RecursionCount;
                for (i = 0; i < count; i++)
                    LeaveCriticalSection(&WDML_CritSect);
1396
                if (PostMessageW(pConv->hwndServer, pXAct->ddeMsg,
1397
                                 (WPARAM)pConv->hwndClient, pXAct->lParam))
1398
                    WDML_SyncWaitTransactionReply(hConv, 10000, pXAct, NULL);
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
                for (i = 0; i < count; i++)
                    EnterCriticalSection(&WDML_CritSect);
                ret = TRUE;
                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");
            }
        }
1411 1412 1413 1414 1415
    }
    LeaveCriticalSection(&WDML_CritSect);

    return ret;
}
1416 1417 1418 1419 1420 1421 1422 1423

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

1425 1426
    TRACE("(%p)\n", hConv);

1427 1428
    EnterCriticalSection(&WDML_CritSect);
    pConv = WDML_GetConv(hConv, TRUE);
1429 1430 1431 1432
    if (pConv)
    {
	ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
    }
1433
    LeaveCriticalSection(&WDML_CritSect);
1434 1435
    return ret;
}