1 /* 2 * Undocumented functions from COMCTL32.DLL 3 * 4 * Copyright 1998 Eric Kohl 5 * 1998 Juergen Schmied <j.schmied@metronet.de> 6 * 2000 Eric Kohl for CodeWeavers 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 * 22 * NOTES 23 * All of these functions are UNDOCUMENTED!! And I mean UNDOCUMENTED!!!! 24 * Do NOT rely on names or contents of undocumented structures and types!!! 25 * These functions are used by EXPLORER.EXE, IEXPLORE.EXE and 26 * COMCTL32.DLL (internally). 27 * 28 */ 29 #include "config.h" 30 #include "wine/port.h" 31 32 #include <stdarg.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <limits.h> 36 37 #define COBJMACROS 38 #define NONAMELESSUNION 39 40 #include "windef.h" 41 #include "winbase.h" 42 #include "wingdi.h" 43 #include "winuser.h" 44 #include "winnls.h" 45 #include "winreg.h" 46 #include "commctrl.h" 47 #include "objbase.h" 48 #include "winerror.h" 49 50 #include "wine/unicode.h" 51 #include "comctl32.h" 52 53 #include "wine/debug.h" 54 55 WINE_DEFAULT_DEBUG_CHANNEL(commctrl); 56 57 static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 }; 58 59 /************************************************************************** 60 * Alloc [COMCTL32.71] 61 * 62 * Allocates memory block from the dll's private heap 63 * 64 * PARAMS 65 * dwSize [I] size of the allocated memory block 66 * 67 * RETURNS 68 * Success: pointer to allocated memory block 69 * Failure: NULL 70 */ 71 LPVOID WINAPI Alloc (DWORD dwSize) 72 { 73 return LocalAlloc( LMEM_ZEROINIT, dwSize ); 74 } 75 76 77 /************************************************************************** 78 * ReAlloc [COMCTL32.72] 79 * 80 * Changes the size of an allocated memory block or allocates a memory 81 * block using the dll's private heap. 82 * 83 * PARAMS 84 * lpSrc [I] pointer to memory block which will be resized 85 * dwSize [I] new size of the memory block. 86 * 87 * RETURNS 88 * Success: pointer to the resized memory block 89 * Failure: NULL 90 * 91 * NOTES 92 * If lpSrc is a NULL-pointer, then ReAlloc allocates a memory 93 * block like Alloc. 94 */ 95 LPVOID WINAPI ReAlloc (LPVOID lpSrc, DWORD dwSize) 96 { 97 if (lpSrc) 98 return LocalReAlloc( lpSrc, dwSize, LMEM_ZEROINIT | LMEM_MOVEABLE ); 99 else 100 return LocalAlloc( LMEM_ZEROINIT, dwSize); 101 } 102 103 104 /************************************************************************** 105 * Free [COMCTL32.73] 106 * 107 * Frees an allocated memory block from the dll's private heap. 108 * 109 * PARAMS 110 * lpMem [I] pointer to memory block which will be freed 111 * 112 * RETURNS 113 * Success: TRUE 114 * Failure: FALSE 115 */ 116 BOOL WINAPI Free (LPVOID lpMem) 117 { 118 return !LocalFree( lpMem ); 119 } 120 121 122 /************************************************************************** 123 * GetSize [COMCTL32.74] 124 * 125 * Retrieves the size of the specified memory block from the dll's 126 * private heap. 127 * 128 * PARAMS 129 * lpMem [I] pointer to an allocated memory block 130 * 131 * RETURNS 132 * Success: size of the specified memory block 133 * Failure: 0 134 */ 135 DWORD WINAPI GetSize (LPVOID lpMem) 136 { 137 return LocalSize( lpMem ); 138 } 139 140 141 /************************************************************************** 142 * MRU-Functions {COMCTL32} 143 * 144 * NOTES 145 * The MRU-API is a set of functions to manipulate lists of M.R.U. (Most Recently 146 * Used) items. It is an undocumented API that is used (at least) by the shell 147 * and explorer to implement their recent documents feature. 148 * 149 * Since these functions are undocumented, they are unsupported by MS and 150 * may change at any time. 151 * 152 * Internally, the list is implemented as a last in, last out list of items 153 * persisted into the system registry under a caller chosen key. Each list 154 * item is given a one character identifier in the Ascii range from 'a' to 155 * '}'. A list of the identifiers in order from newest to oldest is stored 156 * under the same key in a value named "MRUList". 157 * 158 * Items are re-ordered by changing the order of the values in the MRUList 159 * value. When a new item is added, it becomes the new value of the oldest 160 * identifier, and that identifier is moved to the front of the MRUList value. 161 * 162 * Wine stores MRU-lists in the same registry format as Windows, so when 163 * switching between the builtin and native comctl32.dll no problems or 164 * incompatibilities should occur. 165 * 166 * The following undocumented structure is used to create an MRU-list: 167 *|typedef INT (CALLBACK *MRUStringCmpFn)(LPCTSTR lhs, LPCTSTR rhs); 168 *|typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length); 169 *| 170 *|typedef struct tagMRUINFO 171 *|{ 172 *| DWORD cbSize; 173 *| UINT uMax; 174 *| UINT fFlags; 175 *| HKEY hKey; 176 *| LPTSTR lpszSubKey; 177 *| PROC lpfnCompare; 178 *|} MRUINFO, *LPMRUINFO; 179 * 180 * MEMBERS 181 * cbSize [I] The size of the MRUINFO structure. This must be set 182 * to sizeof(MRUINFO) by the caller. 183 * uMax [I] The maximum number of items allowed in the list. Because 184 * of the limited number of identifiers, this should be set to 185 * a value from 1 to 30 by the caller. 186 * fFlags [I] If bit 0 is set, the list will be used to store binary 187 * data, otherwise it is assumed to store strings. If bit 1 188 * is set, every change made to the list will be reflected in 189 * the registry immediately, otherwise changes will only be 190 * written when the list is closed. 191 * hKey [I] The registry key that the list should be written under. 192 * This must be supplied by the caller. 193 * lpszSubKey [I] A caller supplied name of a subkey under hKey to write 194 * the list to. This may not be blank. 195 * lpfnCompare [I] A caller supplied comparison function, which may be either 196 * an MRUStringCmpFn if dwFlags does not have bit 0 set, or a 197 * MRUBinaryCmpFn otherwise. 198 * 199 * FUNCTIONS 200 * - Create an MRU-list with CreateMRUList() or CreateMRUListLazy(). 201 * - Add items to an MRU-list with AddMRUString() or AddMRUData(). 202 * - Remove items from an MRU-list with DelMRUString(). 203 * - Find data in an MRU-list with FindMRUString() or FindMRUData(). 204 * - Iterate through an MRU-list with EnumMRUList(). 205 * - Free an MRU-list with FreeMRUList(). 206 */ 207 208 typedef INT (CALLBACK *MRUStringCmpFnA)(LPCSTR lhs, LPCSTR rhs); 209 typedef INT (CALLBACK *MRUStringCmpFnW)(LPCWSTR lhs, LPCWSTR rhs); 210 typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length); 211 212 typedef struct tagMRUINFOA 213 { 214 DWORD cbSize; 215 UINT uMax; 216 UINT fFlags; 217 HKEY hKey; 218 LPSTR lpszSubKey; 219 union 220 { 221 MRUStringCmpFnA string_cmpfn; 222 MRUBinaryCmpFn binary_cmpfn; 223 } u; 224 } MRUINFOA, *LPMRUINFOA; 225 226 typedef struct tagMRUINFOW 227 { 228 DWORD cbSize; 229 UINT uMax; 230 UINT fFlags; 231 HKEY hKey; 232 LPWSTR lpszSubKey; 233 union 234 { 235 MRUStringCmpFnW string_cmpfn; 236 MRUBinaryCmpFn binary_cmpfn; 237 } u; 238 } MRUINFOW, *LPMRUINFOW; 239 240 /* MRUINFO.fFlags */ 241 #define MRU_STRING 0 /* list will contain strings */ 242 #define MRU_BINARY 1 /* list will contain binary data */ 243 #define MRU_CACHEWRITE 2 /* only save list order to reg. is FreeMRUList */ 244 245 /* If list is a string list lpfnCompare has the following prototype 246 * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2) 247 * for binary lists the prototype is 248 * int CALLBACK MRUCompareBinary(LPCVOID data1, LPCVOID data2, DWORD cbData) 249 * where cbData is the no. of bytes to compare. 250 * Need to check what return value means identical - 0? 251 */ 252 253 typedef struct tagWINEMRUITEM 254 { 255 DWORD size; /* size of data stored */ 256 DWORD itemFlag; /* flags */ 257 BYTE datastart; 258 } WINEMRUITEM, *LPWINEMRUITEM; 259 260 /* itemFlag */ 261 #define WMRUIF_CHANGED 0x0001 /* this dataitem changed */ 262 263 typedef struct tagWINEMRULIST 264 { 265 MRUINFOW extview; /* original create information */ 266 BOOL isUnicode; /* is compare fn Unicode */ 267 DWORD wineFlags; /* internal flags */ 268 DWORD cursize; /* current size of realMRU */ 269 LPWSTR realMRU; /* pointer to string of index names */ 270 LPWINEMRUITEM *array; /* array of pointers to data */ 271 /* in 'a' to 'z' order */ 272 } WINEMRULIST, *LPWINEMRULIST; 273 274 /* wineFlags */ 275 #define WMRUF_CHANGED 0x0001 /* MRU list has changed */ 276 277 /************************************************************************** 278 * MRU_SaveChanged (internal) 279 * 280 * Local MRU saving code 281 */ 282 static void MRU_SaveChanged ( LPWINEMRULIST mp ) 283 { 284 UINT i, err; 285 HKEY newkey; 286 WCHAR realname[2]; 287 LPWINEMRUITEM witem; 288 289 /* or should we do the following instead of RegOpenKeyEx: 290 */ 291 292 /* open the sub key */ 293 if ((err = RegOpenKeyExW( mp->extview.hKey, mp->extview.lpszSubKey, 294 0, KEY_WRITE, &newkey))) { 295 /* not present - what to do ??? */ 296 ERR("Could not open key, error=%d, attempting to create\n", 297 err); 298 if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey, 299 0, 300 NULL, 301 REG_OPTION_NON_VOLATILE, 302 KEY_READ | KEY_WRITE, 303 0, 304 &newkey, 305 0))) { 306 ERR("failed to create key /%s/, err=%d\n", 307 debugstr_w(mp->extview.lpszSubKey), err); 308 return; 309 } 310 } 311 if (mp->wineFlags & WMRUF_CHANGED) { 312 mp->wineFlags &= ~WMRUF_CHANGED; 313 err = RegSetValueExW(newkey, strMRUList, 0, REG_SZ, (LPBYTE)mp->realMRU, 314 (strlenW(mp->realMRU) + 1)*sizeof(WCHAR)); 315 if (err) { 316 ERR("error saving MRUList, err=%d\n", err); 317 } 318 TRACE("saving MRUList=/%s/\n", debugstr_w(mp->realMRU)); 319 } 320 realname[1] = 0; 321 for(i=0; i<mp->cursize; i++) { 322 witem = mp->array[i]; 323 if (witem->itemFlag & WMRUIF_CHANGED) { 324 witem->itemFlag &= ~WMRUIF_CHANGED; 325 realname[0] = 'a' + i; 326 err = RegSetValueExW(newkey, realname, 0, 327 (mp->extview.fFlags & MRU_BINARY) ? 328 REG_BINARY : REG_SZ, 329 &witem->datastart, witem->size); 330 if (err) { 331 ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err); 332 } 333 TRACE("saving value for name /%s/ size=%d\n", 334 debugstr_w(realname), witem->size); 335 } 336 } 337 RegCloseKey( newkey ); 338 } 339 340 /************************************************************************** 341 * FreeMRUList [COMCTL32.152] 342 * 343 * Frees a most-recently-used items list. 344 * 345 * PARAMS 346 * hMRUList [I] Handle to list. 347 * 348 * RETURNS 349 * Nothing. 350 */ 351 void WINAPI FreeMRUList (HANDLE hMRUList) 352 { 353 LPWINEMRULIST mp = hMRUList; 354 UINT i; 355 356 TRACE("(%p)\n", hMRUList); 357 if (!hMRUList) 358 return; 359 360 if (mp->wineFlags & WMRUF_CHANGED) { 361 /* need to open key and then save the info */ 362 MRU_SaveChanged( mp ); 363 } 364 365 for(i=0; i<mp->extview.uMax; i++) 366 Free(mp->array[i]); 367 368 Free(mp->realMRU); 369 Free(mp->array); 370 Free(mp->extview.lpszSubKey); 371 Free(mp); 372 } 373 374 375 /************************************************************************** 376 * FindMRUData [COMCTL32.169] 377 * 378 * Searches binary list for item that matches lpData of length cbData. 379 * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value 380 * corresponding to item's reg. name will be stored in it ('a' -> 0). 381 * 382 * PARAMS 383 * hList [I] list handle 384 * lpData [I] data to find 385 * cbData [I] length of data 386 * lpRegNum [O] position in registry (maybe NULL) 387 * 388 * RETURNS 389 * Position in list 0 -> MRU. -1 if item not found. 390 */ 391 INT WINAPI FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData, 392 LPINT lpRegNum) 393 { 394 const WINEMRULIST *mp = hList; 395 INT ret; 396 UINT i; 397 LPSTR dataA = NULL; 398 399 if (!mp || !mp->extview.u.string_cmpfn) 400 return -1; 401 402 if(!(mp->extview.fFlags & MRU_BINARY) && !mp->isUnicode) { 403 DWORD len = WideCharToMultiByte(CP_ACP, 0, lpData, -1, 404 NULL, 0, NULL, NULL); 405 dataA = Alloc(len); 406 WideCharToMultiByte(CP_ACP, 0, lpData, -1, dataA, len, NULL, NULL); 407 } 408 409 for(i=0; i<mp->cursize; i++) { 410 if (mp->extview.fFlags & MRU_BINARY) { 411 if (!mp->extview.u.binary_cmpfn(lpData, &mp->array[i]->datastart, cbData)) 412 break; 413 } 414 else { 415 if(mp->isUnicode) { 416 if (!mp->extview.u.string_cmpfn(lpData, (LPWSTR)&mp->array[i]->datastart)) 417 break; 418 } else { 419 DWORD len = WideCharToMultiByte(CP_ACP, 0, 420 (LPWSTR)&mp->array[i]->datastart, -1, 421 NULL, 0, NULL, NULL); 422 LPSTR itemA = Alloc(len); 423 INT cmp; 424 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1, 425 itemA, len, NULL, NULL); 426 427 cmp = mp->extview.u.string_cmpfn((LPWSTR)dataA, (LPWSTR)itemA); 428 Free(itemA); 429 if(!cmp) 430 break; 431 } 432 } 433 } 434 Free(dataA); 435 if (i < mp->cursize) 436 ret = i; 437 else 438 ret = -1; 439 if (lpRegNum && (ret != -1)) 440 *lpRegNum = 'a' + i; 441 442 TRACE("(%p, %p, %d, %p) returning %d\n", 443 hList, lpData, cbData, lpRegNum, ret); 444 445 return ret; 446 } 447 448 449 /************************************************************************** 450 * AddMRUData [COMCTL32.167] 451 * 452 * Add item to MRU binary list. If item already exists in list then it is 453 * simply moved up to the top of the list and not added again. If list is 454 * full then the least recently used item is removed to make room. 455 * 456 * PARAMS 457 * hList [I] Handle to list. 458 * lpData [I] ptr to data to add. 459 * cbData [I] no. of bytes of data. 460 * 461 * RETURNS 462 * No. corresponding to registry name where value is stored 'a' -> 0 etc. 463 * -1 on error. 464 */ 465 INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData) 466 { 467 LPWINEMRULIST mp = hList; 468 LPWINEMRUITEM witem; 469 INT i, replace; 470 471 if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) { 472 /* Item exists, just move it to the front */ 473 LPWSTR pos = strchrW(mp->realMRU, replace + 'a'); 474 while (pos > mp->realMRU) 475 { 476 pos[0] = pos[-1]; 477 pos--; 478 } 479 } 480 else { 481 /* either add a new entry or replace oldest */ 482 if (mp->cursize < mp->extview.uMax) { 483 /* Add in a new item */ 484 replace = mp->cursize; 485 mp->cursize++; 486 } 487 else { 488 /* get the oldest entry and replace data */ 489 replace = mp->realMRU[mp->cursize - 1] - 'a'; 490 Free(mp->array[replace]); 491 } 492 493 /* Allocate space for new item and move in the data */ 494 mp->array[replace] = witem = Alloc(cbData + sizeof(WINEMRUITEM)); 495 witem->itemFlag |= WMRUIF_CHANGED; 496 witem->size = cbData; 497 memcpy( &witem->datastart, lpData, cbData); 498 499 /* now rotate MRU list */ 500 for(i=mp->cursize-1; i>=1; i--) 501 mp->realMRU[i] = mp->realMRU[i-1]; 502 } 503 504 /* The new item gets the front spot */ 505 mp->wineFlags |= WMRUF_CHANGED; 506 mp->realMRU[0] = replace + 'a'; 507 508 TRACE("(%p, %p, %d) adding data, /%c/ now most current\n", 509 hList, lpData, cbData, replace+'a'); 510 511 if (!(mp->extview.fFlags & MRU_CACHEWRITE)) { 512 /* save changed stuff right now */ 513 MRU_SaveChanged( mp ); 514 } 515 516 return replace; 517 } 518 519 /************************************************************************** 520 * AddMRUStringW [COMCTL32.401] 521 * 522 * Add an item to an MRU string list. 523 * 524 * PARAMS 525 * hList [I] Handle to list. 526 * lpszString [I] The string to add. 527 * 528 * RETURNS 529 * Success: The number corresponding to the registry name where the string 530 * has been stored (0 maps to 'a', 1 to 'b' and so on). 531 * Failure: -1, if hList is NULL or memory allocation fails. If lpszString 532 * is invalid, the function returns 0, and GetLastError() returns 533 * ERROR_INVALID_PARAMETER. The last error value is set only in 534 * this case. 535 * 536 * NOTES 537 * -If lpszString exists in the list already, it is moved to the top of the 538 * MRU list (it is not duplicated). 539 * -If the list is full the least recently used list entry is replaced with 540 * lpszString. 541 * -If this function returns 0 you should check the last error value to 542 * ensure the call really succeeded. 543 */ 544 INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString) 545 { 546 TRACE("(%p,%s)\n", hList, debugstr_w(lpszString)); 547 548 if (!hList) 549 return -1; 550 551 if (!lpszString || IsBadStringPtrW(lpszString, -1)) 552 { 553 SetLastError(ERROR_INVALID_PARAMETER); 554 return 0; 555 } 556 557 return AddMRUData(hList, lpszString, 558 (strlenW(lpszString) + 1) * sizeof(WCHAR)); 559 } 560 561 /************************************************************************** 562 * AddMRUStringA [COMCTL32.153] 563 * 564 * See AddMRUStringW. 565 */ 566 INT WINAPI AddMRUStringA(HANDLE hList, LPCSTR lpszString) 567 { 568 DWORD len; 569 LPWSTR stringW; 570 INT ret; 571 572 TRACE("(%p,%s)\n", hList, debugstr_a(lpszString)); 573 574 if (!hList) 575 return -1; 576 577 if (IsBadStringPtrA(lpszString, -1)) 578 { 579 SetLastError(ERROR_INVALID_PARAMETER); 580 return 0; 581 } 582 583 len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0) * sizeof(WCHAR); 584 stringW = Alloc(len); 585 if (!stringW) 586 return -1; 587 588 MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len/sizeof(WCHAR)); 589 ret = AddMRUData(hList, stringW, len); 590 Free(stringW); 591 return ret; 592 } 593 594 /************************************************************************** 595 * DelMRUString [COMCTL32.156] 596 * 597 * Removes item from either string or binary list (despite its name) 598 * 599 * PARAMS 600 * hList [I] list handle 601 * nItemPos [I] item position to remove 0 -> MRU 602 * 603 * RETURNS 604 * TRUE if successful, FALSE if nItemPos is out of range. 605 */ 606 BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos) 607 { 608 FIXME("(%p, %d): stub\n", hList, nItemPos); 609 return TRUE; 610 } 611 612 /************************************************************************** 613 * FindMRUStringW [COMCTL32.402] 614 * 615 * See FindMRUStringA. 616 */ 617 INT WINAPI FindMRUStringW (HANDLE hList, LPCWSTR lpszString, LPINT lpRegNum) 618 { 619 return FindMRUData(hList, lpszString, 620 (lstrlenW(lpszString) + 1) * sizeof(WCHAR), lpRegNum); 621 } 622 623 /************************************************************************** 624 * FindMRUStringA [COMCTL32.155] 625 * 626 * Searches string list for item that matches lpszString. 627 * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value 628 * corresponding to item's reg. name will be stored in it ('a' -> 0). 629 * 630 * PARAMS 631 * hList [I] list handle 632 * lpszString [I] string to find 633 * lpRegNum [O] position in registry (maybe NULL) 634 * 635 * RETURNS 636 * Position in list 0 -> MRU. -1 if item not found. 637 */ 638 INT WINAPI FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum) 639 { 640 DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0); 641 LPWSTR stringW = Alloc(len * sizeof(WCHAR)); 642 INT ret; 643 644 MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len); 645 ret = FindMRUData(hList, stringW, len * sizeof(WCHAR), lpRegNum); 646 Free(stringW); 647 return ret; 648 } 649 650 /************************************************************************* 651 * create_mru_list (internal) 652 */ 653 static HANDLE create_mru_list(LPWINEMRULIST mp) 654 { 655 UINT i, err; 656 HKEY newkey; 657 DWORD datasize, dwdisp; 658 WCHAR realname[2]; 659 LPWINEMRUITEM witem; 660 DWORD type; 661 662 /* get space to save indices that will turn into names 663 * but in order of most to least recently used 664 */ 665 mp->realMRU = Alloc((mp->extview.uMax + 2) * sizeof(WCHAR)); 666 667 /* get space to save pointers to actual data in order of 668 * 'a' to 'z' (0 to n). 669 */ 670 mp->array = Alloc(mp->extview.uMax * sizeof(LPVOID)); 671 672 /* open the sub key */ 673 if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey, 674 0, 675 NULL, 676 REG_OPTION_NON_VOLATILE, 677 KEY_READ | KEY_WRITE, 678 0, 679 &newkey, 680 &dwdisp))) { 681 /* error - what to do ??? */ 682 ERR("(%u %u %x %p %s %p): Could not open key, error=%d\n", 683 mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags, 684 mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey), 685 mp->extview.u.string_cmpfn, err); 686 return 0; 687 } 688 689 /* get values from key 'MRUList' */ 690 if (newkey) { 691 datasize = (mp->extview.uMax + 1) * sizeof(WCHAR); 692 if (RegQueryValueExW( newkey, strMRUList, 0, &type, 693 (LPBYTE)mp->realMRU, &datasize)) { 694 /* not present - set size to 1 (will become 0 later) */ 695 datasize = 1; 696 *mp->realMRU = 0; 697 } 698 else 699 datasize /= sizeof(WCHAR); 700 701 TRACE("MRU list = %s, datasize = %d\n", debugstr_w(mp->realMRU), datasize); 702 703 mp->cursize = datasize - 1; 704 /* datasize now has number of items in the MRUList */ 705 706 /* get actual values for each entry */ 707 realname[1] = 0; 708 for(i=0; i<mp->cursize; i++) { 709 realname[0] = 'a' + i; 710 if(RegQueryValueExW( newkey, realname, 0, &type, 0, &datasize)) { 711 /* not present - what to do ??? */ 712 ERR("Key %s not found 1\n", debugstr_w(realname)); 713 } 714 mp->array[i] = witem = Alloc(datasize + sizeof(WINEMRUITEM)); 715 witem->size = datasize; 716 if(RegQueryValueExW( newkey, realname, 0, &type, 717 &witem->datastart, &datasize)) { 718 /* not present - what to do ??? */ 719 ERR("Key %s not found 2\n", debugstr_w(realname)); 720 } 721 } 722 RegCloseKey( newkey ); 723 } 724 else 725 mp->cursize = 0; 726 727 TRACE("(%u %u %x %p %s %p): Current Size = %d\n", 728 mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags, 729 mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey), 730 mp->extview.u.string_cmpfn, mp->cursize); 731 return mp; 732 } 733 734 /************************************************************************** 735 * CreateMRUListLazyW [COMCTL32.404] 736 * 737 * See CreateMRUListLazyA. 738 */ 739 HANDLE WINAPI CreateMRUListLazyW (const MRUINFOW *infoW, DWORD dwParam2, 740 DWORD dwParam3, DWORD dwParam4) 741 { 742 LPWINEMRULIST mp; 743 744 /* Native does not check for a NULL lpcml */ 745 if (!infoW->hKey || IsBadStringPtrW(infoW->lpszSubKey, -1)) 746 return NULL; 747 748 mp = Alloc(sizeof(WINEMRULIST)); 749 memcpy(&mp->extview, infoW, sizeof(MRUINFOW)); 750 mp->extview.lpszSubKey = Alloc((strlenW(infoW->lpszSubKey) + 1) * sizeof(WCHAR)); 751 strcpyW(mp->extview.lpszSubKey, infoW->lpszSubKey); 752 mp->isUnicode = TRUE; 753 754 return create_mru_list(mp); 755 } 756 757 /************************************************************************** 758 * CreateMRUListLazyA [COMCTL32.157] 759 * 760 * Creates a most-recently-used list. 761 * 762 * PARAMS 763 * lpcml [I] ptr to CREATEMRULIST structure. 764 * dwParam2 [I] Unknown 765 * dwParam3 [I] Unknown 766 * dwParam4 [I] Unknown 767 * 768 * RETURNS 769 * Handle to MRU list. 770 */ 771 HANDLE WINAPI CreateMRUListLazyA (const MRUINFOA *lpcml, DWORD dwParam2, 772 DWORD dwParam3, DWORD dwParam4) 773 { 774 LPWINEMRULIST mp; 775 DWORD len; 776 777 /* Native does not check for a NULL lpcml */ 778 779 if (!lpcml->hKey || IsBadStringPtrA(lpcml->lpszSubKey, -1)) 780 return 0; 781 782 mp = Alloc(sizeof(WINEMRULIST)); 783 memcpy(&mp->extview, lpcml, sizeof(MRUINFOA)); 784 len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0); 785 mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR)); 786 MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, 787 mp->extview.lpszSubKey, len); 788 mp->isUnicode = FALSE; 789 return create_mru_list(mp); 790 } 791 792 /************************************************************************** 793 * CreateMRUListW [COMCTL32.400] 794 * 795 * See CreateMRUListA. 796 */ 797 HANDLE WINAPI CreateMRUListW (const MRUINFOW *infoW) 798 { 799 return CreateMRUListLazyW(infoW, 0, 0, 0); 800 } 801 802 /************************************************************************** 803 * CreateMRUListA [COMCTL32.151] 804 * 805 * Creates a most-recently-used list. 806 * 807 * PARAMS 808 * lpcml [I] ptr to CREATEMRULIST structure. 809 * 810 * RETURNS 811 * Handle to MRU list. 812 */ 813 HANDLE WINAPI CreateMRUListA (const MRUINFOA *lpcml) 814 { 815 return CreateMRUListLazyA (lpcml, 0, 0, 0); 816 } 817 818 819 /************************************************************************** 820 * EnumMRUListW [COMCTL32.403] 821 * 822 * Enumerate item in a most-recently-used list 823 * 824 * PARAMS 825 * hList [I] list handle 826 * nItemPos [I] item position to enumerate 827 * lpBuffer [O] buffer to receive item 828 * nBufferSize [I] size of buffer 829 * 830 * RETURNS 831 * For binary lists specifies how many bytes were copied to buffer, for 832 * string lists specifies full length of string. Enumerating past the end 833 * of list returns -1. 834 * If lpBuffer == NULL or nItemPos is -ve return value is no. of items in 835 * the list. 836 */ 837 INT WINAPI EnumMRUListW (HANDLE hList, INT nItemPos, LPVOID lpBuffer, 838 DWORD nBufferSize) 839 { 840 const WINEMRULIST *mp = hList; 841 const WINEMRUITEM *witem; 842 INT desired, datasize; 843 844 if (!mp) return -1; 845 if ((nItemPos < 0) || !lpBuffer) return mp->cursize; 846 if (nItemPos >= mp->cursize) return -1; 847 desired = mp->realMRU[nItemPos]; 848 desired -= 'a'; 849 TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired); 850 witem = mp->array[desired]; 851 datasize = min( witem->size, nBufferSize ); 852 memcpy( lpBuffer, &witem->datastart, datasize); 853 TRACE("(%p, %d, %p, %d): returning len=%d\n", 854 hList, nItemPos, lpBuffer, nBufferSize, datasize); 855 return datasize; 856 } 857 858 /************************************************************************** 859 * EnumMRUListA [COMCTL32.154] 860 * 861 * See EnumMRUListW. 862 */ 863 INT WINAPI EnumMRUListA (HANDLE hList, INT nItemPos, LPVOID lpBuffer, 864 DWORD nBufferSize) 865 { 866 const WINEMRULIST *mp = hList; 867 LPWINEMRUITEM witem; 868 INT desired, datasize; 869 DWORD lenA; 870 871 if (!mp) return -1; 872 if ((nItemPos < 0) || !lpBuffer) return mp->cursize; 873 if (nItemPos >= mp->cursize) return -1; 874 desired = mp->realMRU[nItemPos]; 875 desired -= 'a'; 876 TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired); 877 witem = mp->array[desired]; 878 if(mp->extview.fFlags & MRU_BINARY) { 879 datasize = min( witem->size, nBufferSize ); 880 memcpy( lpBuffer, &witem->datastart, datasize); 881 } else { 882 lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1, 883 NULL, 0, NULL, NULL); 884 datasize = min( lenA, nBufferSize ); 885 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1, 886 lpBuffer, datasize, NULL, NULL); 887 ((char *)lpBuffer)[ datasize - 1 ] = '\0'; 888 datasize = lenA - 1; 889 } 890 TRACE("(%p, %d, %p, %d): returning len=%d\n", 891 hList, nItemPos, lpBuffer, nBufferSize, datasize); 892 return datasize; 893 } 894 895 /************************************************************************** 896 * Str_GetPtrWtoA [internal] 897 * 898 * Converts a unicode string into a multi byte string 899 * 900 * PARAMS 901 * lpSrc [I] Pointer to the unicode source string 902 * lpDest [O] Pointer to caller supplied storage for the multi byte string 903 * nMaxLen [I] Size, in bytes, of the destination buffer 904 * 905 * RETURNS 906 * Length, in bytes, of the converted string. 907 */ 908 909 INT Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen) 910 { 911 INT len; 912 913 TRACE("(%s %p %d)\n", debugstr_w(lpSrc), lpDest, nMaxLen); 914 915 if (!lpDest && lpSrc) 916 return WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL); 917 918 if (nMaxLen == 0) 919 return 0; 920 921 if (lpSrc == NULL) { 922 lpDest[0] = '\0'; 923 return 0; 924 } 925 926 len = WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL); 927 if (len >= nMaxLen) 928 len = nMaxLen - 1; 929 930 WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, lpDest, len, NULL, NULL); 931 lpDest[len] = '\0'; 932 933 return len; 934 } 935 936 /************************************************************************** 937 * Str_GetPtrAtoW [internal] 938 * 939 * Converts a multibyte string into a unicode string 940 * 941 * PARAMS 942 * lpSrc [I] Pointer to the multibyte source string 943 * lpDest [O] Pointer to caller supplied storage for the unicode string 944 * nMaxLen [I] Size, in characters, of the destination buffer 945 * 946 * RETURNS 947 * Length, in characters, of the converted string. 948 */ 949 950 INT Str_GetPtrAtoW (LPCSTR lpSrc, LPWSTR lpDest, INT nMaxLen) 951 { 952 INT len; 953 954 TRACE("(%s %p %d)\n", debugstr_a(lpSrc), lpDest, nMaxLen); 955 956 if (!lpDest && lpSrc) 957 return MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, 0, 0); 958 959 if (nMaxLen == 0) 960 return 0; 961 962 if (lpSrc == NULL) { 963 lpDest[0] = '\0'; 964 return 0; 965 } 966 967 len = MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, 0, 0); 968 if (len >= nMaxLen) 969 len = nMaxLen - 1; 970 971 MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, lpDest, len); 972 lpDest[len] = '\0'; 973 974 return len; 975 } 976 977 978 /************************************************************************** 979 * Str_SetPtrAtoW [internal] 980 * 981 * Converts a multi byte string to a unicode string. 982 * If the pointer to the destination buffer is NULL a buffer is allocated. 983 * If the destination buffer is too small to keep the converted multi byte 984 * string the destination buffer is reallocated. If the source pointer is 985 * NULL, the destination buffer is freed. 986 * 987 * PARAMS 988 * lppDest [I/O] pointer to a pointer to the destination buffer 989 * lpSrc [I] pointer to a multi byte string 990 * 991 * RETURNS 992 * TRUE: conversion successful 993 * FALSE: error 994 */ 995 BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc) 996 { 997 TRACE("(%p %s)\n", lppDest, lpSrc); 998 999 if (lpSrc) { 1000 INT len = MultiByteToWideChar(CP_ACP,0,lpSrc,-1,NULL,0); 1001 LPWSTR ptr = ReAlloc (*lppDest, len*sizeof(WCHAR)); 1002 1003 if (!ptr) 1004 return FALSE; 1005 MultiByteToWideChar(CP_ACP,0,lpSrc,-1,ptr,len); 1006 *lppDest = ptr; 1007 } 1008 else { 1009 Free (*lppDest); 1010 *lppDest = NULL; 1011 } 1012 1013 return TRUE; 1014 } 1015 1016 /************************************************************************** 1017 * Str_SetPtrWtoA [internal] 1018 * 1019 * Converts a unicode string to a multi byte string. 1020 * If the pointer to the destination buffer is NULL a buffer is allocated. 1021 * If the destination buffer is too small to keep the converted wide 1022 * string the destination buffer is reallocated. If the source pointer is 1023 * NULL, the destination buffer is freed. 1024 * 1025 * PARAMS 1026 * lppDest [I/O] pointer to a pointer to the destination buffer 1027 * lpSrc [I] pointer to a wide string 1028 * 1029 * RETURNS 1030 * TRUE: conversion successful 1031 * FALSE: error 1032 */ 1033 BOOL Str_SetPtrWtoA (LPSTR *lppDest, LPCWSTR lpSrc) 1034 { 1035 TRACE("(%p %s)\n", lppDest, debugstr_w(lpSrc)); 1036 1037 if (lpSrc) { 1038 INT len = WideCharToMultiByte(CP_ACP,0,lpSrc,-1,NULL,0,NULL,FALSE); 1039 LPSTR ptr = ReAlloc (*lppDest, len*sizeof(CHAR)); 1040 1041 if (!ptr) 1042 return FALSE; 1043 WideCharToMultiByte(CP_ACP,0,lpSrc,-1,ptr,len,NULL,FALSE); 1044 *lppDest = ptr; 1045 } 1046 else { 1047 Free (*lppDest); 1048 *lppDest = NULL; 1049 } 1050 1051 return TRUE; 1052 } 1053 1054 1055 /************************************************************************** 1056 * Notification functions 1057 */ 1058 1059 typedef struct tagNOTIFYDATA 1060 { 1061 HWND hwndFrom; 1062 HWND hwndTo; 1063 DWORD dwParam3; 1064 DWORD dwParam4; 1065 DWORD dwParam5; 1066 DWORD dwParam6; 1067 } NOTIFYDATA, *LPNOTIFYDATA; 1068 1069 1070 /************************************************************************** 1071 * DoNotify [Internal] 1072 */ 1073 1074 static LRESULT DoNotify (const NOTIFYDATA *lpNotify, UINT uCode, LPNMHDR lpHdr) 1075 { 1076 NMHDR nmhdr; 1077 LPNMHDR lpNmh = NULL; 1078 UINT idFrom = 0; 1079 1080 TRACE("(%p %p %d %p 0x%08x)\n", 1081 lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr, 1082 lpNotify->dwParam5); 1083 1084 if (!lpNotify->hwndTo) 1085 return 0; 1086 1087 if (lpNotify->hwndFrom == (HWND)-1) { 1088 lpNmh = lpHdr; 1089 idFrom = lpHdr->idFrom; 1090 } 1091 else { 1092 if (lpNotify->hwndFrom) 1093 idFrom = GetDlgCtrlID (lpNotify->hwndFrom); 1094 1095 lpNmh = (lpHdr) ? lpHdr : &nmhdr; 1096 1097 lpNmh->hwndFrom = lpNotify->hwndFrom; 1098 lpNmh->idFrom = idFrom; 1099 lpNmh->code = uCode; 1100 } 1101 1102 return SendMessageW (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh); 1103 } 1104 1105 1106 /************************************************************************** 1107 * SendNotify [COMCTL32.341] 1108 * 1109 * Sends a WM_NOTIFY message to the specified window. 1110 * 1111 * PARAMS 1112 * hwndTo [I] Window to receive the message 1113 * hwndFrom [I] Window that the message is from (see notes) 1114 * uCode [I] Notification code 1115 * lpHdr [I] The NMHDR and any additional information to send or NULL 1116 * 1117 * RETURNS 1118 * Success: return value from notification 1119 * Failure: 0 1120 * 1121 * NOTES 1122 * If hwndFrom is -1 then the identifier of the control sending the 1123 * message is taken from the NMHDR structure. 1124 * If hwndFrom is not -1 then lpHdr can be NULL. 1125 */ 1126 LRESULT WINAPI SendNotify (HWND hwndTo, HWND hwndFrom, UINT uCode, LPNMHDR lpHdr) 1127 { 1128 NOTIFYDATA notify; 1129 1130 TRACE("(%p %p %d %p)\n", 1131 hwndTo, hwndFrom, uCode, lpHdr); 1132 1133 notify.hwndFrom = hwndFrom; 1134 notify.hwndTo = hwndTo; 1135 notify.dwParam5 = 0; 1136 notify.dwParam6 = 0; 1137 1138 return DoNotify (¬ify, uCode, lpHdr); 1139 } 1140 1141 1142 /************************************************************************** 1143 * SendNotifyEx [COMCTL32.342] 1144 * 1145 * Sends a WM_NOTIFY message to the specified window. 1146 * 1147 * PARAMS 1148 * hwndFrom [I] Window to receive the message 1149 * hwndTo [I] Window that the message is from 1150 * uCode [I] Notification code 1151 * lpHdr [I] The NMHDR and any additional information to send or NULL 1152 * dwParam5 [I] Unknown 1153 * 1154 * RETURNS 1155 * Success: return value from notification 1156 * Failure: 0 1157 * 1158 * NOTES 1159 * If hwndFrom is -1 then the identifier of the control sending the 1160 * message is taken from the NMHDR structure. 1161 * If hwndFrom is not -1 then lpHdr can be NULL. 1162 */ 1163 LRESULT WINAPI SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode, 1164 LPNMHDR lpHdr, DWORD dwParam5) 1165 { 1166 NOTIFYDATA notify; 1167 HWND hwndNotify; 1168 1169 TRACE("(%p %p %d %p 0x%08x)\n", 1170 hwndFrom, hwndTo, uCode, lpHdr, dwParam5); 1171 1172 hwndNotify = hwndTo; 1173 if (!hwndTo) { 1174 if (IsWindow (hwndFrom)) { 1175 hwndNotify = GetParent (hwndFrom); 1176 if (!hwndNotify) 1177 return 0; 1178 } 1179 } 1180 1181 notify.hwndFrom = hwndFrom; 1182 notify.hwndTo = hwndNotify; 1183 notify.dwParam5 = dwParam5; 1184 notify.dwParam6 = 0; 1185 1186 return DoNotify (¬ify, uCode, lpHdr); 1187 } 1188