Partial implementation for the following DDE APIs:
DdeCmpStringHandles, DdeCreateStringHandle, DdeFreeStringHandle,
DdeQueryString, DdeUninitialize.

diff --git a/include/ddeml.h b/include/ddeml.h
index 79c12f0..73f2454 100644
--- a/include/ddeml.h
+++ b/include/ddeml.h
@@ -10,6 +10,11 @@
 
 #include "wintypes.h"
 
+/* Codepage Constants
+ */
+#define CP_WINANSI      1004
+#define CP_WINUNICODE   1200
+
 #define MSGF_DDEMGR 0x8001
 
 typedef DWORD HCONVLIST;
@@ -61,7 +66,7 @@
 #define   DdeQueryNextServer WINELIB_NAME(DdeQueryNextServer)
 DWORD     WINAPI DdeQueryString32A(DWORD, HSZ, LPSTR, DWORD, INT32);
 DWORD     WINAPI DdeQueryString32W(DWORD, HSZ, LPWSTR, DWORD, INT32);
-#define   DdeQueryString WINELIB_NAME(DdeQueryString)
+#define   DdeQueryString WINELIB_NAME_AW(DdeQueryString)
 BOOL16    WINAPI DdeDisconnectList16(HCONVLIST);
 BOOL32    WINAPI DdeDisconnectList32(HCONVLIST);
 #define   DdeDisConnectList WINELIB_NAME(DdeDisconnectList)
@@ -110,9 +115,9 @@
 BOOL32    WINAPI DdeEnableCallback32(DWORD,HCONV,UINT32);
 #define   DdeEnableCallback WINELIB_NAME(DdeEnableCallback)
 int       WINAPI DdeCmpStringHandles16(HSZ,HSZ);
-int       WINAPI DdeCmpStringHandles32(HSZ,HSZ);
-#define   DdeCmpStringHandles WINELIB_NAME(DdeCmpStringHandles)
-
+int       WINAPI DdeCmpStringHandles32A(HSZ,HSZ);
+int       WINAPI DdeCmpStringHandles32W(HSZ,HSZ);
+#define   DdeCmpStringHandles WINELIB_NAME_AW(DdeCmpStringHandles)
 
 HDDEDATA  WINAPI DdeNameService16(DWORD,HSZ,HSZ,UINT16);
 HDDEDATA  WINAPI DdeNameService32(DWORD,HSZ,HSZ,UINT32);
diff --git a/misc/ddeml.c b/misc/ddeml.c
index c0b6197..c9740a0 100644
--- a/misc/ddeml.c
+++ b/misc/ddeml.c
@@ -7,15 +7,142 @@
 
 /* Only empty stubs for now */
 
+#include <stdlib.h>
+#include <strings.h>
 #include "ddeml.h"
 #include "debug.h"
 #include "windows.h"
+#include "heap.h"
 
 /* FIXME: What are these values? */
 #define DMLERR_NO_ERROR		0
 
+/* Has defined in atom.c file.
+ */
+#define MAX_ATOM_LEN              255
+
+/* Maximum buffer size ( including the '\0' ).
+ */
+#define MAX_BUFFER_LEN            (MAX_ATOM_LEN + 1)
+
 static LONG     DDE_current_handle;
 
