shell32/tests: Run tests again on NT4.
[wine] / dlls / shell32 / tests / progman_dde.c
1 /*
2  * Unit test of the Program Manager DDE Interfaces
3  *
4  * Copyright 2009 Mikey Alexander
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /* DDE Program Manager Tests
22  * - Covers basic CreateGroup, ShowGroup, DeleteGroup, AddItem, and DeleteItem
23  *   functionality
24  * - Todo: Handle CommonGroupFlag
25  *         Handle Difference between administrator and non-administrator calls
26  *           as documented
27  *         Better AddItem Tests (Lots of parameters to test)
28  *         Tests for Invalid Characters in Names / Invalid Parameters
29  */
30
31 #include <stdio.h>
32 #include <wine/test.h>
33 #include <winbase.h>
34 #include "dde.h"
35 #include "ddeml.h"
36 #include "winuser.h"
37 #include "shlobj.h"
38
39 /* Timeout on DdeClientTransaction Call */
40 #define MS_TIMEOUT_VAL 1000
41 /* # of times to poll for window creation */
42 #define PDDE_POLL_NUM 50
43 /* time to sleep between polls */
44 #define PDDE_POLL_TIME 200
45
46 /* Call Info */
47 #define DDE_TEST_MISC            0x00010000
48 #define DDE_TEST_CREATEGROUP     0x00020000
49 #define DDE_TEST_DELETEGROUP     0x00030000
50 #define DDE_TEST_SHOWGROUP       0x00040000
51 #define DDE_TEST_ADDITEM         0x00050000
52 #define DDE_TEST_DELETEITEM      0x00060000
53 #define DDE_TEST_COMPOUND        0x00070000
54 #define DDE_TEST_CALLMASK        0x00ff0000
55
56 /* Type of Test (Common, Individual) */
57 #define DDE_TEST_COMMON            0x01000000
58 #define DDE_TEST_INDIVIDUAL        0x02000000
59
60 #define DDE_TEST_NUMMASK           0x0000ffff
61
62 static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
63
64 static void init_function_pointers(void)
65 {
66     HMODULE hmod;
67
68     hmod = GetModuleHandleA("shell32.dll");
69     pSHGetSpecialFolderPathA = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathA");
70 }
71
72 static HDDEDATA CALLBACK DdeCallback(UINT type, UINT format, HCONV hConv, HSZ hsz1, HSZ hsz2,
73                                      HDDEDATA hDDEData, ULONG_PTR data1, ULONG_PTR data2)
74 {
75     trace("Callback: type=%i, format=%i\n", type, format);
76     return NULL;
77 }
78
79 /*
80  * Encoded String for Error Messages so that inner failures can determine
81  * what test is failing.  Format is: [Code:TestNum]
82  */
83 static const char * GetStringFromTestParams(int testParams)
84 {
85     int testNum;
86     static char testParamString[64];
87     const char *callId;
88
89     testNum = testParams & DDE_TEST_NUMMASK;
90     switch (testParams & DDE_TEST_CALLMASK)
91     {
92     default:
93     case DDE_TEST_MISC:
94         callId = "MISC";
95         break;
96     case DDE_TEST_CREATEGROUP:
97         callId = "C_G";
98         break;
99     case DDE_TEST_DELETEGROUP:
100         callId = "D_G";
101         break;
102     case DDE_TEST_SHOWGROUP:
103         callId = "S_G";
104         break;
105     case DDE_TEST_ADDITEM:
106         callId = "A_I";
107         break;
108     case DDE_TEST_DELETEITEM:
109         callId = "D_I";
110         break;
111     case DDE_TEST_COMPOUND:
112         callId = "CPD";
113         break;
114     }
115
116     sprintf(testParamString, "  [%s:%i]", callId, testNum);
117     return testParamString;
118 }
119
120 /* Transfer DMLERR's into text readable strings for Error Messages */
121 #define DMLERR_TO_STR(x) case x: return#x;
122 static const char * GetStringFromError(UINT err)
123 {
124     const char * retstr;
125     switch (err)
126     {
127     DMLERR_TO_STR(DMLERR_NO_ERROR);
128     DMLERR_TO_STR(DMLERR_ADVACKTIMEOUT);
129     DMLERR_TO_STR(DMLERR_BUSY);
130     DMLERR_TO_STR(DMLERR_DATAACKTIMEOUT);
131     DMLERR_TO_STR(DMLERR_DLL_NOT_INITIALIZED);
132     DMLERR_TO_STR(DMLERR_DLL_USAGE);
133     DMLERR_TO_STR(DMLERR_EXECACKTIMEOUT);
134     DMLERR_TO_STR(DMLERR_INVALIDPARAMETER);
135     DMLERR_TO_STR(DMLERR_LOW_MEMORY);
136     DMLERR_TO_STR(DMLERR_MEMORY_ERROR);
137     DMLERR_TO_STR(DMLERR_NOTPROCESSED);
138     DMLERR_TO_STR(DMLERR_NO_CONV_ESTABLISHED);
139     DMLERR_TO_STR(DMLERR_POKEACKTIMEOUT);
140     DMLERR_TO_STR(DMLERR_POSTMSG_FAILED);
141     DMLERR_TO_STR(DMLERR_REENTRANCY);
142     DMLERR_TO_STR(DMLERR_SERVER_DIED);
143     DMLERR_TO_STR(DMLERR_SYS_ERROR);
144     DMLERR_TO_STR(DMLERR_UNADVACKTIMEOUT);
145     DMLERR_TO_STR(DMLERR_UNFOUND_QUEUE_ID);
146     default:
147         retstr = "Unknown DML Error";
148         break;
149     }
150
151     return retstr;
152 }
153
154 /* Helper Function to Transfer DdeGetLastError into a String */
155 static const char * GetDdeLastErrorStr(DWORD instance)
156 {
157     UINT err = DdeGetLastError(instance);
158
159     return GetStringFromError(err);
160 }
161
162 /* Execute a Dde Command and return the error & result */
163 /* Note: Progman DDE always returns a pointer to 0x00000001 on a successful result */
164 static void DdeExecuteCommand(DWORD instance, HCONV hConv, const char *strCmd, HDDEDATA *hData, UINT *err, int testParams)
165 {
166     HDDEDATA command;
167
168     command = DdeCreateDataHandle(instance, (LPBYTE) strCmd, strlen(strCmd)+1, 0, 0L, 0, 0);
169     ok (command != NULL, "DdeCreateDataHandle Error %s.%s\n",
170         GetDdeLastErrorStr(instance), GetStringFromTestParams(testParams));
171     *hData = DdeClientTransaction((void *) command,
172                                   -1,
173                                   hConv,
174                                   0,
175                                   0,
176                                   XTYP_EXECUTE,
177                                   MS_TIMEOUT_VAL,
178                                   NULL);
179
180     /* hData is technically a pointer, but for Program Manager,
181      * it is NULL (error) or 1 (success)
182      * TODO: Check other versions of Windows to verify 1 is returned.
183      * While it is unlikely that anyone is actually testing that the result is 1
184      * if all versions of windows return 1, Wine should also.
185      */
186     if (*hData == NULL)
187     {
188         *err = DdeGetLastError(instance);
189     }
190     else
191     {
192         *err = DMLERR_NO_ERROR;
193         todo_wine
194         {
195             ok(*hData == (HDDEDATA) 1, "Expected HDDEDATA Handle == 1, actually %p.%s\n",
196                *hData, GetStringFromTestParams(testParams));
197         }
198     }
199 }
200
201 /*
202  * Check if Window is onscreen with the appropriate name.
203  *
204  * Windows are not created synchronously.  So we do not know
205  * when and if the window will be created/shown on screen.
206  * This function implements a polling mechanism to determine
207  * creation.
208  * A more complicated method would be to use SetWindowsHookEx.
209  * Since polling worked fine in my testing, no reason to implement
210  * the other.  Comments about other methods of determining when
211  * window creation happened were not encouraging (not including
212  * SetWindowsHookEx).
213  */
214 static void CheckWindowCreated(const char *winName, int closeWindow, int testParams)
215 {
216     HWND window = NULL;
217     int i;
218
219     /* Poll for Window Creation */
220     for (i = 0; window == NULL && i < PDDE_POLL_NUM; i++)
221     {
222         Sleep(PDDE_POLL_TIME);
223         window = FindWindowA(NULL, winName);
224     }
225     ok (window != NULL, "Window \"%s\" was not created in %i seconds - assumed failure.%s\n",
226         winName, PDDE_POLL_NUM*PDDE_POLL_TIME/1000, GetStringFromTestParams(testParams));
227
228     /* Close Window as desired. */
229     if (window != NULL && closeWindow)
230     {
231         SendMessageA(window, WM_SYSCOMMAND, SC_CLOSE, 0);
232     }
233 }
234
235 /* Check for Existence (or non-existence) of a file or group
236  *   When testing for existence of a group, groupName is not needed
237  */
238 static void CheckFileExistsInProgramGroups(const char *nameToCheck, int shouldExist, int isGroup,
239                                            const char *groupName, int testParams)
240 {
241     char *path;
242     int err;
243     DWORD attributes;
244     int len;
245
246     if (!pSHGetSpecialFolderPathA)
247         return;
248
249     path = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
250     if (path != NULL)
251     {
252         int specialFolder;
253
254         err = FALSE;
255         /* Win 9x doesn't support common */
256         if (testParams & DDE_TEST_COMMON)
257         {
258             specialFolder = CSIDL_COMMON_PROGRAMS;
259             err = pSHGetSpecialFolderPathA(NULL, path, specialFolder, FALSE);
260             /* Win 9x fails, use CSIDL_PROGRAMS (err == FALSE) */
261         }
262         if (err == FALSE)
263         {
264             specialFolder = CSIDL_PROGRAMS;
265             err = pSHGetSpecialFolderPathA(NULL, path, specialFolder, FALSE);
266         }
267         len = strlen(path) + strlen(nameToCheck)+1;
268         if (groupName != NULL)
269         {
270             len += strlen(groupName)+1;
271         }
272         ok (len <= MAX_PATH, "Path Too Long.%s\n", GetStringFromTestParams(testParams));
273         if (len <= MAX_PATH)
274         {
275             if (groupName != NULL)
276             {
277                 strcat(path, "\\");
278                 strcat(path, groupName);
279             }
280             strcat(path, "\\");
281             strcat(path, nameToCheck);
282             attributes = GetFileAttributes(path);
283             if (!shouldExist)
284             {
285                 ok (attributes == INVALID_FILE_ATTRIBUTES , "File exists and shouldn't %s.%s\n",
286                     path, GetStringFromTestParams(testParams));
287             } else {
288                 if (attributes == INVALID_FILE_ATTRIBUTES)
289                 {
290                     ok (FALSE, "Created File %s doesn't exist.%s\n", path, GetStringFromTestParams(testParams));
291                 } else if (isGroup) {
292                     ok (attributes & FILE_ATTRIBUTE_DIRECTORY, "%s is not a folder (attr=%x).%s\n",
293                         path, attributes, GetStringFromTestParams(testParams));
294                 } else {
295                     ok (attributes & FILE_ATTRIBUTE_ARCHIVE, "Created File %s has wrong attributes (%x).%s\n",
296                         path, attributes, GetStringFromTestParams(testParams));
297                 }
298             }
299         }
300         HeapFree(GetProcessHeap(), 0, path);
301     }
302     else
303     {
304         ok (FALSE, "Could not Allocate Path Buffer\n");
305     }
306 }
307
308 /* Create Group Test.
309  *   command and expected_result.
310  *   if expected_result is DMLERR_NO_ERROR, test
311  *        1. group was created
312  *        2. window is open
313  */
314 static void CreateGroupTest(DWORD instance, HCONV hConv, const char *command, UINT expected_result,
315                             const char *groupName, int testParams)
316 {
317     HDDEDATA hData;
318     UINT error;
319
320     /* Execute Command & Check Result */
321     DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams);
322     todo_wine
323     {
324         ok (expected_result == error, "CreateGroup %s: Expected Error %s, received %s.%s\n",
325             groupName, GetStringFromError(expected_result), GetStringFromError(error),
326             GetStringFromTestParams(testParams));
327     }
328
329     /* No Error */
330     if (error == DMLERR_NO_ERROR)
331     {
332
333         /* Check if Group Now Exists */
334         CheckFileExistsInProgramGroups(groupName, TRUE, TRUE, NULL, testParams);
335         /* Check if Window is Open (polling) */
336         CheckWindowCreated(groupName, TRUE, testParams);
337     }
338 }
339
340 /* Show Group Test.
341  *   DDE command, expected_result, and the group name to check for existence
342  *   if expected_result is DMLERR_NO_ERROR, test
343  *        1. window is open
344  */
345 static void ShowGroupTest(DWORD instance, HCONV hConv, const char *command, UINT expected_result,
346                           const char *groupName, int closeAfterShowing, int testParams)
347 {
348     HDDEDATA hData;
349     UINT error;
350
351     DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams);
352 /* todo_wine...  Is expected to fail, wine stubbed functions DO fail */
353 /* TODO REMOVE THIS CODE!!! */
354     if (expected_result == DMLERR_NOTPROCESSED)
355     {
356         ok (expected_result == error, "ShowGroup %s: Expected Error %s, received %s.%s\n",
357             groupName, GetStringFromError(expected_result), GetStringFromError(error),
358             GetStringFromTestParams(testParams));
359     } else {
360         todo_wine
361         {
362             ok (expected_result == error, "ShowGroup %s: Expected Error %s, received %s.%s\n",
363                 groupName, GetStringFromError(expected_result), GetStringFromError(error),
364                 GetStringFromTestParams(testParams));
365         }
366     }
367
368     if (error == DMLERR_NO_ERROR)
369     {
370         /* Check if Window is Open (polling) */
371         CheckWindowCreated(groupName, closeAfterShowing, testParams);
372     }
373 }
374
375 /* Delete Group Test.
376  *   DDE command, expected_result, and the group name to check for existence
377  *   if expected_result is DMLERR_NO_ERROR, test
378  *        1. group does not exist
379  */
380 static void DeleteGroupTest(DWORD instance, HCONV hConv, const char *command, UINT expected_result,
381                             const char *groupName, int testParams)
382 {
383     HDDEDATA hData;
384     UINT error;
385
386     DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams);
387     todo_wine
388     {
389         ok (expected_result == error, "DeleteGroup %s: Expected Error %s, received %s.%s\n",
390             groupName, GetStringFromError(expected_result), GetStringFromError(error),
391             GetStringFromTestParams(testParams));
392     }
393
394     if (error == DMLERR_NO_ERROR)
395     {
396         /* Check that Group does not exist */
397         CheckFileExistsInProgramGroups(groupName, FALSE, TRUE, NULL, testParams);
398     }
399 }
400
401 /* Add Item Test
402  *   DDE command, expected result, and group and file name where it should exist.
403  *   checks to make sure error code matches expected error code
404  *   checks to make sure item exists if successful
405  */
406 static void AddItemTest(DWORD instance, HCONV hConv, const char *command, UINT expected_result,
407                         const char *fileName, const char *groupName, int testParams)
408 {
409     HDDEDATA hData;
410     UINT error;
411
412     DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams);
413     todo_wine
414     {
415         ok (expected_result == error, "AddItem %s: Expected Error %s, received %s.%s\n",
416             fileName, GetStringFromError(expected_result), GetStringFromError(error),
417             GetStringFromTestParams(testParams));
418     }
419
420     if (error == DMLERR_NO_ERROR)
421     {
422         /* Check that File exists */
423         CheckFileExistsInProgramGroups(fileName, TRUE, FALSE, groupName, testParams);
424     }
425 }
426
427 /* Delete Item Test.
428  *   DDE command, expected result, and group and file name where it should exist.
429  *   checks to make sure error code matches expected error code
430  *   checks to make sure item does not exist if successful
431  */
432 static void DeleteItemTest(DWORD instance, HCONV hConv, const char *command, UINT expected_result,
433                            const char *fileName, const char *groupName, int testParams)
434 {
435     HDDEDATA hData;
436     UINT error;
437
438     DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams);
439     todo_wine
440     {
441         ok (expected_result == error, "DeleteItem %s: Expected Error %s, received %s.%s\n",
442             fileName, GetStringFromError(expected_result), GetStringFromError(error),
443             GetStringFromTestParams(testParams));
444     }
445
446     if (error == DMLERR_NO_ERROR)
447     {
448         /* Check that File does not exist */
449         CheckFileExistsInProgramGroups(fileName, FALSE, FALSE, groupName, testParams);
450     }
451 }
452
453 /* Compound Command Test.
454  *   not really generic, assumes command of the form:
455  *          [CreateGroup ...][AddItem ...][AddItem ...]
456  *   All samples I've seen using Compound were of this form (CreateGroup,
457  *   AddItems) so this covers minimum expected functionality.
458  */
459 static void CompoundCommandTest(DWORD instance, HCONV hConv, const char *command, UINT expected_result,
460                                 const char *groupName, const char *fileName1,
461                                 const char *fileName2, int testParams)
462 {
463     HDDEDATA hData;
464     UINT error;
465
466     DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams);
467     todo_wine
468     {
469         ok (expected_result == error, "Compound String %s: Expected Error %s, received %s.%s\n",
470             command, GetStringFromError(expected_result), GetStringFromError(error),
471             GetStringFromTestParams(testParams));
472     }
473
474     if (error == DMLERR_NO_ERROR)
475     {
476         /* Check that File exists */
477         CheckFileExistsInProgramGroups(groupName, TRUE, TRUE, NULL, testParams);
478         CheckWindowCreated(groupName, FALSE, testParams);
479         CheckFileExistsInProgramGroups(fileName1, TRUE, FALSE, groupName, testParams);
480         CheckFileExistsInProgramGroups(fileName2, TRUE, FALSE, groupName, testParams);
481     }
482 }
483
484 /* 1st set of tests */
485 static int DdeTestProgman(DWORD instance, HCONV hConv)
486 {
487     HDDEDATA hData;
488     UINT error;
489     int testnum;
490
491     testnum = 1;
492     /* Invalid Command */
493     DdeExecuteCommand(instance, hConv, "[InvalidCommand()]", &hData, &error, DDE_TEST_MISC|testnum++);
494     ok (error == DMLERR_NOTPROCESSED, "InvalidCommand(), expected error %s, received %s.\n",
495         GetStringFromError(DMLERR_NOTPROCESSED), GetStringFromError(error));
496
497     /* CreateGroup Tests (including AddItem, DeleteItem) */
498     CreateGroupTest(instance, hConv, "[CreateGroup(Group1)]", DMLERR_NO_ERROR, "Group1", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++);
499     AddItemTest(instance, hConv, "[AddItem(c:\\f1g1,f1g1Name)]", DMLERR_NO_ERROR, "f1g1Name.lnk", "Group1", DDE_TEST_COMMON|DDE_TEST_ADDITEM|testnum++);
500     AddItemTest(instance, hConv, "[AddItem(c:\\f2g1,f2g1Name)]", DMLERR_NO_ERROR, "f2g1Name.lnk", "Group1", DDE_TEST_COMMON|DDE_TEST_ADDITEM|testnum++);
501     DeleteItemTest(instance, hConv, "[DeleteItem(f2g1Name)]", DMLERR_NO_ERROR, "f2g1Name.lnk", "Group1", DDE_TEST_COMMON|DDE_TEST_DELETEITEM|testnum++);
502     AddItemTest(instance, hConv, "[AddItem(c:\\f3g1,f3g1Name)]", DMLERR_NO_ERROR, "f3g1Name.lnk", "Group1", DDE_TEST_COMMON|DDE_TEST_ADDITEM|testnum++);
503     CreateGroupTest(instance, hConv, "[CreateGroup(Group2)]", DMLERR_NO_ERROR, "Group2", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++);
504     /* Create Group that already exists - same instance */
505     CreateGroupTest(instance, hConv, "[CreateGroup(Group1)]", DMLERR_NO_ERROR, "Group1", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++);
506
507     /* ShowGroup Tests */
508     ShowGroupTest(instance, hConv, "[ShowGroup(Group1)]", DMLERR_NOTPROCESSED, "Startup", TRUE, DDE_TEST_SHOWGROUP|testnum++);
509     DeleteItemTest(instance, hConv, "[DeleteItem(f3g1Name)]", DMLERR_NO_ERROR, "f3g1Name.lnk", "Group1", DDE_TEST_COMMON|DDE_TEST_DELETEITEM|testnum++);
510     ShowGroupTest(instance, hConv, "[ShowGroup(Startup,0)]", DMLERR_NO_ERROR, "Startup", TRUE, DDE_TEST_SHOWGROUP|testnum++);
511     ShowGroupTest(instance, hConv, "[ShowGroup(Group1,0)]", DMLERR_NO_ERROR, "Group1", FALSE, DDE_TEST_SHOWGROUP|testnum++);
512
513     /* DeleteGroup Test - Note that Window is Open for this test */
514     DeleteGroupTest(instance, hConv, "[DeleteGroup(Group1)]", DMLERR_NO_ERROR, "Group1", DDE_TEST_DELETEGROUP|testnum++);
515
516     /* Compound Execute String Command */
517     CompoundCommandTest(instance, hConv, "[CreateGroup(Group3)][AddItem(c:\\f1g3,f1g3Name)][AddItem(c:\\f2g3,f2g3Name)]", DMLERR_NO_ERROR, "Group3", "f1g3Name.lnk", "f2g3Name.lnk", DDE_TEST_COMMON|DDE_TEST_COMPOUND|testnum++);
518     DeleteGroupTest(instance, hConv, "[DeleteGroup(Group3)]", DMLERR_NO_ERROR, "Group3", DDE_TEST_DELETEGROUP|testnum++);
519
520     /* Full Parameters of Add Item */
521     /* AddItem(CmdLine[,Name[,IconPath[,IconIndex[,xPos,yPos[,DefDir[,HotKey[,fMinimize[fSeparateSpace]]]]]]]) */
522
523     return testnum;
524 }
525
526 /* 2nd set of tests - 2nd connection */
527 static void DdeTestProgman2(DWORD instance, HCONV hConv, int testnum)
528 {
529     /* Create Group that already exists on a separate connection */
530     CreateGroupTest(instance, hConv, "[CreateGroup(Group2)]", DMLERR_NO_ERROR, "Group2", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++);
531     DeleteGroupTest(instance, hConv, "[DeleteGroup(Group2)]", DMLERR_NO_ERROR, "Group2", DDE_TEST_COMMON|DDE_TEST_DELETEGROUP|testnum++);
532 }
533
534 START_TEST(progman_dde)
535 {
536     DWORD instance = 0;
537     UINT err;
538     HSZ hszProgman;
539     HCONV hConv;
540     int testnum;
541
542     init_function_pointers();
543
544     /* Only report this once */
545     if (!pSHGetSpecialFolderPathA)
546         win_skip("SHGetSpecialFolderPathA is not available\n");
547
548     /* Initialize DDE Instance */
549     err = DdeInitialize(&instance, DdeCallback, APPCMD_CLIENTONLY, 0);
550     ok (err == DMLERR_NO_ERROR, "DdeInitialize Error %s\n", GetStringFromError(err));
551
552     /* Create Connection */
553     hszProgman = DdeCreateStringHandle(instance, "PROGMAN", CP_WINANSI);
554     ok (hszProgman != NULL, "DdeCreateStringHandle Error %s\n", GetDdeLastErrorStr(instance));
555     hConv = DdeConnect(instance, hszProgman, hszProgman, NULL);
556     ok (hConv != NULL, "DdeConnect Error %s\n", GetDdeLastErrorStr(instance));
557     ok (DdeFreeStringHandle(instance, hszProgman), "DdeFreeStringHandle failure\n");
558
559     /* Run Tests */
560     testnum = DdeTestProgman(instance, hConv);
561
562     /* Cleanup & Exit */
563     ok (DdeDisconnect(hConv), "DdeDisonnect Error %s\n", GetDdeLastErrorStr(instance));
564     ok (DdeUninitialize(instance), "DdeUninitialize failed\n");
565
566     /* 2nd Instance (Followup Tests) */
567     /* Initialize DDE Instance */
568     instance = 0;
569     err = DdeInitialize(&instance, DdeCallback, APPCMD_CLIENTONLY, 0);
570     ok (err == DMLERR_NO_ERROR, "DdeInitialize Error %s\n", GetStringFromError(err));
571
572     /* Create Connection */
573     hszProgman = DdeCreateStringHandle(instance, "PROGMAN", CP_WINANSI);
574     ok (hszProgman != NULL, "DdeCreateStringHandle Error %s\n", GetDdeLastErrorStr(instance));
575     hConv = DdeConnect(instance, hszProgman, hszProgman, NULL);
576     ok (hConv != NULL, "DdeConnect Error %s\n", GetDdeLastErrorStr(instance));
577     ok (DdeFreeStringHandle(instance, hszProgman), "DdeFreeStringHandle failure\n");
578
579     /* Run Tests */
580     DdeTestProgman2(instance, hConv, testnum);
581
582     /* Cleanup & Exit */
583     ok (DdeDisconnect(hConv), "DdeDisonnect Error %s\n", GetDdeLastErrorStr(instance));
584     ok (DdeUninitialize(instance), "DdeUninitialize failed\n");
585 }