[User32]
[reactos.git] / reactos / win32ss / user / user32 / misc / ddeserver.c
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 <wine/debug.h>
29
30 WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
31
32 static const WCHAR szServerNameClass[] = {'D','d','e','S','e','r','v','e','r','N','a','m','e',0};
33 const char WDML_szServerConvClassA[] = "DdeServerConvA";
34 const WCHAR WDML_szServerConvClassW[] = {'D','d','e','S','e','r','v','e','r','C','o','n','v','W',0};
35
36 static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM);
37 static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM);
38
39 /******************************************************************************
40 * DdePostAdvise [USER32.@] Send transaction to DDE callback function.
41 *
42 * PARAMS
43 * idInst [I] Instance identifier
44 * hszTopic [I] Handle to topic name string
45 * hszItem [I] Handle to item name string
46 *
47 * RETURNS
48 * Success: TRUE
49 * Failure: FALSE
50 */
51 BOOL WINAPI DdePostAdvise(DWORD idInst, HSZ hszTopic, HSZ hszItem)
52 {
53 WDML_INSTANCE* pInstance = NULL;
54 WDML_LINK* pLink = NULL;
55 HDDEDATA hDdeData = 0;
56 HGLOBAL hItemData = 0;
57 WDML_CONV* pConv = NULL;
58 ATOM atom = 0;
59 UINT count;
60
61 TRACE("(%d,%p,%p)\n", idInst, hszTopic, hszItem);
62
63 pInstance = WDML_GetInstance(idInst);
64
65 if (pInstance == NULL)
66 return FALSE;
67
68 atom = WDML_MakeAtomFromHsz(hszItem);
69 if (!atom) return FALSE;
70
71 /* first compute the number of links which will trigger a message */
72 count = 0;
73 for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
74 {
75 if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
76 {
77 count++;
78 }
79 }
80 if (count >= CADV_LATEACK)
81 {
82 FIXME("too high value for count\n");
83 count &= 0xFFFF;
84 }
85
86 for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
87 {
88 if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
89 {
90 hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv,
91 hszTopic, hszItem, 0, --count, 0);
92
93 if (hDdeData == CBR_BLOCK)
94 {
95 /* MS doc is not consistent here */
96 FIXME("CBR_BLOCK returned for ADVREQ\n");
97 continue;
98 }
99 if (hDdeData)
100 {
101 if (pLink->transactionType & XTYPF_NODATA)
102 {
103 TRACE("no data\n");
104 hItemData = 0;
105 }
106 else
107 {
108 TRACE("with data\n");
109
110 hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
111 }
112
113 pConv = WDML_GetConv(pLink->hConv, TRUE);
114
115 if (pConv == NULL)
116 {
117 if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData);
118 goto theError;
119 }
120
121 if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
122 PackDDElParam(WM_DDE_DATA, (UINT_PTR)hItemData, atom)))
123 {
124 ERR("post message failed\n");
125 pConv->wStatus &= ~ST_CONNECTED;
126 pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
127 if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData);
128 GlobalFree(hItemData);
129 goto theError;
130 }
131 if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData);
132 }
133 }
134 }
135 return TRUE;
136
137 theError:
138 GlobalDeleteAtom(atom);
139 return FALSE;
140 }
141
142
143 /******************************************************************************
144 * DdeNameService [USER32.@] {Un}registers service name of DDE server
145 *
146 * PARAMS
147 * idInst [I] Instance identifier
148 * hsz1 [I] Handle to service name string
149 * hsz2 [I] Reserved
150 * afCmd [I] Service name flags
151 *
152 * RETURNS
153 * Success: Non-zero
154 * Failure: 0
155 */
156 HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd)
157 {
158 WDML_SERVER* pServer;
159 WDML_INSTANCE* pInstance;
160 HWND hwndServer;
161 WNDCLASSEXW wndclass;
162
163 TRACE("(%d,%p,%p,%x)\n", idInst, hsz1, hsz2, afCmd);
164
165 /* First check instance
166 */
167 pInstance = WDML_GetInstance(idInst);
168 if (pInstance == NULL)
169 {
170 TRACE("Instance not found as initialised\n");
171 /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
172 return NULL;
173 }
174
175 if (hsz2 != 0L)
176 {
177 /* Illegal, reserved parameter
178 */
179 pInstance->lastError = DMLERR_INVALIDPARAMETER;
180 WARN("Reserved parameter no-zero !!\n");
181 return NULL;
182 }
183 if (hsz1 == 0 && !(afCmd & DNS_UNREGISTER))
184 {
185 /* don't know if we should check this but it makes sense
186 * why supply REGISTER or filter flags if de-registering all
187 */
188 TRACE("General unregister unexpected flags\n");
189 pInstance->lastError = DMLERR_INVALIDPARAMETER;
190 return NULL;
191 }
192
193 switch (afCmd & (DNS_REGISTER | DNS_UNREGISTER))
194 {
195 case DNS_REGISTER:
196 pServer = WDML_FindServer(pInstance, hsz1, 0);
197 if (pServer)
198 {
199 ERR("Trying to register already registered service!\n");
200 pInstance->lastError = DMLERR_DLL_USAGE;
201 return NULL;
202 }
203
204 TRACE("Adding service name\n");
205
206 WDML_IncHSZ(pInstance, hsz1);
207
208 pServer = WDML_AddServer(pInstance, hsz1, 0);
209
210 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER,
211 pServer->atomService, pServer->atomServiceSpec);
212
213 wndclass.cbSize = sizeof(wndclass);
214 wndclass.style = 0;
215 wndclass.lpfnWndProc = WDML_ServerNameProc;
216 wndclass.cbClsExtra = 0;
217 wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR);
218 wndclass.hInstance = 0;
219 wndclass.hIcon = 0;
220 wndclass.hCursor = 0;
221 wndclass.hbrBackground = 0;
222 wndclass.lpszMenuName = NULL;
223 wndclass.lpszClassName = szServerNameClass;
224 wndclass.hIconSm = 0;
225
226 RegisterClassExW(&wndclass);
227
228 hwndServer = CreateWindowW(szServerNameClass, NULL,
229 WS_POPUP, 0, 0, 0, 0,
230 0, 0, 0, 0);
231
232 SetWindowLongPtrW(hwndServer, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
233 SetWindowLongPtrW(hwndServer, GWL_WDML_SERVER, (ULONG_PTR)pServer);
234 TRACE("Created nameServer=%p for instance=%08x\n", hwndServer, idInst);
235
236 pServer->hwndServer = hwndServer;
237 break;
238
239 case DNS_UNREGISTER:
240 if (hsz1 == 0L)
241 {
242 /* General unregister situation
243 * terminate all server side pending conversations
244 */
245 while (pInstance->servers)
246 WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0);
247 pInstance->servers = NULL;
248 TRACE("General de-register - finished\n");
249 }
250 else
251 {
252 WDML_RemoveServer(pInstance, hsz1, 0L);
253 }
254 break;
255 }
256
257 if (afCmd & (DNS_FILTERON | DNS_FILTEROFF))
258 {
259 /* Set filter flags on to hold notifications of connection
260 */
261 pServer = WDML_FindServer(pInstance, hsz1, 0);
262 if (!pServer)
263 {
264 /* trying to filter where no service names !!
265 */
266 pInstance->lastError = DMLERR_DLL_USAGE;
267 return NULL;
268 }
269 else
270 {
271 pServer->filterOn = (afCmd & DNS_FILTERON) != 0;
272 }
273 }
274 return (HDDEDATA)TRUE;
275 }
276
277 /******************************************************************
278 * WDML_CreateServerConv
279 *
280 *
281 */
282 static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClient,
283 HWND hwndServerName, HSZ hszApp, HSZ hszTopic)
284 {
285 HWND hwndServerConv;
286 WDML_CONV* pConv;
287
288 if (pInstance->unicode)
289 {
290 WNDCLASSEXW wndclass;
291
292 wndclass.cbSize = sizeof(wndclass);
293 wndclass.style = 0;
294 wndclass.lpfnWndProc = WDML_ServerConvProc;
295 wndclass.cbClsExtra = 0;
296 wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR);
297 wndclass.hInstance = 0;
298 wndclass.hIcon = 0;
299 wndclass.hCursor = 0;
300 wndclass.hbrBackground = 0;
301 wndclass.lpszMenuName = NULL;
302 wndclass.lpszClassName = WDML_szServerConvClassW;
303 wndclass.hIconSm = 0;
304
305 RegisterClassExW(&wndclass);
306
307 hwndServerConv = CreateWindowW(WDML_szServerConvClassW, 0,
308 WS_CHILD, 0, 0, 0, 0,
309 hwndServerName, 0, 0, 0);
310 }
311 else
312 {
313 WNDCLASSEXA wndclass;
314
315 wndclass.cbSize = sizeof(wndclass);
316 wndclass.style = 0;
317 wndclass.lpfnWndProc = WDML_ServerConvProc;
318 wndclass.cbClsExtra = 0;
319 wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR);
320 wndclass.hInstance = 0;
321 wndclass.hIcon = 0;
322 wndclass.hCursor = 0;
323 wndclass.hbrBackground = 0;
324 wndclass.lpszMenuName = NULL;
325 wndclass.lpszClassName = WDML_szServerConvClassA;
326 wndclass.hIconSm = 0;
327
328 RegisterClassExA(&wndclass);
329
330 hwndServerConv = CreateWindowA(WDML_szServerConvClassA, 0,
331 WS_CHILD, 0, 0, 0, 0,
332 hwndServerName, 0, 0, 0);
333 }
334
335 TRACE("Created convServer=%p (nameServer=%p) for instance=%08x unicode=%d\n",
336 hwndServerConv, hwndServerName, pInstance->instanceID, pInstance->unicode);
337
338 pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic,
339 hwndClient, hwndServerConv);
340 if (pConv)
341 {
342 SetWindowLongPtrW(hwndServerConv, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
343 SetWindowLongPtrW(hwndServerConv, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
344
345 /* this should be the only place using SendMessage for WM_DDE_ACK */
346 /* note: sent messages shall not use packing */
347 SendMessageW(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
348 MAKELPARAM(WDML_MakeAtomFromHsz(hszApp), WDML_MakeAtomFromHsz(hszTopic)));
349 /* we assume we're connected since we've sent an answer...
350 * I'm not sure what we can do... it doesn't look like the return value
351 * of SendMessage is used... sigh...
352 */
353 pConv->wStatus |= ST_CONNECTED;
354 }
355 else
356 {
357 DestroyWindow(hwndServerConv);
358 }
359 return pConv;
360 }
361
362 /******************************************************************
363 * WDML_ServerNameProc
364 *
365 *
366 */
367 static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
368 {
369 HWND hwndClient;
370 HSZ hszApp, hszTop;
371 HDDEDATA hDdeData = 0;
372 WDML_INSTANCE* pInstance;
373 UINT_PTR uiLo, uiHi;
374
375 switch (iMsg)
376 {
377 case WM_DDE_INITIATE:
378
379 /* wParam -- sending window handle
380 LOWORD(lParam) -- application atom
381 HIWORD(lParam) -- topic atom */
382
383 ERR("WM_DDE_INITIATE message received!\n");
384 hwndClient = (HWND)wParam;
385
386 pInstance = WDML_GetInstanceFromWnd(hwndServer);
387 if (!pInstance) return 0;
388 ERR("idInst=%d, threadID=0x%x\n", pInstance->instanceID, GetCurrentThreadId());
389
390 /* don't free DDEParams, since this is a broadcast */
391 UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLo, &uiHi);
392
393 hszApp = WDML_MakeHszFromAtom(pInstance, uiLo);
394 hszTop = WDML_MakeHszFromAtom(pInstance, uiHi);
395
396 if (!(pInstance->CBFflags & CBF_FAIL_CONNECTIONS))
397 {
398 BOOL self = FALSE;
399 CONVCONTEXT cc;
400 CONVCONTEXT* pcc = NULL;
401 WDML_CONV* pConv;
402 char buf[256];
403
404 if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
405 WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
406 {
407 self = TRUE;
408 }
409 /* FIXME: so far, we don't grab distant convcontext, so only check if remote is
410 * handled under DDEML, and if so build a default context
411 */
412 if ((GetClassNameA(hwndClient, buf, sizeof(buf)) &&
413 lstrcmpiA(buf, WDML_szClientConvClassA) == 0) ||
414 (GetClassNameW(hwndClient, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) &&
415 lstrcmpiW((LPWSTR)buf, WDML_szClientConvClassW) == 0))
416 {
417 pcc = &cc;
418 memset(pcc, 0, sizeof(*pcc));
419 pcc->cb = sizeof(*pcc);
420 pcc->iCodePage = IsWindowUnicode(hwndClient) ? CP_WINUNICODE : CP_WINANSI;
421 }
422 if ((pInstance->CBFflags & CBF_FAIL_SELFCONNECTIONS) && self)
423 {
424 TRACE("Don't do self connection as requested\n");
425 }
426 else if (hszApp && hszTop)
427 {
428 WDML_SERVER* pServer = (WDML_SERVER*)GetWindowLongPtrW(hwndServer, GWL_WDML_SERVER);
429
430 /* check filters for name service */
431 if (!pServer->filterOn || DdeCmpStringHandles(pServer->hszService, hszApp) == 0)
432 {
433 /* pass on to the callback */
434 hDdeData = WDML_InvokeCallback(pInstance, XTYP_CONNECT,
435 0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
436 if ((ULONG_PTR)hDdeData)
437 {
438 pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer,
439 hszApp, hszTop);
440 if (pConv)
441 {
442 if (pcc) pConv->wStatus |= ST_ISLOCAL;
443 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
444 hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
445 }
446 }
447 }
448 }
449 else if (pInstance->servers)
450 {
451 /* pass on to the callback */
452 hDdeData = WDML_InvokeCallback(pInstance, XTYP_WILDCONNECT,
453 0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
454
455 if (hDdeData == CBR_BLOCK)
456 {
457 /* MS doc is not consistent here */
458 FIXME("CBR_BLOCK returned for WILDCONNECT\n");
459 }
460 else if ((ULONG_PTR)hDdeData != 0)
461 {
462 HSZPAIR* hszp;
463
464 hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL);
465 if (hszp)
466 {
467 int i;
468 for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++)
469 {
470 pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer,
471 hszp[i].hszSvc, hszp[i].hszTopic);
472 if (pConv)
473 {
474 if (pcc) pConv->wStatus |= ST_ISLOCAL;
475 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
476 hszp[i].hszTopic, hszp[i].hszSvc, 0, (ULONG_PTR)pcc, self);
477 }
478 }
479 DdeUnaccessData(hDdeData);
480 }
481 if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData);
482 }
483 }
484 }
485
486 return 0;
487
488 case WM_DDE_REQUEST:
489 FIXME("WM_DDE_REQUEST message received!\n");
490 return 0;
491 case WM_DDE_ADVISE:
492 FIXME("WM_DDE_ADVISE message received!\n");
493 return 0;
494 case WM_DDE_UNADVISE:
495 FIXME("WM_DDE_UNADVISE message received!\n");
496 return 0;
497 case WM_DDE_EXECUTE:
498 FIXME("WM_DDE_EXECUTE message received!\n");
499 return 0;
500 case WM_DDE_POKE:
501 FIXME("WM_DDE_POKE message received!\n");
502 return 0;
503 case WM_DDE_TERMINATE:
504 FIXME("WM_DDE_TERMINATE message received!\n");
505 return 0;
506 default:
507 break;
508 }
509
510 return DefWindowProcW(hwndServer, iMsg, wParam, lParam);
511 }
512
513 /******************************************************************
514 * WDML_ServerQueueRequest
515 *
516 *
517 */
518 static WDML_XACT* WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam)
519 {
520 UINT_PTR uiLo, uiHi;
521 WDML_XACT* pXAct;
522
523 UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi);
524
525 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST,
526 uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
527 if (pXAct) pXAct->atom = uiHi;
528 return pXAct;
529 }
530
531 /******************************************************************
532 * WDML_ServerHandleRequest
533 *
534 *
535 */
536 static WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct)
537 {
538 HDDEDATA hDdeData = 0;
539 BOOL fAck = TRUE;
540
541 if (!(pConv->instance->CBFflags & CBF_FAIL_REQUESTS))
542 {
543
544 hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_REQUEST, pXAct->wFmt, (HCONV)pConv,
545 pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
546 }
547
548 switch ((ULONG_PTR)hDdeData)
549 {
550 case 0:
551 TRACE("No data returned from the Callback\n");
552 fAck = FALSE;
553 break;
554
555 case (ULONG_PTR)CBR_BLOCK:
556 return WDML_QS_BLOCK;
557
558 default:
559 {
560 HGLOBAL hMem = WDML_DataHandle2Global(hDdeData, TRUE, FALSE, FALSE, FALSE);
561 if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
562 ReuseDDElParam(pXAct->lParam, WM_DDE_REQUEST, WM_DDE_DATA,
563 (UINT_PTR)hMem, (UINT_PTR)pXAct->atom)))
564 {
565 pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
566 DdeFreeDataHandle(hDdeData);
567 GlobalFree(hMem);
568 fAck = FALSE;
569 }
570 }
571 break;
572 }
573
574 WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_REQUEST);
575
576 WDML_DecHSZ(pConv->instance, pXAct->hszItem);
577
578 return WDML_QS_HANDLED;
579 }
580
581 /******************************************************************
582 * WDML_ServerQueueAdvise
583 *
584 *
585 */
586 static WDML_XACT* WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam)
587 {
588 UINT_PTR uiLo, uiHi;
589 WDML_XACT* pXAct;
590
591 /* XTYP_ADVSTART transaction:
592 establish link and save link info to InstanceInfoTable */
593
594 if (!UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi))
595 return NULL;
596
597 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE,
598 0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
599 if (pXAct)
600 {
601 pXAct->hMem = (HGLOBAL)uiLo;
602 pXAct->atom = uiHi;
603 }
604 return pXAct;
605 }
606
607 /******************************************************************
608 * WDML_ServerHandleAdvise
609 *
610 *
611 */
612 static WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct)
613 {
614 UINT uType;
615 WDML_LINK* pLink;
616 DDEADVISE* pDdeAdvise;
617 HDDEDATA hDdeData = 0;
618 BOOL fAck = TRUE;
619
620 pDdeAdvise = GlobalLock(pXAct->hMem);
621 uType = XTYP_ADVSTART |
622 (pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) |
623 (pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0);
624
625 if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
626 {
627 hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_ADVSTART, pDdeAdvise->cfFormat,
628 (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
629 }
630
631 switch ((ULONG_PTR)hDdeData)
632 {
633 case 0:
634 TRACE("No data returned from the Callback\n");
635 fAck = FALSE;
636 break;
637
638 case (ULONG_PTR)CBR_BLOCK:
639 return WDML_QS_BLOCK;
640
641 default:
642 /* billx: first to see if the link is already created. */
643 pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
644 pXAct->hszItem, TRUE, pDdeAdvise->cfFormat);
645
646 if (pLink != NULL)
647 {
648 /* we found a link, and only need to modify it in case it changes */
649 pLink->transactionType = uType;
650 }
651 else
652 {
653 TRACE("Adding Link with hConv %p\n", pConv);
654 WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
655 uType, pXAct->hszItem, pDdeAdvise->cfFormat);
656 }
657 break;
658 }
659
660 GlobalUnlock(pXAct->hMem);
661 if (fAck)
662 {
663 GlobalFree(pXAct->hMem);
664 }
665 pXAct->hMem = 0;
666
667 WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE);
668
669 WDML_DecHSZ(pConv->instance, pXAct->hszItem);
670
671 return WDML_QS_HANDLED;
672 }
673
674 /******************************************************************
675 * WDML_ServerQueueUnadvise
676 *
677 *
678 */
679 static WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam)
680 {
681 UINT_PTR uiLo, uiHi;
682 WDML_XACT* pXAct;
683
684 UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
685
686 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE,
687 uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
688 if (pXAct) pXAct->atom = uiHi;
689 return pXAct;
690 }
691
692 /******************************************************************
693 * WDML_ServerHandleUnadvise
694 *
695 *
696 */
697 static WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct)
698 {
699 WDML_LINK* pLink;
700
701 if (pXAct->hszItem == NULL || pXAct->wFmt == 0)
702 {
703 ERR("Unsupported yet options (null item or clipboard format)\n");
704 return WDML_QS_ERROR;
705 }
706
707 pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
708 pXAct->hszItem, TRUE, pXAct->wFmt);
709 if (pLink == NULL)
710 {
711 ERR("Couldn't find link for %p, dropping request\n", pXAct->hszItem);
712 FreeDDElParam(WM_DDE_UNADVISE, pXAct->lParam);
713 return WDML_QS_ERROR;
714 }
715
716 if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
717 {
718 WDML_InvokeCallback(pConv->instance, XTYP_ADVSTOP, pXAct->wFmt, (HCONV)pConv,
719 pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
720 }
721
722 WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
723 pXAct->hszItem, pXAct->wFmt);
724
725 /* send back ack */
726 WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, TRUE, pXAct->atom,
727 pXAct->lParam, WM_DDE_UNADVISE);
728
729 WDML_DecHSZ(pConv->instance, pXAct->hszItem);
730
731 return WDML_QS_HANDLED;
732 }
733
734 /******************************************************************
735 * WDML_QueueExecute
736 *
737 *
738 */
739 static WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam)
740 {
741 WDML_XACT* pXAct;
742
743 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
744 if (pXAct)
745 {
746 pXAct->hMem = (HGLOBAL)lParam;
747 }
748 return pXAct;
749 }
750
751 static BOOL data_looks_unicode( const WCHAR *data, DWORD size )
752 {
753 DWORD i;
754
755 if (size % sizeof(WCHAR)) return FALSE;
756 for (i = 0; i < size / sizeof(WCHAR); i++) if (data[i] > 255) return FALSE;
757 return TRUE;
758 }
759
760 /* convert data to Unicode, unless it looks like it's already Unicode */
761 static HDDEDATA map_A_to_W( DWORD instance, void *ptr, DWORD size )
762 {
763 HDDEDATA ret;
764 DWORD len;
765 const char *end;
766
767 if (!data_looks_unicode( ptr, size ))
768 {
769 if ((end = memchr( ptr, 0, size ))) size = end + 1 - (const char *)ptr;
770 len = MultiByteToWideChar( CP_ACP, 0, ptr, size, NULL, 0 );
771 ret = DdeCreateDataHandle( instance, NULL, len * sizeof(WCHAR), 0, 0, CF_TEXT, 0);
772 MultiByteToWideChar( CP_ACP, 0, ptr, size, (WCHAR *)DdeAccessData(ret, NULL), len );
773 }
774 else ret = DdeCreateDataHandle( instance, ptr, size, 0, 0, CF_TEXT, 0 );
775
776 return ret;
777 }
778
779 /* convert data to ASCII, unless it looks like it's not in Unicode format */
780 static HDDEDATA map_W_to_A( DWORD instance, void *ptr, DWORD size )
781 {
782 HDDEDATA ret;
783 DWORD len;
784 const WCHAR *end;
785
786 if (data_looks_unicode( ptr, size ))
787 {
788 size /= sizeof(WCHAR);
789 if ((end = memchrW( ptr, 0, size ))) size = end + 1 - (const WCHAR *)ptr;
790 len = WideCharToMultiByte( CP_ACP, 0, ptr, size, NULL, 0, NULL, NULL );
791 ret = DdeCreateDataHandle( instance, NULL, len, 0, 0, CF_TEXT, 0);
792 WideCharToMultiByte( CP_ACP, 0, ptr, size, (char *)DdeAccessData(ret, NULL), len, NULL, NULL );
793 }
794 else ret = DdeCreateDataHandle( instance, ptr, size, 0, 0, CF_TEXT, 0 );
795
796 return ret;
797 }
798
799 /******************************************************************
800 * WDML_ServerHandleExecute
801 *
802 *
803 */
804 static WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct)
805 {
806 HDDEDATA hDdeData = DDE_FNOTPROCESSED;
807 BOOL fAck = FALSE, fBusy = FALSE;
808
809 if (!(pConv->instance->CBFflags & CBF_FAIL_EXECUTES))
810 {
811 LPVOID ptr = GlobalLock(pXAct->hMem);
812 DWORD size = GlobalSize(pXAct->hMem);
813
814 if (ptr)
815 {
816 if (pConv->instance->unicode) /* Unicode server, try to map A->W */
817 hDdeData = map_A_to_W( pConv->instance->instanceID, ptr, size );
818 else if (!IsWindowUnicode( pConv->hwndClient )) /* ASCII server and client, try to map W->A */
819 hDdeData = map_W_to_A( pConv->instance->instanceID, ptr, size );
820 else
821 hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, ptr, size, 0, 0, CF_TEXT, 0);
822 GlobalUnlock(pXAct->hMem);
823 }
824 hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_EXECUTE, 0, (HCONV)pConv,
825 pConv->hszTopic, 0, hDdeData, 0L, 0L);
826 }
827
828 switch ((ULONG_PTR)hDdeData)
829 {
830 case (ULONG_PTR)CBR_BLOCK:
831 return WDML_QS_BLOCK;
832
833 case DDE_FACK:
834 fAck = TRUE;
835 break;
836 case DDE_FBUSY:
837 fBusy = TRUE;
838 break;
839 default:
840 FIXME("Unsupported returned value %p\n", hDdeData);
841 /* fall through */
842 case DDE_FNOTPROCESSED:
843 break;
844 }
845 WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, (UINT_PTR)pXAct->hMem, 0, 0);
846
847 return WDML_QS_HANDLED;
848 }
849
850 /******************************************************************
851 * WDML_ServerQueuePoke
852 *
853 *
854 */
855 static WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam)
856 {
857 UINT_PTR uiLo, uiHi;
858 WDML_XACT* pXAct;
859
860 UnpackDDElParam(WM_DDE_POKE, lParam, &uiLo, &uiHi);
861
862 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE,
863 0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
864 if (pXAct)
865 {
866 pXAct->atom = uiHi;
867 pXAct->hMem = (HGLOBAL)uiLo;
868 }
869 return pXAct;
870 }
871
872 /******************************************************************
873 * WDML_ServerHandlePoke
874 *
875 *
876 */
877 static WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct)
878 {
879 DDEPOKE* pDdePoke;
880 HDDEDATA hDdeData;
881 BOOL fBusy = FALSE, fAck = FALSE;
882
883 pDdePoke = GlobalLock(pXAct->hMem);
884 if (!pDdePoke)
885 {
886 return WDML_QS_ERROR;
887 }
888
889 if (!(pConv->instance->CBFflags & CBF_FAIL_POKES))
890 {
891 hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, pDdePoke->Value,
892 GlobalSize(pXAct->hMem) - FIELD_OFFSET(DDEPOKE, Value),
893 0, 0, pDdePoke->cfFormat, 0);
894 if (hDdeData)
895 {
896 HDDEDATA hDdeDataOut;
897
898 hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_POKE, pDdePoke->cfFormat,
899 (HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
900 hDdeData, 0, 0);
901 switch ((ULONG_PTR)hDdeDataOut)
902 {
903 case DDE_FACK:
904 fAck = TRUE;
905 break;
906 case DDE_FBUSY:
907 fBusy = TRUE;
908 break;
909 default:
910 FIXME("Unsupported returned value %p\n", hDdeDataOut);
911 /* fal through */
912 case DDE_FNOTPROCESSED:
913 break;
914 }
915 DdeFreeDataHandle(hDdeData);
916 }
917 }
918 GlobalUnlock(pXAct->hMem);
919
920 if (!fAck)
921 {
922 GlobalFree(pXAct->hMem);
923 }
924 WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->atom, pXAct->lParam, WM_DDE_POKE);
925
926 WDML_DecHSZ(pConv->instance, pXAct->hszItem);
927
928 return WDML_QS_HANDLED;
929 }
930
931 /******************************************************************
932 * WDML_ServerQueueTerminate
933 *
934 *
935 */
936 static WDML_XACT* WDML_ServerQueueTerminate(WDML_CONV* pConv, LPARAM lParam)
937 {
938 WDML_XACT* pXAct;
939
940 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
941 return pXAct;
942 }
943
944 /******************************************************************
945 * WDML_ServerHandleTerminate
946 *
947 *
948 */
949 static WDML_QUEUE_STATE WDML_ServerHandleTerminate(WDML_CONV* pConv, WDML_XACT* pXAct)
950 {
951 /* billx: two things to remove: the conv, and associated links.
952 * Respond with another WM_DDE_TERMINATE iMsg.
953 */
954 if (!(pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS))
955 {
956 WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 0, 0,
957 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
958 }
959 PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
960 WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
961
962 return WDML_QS_HANDLED;
963 }
964
965 /******************************************************************
966 * WDML_ServerHandle
967 *
968 *
969 */
970 WDML_QUEUE_STATE WDML_ServerHandle(WDML_CONV* pConv, WDML_XACT* pXAct)
971 {
972 WDML_QUEUE_STATE qs = WDML_QS_ERROR;
973
974 switch (pXAct->ddeMsg)
975 {
976 case WM_DDE_INITIATE:
977 FIXME("WM_DDE_INITIATE shouldn't be there!\n");
978 break;
979 case WM_DDE_REQUEST:
980 qs = WDML_ServerHandleRequest(pConv, pXAct);
981 break;
982
983 case WM_DDE_ADVISE:
984 qs = WDML_ServerHandleAdvise(pConv, pXAct);
985 break;
986
987 case WM_DDE_UNADVISE:
988 qs = WDML_ServerHandleUnadvise(pConv, pXAct);
989 break;
990
991 case WM_DDE_EXECUTE:
992 qs = WDML_ServerHandleExecute(pConv, pXAct);
993 break;
994
995 case WM_DDE_POKE:
996 qs = WDML_ServerHandlePoke(pConv, pXAct);
997 break;
998
999 case WM_DDE_TERMINATE:
1000 qs = WDML_ServerHandleTerminate(pConv, pXAct);
1001 break;
1002
1003 case WM_DDE_ACK:
1004 WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
1005 break;
1006
1007 default:
1008 FIXME("Unsupported message %d\n", pXAct->ddeMsg);
1009 }
1010 return qs;
1011 }
1012
1013 /******************************************************************
1014 * WDML_ServerConvProc
1015 *
1016 *
1017 */
1018 static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
1019 {
1020 WDML_INSTANCE* pInstance;
1021 WDML_CONV* pConv;
1022 WDML_XACT* pXAct = NULL;
1023
1024 TRACE("%p %04x %08lx %08lx\n", hwndServer, iMsg, wParam, lParam);
1025
1026 if (iMsg == WM_DESTROY)
1027 {
1028 pConv = WDML_GetConvFromWnd(hwndServer);
1029 if (pConv && !(pConv->wStatus & ST_TERMINATED))
1030 {
1031 WDML_ServerHandleTerminate(pConv, NULL);
1032 }
1033 }
1034 if (iMsg < WM_DDE_FIRST || iMsg > WM_DDE_LAST)
1035 {
1036 return IsWindowUnicode(hwndServer) ? DefWindowProcW(hwndServer, iMsg, wParam, lParam) :
1037 DefWindowProcA(hwndServer, iMsg, wParam, lParam);
1038 }
1039
1040 pInstance = WDML_GetInstanceFromWnd(hwndServer);
1041 pConv = WDML_GetConvFromWnd(hwndServer);
1042
1043 if (!pConv)
1044 {
1045 ERR("Got a message (%x) on a not known conversation, dropping request\n", iMsg);
1046 return 0;
1047 }
1048 if (pConv->hwndClient != WIN_GetFullHandle( (HWND)wParam ) || pConv->hwndServer != hwndServer)
1049 {
1050 ERR("mismatch between C/S windows and conversation\n");
1051 return 0;
1052 }
1053 if (pConv->instance != pInstance || pConv->instance == NULL)
1054 {
1055 ERR("mismatch in instances\n");
1056 return 0;
1057 }
1058
1059 switch (iMsg)
1060 {
1061 case WM_DDE_INITIATE:
1062 FIXME("WM_DDE_INITIATE message received!\n");
1063 break;
1064
1065 case WM_DDE_REQUEST:
1066 pXAct = WDML_ServerQueueRequest(pConv, lParam);
1067 break;
1068
1069 case WM_DDE_ADVISE:
1070 pXAct = WDML_ServerQueueAdvise(pConv, lParam);
1071 break;
1072
1073 case WM_DDE_UNADVISE:
1074 pXAct = WDML_ServerQueueUnadvise(pConv, lParam);
1075 break;
1076
1077 case WM_DDE_EXECUTE:
1078 pXAct = WDML_ServerQueueExecute(pConv, lParam);
1079 break;
1080
1081 case WM_DDE_POKE:
1082 pXAct = WDML_ServerQueuePoke(pConv, lParam);
1083 break;
1084
1085 case WM_DDE_TERMINATE:
1086 pXAct = WDML_ServerQueueTerminate(pConv, lParam);
1087 break;
1088
1089 case WM_DDE_ACK:
1090 WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
1091 break;
1092
1093 default:
1094 FIXME("Unsupported message %x\n", iMsg);
1095 break;
1096 }
1097
1098 if (pXAct)
1099 {
1100 pXAct->lParam = lParam;
1101
1102 if ((pConv->wStatus & ST_BLOCKED) || WDML_ServerHandle(pConv, pXAct) == WDML_QS_BLOCK)
1103 {
1104 TRACE("Transactions are blocked, add to the queue and exit\n");
1105 WDML_QueueTransaction(pConv, pXAct);
1106 }
1107 else
1108 {
1109 WDML_FreeTransaction(pInstance, pXAct, TRUE);
1110 }
1111 }
1112 else
1113 pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1114
1115 return 0;
1116 }