+/* This is a simple list to keep track of the strings created
+ * by DdeCreateStringHandle.  The list is used to free
+ * the strings whenever DdeUninitialize is called.
+ * This mechanism is not complete and does not handle multiple instances.
+ * Most of the DDE API use a DWORD parameter indicating witch instance
+ * of a given program is calling them.  The API are supposed to
+ * associate the data to the instance that created it.
+ */
+typedef struct tagHSZNode HSZNode;
+struct tagHSZNode
+{
+    HSZNode* next;
+    HSZ hsz;
+};
+
+/* Start off the list pointer with a NULL.
+ */
+static HSZNode* pHSZNodes = NULL;
+
+
+/******************************************************************************
+ *            RemoveHSZNodes    (INTERNAL)
+ *
+ * Remove a node from the list of HSZ nodes.
+ */
+static void RemoveHSZNode( DWORD idInst, HSZ hsz )
+{
+    HSZNode* pPrev = NULL;
+    HSZNode* pCurrent = NULL;
+
+    /* Set the current node at the start of the list.
+     */
+    pCurrent = pHSZNodes;
+    /* While we have more nodes.
+     */
+    while( pCurrent != NULL )
+    {
+        /* If we found the node we were looking for.
+         */
+        if( pCurrent->hsz == hsz )
+        {
+            /* Remove the node.
+             */
+            /* If the first node in the list is to to be removed.
+             * Set the global list pointer to the next node.
+             */
+            if( pCurrent == pHSZNodes )
+            {
+                pHSZNodes = pCurrent->next;
+            }
+            /* Just fix the pointers has to skip the current
+             * node so we can delete it.
+             */
+            else
+            {
+                pPrev->next = pCurrent->next;
+            }
+            /* Destroy this node.
+             */
+            free( pCurrent );
+            break;
+        }
+        /* Save the previous node pointer.
+         */
+        pPrev = pCurrent;
+        /* Move on to the next node.
+         */
+        pCurrent = pCurrent->next;
+    }
+}
+
+/******************************************************************************
+ *            FreeAndRemoveHSZNodes    (INTERNAL)
+ *
+ * Frees up all the strings still allocated in the list and
+ * remove all the nodes from the list of HSZ nodes.
+ */
+static void FreeAndRemoveHSZNodes( DWORD idInst )
+{
+    /* Free any strings created in this instance.
+     */
+    while( pHSZNodes != NULL )
+    {
+        DdeFreeStringHandle32( idInst, pHSZNodes->hsz );
+    }
+}
+
+/******************************************************************************
+ *            InsertHSZNode    (INTERNAL)
+ *
+ * Insert a node to the head of the list.
+ */
+static void InsertHSZNode( DWORD idInst, HSZ hsz )
+{
+    if( hsz != 0 )
+    {
+        HSZNode* pNew = NULL;
+        /* Create a new node for this HSZ.
+         */
+        pNew = (HSZNode*) malloc( sizeof( HSZNode ) );
+        if( pNew != NULL )
+        {
+            /* Set the handle value.
+             */
+            pNew->hsz = hsz;
+            /* Attach the node to the head of the list.
+             */
+            pNew->next = pHSZNodes;
+            /* The new node is now at the head of the list
+             * so set the global list pointer to it.
+             */
+            pHSZNodes = pNew;
+        }
+    }
+}
+
 
 /******************************************************************************
  *            DdeInitialize16   (DDEML.2)
@@ -88,7 +215,14 @@
  */
 BOOL32 WINAPI DdeUninitialize32( DWORD idInst )
 {
+
     FIXME(ddeml, "(%ld): stub\n", idInst);
+
+    /* Free the nodes that were not freed by this instance
+     * and remove the nodes from the list of HSZ nodes.
+     */
+    FreeAndRemoveHSZNodes( idInst );
+    
     return TRUE;
 }
 
@@ -150,6 +284,9 @@
  */
 DWORD WINAPI DdeQueryString32A(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT32 iCodePage)
 {
+    DWORD ret = 0;
+    CHAR pString[MAX_BUFFER_LEN];
+
     FIXME(ddeml,
          "(%ld, 0x%lx, %p, %ld, %d): stub\n",
          idInst,
@@ -158,7 +295,21 @@
          cchMax,
          iCodePage);
 
-    return 0;
+    if( iCodePage == CP_WINANSI )
+    {
+        /* If psz is null, we have to return only the length
+         * of the string.
+         */
+        if( psz == NULL )
+        {
+            psz = pString;
+            cchMax = MAX_BUFFER_LEN;
+}
+
+        ret = GlobalGetAtomName32A( hsz, (LPSTR)psz, cchMax );
+    }
+    
+    return ret;
 }
 
 /*****************************************************************
@@ -166,6 +317,10 @@
  */
 DWORD WINAPI DdeQueryString32W(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT32 iCodePage)
 {
+    DWORD ret = 0;
+    WCHAR pString[MAX_BUFFER_LEN];
+    int factor = 1;
+
     FIXME(ddeml,
          "(%ld, 0x%lx, %p, %ld, %d): stub\n",
          idInst,
@@ -174,7 +329,23 @@
          cchMax,
          iCodePage);
 
-    return 0;
+    if( iCodePage == CP_WINUNICODE )
+    {
+        /* If psz is null, we have to return only the length
+         * of the string.
+         */
+        if( psz == NULL )
+        {
+            psz = pString;
+            cchMax = MAX_BUFFER_LEN;
+            /* Note: According to documentation if the psz parameter
+             * was NULL this API must return the length of the string in bytes.
+             */
+            factor = (int) sizeof(WCHAR)/sizeof(BYTE);
+        }
+        ret = GlobalGetAtomName32W( hsz, (LPWSTR)psz, cchMax ) * factor;
+    }
+    return ret;
 }
 
 
@@ -315,11 +486,19 @@
  *    Failure: 0
  */
 HSZ WINAPI DdeCreateStringHandle32A( DWORD idInst, LPCSTR psz, INT32 codepage )
