Moved DDEML into dlls/user.
[wine] / dlls / user / ddeml.c
1 /*
2  * DDEML library
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1997 Len White
6  * Copyright 1999 Keith Matthews
7  */
8
9 /* Only empty stubs for now */
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include "winbase.h"
14 #include "windef.h"
15 #include "wingdi.h"
16 #include "winuser.h"
17 #include "ddeml.h"
18 #include "winerror.h"
19 #include "heap.h"
20 #include "debugtools.h"
21 #include "tchar.h"
22 #include "winnt.h"
23
24 DEFAULT_DEBUG_CHANNEL(ddeml)
25
26 /* Has defined in atom.c file.
27  */
28 #define MAX_ATOM_LEN              255
29
30 /* Maximum buffer size ( including the '\0' ).
31  */
32 #define MAX_BUFFER_LEN            (MAX_ATOM_LEN + 1)
33
34 /*  typedef struct {
35         DWORD           nLength;
36         LPVOID          lpSecurityDescriptor;
37         BOOL            bInheritHandle;
38 }       SECURITY_ATTRIBUTES; */
39
40 /* This is a simple list to keep track of the strings created
41  * by DdeCreateStringHandle.  The list is used to free
42  * the strings whenever DdeUninitialize is called.
43  * This mechanism is not complete and does not handle multiple instances.
44  * Most of the DDE API use a DWORD parameter indicating which instance
45  * of a given program is calling them.  The API are supposed to
46  * associate the data to the instance that created it.
47  */
48 typedef struct tagHSZNode HSZNode;
49 struct tagHSZNode
50 {
51     HSZNode* next;
52     HSZ hsz;
53 };
54
55
56 typedef struct tagServiceNode ServiceNode;
57 struct tagServiceNode
58 {
59     ServiceNode* next;
60     HSZ hsz;
61     BOOL16              FilterOn;
62 };
63 typedef struct DDE_HANDLE_ENTRY {
64     BOOL16              Monitor;        /* have these two as full Booleans cos they'll be tested frequently */
65     BOOL16              Client_only;    /* bit wasteful of space but it will be faster */
66     BOOL16              Unicode;        /* Flag to indicate Win32 API used to initialise */
67     BOOL16              Win16;          /* flag to indicate Win16 API used to initialize */
68     DWORD               Instance_id;  /* needed to track monitor usage */
69     struct DDE_HANDLE_ENTRY    *Next_Entry;
70     HSZNode     *Node_list;
71     PFNCALLBACK         CallBack;
72     DWORD               CBF_Flags;
73     DWORD               Monitor_flags;
74     UINT              Txn_count;      /* count transactions open to simplify closure */
75     DWORD               Last_Error; 
76     ServiceNode*                ServiceNames;
77 } DDE_HANDLE_ENTRY;
78
79 static DDE_HANDLE_ENTRY *DDE_Handle_Table_Base = NULL;
80 static DWORD            DDE_Max_Assigned_Instance = 0;  /* OK for present, may have to worry about wrap-around later */
81 static const char       *DDEInstanceAccess = "DDEMaxInstance";
82 static const char       *DDEHandleAccess = "DDEHandleAccess";
83 static HANDLE           inst_count_mutex = 0;
84 static HANDLE           handle_mutex = 0;
85
86 #define TRUE    1
87 #define FALSE   0
88
89
90 /******************************************************************************
91  *            RemoveHSZNodes    (INTERNAL)
92  *
93  * Remove a node from the list of HSZ nodes.
94  *
95  ******************************************************************************
96  *
97  *      Change History
98  *
99  *  Vn       Date       Author                  Comment
100  *
101  *  1.0      Dec 1998  Corel/Macadamian    Initial version
102  *  1.1      Mar 1999  Keith Matthews      Added multiple instance handling
103  *
104  */
105 static void RemoveHSZNode( HSZ hsz, DDE_HANDLE_ENTRY * reference_inst )
106 {
107     HSZNode* pPrev = NULL;
108     HSZNode* pCurrent = NULL;
109
110     /* Set the current node at the start of the list.
111      */
112     pCurrent = reference_inst->Node_list;
113     /* While we have more nodes.
114      */
115     while( pCurrent != NULL )
116     {
117         /* If we found the node we were looking for.
118          */
119         if( pCurrent->hsz == hsz )
120         {
121             /* Remove the node.
122              */
123             /* If the first node in the list is to to be removed.
124              * Set the global list pointer to the next node.
125              */
126             if( pCurrent == reference_inst->Node_list )
127             {
128                 reference_inst->Node_list = pCurrent->next;
129             }
130             /* Just fix the pointers has to skip the current
131              * node so we can delete it.
132              */
133             else
134             {
135                 pPrev->next = pCurrent->next;
136             }
137             /* Destroy this node.
138              */
139             free( pCurrent );
140             break;
141         }
142         /* Save the previous node pointer.
143          */
144         pPrev = pCurrent;
145         /* Move on to the next node.
146          */
147         pCurrent = pCurrent->next;
148     }
149 }
150
151 /******************************************************************************
152  *            FreeAndRemoveHSZNodes    (INTERNAL)
153  *
154  * Frees up all the strings still allocated in the list and
155  * remove all the nodes from the list of HSZ nodes.
156  *
157  ******************************************************************************
158  *
159  *      Change History
160  *
161  *  Vn       Date       Author                  Comment
162  *
163  *  1.0      Dec 1998  Corel/Macadamian    Initial version
164  *  1.1      Mar 1999  Keith Matthews      Added multiple instance handling
165  *
166  */
167 static void FreeAndRemoveHSZNodes( DWORD idInst, DDE_HANDLE_ENTRY * reference_inst )
168 {
169     /* Free any strings created in this instance.
170      */
171     while( reference_inst->Node_list != NULL )
172     {
173         DdeFreeStringHandle( idInst, reference_inst->Node_list->hsz );
174     }
175 }
176
177 /******************************************************************************
178  *            InsertHSZNode    (INTERNAL)
179  *
180  * Insert a node to the head of the list.
181  *
182  ******************************************************************************
183  *
184  *      Change History
185  *
186  *  Vn       Date       Author                  Comment
187  *
188  *  1.0      Dec 1998  Corel/Macadamian    Initial version
189  *  1.1      Mar 1999  Keith Matthews      Added instance handling
190  *  1.2      Jun 1999  Keith Matthews      Added Usage count handling
191  *
192  */
193 static void InsertHSZNode( HSZ hsz, DDE_HANDLE_ENTRY * reference_inst )
194 {
195     if( hsz != 0 )
196     {
197         HSZNode* pNew = NULL;
198         /* Create a new node for this HSZ.
199          */
200         pNew = (HSZNode*) malloc( sizeof( HSZNode ) );
201         if( pNew != NULL )
202         {
203             /* Set the handle value.
204              */
205             pNew->hsz = hsz;
206             /* Attach the node to the head of the list. i.e most recently added is first
207              */
208             pNew->next = reference_inst->Node_list;
209
210             /* The new node is now at the head of the list
211              * so set the global list pointer to it.
212              */
213             reference_inst->Node_list = pNew;
214             TRACE("HSZ node list entry added\n");
215         }
216     }
217 }
218
219 /*****************************************************************************
220  *      Find_Instance_Entry
221  *
222  *      generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
223  *      for an instance Id, or NULL if the entry does not exist
224  *
225  *      ASSUMES the mutex protecting the handle entry list is reserved before calling
226  *
227  ******************************************************************************
228  *
229  *      Change History
230  *
231  *  Vn       Date       Author                  Comment
232  *
233  *  1.0      March 1999  Keith Matthews      1st implementation
234              */
235  DDE_HANDLE_ENTRY *Find_Instance_Entry (DWORD InstId)
236 {
237         DDE_HANDLE_ENTRY * reference_inst;
238         reference_inst =  DDE_Handle_Table_Base;
239         while ( reference_inst != NULL )
240         {
241                 if ( reference_inst->Instance_id == InstId )
242                 {
243                         TRACE("Instance entry found\n");
244                         return reference_inst;
245         }
246                 reference_inst = reference_inst->Next_Entry;
247     }
248         TRACE("Instance entry missing\n");
249         return NULL;
250 }
251
252 /*****************************************************************************
253  *      Find_Service_Name
254  *
255  *      generic routine to return a pointer to the relevant ServiceNode
256  *      for a given service name, or NULL if the entry does not exist
257  *
258  *      ASSUMES the mutex protecting the handle entry list is reserved before calling
259  *
260  ******************************************************************************
261  *
262  *      Change History
263  *
264  *  Vn       Date       Author                  Comment
265  *
266  *  1.0      May 1999  Keith Matthews      1st implementation
267              */
268  ServiceNode *Find_Service_Name (HSZ Service_Name, DDE_HANDLE_ENTRY* this_instance)
269 {
270         ServiceNode * reference_name=  this_instance->ServiceNames;
271         while ( reference_name != NULL )
272         {
273                 if ( reference_name->hsz == Service_Name )
274                 {
275                         TRACE("Service Name found\n");
276                         return reference_name;
277         }
278                 reference_name = reference_name->next;
279     }
280         TRACE("Service name missing\n");
281         return NULL;
282 }
283
284
285 /******************************************************************************
286  *      Release_reserved_mutex
287  *
288  *      generic routine to release a reserved mutex
289  *
290  *
291  ******************************************************************************
292  *
293  *      Change History
294  *
295  *  Vn       Date       Author                  Comment
296  *
297  *  1.0      Jan 1999  Keith Matthews        Initial version
298  *  1.1      Mar 1999  Keith Matthews        Corrected Heap handling. Corrected re-initialisation handling
299  *  1.2      Aug 1999  Jürgen Schmied        Corrected error handling
300  *
301  */
302 static DWORD Release_reserved_mutex (HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m, BOOL release_this_i , 
303     DDE_HANDLE_ENTRY *this_instance)
304 {
305         if (!ReleaseMutex(mutex))
306         {
307                 ERR("ReleaseMutex failed - %s mutex %li\n",mutex_name,GetLastError());
308                 HeapFree(GetProcessHeap(), 0, this_instance);
309                 if ( release_handle_m )
310                 {
311                         ReleaseMutex(handle_mutex);
312                 }
313                 return DMLERR_SYS_ERROR;
314          }
315         if ( release_this_i )
316         {
317                 HeapFree(GetProcessHeap(), 0, this_instance);
318         }
319         return DMLERR_NO_ERROR;
320 }
321
322 /******************************************************************************
323  *      WaitForMutex
324  *
325  *      generic routine to wait for the mutex
326  *
327  *
328  ******************************************************************************
329  *
330  *      Change History
331  *
332  *  Vn       Date       Author                  Comment
333  *
334  *  1.0      Aug 1999  Juergen Schmied       Initial version
335  *
336  */
337 static BOOL WaitForMutex (HANDLE mutex)
338 {
339         DWORD result;
340
341         result = WaitForSingleObject(mutex,1000);
342
343         /* both errors should never occur */
344         if (WAIT_TIMEOUT == result)
345         {
346                 ERR("WaitForSingleObject timed out\n");
347                 return FALSE;
348         }
349
350         if (WAIT_FAILED == result)
351         {
352                 ERR("WaitForSingleObject failed - error %li\n", GetLastError());
353                 return FALSE;
354         }
355         return TRUE;
356 }
357 /******************************************************************************
358  *              IncrementInstanceId
359  *
360  *      generic routine to increment the max instance Id and allocate a new application instance
361  *
362  ******************************************************************************
363  *
364  *      Change History
365  *
366  *  Vn       Date       Author                  Comment
367  *
368  *  1.0      Jan 1999  Keith Matthews        Initial version
369  *
370  */
371 DWORD IncrementInstanceId( DDE_HANDLE_ENTRY *this_instance)
372 {
373         SECURITY_ATTRIBUTES s_attrib;
374
375         /*  Need to set up Mutex in case it is not already present */
376         /* increment handle count & get value */
377         if ( !inst_count_mutex )
378         {
379                 s_attrib.bInheritHandle = TRUE;
380                 s_attrib.lpSecurityDescriptor = NULL;
381                 s_attrib.nLength = sizeof(s_attrib);
382                 inst_count_mutex = CreateMutexA(&s_attrib,1,DDEInstanceAccess); /* 1st time through */
383                 inst_count_mutex = ConvertToGlobalHandle(inst_count_mutex); /* fixme when having seperate adresspaces*/
384         } else {
385                 if ( !WaitForMutex(inst_count_mutex) )
386                 {
387                         return DMLERR_SYS_ERROR;
388                 }
389         }
390         if ( !inst_count_mutex )
391         {
392                 ERR("CreateMutex failed - inst_count %li\n",GetLastError());
393                 Release_reserved_mutex (handle_mutex,"handle_mutex",0,1,this_instance);
394                 return DMLERR_SYS_ERROR;
395         }
396         DDE_Max_Assigned_Instance++;
397         this_instance->Instance_id = DDE_Max_Assigned_Instance;
398         TRACE("New instance id %ld allocated\n",DDE_Max_Assigned_Instance);
399         if (Release_reserved_mutex(inst_count_mutex,"instance_count",1,0,this_instance)) return DMLERR_SYS_ERROR;
400         return DMLERR_NO_ERROR;
401 }
402
403 /******************************************************************************
404  *              FindNotifyMonitorCallbacks
405  *
406  *      Routine to find instances that need to be notified via their callback
407  *      of some event they are monitoring
408  *
409  ******************************************************************************
410  *
411  *      Change History
412  *
413  *  Vn       Date       Author                  Comment
414  *
415  *  1.0    May 1999    Keith Matthews       Initial Version
416  *
417  */
418
419 void FindNotifyMonitorCallbacks(DWORD ThisInstance, DWORD DdeEvent )
420 {
421         DDE_HANDLE_ENTRY *InstanceHandle;
422         InstanceHandle = DDE_Handle_Table_Base;
423         while ( InstanceHandle != NULL )
424         {
425                 if (  (InstanceHandle->Monitor ) && InstanceHandle->Instance_id == ThisInstance )
426                 {
427                         /*   Found an instance registered as monitor and is not ourselves
428                          *      use callback to notify where appropriate
429                          */
430                 }
431                 InstanceHandle = InstanceHandle->Next_Entry;
432         }
433 }
434
435 /******************************************************************************
436  *              DdeReserveAtom
437  *
438  *      Routine to make an extra Add on an atom to reserve it a bit longer
439  *
440  ******************************************************************************
441  *
442  *      Change History
443  *
444  *  Vn       Date       Author                  Comment
445  *
446  *  1.0    Jun 1999    Keith Matthews       Initial Version
447  *
448  */
449
450 static void DdeReserveAtom( DDE_HANDLE_ENTRY * reference_inst,HSZ hsz)
451 {
452   if ( reference_inst->Unicode)
453   {
454         WCHAR SNameBuffer[MAX_BUFFER_LEN];
455         GlobalGetAtomNameW(hsz,SNameBuffer,MAX_BUFFER_LEN);
456         GlobalAddAtomW(SNameBuffer);
457   } else {
458         CHAR SNameBuffer[MAX_BUFFER_LEN];
459         GlobalGetAtomNameA(hsz,SNameBuffer,MAX_BUFFER_LEN);
460         GlobalAddAtomA(SNameBuffer);
461   }
462 }
463
464
465 /******************************************************************************
466  *              DdeReleaseAtom
467  *
468  *      Routine to make a delete on an atom to release it a bit sooner
469  *
470  ******************************************************************************
471  *
472  *      Change History
473  *
474  *  Vn       Date       Author                  Comment
475  *
476  *  1.0    Jun 1999    Keith Matthews       Initial Version
477  *
478  */
479
480 static void DdeReleaseAtom( DDE_HANDLE_ENTRY * reference_inst,HSZ hsz)
481 {
482     GlobalDeleteAtom( hsz );
483 }
484
485 /******************************************************************************
486  *            DdeInitialize16   (DDEML.2)
487  */
488 UINT16 WINAPI DdeInitialize16( LPDWORD pidInst, PFNCALLBACK16 pfnCallback,
489                                DWORD afCmd, DWORD ulRes)
490 {
491     TRACE("DdeInitialize16 called - calling DdeInitializeA\n");
492     return (UINT16)DdeInitializeA(pidInst,(PFNCALLBACK)pfnCallback,
493                                     afCmd, ulRes);
494 }
495
496
497 /******************************************************************************
498  *            DdeInitializeA   (USER32.106)
499  */
500 UINT WINAPI DdeInitializeA( LPDWORD pidInst, PFNCALLBACK pfnCallback,
501                                 DWORD afCmd, DWORD ulRes )
502 {
503     TRACE("DdeInitializeA called - calling DdeInitializeW\n");
504     return DdeInitializeW(pidInst,pfnCallback,afCmd,ulRes);
505 }
506
507
508 /******************************************************************************
509  * DdeInitializeW [USER32.107]
510  * Registers an application with the DDEML
511  *
512  * PARAMS
513  *    pidInst     [I] Pointer to instance identifier
514  *    pfnCallback [I] Pointer to callback function
515  *    afCmd       [I] Set of command and filter flags
516  *    ulRes       [I] Reserved
517  *
518  * RETURNS
519  *    Success: DMLERR_NO_ERROR
520  *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
521  *
522  ******************************************************************************
523  *
524  *      Change History
525  *
526  *  Vn       Date       Author                  Comment
527  *
528  *  1.0      Pre 1998  Alexandre/Len         Initial Stub
529  *  1.1      Jan 1999  Keith Matthews        Initial (near-)complete version
530  *  1.2      Mar 1999  Keith Matthews        Corrected Heap handling, CreateMutex failure handling
531  *
532  */
533 UINT WINAPI DdeInitializeW( LPDWORD pidInst, PFNCALLBACK pfnCallback,
534                                 DWORD afCmd, DWORD ulRes )
535 {
536
537 /*  probably not really capable of handling multiple processes, but should handle
538  *      multiple instances within one process */
539
540     SECURITY_ATTRIBUTES *s_att= NULL;
541     SECURITY_ATTRIBUTES s_attrib;
542     DWORD               err_no = 0;
543     DDE_HANDLE_ENTRY *this_instance;
544     DDE_HANDLE_ENTRY *reference_inst;
545     s_att = &s_attrib;
546
547     if( ulRes )
548     {
549         ERR("Reserved value not zero?  What does this mean?\n");
550         FIXME("(%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback,
551               afCmd,ulRes);
552         /* trap this and no more until we know more */
553         return DMLERR_NO_ERROR;
554     }
555     if (!pfnCallback ) 
556     {
557         /*  this one may be wrong - MS dll seems to accept the condition, 
558             leave this until we find out more !! */
559
560
561         /* can't set up the instance with nothing to act as a callback */
562         TRACE("No callback provided\n");
563         return DMLERR_INVALIDPARAMETER; /* might be DMLERR_DLL_USAGE */
564      }
565
566      /* grab enough heap for one control struct - not really necessary for re-initialise
567       * but allows us to use same validation routines */
568      this_instance= (DDE_HANDLE_ENTRY*)HeapAlloc( GetProcessHeap(), 0, sizeof(DDE_HANDLE_ENTRY) );
569      if ( this_instance == NULL )
570      {
571         /* catastrophe !! warn user & abort */
572         ERR("Instance create failed - out of memory\n");
573         return DMLERR_SYS_ERROR;
574      }
575      this_instance->Next_Entry = NULL;
576      this_instance->Monitor=(afCmd|APPCLASS_MONITOR);
577
578      /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
579
580      this_instance->Client_only=afCmd&APPCMD_CLIENTONLY;
581      this_instance->Instance_id = *pidInst; /* May need to add calling proc Id */
582      this_instance->CallBack=*pfnCallback;
583      this_instance->Txn_count=0;
584      this_instance->Unicode = TRUE;
585      this_instance->Win16 = FALSE;
586      this_instance->Node_list = NULL; /* node will be added later */
587      this_instance->Monitor_flags = afCmd & MF_MASK;
588      this_instance->ServiceNames = NULL;
589
590      /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
591
592      this_instance->CBF_Flags=afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
593
594      if ( ! this_instance->Client_only )
595      {
596
597         /* Check for other way of setting Client-only !! */
598
599         this_instance->Client_only=(this_instance->CBF_Flags&CBF_FAIL_ALLSVRXACTIONS)
600                         ==CBF_FAIL_ALLSVRXACTIONS;
601      }
602
603      TRACE("instance created - checking validity \n");
604
605     if( *pidInst == 0 ) {
606         /*  Initialisation of new Instance Identifier */
607         TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
608         if ( DDE_Max_Assigned_Instance == 0 )
609         {
610                 /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
611         /*  Need to set up Mutex in case it is not already present */
612         s_att->bInheritHandle = TRUE;
613         s_att->lpSecurityDescriptor = NULL;
614         s_att->nLength = sizeof(s_att);
615         handle_mutex = CreateMutexA(s_att,1,DDEHandleAccess);
616         handle_mutex = ConvertToGlobalHandle(handle_mutex); /* fixme when having seperate adresspaces*/
617         if ( !handle_mutex ) {
618                 ERR("CreateMutex failed - handle list  %li\n",GetLastError());
619                 HeapFree(GetProcessHeap(), 0, this_instance);
620                 return DMLERR_SYS_ERROR;
621         }
622     } else {
623         if ( !WaitForMutex(handle_mutex) )
624         {
625                 return DMLERR_SYS_ERROR;
626         }
627     }
628
629         TRACE("Handle Mutex created/reserved\n");
630         if (DDE_Handle_Table_Base == NULL ) 
631         {
632                 /* can't be another instance in this case, assign to the base pointer */
633                 DDE_Handle_Table_Base= this_instance;
634
635                 /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
636                  *              present 
637                  *      -------------------------------      NOTE NOTE NOTE    --------------------------
638                  *              
639                  *      the manual is not clear if this condition
640                  *      applies to the first call to DdeInitialize from an application, or the 
641                  *      first call for a given callback !!!
642                 */
643
644                 this_instance->CBF_Flags=this_instance->CBF_Flags|APPCMD_FILTERINITS;
645                 TRACE("First application instance detected OK\n");
646                 /*      allocate new instance ID */
647                 if ((err_no = IncrementInstanceId( this_instance)) ) return err_no;
648
649         } else {
650                 /* really need to chain the new one in to the latest here, but after checking conditions
651                  *      such as trying to start a conversation from an application trying to monitor */
652                 reference_inst =  DDE_Handle_Table_Base;
653                 TRACE("Subsequent application instance - starting checks\n");
654                 while ( reference_inst->Next_Entry != NULL ) 
655                 {
656                         /*
657                         *       This set of tests will work if application uses same instance Id
658                         *       at application level once allocated - which is what manual implies
659                         *       should happen. If someone tries to be 
660                         *       clever (lazy ?) it will fail to pick up that later calls are for
661                         *       the same application - should we trust them ?
662                         */
663                         if ( this_instance->Instance_id == reference_inst->Instance_id) 
664                         {
665                                 /* Check 1 - must be same Client-only state */
666
667                                 if ( this_instance->Client_only != reference_inst->Client_only)
668                                 {
669                                         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
670                                                 return DMLERR_SYS_ERROR;
671                                         return DMLERR_DLL_USAGE;
672                                 }
673
674                                 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
675
676                                 if ( this_instance->Monitor != reference_inst->Monitor) 
677                                 {
678                                         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
679                                                 return DMLERR_SYS_ERROR;
680                                         return DMLERR_INVALIDPARAMETER;
681                                 }
682
683                                 /* Check 3 - must supply different callback address */
684
685                                 if ( this_instance->CallBack == reference_inst->CallBack)
686                                 {
687                                         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
688                                                 return DMLERR_SYS_ERROR;
689                                         return DMLERR_DLL_USAGE;
690                                 }
691                         }
692                         reference_inst = reference_inst->Next_Entry;
693                 }
694                 /*  All cleared, add to chain */
695
696                 TRACE("Application Instance checks finished\n");
697                 if ((err_no = IncrementInstanceId( this_instance)) ) return err_no;
698                 if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,0,this_instance)) return DMLERR_SYS_ERROR;
699                 reference_inst->Next_Entry = this_instance;
700         }
701         *pidInst = this_instance->Instance_id;
702         TRACE("New application instance processing finished OK\n");
703      } else {
704         /* Reinitialisation situation   --- FIX  */
705         TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes);
706
707         if ( !WaitForMutex(handle_mutex)  )
708         {
709                 HeapFree(GetProcessHeap(), 0, this_instance);
710                 return DMLERR_SYS_ERROR;
711         }
712
713         if (DDE_Handle_Table_Base == NULL ) 
714         {
715                 if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance)) return DMLERR_SYS_ERROR;
716                 return DMLERR_DLL_USAGE;
717         }
718         HeapFree(GetProcessHeap(), 0, this_instance); /* finished - release heap space used as work store */
719         /* can't reinitialise if we have initialised nothing !! */
720         reference_inst =  DDE_Handle_Table_Base;
721         /* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
722         /*
723         *       MS allows initialisation without specifying a callback, should we allow addition of the
724         *       callback by a later call to initialise ? - if so this lot will have to change
725         */
726         while ( reference_inst->Next_Entry != NULL )
727         {
728                 if ( *pidInst == reference_inst->Instance_id && pfnCallback == reference_inst->CallBack )
729                 {
730                         /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
731
732                         if (  reference_inst->Client_only )
733                         {
734                            if  ((reference_inst->CBF_Flags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS) 
735                            {
736                                 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
737
738                                 if ( ! ( afCmd & APPCMD_CLIENTONLY))
739                                 {
740                                         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
741                                                 return DMLERR_SYS_ERROR;
742                                         return DMLERR_DLL_USAGE;
743                                 }
744                            }
745                         }
746                         /* Check 2 - cannot change monitor modes */
747
748                         if ( this_instance->Monitor != reference_inst->Monitor) 
749                         {
750                                 if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
751                                         return DMLERR_SYS_ERROR;
752                                 return DMLERR_DLL_USAGE;
753                         }
754
755                         /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
756
757                         if (( afCmd&APPCMD_CLIENTONLY) && ! reference_inst->Client_only )
758                         {
759                                 if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
760                                         return DMLERR_SYS_ERROR;
761                                 return DMLERR_DLL_USAGE;
762                         }
763                         break;
764                 }
765                 reference_inst = reference_inst->Next_Entry;
766         }
767         if ( reference_inst->Next_Entry == NULL )
768         {
769                 /* Crazy situation - trying to re-initialize something that has not beeen initialized !! 
770                 *       
771                 *       Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
772                 */
773                 if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
774                         return DMLERR_SYS_ERROR;
775                 return DMLERR_INVALIDPARAMETER;
776         }
777         /*              All checked - change relevant flags */
778
779         reference_inst->CBF_Flags = this_instance->CBF_Flags;
780         reference_inst->Client_only = this_instance->Client_only;
781         reference_inst->Monitor_flags = this_instance->Monitor_flags;
782         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
783                 return DMLERR_SYS_ERROR;
784      }
785
786     return DMLERR_NO_ERROR;
787 }
788
789
790 /*****************************************************************
791  *            DdeUninitialize16   (DDEML.3)
792  */
793 BOOL16 WINAPI DdeUninitialize16( DWORD idInst )
794 {
795     FIXME(" stub calling DdeUninitialize\n");
796     return (BOOL16)DdeUninitialize( idInst );
797 }
798
799
800 /*****************************************************************
801  * DdeUninitialize [USER32.119]  Frees DDEML resources
802  *
803  * PARAMS
804  *    idInst [I] Instance identifier
805  *
806  * RETURNS
807  *    Success: TRUE
808  *    Failure: FALSE
809  */
810
811 BOOL WINAPI DdeUninitialize( DWORD idInst )
812 {
813         /*  Stage one - check if we have a handle for this instance
814                                                                         */
815         SECURITY_ATTRIBUTES *s_att= NULL;
816         SECURITY_ATTRIBUTES s_attrib;
817         DDE_HANDLE_ENTRY *this_instance;
818         DDE_HANDLE_ENTRY *reference_inst;
819         s_att = &s_attrib;
820
821         if ( DDE_Max_Assigned_Instance == 0 )
822         {
823                 /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
824                 return TRUE;
825         }
826
827         if ( !WaitForMutex(handle_mutex) )
828         {
829                 return DMLERR_SYS_ERROR;
830         }
831         TRACE("Handle Mutex created/reserved\n");
832         /*  First check instance 
833         */
834         this_instance = Find_Instance_Entry(idInst);
835         if ( this_instance == NULL )
836         {
837                 if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance)) return FALSE;
838                 /*
839                   *     Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
840                 */
841                 return FALSE;
842         }
843         FIXME("(%ld): partial stub\n", idInst);
844
845         /*   FIXME      ++++++++++++++++++++++++++++++++++++++++++
846          *      Needs to de-register all service names
847          *      
848          */
849     /* Free the nodes that were not freed by this instance
850      * and remove the nodes from the list of HSZ nodes.
851      */
852         FreeAndRemoveHSZNodes( idInst, this_instance );
853     
854         /* OK now delete the instance handle itself */
855
856         if ( DDE_Handle_Table_Base == this_instance )
857         {
858                 /* special case - the first/only entry
859                 */
860                 DDE_Handle_Table_Base = this_instance->Next_Entry;
861         } else
862         {
863                 /* general case
864                 */
865                 reference_inst = DDE_Handle_Table_Base;
866                 while ( reference_inst->Next_Entry != this_instance )
867                 {
868                         reference_inst = this_instance->Next_Entry;
869                 }
870                 reference_inst->Next_Entry = this_instance->Next_Entry;
871         }
872         /* release the mutex and the heap entry
873         */
874         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,TRUE,this_instance)) 
875         {
876                 /* should record something here, but nothing left to hang it from !!
877                 */
878                 return FALSE;
879         }
880     return TRUE;
881 }
882
883
884 /*****************************************************************
885  * DdeConnectList16 [DDEML.4]
886  */
887
888 HCONVLIST WINAPI DdeConnectList16( DWORD idInst, HSZ hszService, HSZ hszTopic,
889                  HCONVLIST hConvList, LPCONVCONTEXT16 pCC )
890 {
891     return DdeConnectList(idInst, hszService, hszTopic, hConvList, 
892                             (LPCONVCONTEXT)pCC);
893 }
894
895
896 /******************************************************************************
897  * DdeConnectList [USER32.93]  Establishes conversation with DDE servers
898  *
899  * PARAMS
900  *    idInst     [I] Instance identifier
901  *    hszService [I] Handle to service name string
902  *    hszTopic   [I] Handle to topic name string
903  *    hConvList  [I] Handle to conversation list
904  *    pCC        [I] Pointer to structure with context data
905  *
906  * RETURNS
907  *    Success: Handle to new conversation list
908  *    Failure: 0
909  */
910 HCONVLIST WINAPI DdeConnectList( DWORD idInst, HSZ hszService, HSZ hszTopic,
911                  HCONVLIST hConvList, LPCONVCONTEXT pCC )
912 {
913     FIXME("(%ld,%d,%d,%d,%p): stub\n", idInst, hszService, hszTopic,
914           hConvList,pCC);
915     return 1;
916 }
917
918
919 /*****************************************************************
920  * DdeQueryNextServer16 [DDEML.5]
921  */
922 HCONV WINAPI DdeQueryNextServer16( HCONVLIST hConvList, HCONV hConvPrev )
923 {
924     return DdeQueryNextServer(hConvList, hConvPrev);
925 }
926
927
928 /*****************************************************************
929  * DdeQueryNextServer [USER32.112]
930  */
931 HCONV WINAPI DdeQueryNextServer( HCONVLIST hConvList, HCONV hConvPrev )
932 {
933     FIXME("(%d,%d): stub\n",hConvList,hConvPrev);
934     return 0;
935 }
936
937 /*****************************************************************
938  * DdeQueryStringA [USER32.113]
939  *
940  *****************************************************************
941  *
942  *      Change History
943  *
944  *  Vn       Date       Author                  Comment
945  *
946  *  1.0      Dec 1998  Corel/Macadamian    Initial version
947  *  1.1      Mar 1999  Keith Matthews      Added links to instance table and related processing
948  *
949  */
950 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
951 {
952     DWORD ret = 0;
953     CHAR pString[MAX_BUFFER_LEN];
954     DDE_HANDLE_ENTRY *reference_inst;
955
956     FIXME(
957          "(%ld, 0x%x, %p, %ld, %d): partial stub\n",
958          idInst,
959          hsz,
960          psz, 
961          cchMax,
962          iCodePage);
963   if ( DDE_Max_Assigned_Instance == 0 )
964   {
965           /*  Nothing has been initialised - exit now ! */
966           /*  needs something for DdeGetLAstError even if the manual doesn't say so */
967           return FALSE;
968   }
969   
970         if ( !WaitForMutex(handle_mutex) )
971         {
972                 return FALSE;
973         }
974
975   TRACE("Handle Mutex created/reserved\n");
976
977   /*  First check instance 
978   */
979   reference_inst = Find_Instance_Entry(idInst);
980   if ( reference_inst == NULL )
981   {
982         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return FALSE;
983         /*
984         Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
985         */
986         return FALSE;
987   }
988
989     if( iCodePage == CP_WINANSI )
990     {
991         /* If psz is null, we have to return only the length
992          * of the string.
993          */
994         if( psz == NULL )
995         {
996             psz = pString;
997             cchMax = MAX_BUFFER_LEN;
998 }
999
1000         ret = GlobalGetAtomNameA( hsz, (LPSTR)psz, cchMax );
1001   } else {
1002         Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
1003     }
1004    TRACE("returning pointer\n"); 
1005     return ret;
1006 }
1007
1008 /*****************************************************************
1009  * DdeQueryStringW [USER32.114]
1010  *
1011  *****************************************************************
1012  *
1013  *      Change History
1014  *
1015  *  Vn       Date       Author                  Comment
1016  *
1017  *  1.0      Dec 1998  Corel/Macadamian    Initial version
1018  *
1019  */
1020
1021 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1022 {
1023     DWORD ret = 0;
1024     WCHAR pString[MAX_BUFFER_LEN];
1025     int factor = 1;
1026
1027     FIXME(
1028          "(%ld, 0x%x, %p, %ld, %d): stub\n",
1029          idInst,
1030          hsz,
1031          psz, 
1032          cchMax,
1033          iCodePage);
1034
1035     if( iCodePage == CP_WINUNICODE )
1036     {
1037         /* If psz is null, we have to return only the length
1038          * of the string.
1039          */
1040         if( psz == NULL )
1041         {
1042             psz = pString;
1043             cchMax = MAX_BUFFER_LEN;
1044             /* Note: According to documentation if the psz parameter
1045              * was NULL this API must return the length of the string in bytes.
1046              */
1047             factor = (int) sizeof(WCHAR)/sizeof(BYTE);
1048         }
1049         ret = GlobalGetAtomNameW( hsz, (LPWSTR)psz, cchMax ) * factor;
1050     }
1051     return ret;
1052 }
1053
1054 /*****************************************************************
1055 *
1056 *               DdeQueryString16 (DDEML.23)
1057 *
1058 ******************************************************************
1059  *
1060  *      Change History
1061  *
1062  *  Vn       Date       Author                  Comment
1063  *
1064  *  1.0      March 1999 K Matthews              stub only
1065  */
1066
1067 DWORD WINAPI DdeQueryString16(DWORD idInst, HSZ hsz, LPSTR lpsz, DWORD cchMax, INT16 codepage)
1068 {
1069         FIXME("(%ld, 0x%x, %p, %ld, %d): stub \n", 
1070          idInst,
1071          hsz,
1072          lpsz, 
1073          cchMax,
1074          codepage);
1075         return 0;
1076 }
1077
1078
1079 /*****************************************************************
1080  *            DdeDisconnectList (DDEML.6)
1081  */
1082 BOOL16 WINAPI DdeDisconnectList16( HCONVLIST hConvList )
1083 {
1084     return (BOOL16)DdeDisconnectList(hConvList);
1085 }
1086
1087
1088 /******************************************************************************
1089  * DdeDisconnectList [USER32.98]  Destroys list and terminates conversations
1090  *
1091  * RETURNS
1092  *    Success: TRUE
1093  *    Failure: FALSE
1094  */
1095 BOOL WINAPI DdeDisconnectList(
1096     HCONVLIST hConvList) /* [in] Handle to conversation list */
1097 {
1098     FIXME("(%d): stub\n", hConvList);
1099     return TRUE;
1100 }
1101
1102
1103 /*****************************************************************
1104  *            DdeConnect16   (DDEML.7)
1105  */
1106 HCONV WINAPI DdeConnect16( DWORD idInst, HSZ hszService, HSZ hszTopic,
1107                            LPCONVCONTEXT16 pCC )
1108 {
1109     FIXME("empty stub\n" );
1110     return 0;
1111 }
1112
1113
1114 /*****************************************************************
1115  *            DdeConnect   (USER32.92)
1116  */
1117 HCONV WINAPI DdeConnect( DWORD idInst, HSZ hszService, HSZ hszTopic,
1118                            LPCONVCONTEXT pCC )
1119 {
1120     FIXME("(0x%lx,%d,%d,%p): stub\n",idInst,hszService,hszTopic,
1121           pCC);
1122     return 0;
1123 }
1124
1125
1126 /*****************************************************************
1127  *            DdeDisconnect16   (DDEML.8)
1128  */
1129 BOOL16 WINAPI DdeDisconnect16( HCONV hConv )
1130 {
1131     return (BOOL16)DdeDisconnect( hConv );
1132 }
1133
1134 /*****************************************************************
1135  *            DdeSetUserHandle16 (DDEML.10)
1136  */
1137 BOOL16 WINAPI DdeSetUserHandle16( HCONV hConv, DWORD id, DWORD hUser )
1138 {
1139     FIXME("(%d,%ld,%ld): stub\n",hConv,id, hUser );
1140     return 0;
1141 }
1142
1143 /*****************************************************************
1144  *            DdeCreateDataHandle16 (DDEML.14)
1145  */
1146 HDDEDATA WINAPI DdeCreateDataHandle16( DWORD idInst, LPBYTE pSrc, DWORD cb, 
1147                                      DWORD cbOff, HSZ hszItem, UINT16 wFmt, 
1148                                      UINT16 afCmd )
1149 {
1150     return DdeCreateDataHandle(idInst,
1151                                 pSrc,
1152                                 cb,
1153                                 cbOff,
1154                                 hszItem,
1155                                 wFmt,
1156                                 afCmd);
1157 }
1158
1159 /*****************************************************************
1160  *            DdeCreateDataHandle (USER32.94)
1161  */
1162 HDDEDATA WINAPI DdeCreateDataHandle( DWORD idInst, LPBYTE pSrc, DWORD cb, 
1163                                        DWORD cbOff, HSZ hszItem, UINT wFmt, 
1164                                        UINT afCmd )
1165 {
1166     FIXME(
1167           "(%ld,%p,%ld,%ld,0x%x,%d,%d): stub\n",
1168           idInst,
1169           pSrc,
1170           cb,
1171           cbOff,
1172            hszItem,
1173           wFmt, 
1174           afCmd );
1175
1176     return 0;
1177 }
1178
1179 /*****************************************************************
1180  *            DdeDisconnect   (USER32.97)
1181  */
1182 BOOL WINAPI DdeDisconnect( HCONV hConv )
1183 {
1184     FIXME("empty stub\n" );
1185     return 0;
1186 }
1187
1188
1189 /*****************************************************************
1190  *            DdeReconnect   (DDEML.37) (USER32.115)
1191  */
1192 HCONV WINAPI DdeReconnect( HCONV hConv )
1193 {
1194     FIXME("empty stub\n" );
1195     return 0;
1196 }
1197
1198
1199 /*****************************************************************
1200  *            DdeCreateStringHandle16   (DDEML.21)
1201  *
1202  *****************************************************************
1203  *
1204  *      Change History
1205  *
1206  *  Vn       Date       Author                  Comment
1207  *
1208  *  1.0      ?          ?                       basic stub
1209  *  1.1      June 1999  Keith Matthews          amended onward call to supply default
1210  *                                              code page if none supplied by caller
1211  */
1212 HSZ WINAPI DdeCreateStringHandle16( DWORD idInst, LPCSTR str, INT16 codepage )
1213 {
1214     if  ( codepage )
1215     {
1216     return DdeCreateStringHandleA( idInst, str, codepage );
1217      } else {
1218         TRACE("Default codepage supplied\n");
1219          return DdeCreateStringHandleA( idInst, str, CP_WINANSI);
1220      }
1221 }
1222
1223
1224 /*****************************************************************
1225  * DdeCreateStringHandleA [USER32.95]
1226  *
1227  * RETURNS
1228  *    Success: String handle
1229  *    Failure: 0
1230  *
1231  *****************************************************************
1232  *
1233  *      Change History
1234  *
1235  *  Vn       Date       Author                  Comment
1236  *
1237  *  1.0      Dec 1998  Corel/Macadamian    Initial version
1238  *  1.1      Mar 1999  Keith Matthews      Added links to instance table and related processing
1239  *
1240  */
1241 HSZ WINAPI DdeCreateStringHandleA( DWORD idInst, LPCSTR psz, INT codepage )
1242 {
1243   HSZ hsz = 0;
1244   DDE_HANDLE_ENTRY *reference_inst;
1245   TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_a(psz),codepage);
1246   
1247
1248   if ( DDE_Max_Assigned_Instance == 0 )
1249   {
1250           /*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1251           return FALSE;
1252   }
1253   
1254         if ( !WaitForMutex(handle_mutex) )
1255         {
1256                 return DMLERR_SYS_ERROR;
1257         }
1258
1259   TRACE("Handle Mutex created/reserved\n");
1260
1261   /*  First check instance 
1262   */
1263   reference_inst = Find_Instance_Entry(idInst);
1264   if ( reference_inst == NULL )
1265   {
1266         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return 0;
1267         /*
1268         Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
1269         */
1270         return 0;
1271   }
1272   
1273   if (codepage==CP_WINANSI)
1274   {
1275       hsz = GlobalAddAtomA (psz);
1276       /* Save the handle so we know to clean it when
1277        * uninitialize is called.
1278        */
1279       TRACE("added atom %s with HSZ 0x%x, \n",debugstr_a(psz),hsz);
1280       InsertHSZNode( hsz, reference_inst );
1281       if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) 
1282         {
1283                 reference_inst->Last_Error = DMLERR_SYS_ERROR;
1284                 return 0;
1285         }
1286       TRACE("Returning pointer\n");
1287       return hsz;
1288   } else {
1289         Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
1290   }
1291     TRACE("Returning error\n");
1292     return 0;  
1293 }
1294
1295
1296 /******************************************************************************
1297  * DdeCreateStringHandleW [USER32.96]  Creates handle to identify string
1298  *
1299  * RETURNS
1300  *    Success: String handle
1301  *    Failure: 0
1302  *
1303  *****************************************************************
1304  *
1305  *      Change History
1306  *
1307  *  Vn       Date       Author                  Comment
1308  *
1309  *  1.0      Dec 1998  Corel/Macadamian    Initial version
1310  *  1.1      Mar 1999  Keith Matthews      Added links to instance table and related processing
1311  *
1312  */
1313 HSZ WINAPI DdeCreateStringHandleW(
1314     DWORD idInst,   /* [in] Instance identifier */
1315     LPCWSTR psz,    /* [in] Pointer to string */
1316     INT codepage) /* [in] Code page identifier */
1317 {
1318    DDE_HANDLE_ENTRY *reference_inst;
1319    HSZ hsz = 0;
1320
1321    TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
1322   
1323
1324   if ( DDE_Max_Assigned_Instance == 0 )
1325   {
1326           /*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1327           return FALSE;
1328   }
1329   
1330         if ( !WaitForMutex(handle_mutex) )
1331         {
1332                 return DMLERR_SYS_ERROR;
1333         }
1334
1335   TRACE("CreateString - Handle Mutex created/reserved\n");
1336   
1337   /*  First check instance 
1338   */
1339   reference_inst = Find_Instance_Entry(idInst);
1340   if ( reference_inst == NULL )
1341   {
1342         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return 0;
1343         /*
1344         Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
1345         */
1346         return 0;
1347   }
1348
1349     FIXME("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
1350
1351   if (codepage==CP_WINUNICODE)
1352   /*
1353   Should we be checking this against the unicode/ascii nature of the call to DdeInitialize ?
1354   */
1355   {
1356       hsz = GlobalAddAtomW (psz);
1357       /* Save the handle so we know to clean it when
1358        * uninitialize is called.
1359        */
1360       InsertHSZNode( hsz, reference_inst );
1361       if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) 
1362         {
1363                 reference_inst->Last_Error = DMLERR_SYS_ERROR;
1364                 return 0;
1365         }
1366       TRACE("Returning pointer\n");
1367       return hsz;
1368    } else {
1369         Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
1370 }
1371     TRACE("Returning error\n");
1372   return 0;
1373 }
1374
1375
1376 /*****************************************************************
1377  *            DdeFreeStringHandle16   (DDEML.22)
1378  */
1379 BOOL16 WINAPI DdeFreeStringHandle16( DWORD idInst, HSZ hsz )
1380 {
1381         FIXME("idInst %ld hsz 0x%x\n",idInst,hsz);
1382     return (BOOL)DdeFreeStringHandle( idInst, hsz );
1383 }
1384
1385
1386 /*****************************************************************
1387  *            DdeFreeStringHandle   (USER32.101)
1388  * RETURNS: success: nonzero
1389  *          fail:    zero
1390  *
1391  *****************************************************************
1392  *
1393  *      Change History
1394  *
1395  *  Vn       Date       Author                  Comment
1396  *
1397  *  1.0      Dec 1998  Corel/Macadamian    Initial version
1398  *  1.1      Apr 1999  Keith Matthews      Added links to instance table and related processing
1399  *
1400  */
1401 BOOL WINAPI DdeFreeStringHandle( DWORD idInst, HSZ hsz )
1402 {
1403     DDE_HANDLE_ENTRY *reference_inst;
1404     TRACE("(%ld,%d): \n",idInst,hsz);
1405   if ( DDE_Max_Assigned_Instance == 0 )
1406 {
1407           /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
1408           return TRUE;
1409   }
1410
1411         if ( !WaitForMutex(handle_mutex) )
1412         {
1413                 return DMLERR_SYS_ERROR;
1414         }
1415
1416   TRACE("Handle Mutex created/reserved\n");
1417
1418   /*  First check instance 
1419   */
1420   reference_inst = Find_Instance_Entry(idInst);
1421   if ( (reference_inst == NULL) || (reference_inst->Node_list == NULL))
1422   {
1423         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return TRUE;
1424           /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
1425           return TRUE;
1426
1427   }
1428
1429     /* Remove the node associated with this HSZ.
1430      */
1431     RemoveHSZNode( hsz , reference_inst);
1432     /* Free the string associated with this HSZ.
1433      */
1434   Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
1435     return GlobalDeleteAtom (hsz) ? 0 : hsz;
1436 }
1437
1438
1439 /*****************************************************************
1440  *            DdeFreeDataHandle16   (DDEML.19)
1441  */
1442 BOOL16 WINAPI DdeFreeDataHandle16( HDDEDATA hData )
1443 {
1444     return (BOOL)DdeFreeDataHandle( hData );
1445 }
1446
1447
1448 /*****************************************************************
1449  *            DdeFreeDataHandle   (USER32.100)
1450  */
1451 BOOL WINAPI DdeFreeDataHandle( HDDEDATA hData )
1452 {
1453     FIXME("empty stub\n" );
1454     return TRUE;
1455 }
1456
1457
1458
1459
1460 /*****************************************************************
1461  *            DdeKeepStringHandle16   (DDEML.24)
1462  */
1463 BOOL16 WINAPI DdeKeepStringHandle16( DWORD idInst, HSZ hsz )
1464 {
1465     return (BOOL)DdeKeepStringHandle( idInst, hsz );
1466 }
1467
1468
1469 /*****************************************************************
1470  *            DdeKeepStringHandle  (USER32.108)
1471  *
1472  * RETURNS: success: nonzero
1473  *          fail:    zero
1474  *
1475  *****************************************************************
1476  *
1477  *      Change History
1478  *
1479  *  Vn       Date       Author                  Comment
1480  *
1481  *  1.0      ?           ?                 Stub only
1482  *  1.1      Jun 1999  Keith Matthews      First cut implementation
1483  *
1484  */
1485 BOOL WINAPI DdeKeepStringHandle( DWORD idInst, HSZ hsz )
1486 {
1487
1488   DDE_HANDLE_ENTRY *reference_inst;
1489   TRACE("(%ld,%d): \n",idInst,hsz);
1490   if ( DDE_Max_Assigned_Instance == 0 )
1491   {
1492           /*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1493           return FALSE;
1494   }
1495
1496
1497         if ( !WaitForMutex(handle_mutex) )
1498         {
1499                 return FALSE;
1500         }
1501
1502   TRACE("Handle Mutex created/reserved\n");
1503
1504   /*  First check instance
1505   */
1506   reference_inst = Find_Instance_Entry(idInst);
1507   if ( (reference_inst == NULL) || (reference_inst->Node_list == NULL))
1508   {
1509         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return FALSE;
1510           /*  Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1511           return FALSE;
1512     return FALSE;
1513    }
1514   DdeReserveAtom(reference_inst,hsz);
1515   Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
1516     return TRUE;
1517 }
1518
1519
1520 /*****************************************************************
1521  *            DdeClientTransaction16  (DDEML.11)
1522  */
1523 HDDEDATA WINAPI DdeClientTransaction16( LPVOID pData, DWORD cbData,
1524                                         HCONV hConv, HSZ hszItem, UINT16 wFmt,
1525                                         UINT16 wType, DWORD dwTimeout,
1526                                         LPDWORD pdwResult )
1527 {
1528     return DdeClientTransaction( (LPBYTE)pData, cbData, hConv, hszItem,
1529                                    wFmt, wType, dwTimeout, pdwResult );
1530 }
1531
1532
1533 /*****************************************************************
1534  *            DdeClientTransaction  (USER32.90)
1535  */
1536 HDDEDATA WINAPI DdeClientTransaction( LPBYTE pData, DWORD cbData,
1537                                         HCONV hConv, HSZ hszItem, UINT wFmt,
1538                                         UINT wType, DWORD dwTimeout,
1539                                         LPDWORD pdwResult )
1540 {
1541     FIXME("empty stub\n" );
1542     return 0;
1543 }
1544
1545 /*****************************************************************
1546  *
1547  *            DdeAbandonTransaction16 (DDEML.12)
1548  *
1549  */
1550 BOOL16 WINAPI DdeAbandonTransaction16( DWORD idInst, HCONV hConv, 
1551                                      DWORD idTransaction )
1552 {
1553     FIXME("empty stub\n" );
1554     return TRUE;
1555 }
1556
1557
1558 /*****************************************************************
1559  *
1560  *            DdeAbandonTransaction (USER32.87)
1561  *
1562 ******************************************************************
1563  *
1564  *      Change History
1565  *
1566  *  Vn       Date       Author                  Comment
1567  *
1568  *  1.0      March 1999 K Matthews              stub only
1569  */
1570 BOOL WINAPI DdeAbandonTransaction( DWORD idInst, HCONV hConv, 
1571                                      DWORD idTransaction )
1572 {
1573     FIXME("empty stub\n" );
1574     return TRUE;
1575 }
1576
1577 /*****************************************************************
1578  * DdePostAdvise16 [DDEML.13]
1579  */
1580 BOOL16 WINAPI DdePostAdvise16( DWORD idInst, HSZ hszTopic, HSZ hszItem )
1581 {
1582     return (BOOL16)DdePostAdvise(idInst, hszTopic, hszItem);
1583 }
1584
1585
1586 /******************************************************************************
1587  * DdePostAdvise [USER32.110]  Send transaction to DDE callback function.
1588  *
1589  * RETURNS
1590  *    Success: TRUE
1591  *    Failure: FALSE
1592  */
1593 BOOL WINAPI DdePostAdvise(
1594     DWORD idInst, /* [in] Instance identifier */
1595     HSZ hszTopic, /* [in] Handle to topic name string */
1596     HSZ hszItem)  /* [in] Handle to item name string */
1597 {
1598     FIXME("(%ld,%d,%d): stub\n",idInst,hszTopic,hszItem);
1599     return TRUE;
1600 }
1601
1602
1603 /*****************************************************************
1604  *            DdeAddData16 (DDEML.15)
1605  */
1606 HDDEDATA WINAPI DdeAddData16( HDDEDATA hData, LPBYTE pSrc, DWORD cb,
1607                             DWORD cbOff )
1608 {
1609     FIXME("empty stub\n" );
1610     return 0;
1611 }
1612
1613 /*****************************************************************
1614  *
1615  *            DdeAddData (USER32.89)
1616  *
1617 ******************************************************************
1618  *
1619  *      Change History
1620  *
1621  *  Vn       Date       Author                  Comment
1622  *
1623  *  1.0      March 1999 K Matthews              stub only
1624  */
1625 HDDEDATA WINAPI DdeAddData( HDDEDATA hData, LPBYTE pSrc, DWORD cb,
1626                             DWORD cbOff )
1627 {
1628     FIXME("empty stub\n" );
1629     return 0;
1630 }
1631
1632
1633 /*****************************************************************
1634  *
1635  *            DdeImpersonateClient (USER32.105)
1636  *
1637 ******************************************************************
1638  *
1639  *      Change History
1640  *
1641  *  Vn       Date       Author                  Comment
1642  *
1643  *  1.0      March 1999 K Matthews              stub only
1644  */
1645
1646 BOOL WINAPI DdeImpersonateClient( HCONV hConv)
1647 {
1648     FIXME("empty stub\n" );
1649     return TRUE;
1650 }
1651
1652
1653 /*****************************************************************
1654  *
1655  *            DdeSetQualityOfService (USER32.116)
1656  *
1657 ******************************************************************
1658  *
1659  *      Change History
1660  *
1661  *  Vn       Date       Author                  Comment
1662  *
1663  *  1.0      March 1999 K Matthews              stub only
1664  */
1665
1666 BOOL WINAPI DdeSetQualityOfService( HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
1667                                         PSECURITY_QUALITY_OF_SERVICE pqosPrev)
1668 {
1669     FIXME("empty stub\n" );
1670     return TRUE;
1671 }
1672
1673 /*****************************************************************
1674  *
1675  *            DdeSetUserHandle (USER32.117)
1676  *
1677 ******************************************************************
1678  *
1679  *      Change History
1680  *
1681  *  Vn       Date       Author                  Comment
1682  *
1683  *  1.0      March 1999 K Matthews              stub only
1684  */
1685
1686 BOOL WINAPI DdeSetUserHandle( HCONV hConv, DWORD id, DWORD hUser)
1687 {
1688     FIXME("empty stub\n" );
1689     return TRUE;
1690 }
1691
1692 /******************************************************************************
1693  * DdeGetData [USER32.102]  Copies data from DDE object ot local buffer
1694  *
1695  * RETURNS
1696  *    Size of memory object associated with handle
1697  */
1698 DWORD WINAPI DdeGetData(
1699     HDDEDATA hData, /* [in] Handle to DDE object */
1700     LPBYTE pDst,    /* [in] Pointer to destination buffer */
1701     DWORD cbMax,    /* [in] Amount of data to copy */
1702     DWORD cbOff)    /* [in] Offset to beginning of data */
1703 {
1704     FIXME("(%d,%p,%ld,%ld): stub\n",hData,pDst,cbMax,cbOff);
1705     return cbMax;
1706 }
1707
1708
1709 /*****************************************************************
1710  * DdeGetData16 [DDEML.16]
1711  */
1712 DWORD WINAPI DdeGetData16(
1713     HDDEDATA hData,
1714     LPBYTE pDst,
1715     DWORD cbMax, 
1716     DWORD cbOff)
1717 {
1718     return DdeGetData(hData, pDst, cbMax, cbOff);
1719 }
1720
1721
1722 /*****************************************************************
1723  *            DdeAccessData16 (DDEML.17)
1724  */
1725 LPBYTE WINAPI DdeAccessData16( HDDEDATA hData, LPDWORD pcbDataSize )
1726 {
1727      return DdeAccessData(hData, pcbDataSize);
1728 }
1729
1730 /*****************************************************************
1731  *            DdeAccessData (USER32.88)
1732  */
1733 LPBYTE WINAPI DdeAccessData( HDDEDATA hData, LPDWORD pcbDataSize )
1734 {
1735      FIXME("(%d,%p): stub\n", hData, pcbDataSize);
1736      return 0;
1737 }
1738
1739 /*****************************************************************
1740  *            DdeUnaccessData16 (DDEML.18)
1741  */
1742 BOOL16 WINAPI DdeUnaccessData16( HDDEDATA hData )
1743 {
1744      return DdeUnaccessData(hData);
1745 }
1746
1747 /*****************************************************************
1748  *            DdeUnaccessData (USER32.118)
1749  */
1750 BOOL WINAPI DdeUnaccessData( HDDEDATA hData )
1751 {
1752      FIXME("(0x%x): stub\n", hData);
1753
1754      return 0;
1755 }
1756
1757 /*****************************************************************
1758  *            DdeEnableCallback16 (DDEML.26)
1759  */
1760 BOOL16 WINAPI DdeEnableCallback16( DWORD idInst, HCONV hConv, UINT16 wCmd )
1761 {
1762      return DdeEnableCallback(idInst, hConv, wCmd);
1763 }
1764
1765 /*****************************************************************
1766  *            DdeEnableCallback (USER32.99)
1767  */
1768 BOOL WINAPI DdeEnableCallback( DWORD idInst, HCONV hConv, UINT wCmd )
1769 {
1770      FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
1771
1772      return 0;
1773 }
1774
1775 /*****************************************************************
1776  *            DdeNameService16  (DDEML.27)
1777  */
1778 HDDEDATA WINAPI DdeNameService16( DWORD idInst, HSZ hsz1, HSZ hsz2,
1779                                   UINT16 afCmd )
1780 {
1781     return DdeNameService( idInst, hsz1, hsz2, afCmd );
1782 }
1783
1784
1785 /******************************************************************************
1786  * DdeNameService [USER32.109]  {Un}registers service name of DDE server
1787  *
1788  * PARAMS
1789  *    idInst [I] Instance identifier
1790  *    hsz1   [I] Handle to service name string
1791  *    hsz2   [I] Reserved
1792  *    afCmd  [I] Service name flags
1793  *
1794  * RETURNS
1795  *    Success: Non-zero
1796  *    Failure: 0
1797  *
1798  *****************************************************************
1799  *
1800  *      Change History
1801  *
1802  *  Vn       Date       Author                  Comment
1803  *
1804  *  1.0      ?            ?                Stub
1805  *  1.1      Apr 1999  Keith Matthews      Added trap for non-existent instance (uninitialised instance 0
1806  *                                              used by some MS programs for unfathomable reasons)
1807  *  1.2      May 1999  Keith Matthews      Added parameter validation and basic service name handling.
1808  *                                         Still needs callback parts
1809  *
1810  */
1811 HDDEDATA WINAPI DdeNameService( DWORD idInst, HSZ hsz1, HSZ hsz2,
1812                 UINT afCmd )
1813 {
1814   ServiceNode* this_service, *reference_service ;
1815   DDE_HANDLE_ENTRY *this_instance;
1816   DDE_HANDLE_ENTRY *reference_inst;
1817   this_service = NULL;
1818
1819   FIXME("(%ld,%d,%d,%d): stub\n",idInst,hsz1,hsz2,afCmd);
1820
1821   if ( DDE_Max_Assigned_Instance == 0 )
1822   {
1823           /*  Nothing has been initialised - exit now ! 
1824            *    needs something for DdeGetLastError */
1825           return 0L;
1826   }
1827
1828         if ( !WaitForMutex(handle_mutex) )
1829         {
1830                 return DMLERR_SYS_ERROR;
1831         }
1832
1833    TRACE("Handle Mutex created/reserved\n");
1834
1835    /*  First check instance
1836    */
1837    reference_inst = Find_Instance_Entry(idInst);
1838    this_instance = reference_inst;
1839    if  (reference_inst == NULL)
1840    {
1841         TRACE("Instance not found as initialised\n");
1842         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance)) return TRUE;
1843           /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
1844           return FALSE;
1845
1846    }
1847
1848   if ( hsz2 != 0L )
1849   {
1850         /*      Illegal, reserved parameter
1851          */
1852         reference_inst->Last_Error = DMLERR_INVALIDPARAMETER;
1853         Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1854         FIXME("Reserved parameter no-zero !!\n");
1855         return FALSE;
1856   }
1857   if ( hsz1 == 0L )
1858   {
1859         /*
1860          *      General unregister situation
1861          */
1862         if ( afCmd != DNS_UNREGISTER )
1863         {
1864                 /*      don't know if we should check this but it makes sense
1865                  *      why supply REGISTER or filter flags if de-registering all
1866                  */
1867                 TRACE("General unregister unexpected flags\n");
1868                 reference_inst->Last_Error = DMLERR_DLL_USAGE;
1869                 Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1870                 return FALSE;
1871         }
1872         /*      Loop to find all registered service and de-register them
1873          */
1874         if ( reference_inst->ServiceNames == NULL )
1875         {
1876                 /*  None to unregister !!  
1877                  */
1878                 TRACE("General de-register - nothing registered\n");
1879                 reference_inst->Last_Error = DMLERR_DLL_USAGE;
1880                 Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1881                 return FALSE;
1882         }  else
1883         {
1884                 this_service = reference_inst->ServiceNames;
1885                 while ( this_service->next != NULL)
1886                 {
1887                         TRACE("general deregister - iteration\n");
1888                         reference_service = this_service;
1889                         this_service = this_service->next;
1890                         DdeReleaseAtom(reference_inst,reference_service->hsz);
1891                         HeapFree(GetProcessHeap(), 0, reference_service); /* finished - release heap space used as work store */
1892                 }
1893                 DdeReleaseAtom(reference_inst,this_service->hsz);
1894                 HeapFree(GetProcessHeap(), 0, this_service); /* finished - release heap space used as work store */
1895                 reference_inst->ServiceNames = NULL;
1896                 TRACE("General de-register - finished\n");
1897         }
1898         Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1899         return TRUE;
1900   }
1901   TRACE("Specific name action detected\n");
1902   if ( afCmd & DNS_REGISTER )
1903   {
1904         /* Register new service name
1905          */
1906
1907         this_service = Find_Service_Name( hsz1, reference_inst );
1908         if ( this_service )
1909                 ERR("Trying to register already registered service!\n");
1910         else
1911         {
1912                 TRACE("Adding service name\n");
1913
1914                 DdeReserveAtom(reference_inst, hsz1);
1915
1916                 this_service = (ServiceNode*)HeapAlloc( GetProcessHeap(), 0, sizeof(ServiceNode) );
1917                 this_service->hsz = hsz1;
1918                 this_service->FilterOn = TRUE;
1919
1920                 this_service->next = reference_inst->ServiceNames;
1921                 reference_inst->ServiceNames = this_service;
1922         }
1923   }
1924   if ( afCmd & DNS_UNREGISTER )
1925   {
1926         /*      De-register service name
1927          */
1928
1929         ServiceNode **pServiceNode = &reference_inst->ServiceNames;
1930         while ( *pServiceNode && (*pServiceNode)->hsz != hsz1 )
1931                 pServiceNode = &(*pServiceNode)->next;
1932
1933         this_service = *pServiceNode;
1934         if ( !this_service )
1935                 ERR("Trying to de-register unregistered service!\n");
1936         else
1937         {
1938                 *pServiceNode = this_service->next;
1939                 DdeReleaseAtom(reference_inst,this_service->hsz);
1940                 HeapFree(GetProcessHeap(), 0, this_service);
1941         }
1942   }
1943   if ( afCmd & DNS_FILTERON )
1944   {
1945         /*      Set filter flags on to hold notifications of connection
1946          *
1947          *      test coded this way as this is the default setting
1948          */
1949         this_service = Find_Service_Name( hsz1, reference_inst );
1950         if ( !this_service )
1951         {
1952                 /*  trying to filter where no service names !!
1953                  */
1954                 reference_inst->Last_Error = DMLERR_DLL_USAGE;
1955                 Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1956                 return FALSE;
1957         } else 
1958         {
1959                 this_service->FilterOn = TRUE;
1960         }
1961   }
1962   if ( afCmd & DNS_FILTEROFF )
1963   {
1964         /*      Set filter flags on to hold notifications of connection
1965          */
1966         this_service = Find_Service_Name( hsz1, reference_inst );
1967         if ( !this_service )
1968         {
1969                 /*  trying to filter where no service names !!
1970                  */
1971                 reference_inst->Last_Error = DMLERR_DLL_USAGE;
1972                 Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1973                 return FALSE;
1974         } else 
1975         {
1976                 this_service->FilterOn = FALSE;
1977         }
1978   }
1979   Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
1980   return TRUE;
1981 }
1982
1983
1984 /*****************************************************************
1985  *            DdeGetLastError16  (DDEML.20)
1986  */
1987 UINT16 WINAPI DdeGetLastError16( DWORD idInst )
1988 {
1989     return (UINT16)DdeGetLastError( idInst );
1990 }
1991
1992
1993 /******************************************************************************
1994  * DdeGetLastError [USER32.103]  Gets most recent error code
1995  *
1996  * PARAMS
1997  *    idInst [I] Instance identifier
1998  *
1999  * RETURNS
2000  *    Last error code
2001  *
2002  *****************************************************************
2003  *
2004  *      Change History
2005  *
2006  *  Vn       Date       Author                  Comment
2007  *
2008  *  1.0      ?            ?                Stub
2009  *  1.1      Apr 1999  Keith Matthews      Added response for non-existent instance (uninitialised instance 0
2010  *                                              used by some MS programs for unfathomable reasons)
2011  *  1.2      May 1999  Keith Matthews      Added interrogation of Last_Error for instance handle where found.
2012  *
2013  */
2014 UINT WINAPI DdeGetLastError( DWORD idInst )
2015 {
2016     DWORD       error_code;
2017     DDE_HANDLE_ENTRY *reference_inst;
2018
2019     FIXME("(%ld): stub\n",idInst);
2020
2021     if ( DDE_Max_Assigned_Instance == 0 )
2022     {
2023           /*  Nothing has been initialised - exit now ! */
2024           return DMLERR_DLL_NOT_INITIALIZED;
2025     }
2026
2027         if ( !WaitForMutex(handle_mutex) )
2028         {
2029                 return DMLERR_SYS_ERROR;
2030         }
2031
2032    TRACE("Handle Mutex created/reserved\n");
2033
2034    /*  First check instance
2035    */
2036    reference_inst = Find_Instance_Entry(idInst);
2037    if  (reference_inst == NULL) 
2038    {
2039         if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return TRUE;
2040           /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
2041           return DMLERR_DLL_NOT_INITIALIZED;
2042
2043    }
2044     error_code = reference_inst->Last_Error;
2045     reference_inst->Last_Error = 0;
2046     Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
2047    return error_code;
2048 }
2049
2050
2051 /*****************************************************************
2052  *            DdeCmpStringHandles16 (DDEML.36)
2053  */
2054 INT16 WINAPI DdeCmpStringHandles16( HSZ hsz1, HSZ hsz2 )
2055 {
2056      return DdeCmpStringHandles(hsz1, hsz2);
2057 }
2058
2059 /*****************************************************************
2060  *            DdeCmpStringHandles (USER32.91)
2061  *
2062  * Compares the value of two string handles.  This comparison is
2063  * not case sensitive.
2064  *
2065  * Returns:
2066  * -1 The value of hsz1 is zero or less than hsz2
2067  * 0  The values of hsz 1 and 2 are the same or both zero.
2068  * 1  The value of hsz2 is zero of less than hsz1
2069  */
2070 INT WINAPI DdeCmpStringHandles( HSZ hsz1, HSZ hsz2 )
2071 {
2072     CHAR psz1[MAX_BUFFER_LEN];
2073     CHAR psz2[MAX_BUFFER_LEN];
2074     int ret = 0;
2075     int ret1, ret2;
2076
2077     TRACE("handle 1, handle 2\n" );
2078
2079     ret1 = GlobalGetAtomNameA( hsz1, psz1, MAX_BUFFER_LEN );
2080     ret2 = GlobalGetAtomNameA( hsz2, psz2, MAX_BUFFER_LEN );
2081     /* Make sure we found both strings.
2082      */
2083     if( ret1 == 0 && ret2 == 0 )
2084     {
2085         /* If both are not found, return both  "zero strings".
2086          */
2087         ret = 0;
2088     }
2089     else if( ret1 == 0 )
2090     {
2091         /* If hsz1 is a not found, return hsz1 is "zero string".
2092          */
2093         ret = -1;
2094     }
2095     else if( ret2 == 0 )
2096     {
2097         /* If hsz2 is a not found, return hsz2 is "zero string".
2098          */
2099         ret = 1;
2100     }
2101     else
2102     {
2103         /* Compare the two strings we got ( case insensitive ).
2104          */
2105         ret = strcasecmp( psz1, psz2 );
2106         /* Since strcmp returns any number smaller than
2107          * 0 when the first string is found to be less than
2108          * the second one we must make sure we are returning
2109          * the proper values.
2110          */
2111         if( ret < 0 )
2112         {
2113             ret = -1;
2114         }
2115         else if( ret > 0 )
2116         {
2117             ret = 1;
2118         }
2119     }
2120
2121     return ret;
2122 }
2123
2124 /*****************************************************************
2125  *            PackDDElParam (USER32.414)
2126  *
2127  * RETURNS
2128  *   success: nonzero
2129  *   failure: zero
2130  */
2131 UINT WINAPI PackDDElParam(UINT msg, UINT uiLo, UINT uiHi)
2132 {
2133     FIXME("stub.\n");
2134     return 0;
2135 }
2136
2137
2138 /*****************************************************************
2139  *            UnpackDDElParam (USER32.562)
2140  *
2141  * RETURNS
2142  *   success: nonzero
2143  *   failure: zero
2144  */
2145 UINT WINAPI UnpackDDElParam(UINT msg, UINT lParam,
2146                               UINT *uiLo, UINT *uiHi)
2147 {
2148     FIXME("stub.\n");
2149     return 0;
2150 }
2151
2152
2153 /*****************************************************************
2154  *            FreeDDElParam (USER32.204)
2155  *
2156  * RETURNS
2157  *   success: nonzero
2158  *   failure: zero
2159  */
2160 UINT WINAPI FreeDDElParam(UINT msg, UINT lParam)
2161 {
2162     FIXME("stub.\n");
2163     return 0;
2164 }
2165
2166 /*****************************************************************
2167  *            ReuseDDElParam (USER32.446)
2168  *
2169  */
2170 UINT WINAPI ReuseDDElParam(UINT lParam, UINT msgIn, UINT msgOut,
2171                              UINT uiLi, UINT uiHi)
2172 {
2173     FIXME("stub.\n");
2174     return 0;
2175
2176
2177 /******************************************************************
2178  *              DdeQueryConvInfo16 (DDEML.9)
2179  *
2180  */
2181 UINT16 WINAPI DdeQueryConvInfo16( HCONV hconv, DWORD idTransaction , LPCONVINFO16 lpConvInfo)
2182 {
2183         FIXME("stub.\n");
2184         return 0;
2185 }
2186
2187
2188 /******************************************************************
2189  *              DdeQueryConvInfo (USER32.111)
2190  *
2191  */
2192 UINT WINAPI DdeQueryConvInfo( HCONV hconv, DWORD idTransaction , LPCONVINFO lpConvInfo)
2193 {
2194         FIXME("stub.\n");
2195         return 0;
2196 }