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