-{ TRACE(ddeml, "(%ld,%s,%d): stub\n",idInst,debugstr_a(psz),codepage);
+{
+  HSZ hsz = 0;
+  TRACE(ddeml, "(%ld,%s,%d): stub\n",idInst,debugstr_a(psz),codepage);
   
-  if (codepage==1004)  /*fixme: should be CP_WINANSI*/
-    return GlobalAddAtom32A (psz);
-  else
+  if (codepage==CP_WINANSI)
+  {
+      hsz = GlobalAddAtom32A (psz);
+      /* Save the handle so we know to clean it when
+       * uninitialize is called.
+       */
+      InsertHSZNode( idInst, hsz );
+      return hsz;
+  }
     return 0;  
 }
 
@@ -336,9 +515,20 @@
     LPCWSTR psz,    /* [in] Pointer to string */
     INT32 codepage) /* [in] Code page identifier */
 {
+  HSZ hsz = 0;
+
     FIXME(ddeml, "(%ld,%s,%d): stub\n",idInst,debugstr_w(psz),codepage);
-    DDE_current_handle++;
-    return DDE_current_handle;
+
+  if (codepage==CP_WINUNICODE)
+  {
+      hsz = GlobalAddAtom32W (psz);
+      /* Save the handle so we know to clean it when
+       * uninitialize is called.
+       */
+      InsertHSZNode( idInst, hsz );
+      return hsz;
+}
+  return 0;
 }
 
 
@@ -357,8 +547,14 @@
  *          fail:    zero
  */
 BOOL32 WINAPI DdeFreeStringHandle32( DWORD idInst, HSZ hsz )
-{   TRACE( ddeml, "(%ld,%ld): stub\n",idInst, hsz );
-    return GlobalDeleteAtom (hsz) ? hsz : 0;
+{
+    TRACE( ddeml, "(%ld,%ld): stub\n",idInst, hsz );
+    /* Remove the node associated with this HSZ.
+     */
+    RemoveHSZNode( idInst, hsz );
+    /* Free the string associated with this HSZ.
+     */
+    return GlobalDeleteAtom (hsz) ? 0 : hsz;
 }
 
 
@@ -381,6 +577,8 @@
 }
 
 
+
+
 /*****************************************************************
  *            DdeKeepStringHandle16   (DDEML.24)
  */
@@ -617,16 +815,144 @@
  */
 int WINAPI DdeCmpStringHandles16( HSZ hsz1, HSZ hsz2 )
 {
-     return DdeCmpStringHandles32(hsz1, hsz2);
+     return DdeCmpStringHandles32A(hsz1, hsz2);
 }
 
 /*****************************************************************
- *            DdeCmpStringHandles32 (USER32.91)
+ *            DdeCmpStringHandles32A (USER32.91)
+ *
+ * Compares the value of two string handles.  This comparison is
+ * not case sensitive.
+ *
+ * Returns:
+ * -1 The value of hsz1 is zero or less than hsz2
+ * 0  The values of hsz 1 and 2 are the same or both zero.
+ * 1  The value of hsz2 is zero of less than hsz1
  */
