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  *         Better AddItem Tests (Lots of parameters to test)
26  *         Tests for Invalid Characters in Names / Invalid Parameters
27  */
28 
29 #include <stdio.h>
30 #include <wine/test.h>
31 #include <winbase.h>
32 #include "dde.h"
33 #include "ddeml.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 
37 static HRESULT (WINAPI *pSHGetLocalizedName)(LPCWSTR, LPWSTR, UINT, int *);
38 
39 static void init_function_pointers(void)
40 {
41     HMODULE hmod;
42 
43     hmod = GetModuleHandleA("shell32.dll");
44     pSHGetLocalizedName = (void*)GetProcAddress(hmod, "SHGetLocalizedName");
45 }
46 
47 static BOOL use_common(void)
48 {
49     HMODULE hmod;
50     static BOOL (WINAPI *pIsNTAdmin)(DWORD, LPDWORD);
51 
52     /* IsNTAdmin() is available on all platforms. */
53     hmod = LoadLibraryA("advpack.dll");
54     pIsNTAdmin = (void*)GetProcAddress(hmod, "IsNTAdmin");
55 
56     if (!pIsNTAdmin(0, NULL))
57     {
58         /* We are definitely not an administrator */
59         FreeLibrary(hmod);
60         return FALSE;
61     }
62     FreeLibrary(hmod);
63 
64     /* If we end up here we are on NT4+ as Win9x and WinMe don't have the
65      * notion of administrators (as we need it).
66      */
67 
68     /* As of Vista  we should always use the users directory. Tests with the
69      * real Administrator account on Windows 7 proved this.
70      *
71      * FIXME: We need a better way of identifying Vista+ as currently this check
72      * also covers Wine and we don't know yet which behavior we want to follow.
73      */
74     if (pSHGetLocalizedName)
75         return FALSE;
76 
77     return TRUE;
78 }
79 
80 static BOOL full_title(void)
81 {
82     CABINETSTATE cs;
83 
84     memset(&cs, 0, sizeof(cs));
85     ReadCabinetState(&cs, sizeof(cs));
86 
87     return (cs.fFullPathTitle == -1);
88 }
89 
90 static char ProgramsDir[MAX_PATH];
91 
92 static void init_strings(void)
93 {
94     char commonprograms[MAX_PATH];
95     char programs[MAX_PATH];
96 
97     SHGetSpecialFolderPathA(NULL, programs, CSIDL_PROGRAMS, FALSE);
98     SHGetSpecialFolderPathA(NULL, commonprograms, CSIDL_COMMON_PROGRAMS, FALSE);
99 
100     /* ProgramsDir on Vista+ is always the users one (CSIDL_PROGRAMS). Before Vista
101      * it depends on whether the user is an administrator (CSIDL_COMMON_PROGRAMS) or
102      * not (CSIDL_PROGRAMS).
103      */
104     if (use_common())
105         lstrcpyA(ProgramsDir, commonprograms);
106     else
107         lstrcpyA(ProgramsDir, programs);
108 }
109 
110 static HDDEDATA CALLBACK DdeCallback(UINT type, UINT format, HCONV hConv, HSZ hsz1, HSZ hsz2,
111                                      HDDEDATA hDDEData, ULONG_PTR data1, ULONG_PTR data2)
112 {
113     trace("Callback: type=%i, format=%i\n", type, format);
114     return NULL;
115 }
116 
117 static UINT dde_execute(DWORD instance, HCONV hconv, const char *command_str)
118 {
119     HDDEDATA command, hdata;
120     DWORD result;
121     UINT ret;
122 
123     command = DdeCreateDataHandle(instance, (BYTE *)command_str, strlen(command_str)+1, 0, 0, 0, 0);
124     ok(command != NULL, "DdeCreateDataHandle() failed: %u\n", DdeGetLastError(instance));
125 
126     hdata = DdeClientTransaction((BYTE *)command, -1, hconv, 0, 0, XTYP_EXECUTE, 2000, &result);
127     ret = DdeGetLastError(instance);
128     /* PROGMAN always returns 1 on success */
129     ok((UINT_PTR)hdata == !ret, "expected %u, got %p\n", !ret, hdata);
130 
131     return ret;
132 }
133 
134 static char *dde_request(DWORD instance, HCONV hconv, const char *request_str)
135 {
136     static char data[2000];
137     HDDEDATA hdata;
138     HSZ item;
139     DWORD result;
140 
141     item = DdeCreateStringHandleA(instance, request_str, CP_WINANSI);
142     ok(item != NULL, "DdeCreateStringHandle() failed: %u\n", DdeGetLastError(instance));
143 
144     hdata = DdeClientTransaction(NULL, -1, hconv, item, CF_TEXT, XTYP_REQUEST, 2000, &result);
145     if (hdata == NULL) return NULL;
146 
147     DdeGetData(hdata, (BYTE *)data, 2000, 0);
148 
149     return data;
150 }
151 
152 static BOOL check_window_exists(const char *name)
153 {
154     char title[MAX_PATH];
155     HWND window = NULL;
156     int i;
157 
158     if (full_title())
159     {
160         strcpy(title, ProgramsDir);
161         strcat(title, "\\");
162         strcat(title, name);
163     }
164     else
165         strcpy(title, name);
166 
167     for (i = 0; i < 20; i++)
168     {
169         Sleep(100 * i);
170         if ((window = FindWindowA("ExplorerWClass", title)) ||
171             (window = FindWindowA("CabinetWClass", title)))
172         {
173             SendMessageA(window, WM_SYSCOMMAND, SC_CLOSE, 0);
174             break;
175         }
176     }
177 
178     return (window != NULL);
179 }
180 
181 static BOOL check_exists(const char *name)
182 {
183     char path[MAX_PATH];
184 
185     strcpy(path, ProgramsDir);
186     strcat(path, "\\");
187     strcat(path, name);
188     return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
189 }
190 
191 static void test_parser(DWORD instance, HCONV hConv)
192 {
193     UINT error;
194 
195     /* Invalid Command */
196     error = dde_execute(instance, hConv, "[InvalidCommand()]");
197     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
198 
199     /* test parsing */
200     error = dde_execute(instance, hConv, "");
201     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
202 
203     error = dde_execute(instance, hConv, "CreateGroup");
204     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
205 
206     error = dde_execute(instance, hConv, "[CreateGroup");
207     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
208 
209     error = dde_execute(instance, hConv, "[CreateGroup]");
210     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
211 
212     error = dde_execute(instance, hConv, "[CreateGroup()]");
213     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
214 
215     error = dde_execute(instance, hConv, "[cREATEgROUP(test)]");
216     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
217     ok(check_exists("test"), "directory not created\n");
218     ok(check_window_exists("test"), "window not created\n");
219 
220     error = dde_execute(instance, hConv, "[AddItem(notepad,foobar)]");
221     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
222     ok(check_exists("test/foobar.lnk"), "link not created\n");
223 
224     error = dde_execute(instance, hConv, "[AddItem(notepad,foo bar)]");
225     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
226     ok(check_exists("test/foo bar.lnk"), "link not created\n");
227 
228     error = dde_execute(instance, hConv, "[AddItem(notepad,a[b,c]d)]");
229     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
230 
231     error = dde_execute(instance, hConv, "[AddItem(notepad,\"a[b,c]d\")]");
232     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
233     ok(check_exists("test/a[b,c]d.lnk"), "link not created\n");
234 
235     error = dde_execute(instance, hConv, "  [  AddItem  (  notepad  ,  test  )  ]  ");
236     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
237     ok(check_exists("test/test.lnk"), "link not created\n");
238 
239     error = dde_execute(instance, hConv, "[AddItem(notepad,one)][AddItem(notepad,two)]");
240     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
241     ok(check_exists("test/one.lnk"), "link not created\n");
242     ok(check_exists("test/two.lnk"), "link not created\n");
243 
244     error = dde_execute(instance, hConv, "[FakeCommand(test)][DeleteGroup(test)]");
245     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
246     ok(check_exists("test"), "directory should exist\n");
247 
248     error = dde_execute(instance, hConv, "[DeleteGroup(test)]");
249     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
250     ok(!check_exists("test"), "directory should not exist\n");
251 
252     error = dde_execute(instance, hConv, "[ExitProgman()]");
253     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
254 
255     error = dde_execute(instance, hConv, "[ExitProgman]");
256     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
257 }
258 
259 /* 1st set of tests */
260 static void test_progman_dde(DWORD instance, HCONV hConv)
261 {
262     UINT error;
263 
264     /* test creating and deleting groups and items */
265     error = dde_execute(instance, hConv, "[CreateGroup(Group1)]");
266     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
267     ok(check_exists("Group1"), "directory not created\n");
268     ok(check_window_exists("Group1"), "window not created\n");
269 
270     error = dde_execute(instance, hConv, "[AddItem]");
271     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
272 
273     error = dde_execute(instance, hConv, "[AddItem(test)]");
274     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
275 
276     error = dde_execute(instance, hConv, "[AddItem(notepad.exe)]");
277     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
278     ok(check_exists("Group1/notepad.lnk"), "link not created\n");
279 
280     error = dde_execute(instance, hConv, "[DeleteItem(notepad.exe)]");
281     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
282 
283     error = dde_execute(instance, hConv, "[DeleteItem(notepad)]");
284     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
285     ok(!check_exists("Group1/notepad.lnk"), "link should not exist\n");
286 
287     error = dde_execute(instance, hConv, "[DeleteItem(notepad)]");
288     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
289 
290     error = dde_execute(instance, hConv, "[AddItem(notepad)]");
291     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
292     ok(check_exists("Group1/notepad.lnk"), "link not created\n");
293 
294     error = dde_execute(instance, hConv, "[AddItem(notepad)]");
295     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
296 
297     /* XP allows any valid path even if it does not exist; Vista+ requires that
298      * the path both exist and be a file (directories are invalid). */
299 
300     error = dde_execute(instance, hConv, "[AddItem(C:\\windows\\system.ini)]");
301     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
302     ok(check_exists("Group1/system.lnk"), "link not created\n");
303 
304     error = dde_execute(instance, hConv, "[AddItem(notepad,test1)]");
305     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
306     ok(check_exists("Group1/test1.lnk"), "link not created\n");
307 
308     error = dde_execute(instance, hConv, "[DeleteItem(test1)]");
309     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
310     ok(!check_exists("Group1/test1.lnk"), "link should not exist\n");
311 
312     error = dde_execute(instance, hConv, "[AddItem(notepad,test1)]");
313     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
314     ok(check_exists("Group1/test1.lnk"), "link not created\n");
315 
316     error = dde_execute(instance, hConv, "[ReplaceItem(test1)]");
317     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
318     ok(!check_exists("Group1/test1.lnk"), "link should not exist\n");
319 
320     error = dde_execute(instance, hConv, "[AddItem(regedit)]");
321     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
322     ok(check_exists("Group1/regedit.lnk"), "link not created\n");
323 
324     /* test ShowGroup() and test which group an item gets added to */
325     error = dde_execute(instance, hConv, "[ShowGroup(Group1)]");
326     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
327 
328     error = dde_execute(instance, hConv, "[ShowGroup(Group1, 0)]");
329     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
330     ok(check_window_exists("Group1"), "window not created\n");
331 
332     error = dde_execute(instance, hConv, "[CreateGroup(Group2)]");
333     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
334     ok(check_exists("Group2"), "directory not created\n");
335     ok(check_window_exists("Group2"), "window not created\n");
336 
337     error = dde_execute(instance, hConv, "[AddItem(notepad,test2)]");
338     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
339     ok(check_exists("Group2/test2.lnk"), "link not created\n");
340 
341     error = dde_execute(instance, hConv, "[ShowGroup(Group1, 0)]");
342     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
343     ok(check_window_exists("Group1"), "window not created\n");
344 
345     error = dde_execute(instance, hConv, "[AddItem(notepad,test3)]");
346     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
347     ok(check_exists("Group1/test3.lnk"), "link not created\n");
348 
349     error = dde_execute(instance, hConv, "[DeleteGroup(Group1)]");
350     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
351     ok(!check_exists("Group1"), "directory should not exist\n");
352 
353     error = dde_execute(instance, hConv, "[DeleteGroup(Group1)]");
354     ok(error == DMLERR_NOTPROCESSED, "expected DMLERR_NOTPROCESSED, got %u\n", error);
355 
356     error = dde_execute(instance, hConv, "[ShowGroup(Group2, 0)]");
357     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
358     ok(check_window_exists("Group2"), "window not created\n");
359 
360     error = dde_execute(instance, hConv, "[ExitProgman(1)]");
361     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
362 
363     error = dde_execute(instance, hConv, "[AddItem(notepad,test4)]");
364     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
365     ok(check_exists("Group2/test4.lnk"), "link not created\n");
366 }
367 
368 /* 2nd set of tests - 2nd connection */
369 static void test_progman_dde2(DWORD instance, HCONV hConv)
370 {
371     UINT error;
372 
373     /* last open group is retained across connections */
374     error = dde_execute(instance, hConv, "[AddItem(notepad)]");
375     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
376     ok(check_exists("Group2/notepad.lnk"), "link not created\n");
377 
378     error = dde_execute(instance, hConv, "[DeleteGroup(Group2)]");
379     ok(error == DMLERR_NO_ERROR, "expected DMLERR_NO_ERROR, got %u\n", error);
380     ok(!check_exists("Group2"), "directory should not exist\n");
381 }
382 
383 static BOOL check_in_programs_list(const char *list, const char *group)
384 {
385     while (1)
386     {
387         if (!strncmp(list, group, strlen(group)) && list[strlen(group)] == '\r')
388             return TRUE;
389         if (!(list = strchr(list, '\r'))) break;
390         list += 2;
391     }
392     return FALSE;
393 }
394 
395 static void test_request_groups(DWORD instance, HCONV hconv)
396 {
397     char *list;
398     char programs[MAX_PATH];
399     WIN32_FIND_DATAA finddata;
400     HANDLE hfind;
401 
402     list = dde_request(instance, hconv, "Groups");
403     ok(list != NULL, "request failed: %u\n", DdeGetLastError(instance));
404     strcpy(programs, ProgramsDir);
405     strcat(programs, "/*");
406     hfind = FindFirstFileA(programs, &finddata);
407     do
408     {
409         if ((finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && finddata.cFileName[0] != '.')
410         {
411             ok(check_in_programs_list(list, finddata.cFileName),
412                "directory '%s' missing from group list\n", finddata.cFileName);
413         }
414     } while (FindNextFileA(hfind, &finddata));
415     FindClose(hfind);
416 }
417 
418 START_TEST(progman_dde)
419 {
420     DWORD instance = 0;
421     UINT err;
422     HSZ hszProgman;
423     HCONV hConv;
424     BOOL ret;
425 
426     init_function_pointers();
427     init_strings();
428 
429     /* Initialize DDE Instance */
430     err = DdeInitializeA(&instance, DdeCallback, APPCMD_CLIENTONLY, 0);
431     ok(err == DMLERR_NO_ERROR, "DdeInitialize() failed: %u\n", err);
432 
433     /* Create Connection */
434     hszProgman = DdeCreateStringHandleA(instance, "PROGMAN", CP_WINANSI);
435     ok(hszProgman != NULL, "DdeCreateStringHandle() failed: %u\n", DdeGetLastError(instance));
436     hConv = DdeConnect(instance, hszProgman, hszProgman, NULL);
437     ret = DdeFreeStringHandle(instance, hszProgman);
438     ok(ret, "DdeFreeStringHandle() failed: %u\n", DdeGetLastError(instance));
439     /* Seeing failures on early versions of Windows Connecting to progman, exit if connection fails */
440     if (hConv == NULL)
441     {
442         ok (DdeUninitialize(instance), "DdeUninitialize failed\n");
443         return;
444     }
445 
446     test_parser(instance, hConv);
447     test_progman_dde(instance, hConv);
448     test_request_groups(instance, hConv);
449 
450     /* Cleanup & Exit */
451     ret = DdeDisconnect(hConv);
452     ok(ret, "DdeDisonnect() failed: %u\n", DdeGetLastError(instance));
453     ret = DdeUninitialize(instance);
454     ok(ret, "DdeUninitialize() failed: %u\n", DdeGetLastError(instance));
455 
456     /* 2nd Instance (Followup Tests) */
457     /* Initialize DDE Instance */
458     instance = 0;
459     err = DdeInitializeA(&instance, DdeCallback, APPCMD_CLIENTONLY, 0);
460     ok (err == DMLERR_NO_ERROR, "DdeInitialize() failed: %u\n", err);
461 
462     /* Create Connection */
463     hszProgman = DdeCreateStringHandleA(instance, "PROGMAN", CP_WINANSI);
464     ok(hszProgman != NULL, "DdeCreateStringHandle() failed: %u\n", DdeGetLastError(instance));
465     hConv = DdeConnect(instance, hszProgman, hszProgman, NULL);
466     ok(hConv != NULL, "DdeConnect() failed: %u\n", DdeGetLastError(instance));
467     ret = DdeFreeStringHandle(instance, hszProgman);
468     ok(ret, "DdeFreeStringHandle() failed: %u\n", DdeGetLastError(instance));
469 
470     /* Run Tests */
471     test_progman_dde2(instance, hConv);
472 
473     /* Cleanup & Exit */
474     ret = DdeDisconnect(hConv);
475     ok(ret, "DdeDisonnect() failed: %u\n", DdeGetLastError(instance));
476     ret = DdeUninitialize(instance);
477     ok(ret, "DdeUninitialize() failed: %u\n", DdeGetLastError(instance));
478 }
479