xref: /reactos/win32ss/user/user32/misc/ddemisc.c (revision 3d93998d)
1 /*
2  * DDEML library
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1997 Len White
6  * Copyright 1999 Keith Matthews
7  * Copyright 2000 Corel
8  * Copyright 2001 Eric Pouech
9  * Copyright 2003, 2004, 2005 Dmitry Timoshkov
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * 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 <user32.h>
27 
28 #include "dde_private.h"
29 
30 WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
31 
32 /* convert between ATOM and HSZ avoiding compiler warnings */
33 #define ATOM2HSZ(atom)	((HSZ)	(ULONG_PTR)(atom))
34 #define HSZ2ATOM(hsz)	((ATOM)	(ULONG_PTR)(hsz))
35 
36 static WDML_INSTANCE*	WDML_InstanceList = NULL;
37 static LONG		WDML_MaxInstanceID = 0;  /* OK for present, have to worry about wrap-around later */
38 const WCHAR		WDML_szEventClass[] = L"DDEMLEvent";
39 
40 /* protection for instance list */
41 static CRITICAL_SECTION WDML_CritSect;
42 static CRITICAL_SECTION_DEBUG critsect_debug =
43 {
44     0, 0, &WDML_CritSect,
45     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
46       0, 0, { (DWORD_PTR)(__FILE__ ": WDML_CritSect") }
47 };
48 static CRITICAL_SECTION WDML_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
49 
50 /* ================================================================
51  *
52  * 			Pure DDE (non DDEML) management
53  *
54  * ================================================================ */
55 
56 
57 /*****************************************************************
58  *            PackDDElParam (USER32.@)
59  *
60  * RETURNS
61  *   the packed lParam
62  */
PackDDElParam(UINT msg,UINT_PTR uiLo,UINT_PTR uiHi)63 LPARAM WINAPI PackDDElParam(UINT msg, UINT_PTR uiLo, UINT_PTR uiHi)
64 {
65     HGLOBAL hMem;
66     UINT_PTR *params;
67 
68     switch (msg)
69     {
70     case WM_DDE_ACK:
71     case WM_DDE_ADVISE:
72     case WM_DDE_DATA:
73     case WM_DDE_POKE:
74         if (!(hMem = GlobalAlloc(GMEM_DDESHARE, sizeof(UINT_PTR) * 2)))
75         {
76             ERR("GlobalAlloc failed\n");
77             return 0;
78         }
79         if (!(params = GlobalLock(hMem)))
80         {
81             ERR("GlobalLock failed (%p)\n", hMem);
82             return 0;
83         }
84         params[0] = uiLo;
85         params[1] = uiHi;
86         GlobalUnlock(hMem);
87         return (LPARAM)hMem;
88 
89     case WM_DDE_EXECUTE:
90         return uiHi;
91 
92     default:
93         return MAKELONG(uiLo, uiHi);
94     }
95 }
96 
97 
98 /*****************************************************************
99  *            UnpackDDElParam (USER32.@)
100  *
101  * RETURNS
102  *   success: nonzero
103  *   failure: zero
104  */
UnpackDDElParam(UINT msg,LPARAM lParam,PUINT_PTR uiLo,PUINT_PTR uiHi)105 BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
106 			    PUINT_PTR uiLo, PUINT_PTR uiHi)
107 {
108     UINT_PTR *params;
109 
110     switch (msg)
111     {
112     case WM_DDE_ACK:
113     case WM_DDE_ADVISE:
114     case WM_DDE_DATA:
115     case WM_DDE_POKE:
116         if (!lParam || !(params = GlobalLock((HGLOBAL)lParam)))
117         {
118             if (uiLo) *uiLo = 0;
119             if (uiHi) *uiHi = 0;
120             return FALSE;
121         }
122         if (uiLo) *uiLo = params[0];
123         if (uiHi) *uiHi = params[1];
124         GlobalUnlock( (HGLOBAL)lParam );
125         return TRUE;
126 
127     case WM_DDE_EXECUTE:
128         if (uiLo) *uiLo = 0;
129         if (uiHi) *uiHi = lParam;
130         return TRUE;
131 
132     default:
133         if (uiLo) *uiLo = LOWORD(lParam);
134         if (uiHi) *uiHi = HIWORD(lParam);
135         return TRUE;
136     }
137 }
138 
139 
140 /*****************************************************************
141  *            FreeDDElParam (USER32.@)
142  *
143  * RETURNS
144  *   success: nonzero
145  *   failure: zero
146  */
FreeDDElParam(UINT msg,LPARAM lParam)147 BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
148 {
149     switch (msg)
150     {
151     case WM_DDE_ACK:
152     case WM_DDE_ADVISE:
153     case WM_DDE_DATA:
154     case WM_DDE_POKE:
155         /* first check if it's a global handle */
156         if (!GlobalHandle( (LPVOID)lParam )) return TRUE;
157         return !GlobalFree( (HGLOBAL)lParam );
158 
159     default:
160         return TRUE;
161      }
162 }
163 
164 
165 /*****************************************************************
166  *            ReuseDDElParam (USER32.@)
167  *
168  * RETURNS
169  *   the packed lParam
170  */
ReuseDDElParam(LPARAM lParam,UINT msgIn,UINT msgOut,UINT_PTR uiLo,UINT_PTR uiHi)171 LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
172                              UINT_PTR uiLo, UINT_PTR uiHi)
173 {
174     UINT_PTR *params;
175 
176     switch (msgIn)
177     {
178     case WM_DDE_ACK:
179     case WM_DDE_ADVISE:
180     case WM_DDE_DATA:
181     case WM_DDE_POKE:
182         switch(msgOut)
183         {
184         case WM_DDE_ACK:
185         case WM_DDE_ADVISE:
186         case WM_DDE_DATA:
187         case WM_DDE_POKE:
188             if (!lParam) return 0;
189             if (!(params = GlobalLock( (HGLOBAL)lParam )))
190             {
191                 ERR("GlobalLock failed\n");
192                 return 0;
193             }
194             params[0] = uiLo;
195             params[1] = uiHi;
196             TRACE("Reusing pack %08lx %08lx\n", uiLo, uiHi);
197             GlobalUnlock( (HGLOBAL)lParam );
198             return lParam;
199 
200         case WM_DDE_EXECUTE:
201             FreeDDElParam( msgIn, lParam );
202             return uiHi;
203 
204         default:
205             FreeDDElParam( msgIn, lParam );
206             return MAKELPARAM(uiLo, uiHi);
207         }
208 
209     default:
210         return PackDDElParam( msgOut, uiLo, uiHi );
211     }
212 }
213 
214 /*****************************************************************
215  *            ImpersonateDdeClientWindow (USER32.@)
216  *
217  * PARAMS
218  * hWndClient	  [I] handle to DDE client window
219  * hWndServer	  [I] handle to DDE server window
220  */
ImpersonateDdeClientWindow(HWND hWndClient,HWND hWndServer)221 BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
222 {
223      FIXME("(%p %p): stub\n", hWndClient, hWndServer);
224      return FALSE;
225 }
226 
227 /*****************************************************************
228  *            DdeSetQualityOfService (USER32.@)
229  */
230 
DdeSetQualityOfService(HWND hwndClient,const SECURITY_QUALITY_OF_SERVICE * pqosNew,PSECURITY_QUALITY_OF_SERVICE pqosPrev)231 BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, const SECURITY_QUALITY_OF_SERVICE *pqosNew,
232 				   PSECURITY_QUALITY_OF_SERVICE pqosPrev)
233 {
234      FIXME("(%p %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
235      return TRUE;
236 }
237 
238 /* ================================================================
239  *
240  * 			WDML Error management
241  *
242  * ================================================================ */
243 
244 /******************************************************************************
245  * DdeGetLastError [USER32.@]  Gets most recent error code
246  *
247  * PARAMS
248  *    idInst [I] Instance identifier
249  *
250  * RETURNS
251  *    Last error code
252  */
DdeGetLastError(DWORD idInst)253 UINT WINAPI DdeGetLastError(DWORD idInst)
254 {
255     DWORD		error_code;
256     WDML_INSTANCE*	pInstance;
257 
258     /*  First check instance
259      */
260     pInstance = WDML_GetInstance(idInst);
261     if  (pInstance == NULL)
262     {
263 	error_code = DMLERR_INVALIDPARAMETER;
264     }
265     else
266     {
267 	error_code = pInstance->lastError;
268 	pInstance->lastError = 0;
269     }
270 
271     return error_code;
272 }
273 
274 /******************************************************************
275  *		WDML_SetAllLastError
276  *
277  *
278  */
WDML_SetAllLastError(DWORD lastError)279 static void	WDML_SetAllLastError(DWORD lastError)
280 {
281     DWORD		threadID;
282     WDML_INSTANCE*	pInstance;
283     threadID = GetCurrentThreadId();
284     pInstance = WDML_InstanceList;
285     while (pInstance)
286     {
287 	if (pInstance->threadID == threadID)
288 	    pInstance->lastError = lastError;
289 	pInstance = pInstance->next;
290     }
291 }
292 
293 /* ================================================================
294  *
295  * 			String management
296  *
297  * ================================================================ */
298 
299 
300 /******************************************************************
301  *		WDML_FindNode
302  *
303  *
304  */
WDML_FindNode(WDML_INSTANCE * pInstance,HSZ hsz)305 static HSZNode*	WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
306 {
307     HSZNode*	pNode;
308 
309     if (pInstance == NULL) return NULL;
310 
311     for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
312     {
313 	if (pNode->hsz == hsz) break;
314     }
315     if (!pNode) WARN("HSZ %p not found\n", hsz);
316     return pNode;
317 }
318 
319 /******************************************************************
320  *		WDML_MakeAtomFromHsz
321  *
322  * Creates a global atom from an existing HSZ
323  * Generally used before sending an HSZ as an atom to a remote app
324  */
WDML_MakeAtomFromHsz(HSZ hsz)325 ATOM	WDML_MakeAtomFromHsz(HSZ hsz)
326 {
327     WCHAR nameBuffer[MAX_BUFFER_LEN];
328 
329     if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
330 	return GlobalAddAtomW(nameBuffer);
331     WARN("HSZ %p not found\n", hsz);
332     return 0;
333 }
334 
335 /******************************************************************
336  *		WDML_MakeHszFromAtom
337  *
338  * Creates a HSZ from an existing global atom
339  * Generally used while receiving a global atom and transforming it
340  * into an HSZ
341  */
WDML_MakeHszFromAtom(const WDML_INSTANCE * pInstance,ATOM atom)342 HSZ	WDML_MakeHszFromAtom(const WDML_INSTANCE* pInstance, ATOM atom)
343 {
344     WCHAR nameBuffer[MAX_BUFFER_LEN];
345 
346     if (!atom) return NULL;
347 
348     if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
349     {
350 	TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
351 	return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
352     }
353     WARN("ATOM 0x%x not found\n", atom);
354     return 0;
355 }
356 
357 /******************************************************************
358  *		WDML_IncHSZ
359  *
360  *
361  */
WDML_IncHSZ(WDML_INSTANCE * pInstance,HSZ hsz)362 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
363 {
364     HSZNode*	pNode;
365 
366     pNode = WDML_FindNode(pInstance, hsz);
367     if (!pNode) return FALSE;
368 
369     pNode->refCount++;
370     return TRUE;
371 }
372 
373 /******************************************************************************
374  *           WDML_DecHSZ    (INTERNAL)
375  *
376  * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
377  * of HSZ nodes
378  * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
379  */
WDML_DecHSZ(WDML_INSTANCE * pInstance,HSZ hsz)380 BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
381 {
382     HSZNode* 	pPrev = NULL;
383     HSZNode* 	pCurrent;
384 
385     for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
386     {
387 	/* If we found the node we were looking for and its ref count is one,
388 	 * we can remove it
389 	 */
390 	if (pCurrent->hsz == hsz)
391 	{
392 	    if (--pCurrent->refCount == 0)
393 	    {
394 		if (pCurrent == pInstance->nodeList)
395 		{
396 		    pInstance->nodeList = pCurrent->next;
397 		}
398 		else
399 		{
400 		    pPrev->next = pCurrent->next;
401 		}
402 		HeapFree(GetProcessHeap(), 0, pCurrent);
403 		DeleteAtom(HSZ2ATOM(hsz));
404 	    }
405 	    return TRUE;
406 	}
407     }
408     WARN("HSZ %p not found\n", hsz);
409 
410     return FALSE;
411 }
412 
413 /******************************************************************************
414  *            WDML_FreeAllHSZ    (INTERNAL)
415  *
416  * Frees up all the strings still allocated in the list and
417  * remove all the nodes from the list of HSZ nodes.
418  */
WDML_FreeAllHSZ(WDML_INSTANCE * pInstance)419 static void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
420 {
421     /* Free any strings created in this instance.
422      */
423     while (pInstance->nodeList != NULL)
424     {
425 	DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
426     }
427 }
428 
429 /******************************************************************************
430  *            InsertHSZNode    (INTERNAL)
431  *
432  * Insert a node to the head of the list.
433  */
WDML_InsertHSZNode(WDML_INSTANCE * pInstance,HSZ hsz)434 static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
435 {
436     if (hsz != 0)
437     {
438 	HSZNode* pNew = NULL;
439 	/* Create a new node for this HSZ.
440 	 */
441 	pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
442 	if (pNew != NULL)
443 	{
444 	    pNew->hsz      = hsz;
445 	    pNew->next     = pInstance->nodeList;
446 	    pNew->refCount = 1;
447 	    pInstance->nodeList = pNew;
448 	}
449 	else
450 	{
451 	    ERR("Primary HSZ Node allocation failed - out of memory\n");
452 	}
453     }
454 }
455 
456 /******************************************************************
457  *		WDML_QueryString
458  *
459  *
460  */
WDML_QueryString(WDML_INSTANCE * pInstance,HSZ hsz,LPVOID ptr,DWORD cchMax,int codepage)461 static int	WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
462 				 int codepage)
463 {
464     WCHAR	pString[MAX_BUFFER_LEN];
465     int		ret;
466     /* If psz is null, we have to return only the length
467      * of the string.
468      */
469     if (ptr == NULL)
470     {
471 	ptr = pString;
472 	cchMax = MAX_BUFFER_LEN;
473     }
474 
475     /* if there is no input windows returns a NULL string */
476     if (hsz == NULL)
477     {
478 	CHAR *t_ptr = ptr;
479 	*t_ptr = '\0';
480 	return 1;
481     }
482 
483     switch (codepage)
484     {
485     case CP_WINANSI:
486 	ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
487 	break;
488     case CP_WINUNICODE:
489 	ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
490         break;
491     default:
492 	ERR("Unknown code page %d\n", codepage);
493 	ret = 0;
494     }
495     return ret;
496 }
497 
498 /*****************************************************************
499  * DdeQueryStringA [USER32.@]
500  */
DdeQueryStringA(DWORD idInst,HSZ hsz,LPSTR psz,DWORD cchMax,INT iCodePage)501 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
502 {
503     DWORD		ret = 0;
504     WDML_INSTANCE*	pInstance;
505 
506     TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
507 
508     /*  First check instance
509      */
510     pInstance = WDML_GetInstance(idInst);
511     if (pInstance != NULL)
512     {
513 	if (iCodePage == 0) iCodePage = CP_WINANSI;
514 	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
515     }
516 
517     TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
518     return ret;
519 }
520 
521 /*****************************************************************
522  * DdeQueryStringW [USER32.@]
523  */
524 
DdeQueryStringW(DWORD idInst,HSZ hsz,LPWSTR psz,DWORD cchMax,INT iCodePage)525 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
526 {
527     DWORD		ret = 0;
528     WDML_INSTANCE*	pInstance;
529 
530     TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
531 
532     /*  First check instance
533      */
534     pInstance = WDML_GetInstance(idInst);
535     if (pInstance != NULL)
536     {
537 	if (iCodePage == 0) iCodePage = CP_WINUNICODE;
538 	ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
539     }
540 
541     TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
542     return ret;
543 }
544 
545 /******************************************************************
546  *		DML_CreateString
547  *
548  *
549  */
WDML_CreateString(WDML_INSTANCE * pInstance,LPCVOID ptr,int codepage)550 static	HSZ	WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
551 {
552     HSZ		hsz;
553 
554     switch (codepage)
555     {
556     case CP_WINANSI:
557 	hsz = ATOM2HSZ(AddAtomA(ptr));
558 	TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz);
559 	break;
560     case CP_WINUNICODE:
561 	hsz = ATOM2HSZ(AddAtomW(ptr));
562 	TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz);
563 	break;
564     default:
565 	ERR("Unknown code page %d\n", codepage);
566 	return 0;
567     }
568     WDML_InsertHSZNode(pInstance, hsz);
569     return hsz;
570 }
571 
572 /*****************************************************************
573  * DdeCreateStringHandleA [USER32.@]
574  *
575  * See DdeCreateStringHandleW.
576  */
DdeCreateStringHandleA(DWORD idInst,LPCSTR psz,INT codepage)577 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
578 {
579     HSZ			hsz = 0;
580     WDML_INSTANCE*	pInstance;
581 
582     TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage);
583 
584     pInstance = WDML_GetInstance(idInst);
585     if (pInstance == NULL)
586 	WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
587     else
588     {
589 	if (codepage == 0) codepage = CP_WINANSI;
590 	hsz = WDML_CreateString(pInstance, psz, codepage);
591     }
592 
593     return hsz;
594 }
595 
596 
597 /******************************************************************************
598  * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
599  *
600  * PARAMS
601  * 	idInst   [I] Instance identifier
602  * 	psz      [I] Pointer to string
603  *	codepage [I] Code page identifier
604  * RETURNS
605  *    Success: String handle
606  *    Failure: 0
607  */
DdeCreateStringHandleW(DWORD idInst,LPCWSTR psz,INT codepage)608 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
609 {
610     WDML_INSTANCE*	pInstance;
611     HSZ			hsz = 0;
612 
613     pInstance = WDML_GetInstance(idInst);
614     if (pInstance == NULL)
615 	WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
616     else
617     {
618 	if (codepage == 0) codepage = CP_WINUNICODE;
619 	hsz = WDML_CreateString(pInstance, psz, codepage);
620     }
621 
622     return hsz;
623 }
624 
625 /*****************************************************************
626  *            DdeFreeStringHandle   (USER32.@)
627  * RETURNS
628  *  success: nonzero
629  *  fail:    zero
630  */
DdeFreeStringHandle(DWORD idInst,HSZ hsz)631 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
632 {
633     WDML_INSTANCE*	pInstance;
634     BOOL		ret = FALSE;
635 
636     TRACE("(%d,%p):\n", idInst, hsz);
637 
638     /*  First check instance
639      */
640     pInstance = WDML_GetInstance(idInst);
641     if (pInstance)
642 	ret = WDML_DecHSZ(pInstance, hsz);
643 
644     return ret;
645 }
646 
647 /*****************************************************************
648  *            DdeKeepStringHandle  (USER32.@)
649  *
650  * RETURNS
651  *  success: nonzero
652  *  fail:    zero
653  */
DdeKeepStringHandle(DWORD idInst,HSZ hsz)654 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
655 {
656     WDML_INSTANCE*	pInstance;
657     BOOL		ret = FALSE;
658 
659     TRACE("(%d,%p):\n", idInst, hsz);
660 
661     /*  First check instance
662      */
663     pInstance = WDML_GetInstance(idInst);
664     if (pInstance)
665 	ret = WDML_IncHSZ(pInstance, hsz);
666 
667     return ret;
668 }
669 
670 /*****************************************************************
671  *            DdeCmpStringHandles (USER32.@)
672  *
673  * Compares the value of two string handles.  This comparison is
674  * not case sensitive.
675  *
676  * PARAMS
677  *  hsz1    [I] Handle to the first string
678  *  hsz2    [I] Handle to the second string
679  *
680  * RETURNS
681  *  -1 The value of hsz1 is zero or less than hsz2
682  *  0  The values of hsz 1 and 2 are the same or both zero.
683  *  1  The value of hsz2 is zero of less than hsz1
684  */
DdeCmpStringHandles(HSZ hsz1,HSZ hsz2)685 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
686 {
687     WCHAR	psz1[MAX_BUFFER_LEN];
688     WCHAR	psz2[MAX_BUFFER_LEN];
689     int		ret = 0;
690     int		ret1, ret2;
691 
692     ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
693     ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
694 
695     TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
696 
697     /* Make sure we found both strings. */
698     if (ret1 == 0 && ret2 == 0)
699     {
700 	/* If both are not found, return both  "zero strings". */
701 	ret = 0;
702     }
703     else if (ret1 == 0)
704     {
705 	/* If hsz1 is a not found, return hsz1 is "zero string". */
706 	ret = -1;
707     }
708     else if (ret2 == 0)
709     {
710 	/* If hsz2 is a not found, return hsz2 is "zero string". */
711 	ret = 1;
712     }
713     else
714     {
715 	/* Compare the two strings we got (case insensitive). */
716 	ret = lstrcmpiW(psz1, psz2);
717 	/* Since strcmp returns any number smaller than
718 	 * 0 when the first string is found to be less than
719 	 * the second one we must make sure we are returning
720 	 * the proper values.
721 	 */
722 	if (ret < 0)
723 	{
724 	    ret = -1;
725 	}
726 	else if (ret > 0)
727 	{
728 	    ret = 1;
729 	}
730     }
731 
732     return ret;
733 }
734 
735 /* ================================================================
736  *
737  * 			Instance management
738  *
739  * ================================================================ */
740 
741 /******************************************************************************
742  *		IncrementInstanceId
743  *
744  *	generic routine to increment the max instance Id and allocate a new application instance
745  */
WDML_IncrementInstanceId(WDML_INSTANCE * pInstance)746 static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
747 {
748     DWORD	id = InterlockedIncrement(&WDML_MaxInstanceID);
749 
750     pInstance->instanceID = id;
751     TRACE("New instance id %d allocated\n", id);
752 }
753 
754 /******************************************************************
755  *		WDML_EventProc
756  *
757  *
758  */
WDML_EventProc(HWND hwndEvent,UINT uMsg,WPARAM wParam,LPARAM lParam)759 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
760 {
761     WDML_INSTANCE*	pInstance;
762     HSZ			hsz1, hsz2;
763 
764     switch (uMsg)
765     {
766     case WM_WDML_REGISTER:
767 	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
768         /* try calling the Callback */
769 	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
770 	{
771 	    hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
772 	    hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
773 	    WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
774 	    WDML_DecHSZ(pInstance, hsz1);
775 	    WDML_DecHSZ(pInstance, hsz2);
776 	}
777 	break;
778 
779     case WM_WDML_UNREGISTER:
780 	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
781 	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
782 	{
783 	    hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
784 	    hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
785 	    WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
786 	    WDML_DecHSZ(pInstance, hsz1);
787 	    WDML_DecHSZ(pInstance, hsz2);
788 	}
789 	break;
790 
791     case WM_WDML_CONNECT_CONFIRM:
792 	pInstance = WDML_GetInstanceFromWnd(hwndEvent);
793 	if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
794 	{
795 	    WDML_CONV*	pConv;
796 	    /* confirm connection...
797 	     * lookup for this conv handle
798 	     */
799             HWND client = WIN_GetFullHandle( (HWND)wParam );
800             HWND server = WIN_GetFullHandle( (HWND)lParam );
801 	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
802 	    {
803 		if (pConv->hwndClient == client && pConv->hwndServer == server)
804 		    break;
805 	    }
806 	    if (pConv)
807 	    {
808 		pConv->wStatus |= ST_ISLOCAL;
809 
810 		WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
811 				    pConv->hszTopic, pConv->hszService, 0, 0,
812 				    (pConv->wStatus & ST_ISSELF) ? 1 : 0);
813 	    }
814 	}
815 	break;
816     default:
817 	return DefWindowProcW(hwndEvent, uMsg, wParam, lParam);
818     }
819     return 0;
820 }
821 
822 /******************************************************************
823  *		WDML_Initialize
824  *
825  *
826  */
WDML_Initialize(LPDWORD pidInst,PFNCALLBACK pfnCallback,DWORD afCmd,DWORD ulRes,BOOL bUnicode)827 static UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
828                             DWORD afCmd, DWORD ulRes, BOOL bUnicode)
829 {
830     WDML_INSTANCE*		pInstance;
831     WDML_INSTANCE*		reference_inst;
832     UINT			ret;
833     WNDCLASSEXW			wndclass;
834 
835     TRACE("(%p,%p,0x%x,%d,0x%x)\n",
836 	  pidInst, pfnCallback, afCmd, ulRes, bUnicode);
837 
838     if (ulRes)
839     {
840 	ERR("Reserved value not zero?  What does this mean?\n");
841 	/* trap this and no more until we know more */
842 	return DMLERR_NO_ERROR;
843     }
844 
845     /* grab enough heap for one control struct - not really necessary for re-initialise
846      *	but allows us to use same validation routines */
847     pInstance = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
848     if (pInstance == NULL)
849     {
850 	/* catastrophe !! warn user & abort */
851 	ERR("Instance create failed - out of memory\n");
852 	return DMLERR_SYS_ERROR;
853     }
854     pInstance->next = NULL;
855     pInstance->monitor = (afCmd | APPCLASS_MONITOR);
856 
857     /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
858 
859     pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
860     pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
861     pInstance->threadID = GetCurrentThreadId();
862     pInstance->callback = *pfnCallback;
863     pInstance->unicode = bUnicode;
864     pInstance->nodeList = NULL; /* node will be added later */
865     pInstance->monitorFlags = afCmd & MF_MASK;
866     pInstance->wStatus = 0;
867     pInstance->lastError = DMLERR_NO_ERROR;
868     pInstance->servers = NULL;
869     pInstance->convs[0] = NULL;
870     pInstance->convs[1] = NULL;
871     pInstance->links[0] = NULL;
872     pInstance->links[1] = NULL;
873 
874     /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
875 
876     pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
877 
878     if (!pInstance->clientOnly)
879     {
880 	/* Check for other way of setting Client-only !! */
881 	pInstance->clientOnly =
882 	    (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
883     }
884 
885     TRACE("instance created - checking validity\n");
886 
887     if (*pidInst == 0)
888     {
889 	/*  Initialisation of new Instance Identifier */
890 	TRACE("new instance, callback %p flags %X\n",pfnCallback,afCmd);
891 
892 	EnterCriticalSection(&WDML_CritSect);
893 
894 	if (WDML_InstanceList == NULL)
895 	{
896 	    /* can't be another instance in this case, assign to the base pointer */
897 	    WDML_InstanceList = pInstance;
898 
899 	    /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
900 	     *		present
901 	     *	-------------------------------      NOTE NOTE NOTE    --------------------------
902 	     *
903 	     *	the manual is not clear if this condition
904 	     *	applies to the first call to DdeInitialize from an application, or the
905 	     *	first call for a given callback !!!
906 	     */
907 
908 	    pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
909 	    TRACE("First application instance detected OK\n");
910 	    /*	allocate new instance ID */
911 	    WDML_IncrementInstanceId(pInstance);
912 	}
913 	else
914 	{
915 	    /* really need to chain the new one in to the latest here, but after checking conditions
916 	     *	such as trying to start a conversation from an application trying to monitor */
917 	    reference_inst = WDML_InstanceList;
918 	    TRACE("Subsequent application instance - starting checks\n");
919 	    while (reference_inst->next != NULL)
920 	    {
921 		/*
922 		 *	This set of tests will work if application uses same instance Id
923 		 *	at application level once allocated - which is what manual implies
924 		 *	should happen. If someone tries to be
925 		 *	clever (lazy ?) it will fail to pick up that later calls are for
926 		 *	the same application - should we trust them ?
927 		 */
928 		if (pInstance->instanceID == reference_inst->instanceID)
929 		{
930 		    /* Check 1 - must be same Client-only state */
931 
932 		    if (pInstance->clientOnly != reference_inst->clientOnly)
933 		    {
934 			ret = DMLERR_DLL_USAGE;
935 			goto theError;
936 		    }
937 
938 		    /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
939 
940 		    if (pInstance->monitor != reference_inst->monitor)
941 		    {
942 			ret = DMLERR_INVALIDPARAMETER;
943 			goto theError;
944 		    }
945 
946 		    /* Check 3 - must supply different callback address */
947 
948 		    if (pInstance->callback == reference_inst->callback)
949 		    {
950 			ret = DMLERR_DLL_USAGE;
951 			goto theError;
952 		    }
953 		}
954 		reference_inst = reference_inst->next;
955 	    }
956 	    /*  All cleared, add to chain */
957 
958 	    TRACE("Application Instance checks finished\n");
959 	    WDML_IncrementInstanceId(pInstance);
960 	    reference_inst->next = pInstance;
961 	}
962 	LeaveCriticalSection(&WDML_CritSect);
963 
964 	*pidInst = pInstance->instanceID;
965 
966 	/* for deadlock issues, windows must always be created when outside the critical section */
967 	wndclass.cbSize        = sizeof(wndclass);
968 	wndclass.style         = 0;
969 	wndclass.lpfnWndProc   = WDML_EventProc;
970 	wndclass.cbClsExtra    = 0;
971 	wndclass.cbWndExtra    = sizeof(ULONG_PTR);
972 	wndclass.hInstance     = 0;
973 	wndclass.hIcon         = 0;
974 	wndclass.hCursor       = 0;
975 	wndclass.hbrBackground = 0;
976 	wndclass.lpszMenuName  = NULL;
977 	wndclass.lpszClassName = WDML_szEventClass;
978 	wndclass.hIconSm       = 0;
979 
980 	RegisterClassExW(&wndclass);
981 
982 	pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL,
983 						WS_POPUP, 0, 0, 0, 0,
984 						0, 0, 0, 0);
985 
986 	SetWindowLongPtrW(pInstance->hwndEvent, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
987 
988 	TRACE("New application instance processing finished OK\n");
989     }
990     else
991     {
992 	/* Reinitialisation situation   --- FIX  */
993 	TRACE("reinitialisation of (%p,%p,0x%x,%d): stub\n", pidInst, pfnCallback, afCmd, ulRes);
994 
995 	EnterCriticalSection(&WDML_CritSect);
996 
997 	if (WDML_InstanceList == NULL)
998 	{
999 	    ret = DMLERR_INVALIDPARAMETER;
1000 	    goto theError;
1001 	}
1002 	/* can't reinitialise if we have initialised nothing !! */
1003 	reference_inst = WDML_InstanceList;
1004 	/* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
1005 	/*
1006 	 *	MS allows initialisation without specifying a callback, should we allow addition of the
1007 	 *	callback by a later call to initialise ? - if so this lot will have to change
1008 	 */
1009 	while (reference_inst->next != NULL)
1010 	{
1011 	    if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
1012 	    {
1013 		/* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
1014 
1015 		if (reference_inst->clientOnly)
1016 		{
1017 		    if  ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
1018 		    {
1019 				/* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
1020 
1021 			if (!(afCmd & APPCMD_CLIENTONLY))
1022 			{
1023 			    ret = DMLERR_INVALIDPARAMETER;
1024 			    goto theError;
1025 			}
1026 		    }
1027 		}
1028 		/* Check 2 - cannot change monitor modes */
1029 
1030 		if (pInstance->monitor != reference_inst->monitor)
1031 		{
1032 		    ret = DMLERR_INVALIDPARAMETER;
1033 		    goto theError;
1034 		}
1035 
1036 		/* Check 3 - trying to set Client-only via APPCMD when not set so previously */
1037 
1038 		if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
1039 		{
1040 		    ret = DMLERR_INVALIDPARAMETER;
1041 		    goto theError;
1042 		}
1043 		break;
1044 	    }
1045 	    reference_inst = reference_inst->next;
1046 	}
1047 	if (reference_inst->next == NULL)
1048 	{
1049 	    ret = DMLERR_INVALIDPARAMETER;
1050 	    goto theError;
1051 	}
1052 	/* All checked - change relevant flags */
1053 
1054 	reference_inst->CBFflags = pInstance->CBFflags;
1055 	reference_inst->clientOnly = pInstance->clientOnly;
1056 	reference_inst->monitorFlags = pInstance->monitorFlags;
1057 
1058 	HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
1059 
1060 	LeaveCriticalSection(&WDML_CritSect);
1061     }
1062 
1063     return DMLERR_NO_ERROR;
1064  theError:
1065     HeapFree(GetProcessHeap(), 0, pInstance);
1066     LeaveCriticalSection(&WDML_CritSect);
1067     return ret;
1068 }
1069 
1070 /******************************************************************************
1071  *            DdeInitializeA   (USER32.@)
1072  *
1073  * See DdeInitializeW.
1074  */
DdeInitializeA(LPDWORD pidInst,PFNCALLBACK pfnCallback,DWORD afCmd,DWORD ulRes)1075 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
1076 			   DWORD afCmd, DWORD ulRes)
1077 {
1078     return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE);
1079 }
1080 
1081 /******************************************************************************
1082  * DdeInitializeW [USER32.@]
1083  * Registers an application with the DDEML
1084  *
1085  * PARAMS
1086  *    pidInst     [I] Pointer to instance identifier
1087  *    pfnCallback [I] Pointer to callback function
1088  *    afCmd       [I] Set of command and filter flags
1089  *    ulRes       [I] Reserved
1090  *
1091  * RETURNS
1092  *    Success: DMLERR_NO_ERROR
1093  *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
1094  */
DdeInitializeW(LPDWORD pidInst,PFNCALLBACK pfnCallback,DWORD afCmd,DWORD ulRes)1095 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
1096 			   DWORD afCmd, DWORD ulRes)
1097 {
1098     return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE);
1099 }
1100 
1101 /*****************************************************************
1102  * DdeUninitialize [USER32.@]  Frees DDEML resources
1103  *
1104  * PARAMS
1105  *    idInst [I] Instance identifier
1106  *
1107  * RETURNS
1108  *    Success: TRUE
1109  *    Failure: FALSE
1110  */
1111 
DdeUninitialize(DWORD idInst)1112 BOOL WINAPI DdeUninitialize(DWORD idInst)
1113 {
1114     /*  Stage one - check if we have a handle for this instance
1115      */
1116     WDML_INSTANCE*		pInstance;
1117     WDML_CONV*			pConv;
1118     WDML_CONV*			pConvNext;
1119 
1120     TRACE("(%d)\n", idInst);
1121 
1122     /*  First check instance
1123      */
1124     pInstance = WDML_GetInstance(idInst);
1125     if (pInstance == NULL)
1126     {
1127 	/*
1128 	 *	Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
1129 	 */
1130 	return FALSE;
1131     }
1132 
1133     /* first terminate all conversations client side
1134      * this shall close existing links...
1135      */
1136     for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
1137     {
1138 	pConvNext = pConv->next;
1139 	DdeDisconnect((HCONV)pConv);
1140     }
1141     if (pInstance->convs[WDML_CLIENT_SIDE])
1142 	FIXME("still pending conversations\n");
1143 
1144     /* then unregister all known service names */
1145     DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
1146 
1147     /* Free the nodes that were not freed by this instance
1148      * and remove the nodes from the list of HSZ nodes.
1149      */
1150     WDML_FreeAllHSZ(pInstance);
1151 
1152     DestroyWindow(pInstance->hwndEvent);
1153 
1154     /* OK now delete the instance handle itself */
1155 
1156     if (WDML_InstanceList == pInstance)
1157     {
1158 	/* special case - the first/only entry */
1159 	WDML_InstanceList = pInstance->next;
1160     }
1161     else
1162     {
1163 	/* general case, remove entry */
1164 	WDML_INSTANCE*	inst;
1165 
1166 	for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
1167 	inst->next = pInstance->next;
1168     }
1169     /* release the heap entry
1170      */
1171     HeapFree(GetProcessHeap(), 0, pInstance);
1172 
1173     return TRUE;
1174 }
1175 
1176 /******************************************************************
1177  *		WDML_NotifyThreadExit
1178  *
1179  *
1180  */
WDML_NotifyThreadDetach(void)1181 void WDML_NotifyThreadDetach(void)
1182 {
1183     WDML_INSTANCE*	pInstance;
1184     WDML_INSTANCE*	next;
1185     DWORD		tid = GetCurrentThreadId();
1186 
1187     EnterCriticalSection(&WDML_CritSect);
1188     for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
1189     {
1190 	next = pInstance->next;
1191 	if (pInstance->threadID == tid)
1192 	{
1193             LeaveCriticalSection(&WDML_CritSect);
1194 	    DdeUninitialize(pInstance->instanceID);
1195             EnterCriticalSection(&WDML_CritSect);
1196 	}
1197     }
1198     LeaveCriticalSection(&WDML_CritSect);
1199 }
1200 
1201 /******************************************************************
1202  *		WDML_InvokeCallback
1203  *
1204  *
1205  */
WDML_InvokeCallback(WDML_INSTANCE * pInstance,UINT uType,UINT uFmt,HCONV hConv,HSZ hsz1,HSZ hsz2,HDDEDATA hdata,ULONG_PTR dwData1,ULONG_PTR dwData2)1206 HDDEDATA 	WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
1207 				    HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
1208 				    ULONG_PTR dwData1, ULONG_PTR dwData2)
1209 {
1210     HDDEDATA	ret;
1211 
1212     if (pInstance == NULL)
1213 	return NULL;
1214 
1215     TRACE("invoking CB[%p] (%x %x %p %p %p %p %lx %lx)\n",
1216 	  pInstance->callback, uType, uFmt,
1217 	  hConv, hsz1, hsz2, hdata, dwData1, dwData2);
1218     ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
1219     TRACE("done => %p\n", ret);
1220     return ret;
1221 }
1222 
1223 /*****************************************************************************
1224  *	WDML_GetInstance
1225  *
1226  *	generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
1227  *	for an instance Id, or NULL if the entry does not exist
1228  *
1229  */
WDML_GetInstance(DWORD instId)1230 WDML_INSTANCE*	WDML_GetInstance(DWORD instId)
1231 {
1232     WDML_INSTANCE*	pInstance;
1233 
1234     EnterCriticalSection(&WDML_CritSect);
1235 
1236     for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
1237     {
1238 	if (pInstance->instanceID == instId)
1239 	{
1240 	    if (GetCurrentThreadId() != pInstance->threadID)
1241 	    {
1242 		FIXME("Tried to get instance from wrong thread\n");
1243 		continue;
1244 	    }
1245 	    break;
1246 	}
1247     }
1248 
1249     LeaveCriticalSection(&WDML_CritSect);
1250 
1251     if (!pInstance)
1252         WARN("Instance entry missing for id %04x\n", instId);
1253     return pInstance;
1254 }
1255 
1256 /******************************************************************
1257  *		WDML_GetInstanceFromWnd
1258  *
1259  *
1260  */
WDML_GetInstanceFromWnd(HWND hWnd)1261 WDML_INSTANCE*	WDML_GetInstanceFromWnd(HWND hWnd)
1262 {
1263     return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE);
1264 }
1265 
1266 /* ================================================================
1267  *
1268  * 			Data handle management
1269  *
1270  * ================================================================ */
1271 
1272 /*****************************************************************
1273  *            DdeCreateDataHandle (USER32.@)
1274  */
DdeCreateDataHandle(DWORD idInst,LPBYTE pSrc,DWORD cb,DWORD cbOff,HSZ hszItem,UINT wFmt,UINT afCmd)1275 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff,
1276                                     HSZ hszItem, UINT wFmt, UINT afCmd)
1277 {
1278 
1279     /* Other than check for validity we will ignore for now idInst, hszItem.
1280      * The purpose of these arguments still need to be investigated.
1281      */
1282 
1283     WDML_INSTANCE*		pInstance;
1284     HGLOBAL     		hMem;
1285     LPBYTE      		pByte;
1286     DDE_DATAHANDLE_HEAD*	pDdh;
1287     WCHAR psz[MAX_BUFFER_LEN];
1288 
1289     pInstance = WDML_GetInstance(idInst);
1290     if (pInstance == NULL)
1291     {
1292         WDML_SetAllLastError(DMLERR_INVALIDPARAMETER);
1293         return NULL;
1294     }
1295 
1296     if (!GetAtomNameW(HSZ2ATOM(hszItem), psz, MAX_BUFFER_LEN))
1297     {
1298         psz[0] = HSZ2ATOM(hszItem);
1299         psz[1] = 0;
1300     }
1301 
1302     TRACE("(%d,%p,cb %d, cbOff %d,%p <%s>,fmt %04x,%x)\n",
1303 	  idInst, pSrc, cb, cbOff, hszItem, debugstr_w(psz), wFmt, afCmd);
1304 
1305     if (afCmd != 0 && afCmd != HDATA_APPOWNED)
1306         return 0;
1307 
1308     /* we use the first 4 bytes to store the size */
1309     if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff + sizeof(DDE_DATAHANDLE_HEAD))))
1310     {
1311 	ERR("GlobalAlloc failed\n");
1312 	return 0;
1313     }
1314 
1315     pDdh = GlobalLock(hMem);
1316     if (!pDdh)
1317     {
1318         GlobalFree(hMem);
1319         return 0;
1320     }
1321 
1322     pDdh->cfFormat = wFmt;
1323     pDdh->bAppOwned = (afCmd == HDATA_APPOWNED);
1324 
1325     pByte = (LPBYTE)(pDdh + 1);
1326     if (pSrc)
1327     {
1328 	memcpy(pByte, pSrc + cbOff, cb);
1329     }
1330     GlobalUnlock(hMem);
1331 
1332     TRACE("=> %p\n", hMem);
1333     return hMem;
1334 }
1335 
1336 /*****************************************************************
1337  *
1338  *            DdeAddData (USER32.@)
1339  */
DdeAddData(HDDEDATA hData,LPBYTE pSrc,DWORD cb,DWORD cbOff)1340 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
1341 {
1342     DWORD	old_sz, new_sz;
1343     LPBYTE	pDst;
1344 
1345     TRACE("(%p,%p,cb %d, cbOff %d)\n", hData, pSrc, cb, cbOff);
1346 
1347     pDst = DdeAccessData(hData, &old_sz);
1348     if (!pDst) return 0;
1349 
1350     new_sz = cb + cbOff;
1351     if (new_sz > old_sz)
1352     {
1353 	DdeUnaccessData(hData);
1354 	hData = GlobalReAlloc(hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1355 			      GMEM_MOVEABLE | GMEM_DDESHARE);
1356 	pDst = DdeAccessData(hData, &old_sz);
1357     }
1358 
1359     if (!pDst) return 0;
1360 
1361     memcpy(pDst + cbOff, pSrc, cb);
1362     DdeUnaccessData(hData);
1363     return hData;
1364 }
1365 
1366 /******************************************************************************
1367  * DdeGetData [USER32.@]  Copies data from DDE object to local buffer
1368  *
1369  *
1370  * PARAMS
1371  * hData	[I] Handle to DDE object
1372  * pDst		[I] Pointer to destination buffer
1373  * cbMax	[I] Amount of data to copy
1374  * cbOff	[I] Offset to beginning of data
1375  *
1376  * RETURNS
1377  *    Size of memory object associated with handle
1378  */
DdeGetData(HDDEDATA hData,LPBYTE pDst,DWORD cbMax,DWORD cbOff)1379 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1380 {
1381     DWORD   dwSize, dwRet;
1382     LPBYTE  pByte;
1383 
1384     TRACE("(%p,%p,%d,%d)\n", hData, pDst, cbMax, cbOff);
1385 
1386     pByte = DdeAccessData(hData, &dwSize);
1387 
1388     if (pByte)
1389     {
1390         if (!pDst)
1391         {
1392             dwRet = dwSize;
1393         }
1394         else if (cbOff + cbMax < dwSize)
1395 	{
1396 	    dwRet = cbMax;
1397 	}
1398 	else if (cbOff < dwSize)
1399 	{
1400 	    dwRet = dwSize - cbOff;
1401 	}
1402 	else
1403 	{
1404 	    dwRet = 0;
1405 	}
1406 	if (pDst && dwRet != 0)
1407 	{
1408 	    memcpy(pDst, pByte + cbOff, dwRet);
1409 	}
1410 	DdeUnaccessData(hData);
1411     }
1412     else
1413     {
1414 	dwRet = 0;
1415     }
1416     return dwRet;
1417 }
1418 
1419 /*****************************************************************
1420  *            DdeAccessData (USER32.@)
1421  */
DdeAccessData(HDDEDATA hData,LPDWORD pcbDataSize)1422 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1423 {
1424     HGLOBAL			hMem = hData;
1425     DDE_DATAHANDLE_HEAD*	pDdh;
1426 
1427     TRACE("(%p,%p)\n", hData, pcbDataSize);
1428 
1429     pDdh = GlobalLock(hMem);
1430     if (pDdh == NULL)
1431     {
1432 	ERR("Failed on GlobalLock(%p)\n", hMem);
1433 	return 0;
1434     }
1435 
1436     if (pcbDataSize != NULL)
1437     {
1438 	*pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1439     }
1440     TRACE("=> %p (%lu) fmt %04x\n", pDdh + 1, GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD), pDdh->cfFormat);
1441     return (LPBYTE)(pDdh + 1);
1442 }
1443 
1444 /*****************************************************************
1445  *            DdeUnaccessData (USER32.@)
1446  */
DdeUnaccessData(HDDEDATA hData)1447 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1448 {
1449     HGLOBAL hMem = hData;
1450 
1451     TRACE("(%p)\n", hData);
1452 
1453     GlobalUnlock(hMem);
1454 
1455     return TRUE;
1456 }
1457 
1458 /*****************************************************************
1459  *            DdeFreeDataHandle   (USER32.@)
1460  */
DdeFreeDataHandle(HDDEDATA hData)1461 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1462 {
1463     TRACE("(%p)\n", hData);
1464 
1465     /* 1 is the handle value returned by an asynchronous operation. */
1466     if (hData == (HDDEDATA)1)
1467         return TRUE;
1468 
1469     return GlobalFree(hData) == 0;
1470 }
1471 
1472 /******************************************************************
1473  *		WDML_IsAppOwned
1474  *
1475  *
1476  */
WDML_IsAppOwned(HDDEDATA hData)1477 BOOL WDML_IsAppOwned(HDDEDATA hData)
1478 {
1479     DDE_DATAHANDLE_HEAD*	pDdh;
1480     BOOL                        ret = FALSE;
1481 
1482     pDdh = GlobalLock(hData);
1483     if (pDdh != NULL)
1484     {
1485         ret = pDdh->bAppOwned;
1486         GlobalUnlock(hData);
1487     }
1488     return ret;
1489 }
1490 
1491 /* ================================================================
1492  *
1493  *                  Global <=> Data handle management
1494  *
1495  * ================================================================ */
1496 
1497 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1498  *    offset	  size
1499  *    (bytes)	 (bits)	comment
1500  *	0	   16	bit fields for options (release, ackreq, response...)
1501  *	2	   16	clipboard format
1502  *	4	   ?	data to be used
1503  */
WDML_Global2DataHandle(WDML_CONV * pConv,HGLOBAL hMem,WINE_DDEHEAD * p)1504 HDDEDATA        WDML_Global2DataHandle(WDML_CONV* pConv, HGLOBAL hMem, WINE_DDEHEAD* p)
1505 {
1506     DDEDATA*    pDd;
1507     HDDEDATA	ret = 0;
1508     DWORD       size;
1509 
1510     if (hMem)
1511     {
1512         pDd = GlobalLock(hMem);
1513         size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
1514         if (pDd)
1515         {
1516 	    if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1517             switch (pDd->cfFormat)
1518             {
1519             default:
1520                 FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
1521                       pDd->cfFormat, hMem);
1522                 /* fall through */
1523             case 0:
1524             case CF_TEXT:
1525                 ret = DdeCreateDataHandle(pConv->instance->instanceID, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
1526                 break;
1527             case CF_BITMAP:
1528                 if (size >= sizeof(BITMAP))
1529                 {
1530                     BITMAP*     bmp = (BITMAP*)pDd->Value;
1531                     int         count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes;
1532                     if (size >= sizeof(BITMAP) + count)
1533                     {
1534                         HBITMAP hbmp;
1535 
1536                         if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
1537                                                  bmp->bmPlanes, bmp->bmBitsPixel,
1538                                                  pDd->Value + sizeof(BITMAP))))
1539                         {
1540                             ret = DdeCreateDataHandle(pConv->instance->instanceID, (LPBYTE)&hbmp, sizeof(hbmp),
1541                                                       0, 0, CF_BITMAP, 0);
1542                         }
1543                         else ERR("Can't create bmp\n");
1544                     }
1545                     else
1546                     {
1547                         ERR("Wrong count: %u / %d\n", size, count);
1548                     }
1549                 } else ERR("No bitmap header\n");
1550                 break;
1551             }
1552             GlobalUnlock(hMem);
1553         }
1554     }
1555     return ret;
1556 }
1557 
1558 /******************************************************************
1559  *		WDML_DataHandle2Global
1560  *
1561  *
1562  */
WDML_DataHandle2Global(HDDEDATA hDdeData,BOOL fResponse,BOOL fRelease,BOOL fDeferUpd,BOOL fAckReq)1563 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1564 			       BOOL fDeferUpd, BOOL fAckReq)
1565 {
1566     DDE_DATAHANDLE_HEAD*	pDdh;
1567     DWORD                       dwSize;
1568     HGLOBAL                     hMem = 0;
1569 
1570     dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1571     pDdh = GlobalLock(hDdeData);
1572     if (dwSize && pDdh)
1573     {
1574         WINE_DDEHEAD*    wdh = NULL;
1575 
1576         switch (pDdh->cfFormat)
1577         {
1578         default:
1579             FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
1580                    pDdh->cfFormat, hDdeData);
1581             /* fall through */
1582         case 0:
1583         case CF_TEXT:
1584             hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
1585             if (hMem && (wdh = GlobalLock(hMem)))
1586             {
1587                 memcpy(wdh + 1, pDdh + 1, dwSize);
1588             }
1589             break;
1590         case CF_BITMAP:
1591             if (dwSize >= sizeof(HBITMAP))
1592             {
1593                 BITMAP  bmp;
1594                 DWORD   count;
1595                 HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);
1596 
1597                 if (GetObjectW(hbmp, sizeof(bmp), &bmp))
1598                 {
1599                     count = bmp.bmWidthBytes * bmp.bmHeight;
1600                     hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1601                                        sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
1602                     if (hMem && (wdh = GlobalLock(hMem)))
1603                     {
1604                         memcpy(wdh + 1, &bmp, sizeof(bmp));
1605                         GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
1606                     }
1607                 }
1608             }
1609             break;
1610         }
1611         if (wdh)
1612         {
1613             wdh->unused = 0;
1614             wdh->fResponse = fResponse;
1615             wdh->fRelease = fRelease;
1616             wdh->fDeferUpd = fDeferUpd;
1617             wdh->fAckReq = fAckReq;
1618             wdh->cfFormat = pDdh->cfFormat;
1619             GlobalUnlock(hMem);
1620         }
1621         GlobalUnlock(hDdeData);
1622     }
1623 
1624     return hMem;
1625 }
1626 
1627 /* ================================================================
1628  *
1629  * 			Server management
1630  *
1631  * ================================================================ */
1632 
1633 /******************************************************************
1634  *		WDML_AddServer
1635  *
1636  *
1637  */
WDML_AddServer(WDML_INSTANCE * pInstance,HSZ hszService,HSZ hszTopic)1638 WDML_SERVER*	WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1639 {
1640     static const WCHAR fmtW[] = {'%','s','(','0','x','%','*','x',')',0};
1641     WDML_SERVER* 	pServer;
1642     WCHAR		buf1[256];
1643     WCHAR		buf2[256];
1644 
1645     pServer = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1646     if (pServer == NULL) return NULL;
1647 
1648     pServer->hszService = hszService;
1649     WDML_IncHSZ(pInstance, hszService);
1650 
1651     DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE);
1652     snprintfW(buf2, 256, fmtW, buf1, 2*sizeof(ULONG_PTR), GetCurrentProcessId());
1653     pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE);
1654 
1655     pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1656     pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1657 
1658     pServer->filterOn = TRUE;
1659 
1660     pServer->next = pInstance->servers;
1661     pInstance->servers = pServer;
1662     return pServer;
1663 }
1664 
1665 /******************************************************************
1666  *		WDML_RemoveServer
1667  *
1668  *
1669  */
WDML_RemoveServer(WDML_INSTANCE * pInstance,HSZ hszService,HSZ hszTopic)1670 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1671 {
1672     WDML_SERVER*	pPrev = NULL;
1673     WDML_SERVER*	pServer = NULL;
1674     WDML_CONV*		pConv;
1675     WDML_CONV*		pConvNext;
1676 
1677     pServer = pInstance->servers;
1678 
1679     while (pServer != NULL)
1680     {
1681 	if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1682 	{
1683 	    WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1684 				     pServer->atomService, pServer->atomServiceSpec);
1685 	    /* terminate all conversations for given topic */
1686 	    for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1687 	    {
1688 		pConvNext = pConv->next;
1689 		if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1690 		{
1691                     HWND client = pConv->hwndClient, server = pConv->hwndServer;
1692 		    WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1693 		    /* don't care about return code (whether client window is present or not) */
1694 		    PostMessageW(client, WM_DDE_TERMINATE, (WPARAM)server, 0);
1695 		}
1696 	    }
1697 	    if (pServer == pInstance->servers)
1698 	    {
1699 		pInstance->servers = pServer->next;
1700 	    }
1701 	    else
1702 	    {
1703 		pPrev->next = pServer->next;
1704 	    }
1705 
1706 	    DestroyWindow(pServer->hwndServer);
1707 	    WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1708 	    WDML_DecHSZ(pInstance, pServer->hszService);
1709 
1710 	    GlobalDeleteAtom(pServer->atomService);
1711 	    GlobalDeleteAtom(pServer->atomServiceSpec);
1712 
1713 	    HeapFree(GetProcessHeap(), 0, pServer);
1714 	    break;
1715 	}
1716 
1717 	pPrev = pServer;
1718 	pServer = pServer->next;
1719     }
1720 }
1721 
1722 /*****************************************************************************
1723  *	WDML_FindServer
1724  *
1725  *	generic routine to return a pointer to the relevant ServiceNode
1726  *	for a given service name, or NULL if the entry does not exist
1727  *
1728  */
WDML_FindServer(WDML_INSTANCE * pInstance,HSZ hszService,HSZ hszTopic)1729 WDML_SERVER*	WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1730 {
1731     WDML_SERVER*	pServer;
1732 
1733     for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1734     {
1735 	if (hszService == pServer->hszService)
1736 	{
1737 	    return pServer;
1738 	}
1739     }
1740     TRACE("Service name missing\n");
1741     return NULL;
1742 }
1743 
1744 /* ================================================================
1745  *
1746  * 			Link (hot & warm) management
1747  *
1748  * ================================================================ */
1749 
1750 /******************************************************************
1751  *		WDML_AddLink
1752  *
1753  *
1754  */
WDML_AddLink(WDML_INSTANCE * pInstance,HCONV hConv,WDML_SIDE side,UINT wType,HSZ hszItem,UINT wFmt)1755 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
1756 		  UINT wType, HSZ hszItem, UINT wFmt)
1757 {
1758     WDML_LINK*	pLink;
1759 
1760     pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
1761     if (pLink == NULL)
1762     {
1763 	ERR("OOM\n");
1764 	return;
1765     }
1766 
1767     pLink->hConv = hConv;
1768     pLink->transactionType = wType;
1769     WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
1770     pLink->uFmt = wFmt;
1771     pLink->next = pInstance->links[side];
1772     pInstance->links[side] = pLink;
1773 }
1774 
1775 /******************************************************************
1776  *		WDML_RemoveLink
1777  *
1778  *
1779  */
WDML_RemoveLink(WDML_INSTANCE * pInstance,HCONV hConv,WDML_SIDE side,HSZ hszItem,UINT uFmt)1780 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
1781 		     HSZ hszItem, UINT uFmt)
1782 {
1783     WDML_LINK* pPrev = NULL;
1784     WDML_LINK* pCurrent = NULL;
1785 
1786     pCurrent = pInstance->links[side];
1787 
1788     while (pCurrent != NULL)
1789     {
1790 	if (pCurrent->hConv == hConv &&
1791 	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
1792 	    pCurrent->uFmt == uFmt)
1793 	{
1794 	    if (pCurrent == pInstance->links[side])
1795 	    {
1796 		pInstance->links[side] = pCurrent->next;
1797 	    }
1798 	    else
1799 	    {
1800 		pPrev->next = pCurrent->next;
1801 	    }
1802 
1803 	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
1804 	    HeapFree(GetProcessHeap(), 0, pCurrent);
1805 	    break;
1806 	}
1807 
1808 	pPrev = pCurrent;
1809 	pCurrent = pCurrent->next;
1810     }
1811 }
1812 
1813 /* this function is called to remove all links related to the conv.
1814    It should be called from both client and server when terminating
1815    the conversation.
1816 */
1817 /******************************************************************
1818  *		WDML_RemoveAllLinks
1819  *
1820  *
1821  */
WDML_RemoveAllLinks(WDML_INSTANCE * pInstance,WDML_CONV * pConv,WDML_SIDE side)1822 static void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
1823 {
1824     WDML_LINK* pPrev = NULL;
1825     WDML_LINK* pCurrent = NULL;
1826     WDML_LINK* pNext = NULL;
1827 
1828     pCurrent = pInstance->links[side];
1829 
1830     while (pCurrent != NULL)
1831     {
1832 	if (pCurrent->hConv == (HCONV)pConv)
1833 	{
1834 	    if (pCurrent == pInstance->links[side])
1835 	    {
1836 		pInstance->links[side] = pCurrent->next;
1837 		pNext = pCurrent->next;
1838 	    }
1839 	    else
1840 	    {
1841 		pPrev->next = pCurrent->next;
1842 		pNext = pCurrent->next;
1843 	    }
1844 
1845 	    WDML_DecHSZ(pInstance, pCurrent->hszItem);
1846 
1847 	    HeapFree(GetProcessHeap(), 0, pCurrent);
1848 	    pCurrent = NULL;
1849 	}
1850 
1851 	if (pCurrent)
1852 	{
1853 	    pPrev = pCurrent;
1854 	    pCurrent = pCurrent->next;
1855 	}
1856 	else
1857 	{
1858 	    pCurrent = pNext;
1859 	}
1860     }
1861 }
1862 
1863 /******************************************************************
1864  *		WDML_FindLink
1865  *
1866  *
1867  */
WDML_FindLink(WDML_INSTANCE * pInstance,HCONV hConv,WDML_SIDE side,HSZ hszItem,BOOL use_fmt,UINT uFmt)1868 WDML_LINK* 	WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
1869 			      HSZ hszItem, BOOL use_fmt, UINT uFmt)
1870 {
1871     WDML_LINK*	pCurrent;
1872 
1873     for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1874     {
1875 	/* we don't need to check for transaction type as it can be altered */
1876 
1877 	if (pCurrent->hConv == hConv &&
1878 	    DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
1879 	    (!use_fmt || pCurrent->uFmt == uFmt))
1880 	{
1881 	    break;
1882 	}
1883 
1884     }
1885 
1886     return pCurrent;
1887 }
1888 
1889 /* ================================================================
1890  *
1891  * 			Transaction management
1892  *
1893  * ================================================================ */
1894 
1895 /******************************************************************
1896  *		WDML_AllocTransaction
1897  *
1898  * Alloc a transaction structure for handling the message ddeMsg
1899  */
WDML_AllocTransaction(WDML_INSTANCE * pInstance,UINT ddeMsg,UINT wFmt,HSZ hszItem)1900 WDML_XACT*	WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
1901 				      UINT wFmt, HSZ hszItem)
1902 {
1903     WDML_XACT*		pXAct;
1904     static WORD		tid = 1;	/* FIXME: wrap around */
1905 
1906     pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
1907     if (!pXAct)
1908     {
1909 	pInstance->lastError = DMLERR_MEMORY_ERROR;
1910 	return NULL;
1911     }
1912 
1913     pXAct->xActID = tid++;
1914     pXAct->ddeMsg = ddeMsg;
1915     pXAct->hDdeData = 0;
1916     pXAct->hUser = 0;
1917     pXAct->next = NULL;
1918     pXAct->wType = 0;
1919     pXAct->wFmt = wFmt;
1920     if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
1921     pXAct->atom = 0;
1922     pXAct->hMem = 0;
1923     pXAct->lParam = 0;
1924 
1925     return pXAct;
1926 }
1927 
1928 /******************************************************************
1929  *		WDML_QueueTransaction
1930  *
1931  * Adds a transaction to the list of transaction
1932  */
WDML_QueueTransaction(WDML_CONV * pConv,WDML_XACT * pXAct)1933 void	WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
1934 {
1935     WDML_XACT**	pt;
1936 
1937     /* advance to last in queue */
1938     for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
1939     *pt = pXAct;
1940 }
1941 
1942 /******************************************************************
1943  *		WDML_UnQueueTransaction
1944  *
1945  *
1946  */
WDML_UnQueueTransaction(WDML_CONV * pConv,WDML_XACT * pXAct)1947 BOOL	WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct)
1948 {
1949     WDML_XACT**	pt;
1950 
1951     for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
1952     {
1953 	if (*pt == pXAct)
1954 	{
1955 	    *pt = pXAct->next;
1956 	    return TRUE;
1957 	}
1958     }
1959     return FALSE;
1960 }
1961 
1962 /******************************************************************
1963  *		WDML_FreeTransaction
1964  *
1965  *
1966  */
WDML_FreeTransaction(WDML_INSTANCE * pInstance,WDML_XACT * pXAct,BOOL doFreePmt)1967 void	WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
1968 {
1969     /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
1970     if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
1971     {
1972 	GlobalFree(pXAct->hMem);
1973     }
1974     if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
1975 
1976     HeapFree(GetProcessHeap(), 0, pXAct);
1977 }
1978 
1979 /******************************************************************
1980  *		WDML_FindTransaction
1981  *
1982  *
1983  */
WDML_FindTransaction(WDML_CONV * pConv,DWORD tid)1984 static WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
1985 {
1986     WDML_XACT* pXAct;
1987 
1988     tid = HIWORD(tid);
1989     for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
1990     {
1991 	if (pXAct->xActID == tid)
1992 	    break;
1993     }
1994     return pXAct;
1995 }
1996 
1997 /* ================================================================
1998  *
1999  * 		Conversation management
2000  *
2001  * ================================================================ */
2002 
2003 /******************************************************************
2004  *		WDML_AddConv
2005  *
2006  *
2007  */
WDML_AddConv(WDML_INSTANCE * pInstance,WDML_SIDE side,HSZ hszService,HSZ hszTopic,HWND hwndClient,HWND hwndServer)2008 WDML_CONV*	WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
2009 			     HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
2010 {
2011     WDML_CONV*	pConv;
2012 
2013     /* no conversation yet, add it */
2014     pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
2015     if (!pConv) return NULL;
2016 
2017     pConv->instance = pInstance;
2018     WDML_IncHSZ(pInstance, pConv->hszService = hszService);
2019     WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
2020     pConv->magic = WDML_CONV_MAGIC;
2021     pConv->hwndServer = hwndServer;
2022     pConv->hwndClient = hwndClient;
2023     pConv->transactions = NULL;
2024     pConv->hUser = 0;
2025     pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
2026     pConv->wStatus |= pInstance->wStatus;
2027     /* check if both side of the conversation are of the same instance */
2028     if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
2029 	WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
2030     {
2031 	pConv->wStatus |= ST_ISSELF;
2032     }
2033     pConv->wConvst = XST_NULL;
2034 
2035     pConv->next = pInstance->convs[side];
2036     pInstance->convs[side] = pConv;
2037 
2038     TRACE("pConv->wStatus %04x pInstance(%p)\n", pConv->wStatus, pInstance);
2039 
2040     return pConv;
2041 }
2042 
2043 /******************************************************************
2044  *		WDML_FindConv
2045  *
2046  *
2047  */
WDML_FindConv(WDML_INSTANCE * pInstance,WDML_SIDE side,HSZ hszService,HSZ hszTopic)2048 WDML_CONV*	WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
2049 			      HSZ hszService, HSZ hszTopic)
2050 {
2051     WDML_CONV*	pCurrent;
2052 
2053     for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2054     {
2055 	if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
2056 	    DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
2057 	{
2058 	    return pCurrent;
2059 	}
2060 
2061     }
2062     return NULL;
2063 }
2064 
2065 /******************************************************************
2066  *		WDML_RemoveConv
2067  *
2068  *
2069  */
WDML_RemoveConv(WDML_CONV * pRef,WDML_SIDE side)2070 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
2071 {
2072     WDML_CONV*	pPrev = NULL;
2073     WDML_CONV* 	pCurrent;
2074     WDML_XACT*	pXAct;
2075     WDML_XACT*	pXActNext;
2076     HWND	hWnd;
2077 
2078     if (!pRef)
2079 	return;
2080 
2081     /* remove any pending transaction */
2082     for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
2083     {
2084 	pXActNext = pXAct->next;
2085 	WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
2086     }
2087 
2088     WDML_RemoveAllLinks(pRef->instance, pRef, side);
2089 
2090     /* FIXME: should we keep the window around ? it seems so (at least on client side
2091      * to let QueryConvInfo work after conv termination, but also to implement
2092      * DdeReconnect...
2093      */
2094     /* destroy conversation window, but first remove pConv from hWnd.
2095      * this would help the wndProc do appropriate handling upon a WM_DESTROY message
2096      */
2097     hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
2098     SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
2099 
2100     DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
2101 
2102     WDML_DecHSZ(pRef->instance, pRef->hszService);
2103     WDML_DecHSZ(pRef->instance, pRef->hszTopic);
2104 
2105     for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
2106     {
2107 	if (pCurrent == pRef)
2108 	{
2109 	    if (pCurrent == pRef->instance->convs[side])
2110 	    {
2111 		pRef->instance->convs[side] = pCurrent->next;
2112 	    }
2113 	    else
2114 	    {
2115 		pPrev->next = pCurrent->next;
2116 	    }
2117 	    pCurrent->magic = 0;
2118 	    HeapFree(GetProcessHeap(), 0, pCurrent);
2119 	    break;
2120 	}
2121     }
2122 }
2123 
2124 /******************************************************************
2125  *              WDML_EnableCallback
2126  */
WDML_EnableCallback(WDML_CONV * pConv,UINT wCmd)2127 static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
2128 {
2129     if (wCmd == EC_DISABLE)
2130     {
2131         pConv->wStatus |= ST_BLOCKED;
2132         TRACE("EC_DISABLE: conv %p status flags %04x\n", pConv, pConv->wStatus);
2133         return TRUE;
2134     }
2135 
2136     if (wCmd == EC_QUERYWAITING)
2137         return pConv->transactions != NULL;
2138 
2139     if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
2140     {
2141         FIXME("Unknown command code %04x\n", wCmd);
2142         return FALSE;
2143     }
2144 
2145     if (wCmd == EC_ENABLEALL)
2146     {
2147         pConv->wStatus &= ~ST_BLOCKED;
2148         TRACE("EC_ENABLEALL: conv %p status flags %04x\n", pConv, pConv->wStatus);
2149     }
2150 
2151     while (pConv->transactions)
2152     {
2153         WDML_XACT *pXAct = pConv->transactions;
2154 
2155         if (pConv->wStatus & ST_CLIENT)
2156         {
2157             /* transaction should be in the queue until handled */
2158             WDML_ClientHandle(pConv, pXAct, 0, NULL);
2159             WDML_UnQueueTransaction(pConv, pXAct);
2160         }
2161         else
2162         {
2163             /* transaction should be removed from the queue before handling */
2164             WDML_UnQueueTransaction(pConv, pXAct);
2165             WDML_ServerHandle(pConv, pXAct);
2166         }
2167 
2168         WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
2169 
2170         if (wCmd == EC_ENABLEONE) break;
2171     }
2172     return TRUE;
2173 }
2174 
2175 /*****************************************************************
2176  *            DdeEnableCallback (USER32.@)
2177  */
DdeEnableCallback(DWORD idInst,HCONV hConv,UINT wCmd)2178 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
2179 {
2180     BOOL ret = FALSE;
2181     WDML_CONV *pConv;
2182 
2183     TRACE("(%d, %p, %04x)\n", idInst, hConv, wCmd);
2184 
2185     if (hConv)
2186     {
2187         pConv = WDML_GetConv(hConv, TRUE);
2188 
2189         if (pConv && pConv->instance->instanceID == idInst)
2190             ret = WDML_EnableCallback(pConv, wCmd);
2191     }
2192     else
2193     {
2194         WDML_INSTANCE *pInstance = WDML_GetInstance(idInst);
2195 
2196         if (!pInstance)
2197             return FALSE;
2198 
2199         TRACE("adding flags %04x to instance %p\n", wCmd, pInstance);
2200         pInstance->wStatus |= wCmd;
2201 
2202         if (wCmd == EC_DISABLE)
2203         {
2204             pInstance->wStatus |= ST_BLOCKED;
2205             TRACE("EC_DISABLE: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
2206         }
2207         else if (wCmd == EC_ENABLEALL)
2208         {
2209             pInstance->wStatus &= ~ST_BLOCKED;
2210             TRACE("EC_ENABLEALL: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
2211         }
2212 
2213         ret = TRUE;
2214 
2215         for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConv->next)
2216         {
2217             ret = WDML_EnableCallback(pConv, wCmd);
2218             if (ret && wCmd == EC_QUERYWAITING) break;
2219         }
2220     }
2221 
2222     return ret;
2223 }
2224 
2225 /******************************************************************
2226  *		WDML_GetConv
2227  *
2228  *
2229  */
WDML_GetConv(HCONV hConv,BOOL checkConnected)2230 WDML_CONV*	WDML_GetConv(HCONV hConv, BOOL checkConnected)
2231 {
2232     WDML_CONV*	pConv = (WDML_CONV*)hConv;
2233 
2234     /* FIXME: should do better checking */
2235     if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL;
2236 
2237     if (!pConv->instance)
2238     {
2239         WARN("wrong thread ID, no instance\n");
2240 	return NULL;
2241     }
2242 
2243     if (pConv->instance->threadID != GetCurrentThreadId())
2244     {
2245         WARN("wrong thread ID\n");
2246         pConv->instance->lastError = DMLERR_INVALIDPARAMETER; /* FIXME: check */
2247 	return NULL;
2248     }
2249 
2250     if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
2251     {
2252         WARN("found conv but ain't connected\n");
2253         pConv->instance->lastError = DMLERR_NO_CONV_ESTABLISHED;
2254 	return NULL;
2255     }
2256 
2257     return pConv;
2258 }
2259 
2260 /******************************************************************
2261  *		WDML_GetConvFromWnd
2262  *
2263  *
2264  */
WDML_GetConvFromWnd(HWND hWnd)2265 WDML_CONV*	WDML_GetConvFromWnd(HWND hWnd)
2266 {
2267     return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION);
2268 }
2269 
2270 /******************************************************************
2271  *		WDML_PostAck
2272  *
2273  *
2274  */
WDML_PostAck(WDML_CONV * pConv,WDML_SIDE side,WORD appRetCode,BOOL fBusy,BOOL fAck,UINT_PTR pmt,LPARAM lParam,UINT oldMsg)2275 BOOL		WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
2276 			     BOOL fBusy, BOOL fAck, UINT_PTR pmt, LPARAM lParam, UINT oldMsg)
2277 {
2278     DDEACK	ddeAck;
2279     HWND	from, to;
2280 
2281     if (side == WDML_SERVER_SIDE)
2282     {
2283 	from = pConv->hwndServer;
2284 	to   = pConv->hwndClient;
2285     }
2286     else
2287     {
2288 	to   = pConv->hwndServer;
2289 	from = pConv->hwndClient;
2290     }
2291 
2292     ddeAck.bAppReturnCode = appRetCode;
2293     ddeAck.reserved       = 0;
2294     ddeAck.fBusy          = fBusy;
2295     ddeAck.fAck           = fAck;
2296 
2297     TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
2298 
2299     lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) :
2300         PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt);
2301     if (!PostMessageW(to, WM_DDE_ACK, (WPARAM)from, lParam))
2302     {
2303 	pConv->wStatus &= ~ST_CONNECTED;
2304         pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
2305         FreeDDElParam(WM_DDE_ACK, lParam);
2306         return FALSE;
2307     }
2308     return TRUE;
2309 }
2310 
2311 /*****************************************************************
2312  *            DdeSetUserHandle (USER32.@)
2313  */
DdeSetUserHandle(HCONV hConv,DWORD id,DWORD hUser)2314 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
2315 {
2316     WDML_CONV*	pConv;
2317 
2318     pConv = WDML_GetConv(hConv, FALSE);
2319     if (pConv == NULL)
2320 	return FALSE;
2321 
2322     if (id == QID_SYNC)
2323     {
2324 	pConv->hUser = hUser;
2325     }
2326     else
2327     {
2328 	WDML_XACT*	pXAct;
2329 
2330 	pXAct = WDML_FindTransaction(pConv, id);
2331 	if (pXAct)
2332 	{
2333 	    pXAct->hUser = hUser;
2334 	}
2335 	else
2336 	{
2337 	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2338 	    return  FALSE;
2339 	}
2340     }
2341     return TRUE;
2342 }
2343 
2344 /******************************************************************
2345  *		WDML_GetLocalConvInfo
2346  *
2347  *
2348  */
WDML_GetLocalConvInfo(WDML_CONV * pConv,CONVINFO * ci,DWORD id)2349 static	BOOL	WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
2350 {
2351     BOOL 	ret = TRUE;
2352     WDML_LINK*	pLink;
2353     WDML_SIDE	side;
2354 
2355     ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((ULONG_PTR)pConv | 1) : 0;
2356     ci->hszSvcPartner = pConv->hszService;
2357     ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
2358     ci->hszTopic = pConv->hszTopic;
2359     ci->wStatus = pConv->wStatus;
2360 
2361     side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
2362 
2363     for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
2364     {
2365 	if (pLink->hConv == (HCONV)pConv)
2366 	{
2367 	    ci->wStatus |= ST_ADVISE;
2368 	    break;
2369 	}
2370     }
2371 
2372     /* FIXME: non handled status flags:
2373        ST_BLOCKED
2374        ST_BLOCKNEXT
2375        ST_INLIST
2376     */
2377 
2378     ci->wConvst = pConv->wConvst; /* FIXME */
2379 
2380     ci->wLastError = 0; /* FIXME: note it's not the instance last error */
2381     ci->hConvList = 0;
2382     ci->ConvCtxt = pConv->convContext;
2383     if (ci->wStatus & ST_CLIENT)
2384     {
2385 	ci->hwnd = pConv->hwndClient;
2386 	ci->hwndPartner = pConv->hwndServer;
2387     }
2388     else
2389     {
2390 	ci->hwnd = pConv->hwndServer;
2391 	ci->hwndPartner = pConv->hwndClient;
2392     }
2393     if (id == QID_SYNC)
2394     {
2395 	ci->hUser = pConv->hUser;
2396 	ci->hszItem = 0;
2397 	ci->wFmt = 0;
2398 	ci->wType = 0;
2399     }
2400     else
2401     {
2402 	WDML_XACT*	pXAct;
2403 
2404 	pXAct = WDML_FindTransaction(pConv, id);
2405 	if (pXAct)
2406 	{
2407 	    ci->hUser = pXAct->hUser;
2408 	    ci->hszItem = pXAct->hszItem;
2409 	    ci->wFmt = pXAct->wFmt;
2410 	    ci->wType = pXAct->wType;
2411 	}
2412 	else
2413 	{
2414 	    ret = 0;
2415 	    pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2416 	}
2417     }
2418     return ret;
2419 }
2420 
2421 /******************************************************************
2422  *		DdeQueryConvInfo (USER32.@)
2423  *
2424  * FIXME: Set last DDE error on failure.
2425  */
DdeQueryConvInfo(HCONV hConv,DWORD id,PCONVINFO lpConvInfo)2426 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
2427 {
2428     UINT	ret = lpConvInfo->cb;
2429     CONVINFO	ci;
2430     WDML_CONV*	pConv;
2431 
2432     TRACE("(%p,%x,%p)\n", hConv, id, lpConvInfo);
2433 
2434     if (!hConv)
2435     {
2436         FIXME("hConv is NULL\n");
2437         return 0;
2438     }
2439 
2440     pConv = WDML_GetConv(hConv, FALSE);
2441     if (pConv != NULL)
2442     {
2443         if (!WDML_GetLocalConvInfo(pConv, &ci, id))
2444             ret = 0;
2445     }
2446     else
2447     {
2448         if ((ULONG_PTR)hConv & 1)
2449         {
2450             pConv = WDML_GetConv((HCONV)((ULONG_PTR)hConv & ~1), FALSE);
2451             if (pConv != NULL)
2452                 FIXME("Request on remote conversation information is not implemented yet\n");
2453         }
2454         ret = 0;
2455     }
2456 
2457     if (ret != 0)
2458     {
2459 	ci.cb = lpConvInfo->cb;
2460 	memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci)));
2461     }
2462     return ret;
2463 }
2464 
2465 /* ================================================================
2466  *
2467  * 	   Information broadcast across DDEML implementations
2468  *
2469  * ================================================================ */
2470 
2471 struct tagWDML_BroadcastPmt
2472 {
2473     LPCWSTR	clsName;
2474     UINT	uMsg;
2475     WPARAM	wParam;
2476     LPARAM	lParam;
2477 };
2478 
2479 /******************************************************************
2480  *		WDML_BroadcastEnumProc
2481  *
2482  *
2483  */
WDML_BroadcastEnumProc(HWND hWnd,LPARAM lParam)2484 static	BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2485 {
2486     struct tagWDML_BroadcastPmt*	s = (struct tagWDML_BroadcastPmt*)lParam;
2487     WCHAR				buffer[128];
2488 
2489     if (GetClassNameW(hWnd, buffer, 128) > 0 &&
2490 	lstrcmpiW(buffer, s->clsName) == 0)
2491     {
2492 	PostMessageW(hWnd, s->uMsg, s->wParam, s->lParam);
2493     }
2494     return TRUE;
2495 }
2496 
2497 /******************************************************************
2498  *		WDML_BroadcastDDEWindows
2499  *
2500  *
2501  */
WDML_BroadcastDDEWindows(LPCWSTR clsName,UINT uMsg,WPARAM wParam,LPARAM lParam)2502 void WDML_BroadcastDDEWindows(LPCWSTR clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2503 {
2504     struct tagWDML_BroadcastPmt	s;
2505 
2506     s.clsName = clsName;
2507     s.uMsg    = uMsg;
2508     s.wParam  = wParam;
2509     s.lParam  = lParam;
2510     EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
2511 }
2512