-int WINAPI DdeCmpStringHandles32( HSZ hsz1, HSZ hsz2 )
+int WINAPI DdeCmpStringHandles32A( HSZ hsz1, HSZ hsz2 )
 {
-     FIXME( ddeml, "(0x%lx, 0x%lx): stub\n", hsz1, hsz2 );
-     return 0;
+    CHAR psz1[MAX_BUFFER_LEN];
+    CHAR psz2[MAX_BUFFER_LEN];
+    int ret = 0;
+    int ret1, ret2;
+
+    TRACE( ddeml, "handle 1, handle 2\n" );
+
+    ret1 = GlobalGetAtomName32A( hsz1, psz1, MAX_BUFFER_LEN );
+    ret2 = GlobalGetAtomName32A( hsz2, psz2, MAX_BUFFER_LEN );
+    /* Make sure we found both strings.
+     */
+    if( ret1 == 0 && ret2 == 0 )
+    {
+        /* If both are not found, return both  "zero strings".
+         */
+        ret = 0;
+    }
+    else if( ret1 == 0 )
+    {
+        /* If hsz1 is a not found, return hsz1 is "zero string".
+         */
+        ret = -1;
+    }
+    else if( ret2 == 0 )
+    {
+        /* If hsz2 is a not found, return hsz2 is "zero string".
+         */
+        ret = 1;
+    }
+    else
+    {
+        /* Compare the two strings we got ( case insensitive ).
+         */
+        ret = strcasecmp( psz1, psz2 );
+        /* Since strcmp returns any number smaller than
+         * 0 when the first string is found to be less than
+         * the second one we must make sure we are returning
+         * the proper values.
+         */
+        if( ret < 0 )
+        {
+            ret = -1;
+        }
+        else if( ret > 0 )
+        {
+            ret = 1;
+        }
+    }
+
+    return ret;
+}
+
+/*****************************************************************
+ *            DdeCmpStringHandles32W (USER32.623)
+ *
+ * Compares the value of two string handles.  This comparison is
+ * not case sensitive.
+ *
+ * Returns:
+ * -1 The value of hsz1 is zero or less than hsz2
+ * 0  The values of hsz 1 and 2 are the same or both zero.
+ * 1  The value of hsz2 is zero of less than hsz1
+ */
+int WINAPI DdeCmpStringHandles32W( HSZ hsz1, HSZ hsz2 )
+{
+    WCHAR pwsz1[MAX_BUFFER_LEN];
+    WCHAR pwsz2[MAX_BUFFER_LEN];
+    int ret = 0;
+    int ret1, ret2;
+
+    TRACE( ddeml, "handle 1, handle 2\n" );
+
+    ret1 = GlobalGetAtomName32W( hsz1, pwsz1, MAX_BUFFER_LEN );
+    ret2 = GlobalGetAtomName32W( hsz2, pwsz2, MAX_BUFFER_LEN );
+    /* Make sure we found both strings.
+     */
+    if( ret1 == 0 && ret2 == 0 )
+    {
+        /* If both are not found, return both  "zero strings".
+         */
+        ret = 0;
+    }
+    else if( ret1 == 0 )
+    {
+        /* If hsz1 is a not found, return hsz1 is "zero string".
+         */
+        ret = -1;
+    }
+    else if( ret2 == 0 )
+    {
+        /* If hsz2 is a not found, return hsz2 is "zero string".
+         */
+        ret = 1;
+    }
+    else
+    {
+        LPSTR psz1, psz2;
+        psz1 = HEAP_strdupWtoA( GetProcessHeap(), 0, pwsz1 );
+        psz2 = HEAP_strdupWtoA( GetProcessHeap(), 0, pwsz2 );
+        if( psz1 != NULL && psz2 != NULL )
+        {
+            /* Compare the two strings we got ( case insensitive ).
+             */
+            ret = strcasecmp( psz1, psz2 );
+            /* Since strcmp returns any number smaller than
+             * 0 when the first string is found to be less than
+             * the second one we must make sure we are returning
+             * the proper values.
+             */
+            if( ret < 0 )
+            {
+                ret = -1;
+            }
+            else if( ret > 0 )
+            {
+                ret = 1;
+            }
+        }
+        HeapFree( GetProcessHeap(), 0, psz1 );
+        HeapFree( GetProcessHeap(), 0, psz2 );
+    }
+    return ret;
 }
 
 
diff --git a/relay32/user32.spec b/relay32/user32.spec
index 993f541..9a10ec3 100644
--- a/relay32/user32.spec
+++ b/relay32/user32.spec
@@ -92,7 +92,7 @@
  88 stdcall DdeAccessData(long ptr) DdeAccessData32
  89 stub DdeAddData
  90 stdcall DdeClientTransaction(ptr long long long long long long ptr) DdeClientTransaction32
- 91 stdcall DdeCmpStringHandles(long long) DdeCmpStringHandles32
+ 91 stdcall DdeCmpStringHandlesA(long long) DdeCmpStringHandles32A
  92 stdcall DdeConnect(long long long ptr) DdeConnect32
  93 stdcall DdeConnectList(long long long long ptr) DdeConnectList32
  94 stdcall DdeCreateDataHandle(long ptr long long long long long) DdeCreateDataHandle32
@@ -615,7 +615,6 @@
 610 stdcall MonitorFromRect(ptr long) MonitorFromRect
 611 stdcall MonitorFromPoint(long long long) MonitorFromPoint
 612 stdcall EnumDisplayMonitors(long ptr ptr long) EnumDisplayMonitors
-
 613 stdcall PrivateExtractIconExA (long long long long long) PrivateExtractIconExA
 614 stdcall PrivateExtractIconExW (long long long long long) PrivateExtractIconExW
 615 stdcall PrivateExtractIconsW (long long long long long long long long) PrivateExtractIconsW
@@ -626,3 +625,4 @@
 620 stdcall GetTaskmanWindow () GetTaskmanWindow
 621 stdcall SetTaskmanWindow (long) SetTaskmanWindow
 622 stdcall GetProgmanWindow () GetProgmanWindow
+623 stdcall DdeCmpStringHandlesW(long long) DdeCmpStringHandles32W