libraries.c 17.8 KB
Newer Older
1 2 3 4
/*
 * WineCfg libraries tabsheet
 *
 * Copyright 2004 Robert van Herk
5
 * Copyright 2004 Mike Hearn
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22
 *
 */

23
#include "config.h"
24
#include "wine/port.h"
25

26
#define NONAMELESSUNION
27
#define WIN32_LEAN_AND_MEAN
28 29
#include <windows.h>
#include <commdlg.h>
30
#include <wine/library.h>
31 32
#include <wine/debug.h>
#include <stdio.h>
33
#include <dirent.h>
34
#include <assert.h>
35
#include <stdlib.h>
36 37 38
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
39 40 41 42
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

43 44 45 46 47
#include "winecfg.h"
#include "resource.h"

WINE_DEFAULT_DEBUG_CHANNEL(winecfg);

48 49 50 51 52 53 54 55 56
/* dlls that shouldn't be configured anything other than builtin; list must be sorted*/
static const char * const builtin_only[] =
{
    "advapi32",
    "capi2032",
    "dbghelp",
    "ddraw",
    "gdi32",
    "glu32",
57
    "gphoto2.ds",
58
    "icmp",
59 60
    "iphlpapi",
    "kernel32",
61
    "mountmgr.sys",
62 63
    "mswsock",
    "ntdll",
64
    "ntoskrnl.exe",
65
    "opengl32",
66
    "sane.ds",
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    "twain_32",
    "unicows",
    "user32",
    "vdmdbg",
    "w32skrnl",
    "wined3d",
    "winedos",
    "winemp3.acm",
    "wineps",
    "winmm",
    "wintab32",
    "wnaspi32",
    "wow32",
    "ws2_32",
    "wsock32",
};

84
enum dllmode
85
{
86 87
	BUILTIN_NATIVE,
	NATIVE_BUILTIN,
88
	BUILTIN,
89 90
	NATIVE,
	DISABLE,
91
	UNKNOWN /* Special value indicating an erroneous DLL override mode */
92 93 94 95 96 97 98
};

struct dll
{
        char *name;
        enum dllmode mode;
};
99

100 101
static const WCHAR emptyW[1];

102 103
/* Convert a registry string to a dllmode */
static enum dllmode string_to_mode(char *in)
104
{
105
    int i, j, len;
106
    char *out;
107
    enum dllmode res;
108

109 110
    len = strlen(in);
    out = HeapAlloc(GetProcessHeap(), 0, len);
111 112

    /* remove the spaces */
113 114 115
    for (i = j = 0; i <= len; ++i) {
        if (in[i] != ' ') {
            out[j++] = in[i];
116
        }
117
    }
118 119

    /* parse the string */
120 121 122 123 124 125 126 127 128 129
    res = UNKNOWN;
    if (strcmp(out, "builtin,native") == 0) res = BUILTIN_NATIVE;
    if (strcmp(out, "native,builtin") == 0) res = NATIVE_BUILTIN;
    if (strcmp(out, "builtin") == 0) res = BUILTIN;
    if (strcmp(out, "native") == 0) res = NATIVE;
    if (strcmp(out, "") == 0) res = DISABLE;

    HeapFree(GetProcessHeap(), 0, out);
    return res;
}
130

131
/* Convert a dllmode to a registry string. */
132
static const char* mode_to_string(enum dllmode mode)
133
{
134 135 136 137 138 139 140
    switch( mode )
    {
        case NATIVE: return "native";
        case BUILTIN: return "builtin";
        case NATIVE_BUILTIN: return "native,builtin";
        case BUILTIN_NATIVE: return "builtin,native";
        case DISABLE: return "";
141
        default: return "";
142
    }
143 144
}

145
/* Convert a dllmode to a pretty string for display. TODO: use translations. */
146
static const char* mode_to_label(enum dllmode mode)
147
{
148 149 150 151 152 153 154 155 156 157
    static char buffer[256];
    UINT id = 0;

    switch( mode )
    {
    case NATIVE: id = IDS_DLL_NATIVE; break;
    case BUILTIN: id = IDS_DLL_BUILTIN; break;
    case NATIVE_BUILTIN: id = IDS_DLL_NATIVE_BUILTIN; break;
    case BUILTIN_NATIVE: id = IDS_DLL_BUILTIN_NATIVE; break;
    case DISABLE: id = IDS_DLL_DISABLED; break;
158
    default: return "??";
159 160 161
    }
    if (!LoadStringA( GetModuleHandleA(NULL), id, buffer, sizeof(buffer) )) buffer[0] = 0;
    return buffer;
162
}
163

164 165 166
/* Convert a control id (IDC_ constant) to a dllmode */
static enum dllmode id_to_mode(DWORD id)
{
167 168 169 170 171 172 173 174 175
    switch( id )
    {
        case IDC_RAD_BUILTIN: return BUILTIN;
        case IDC_RAD_NATIVE: return NATIVE;
        case IDC_RAD_NATIVE_BUILTIN: return NATIVE_BUILTIN;
        case IDC_RAD_BUILTIN_NATIVE: return BUILTIN_NATIVE;
        case IDC_RAD_DISABLE: return DISABLE;
        default: assert( FALSE ); return 0; /* should not be reached  */
    }
176 177 178 179 180
}

/* Convert a dllmode to a control id (IDC_ constant) */
static DWORD mode_to_id(enum dllmode mode)
{
181 182 183 184 185 186 187
    switch( mode )
    {
        case BUILTIN: return IDC_RAD_BUILTIN;
        case NATIVE: return IDC_RAD_NATIVE;
        case NATIVE_BUILTIN: return IDC_RAD_NATIVE_BUILTIN;
        case BUILTIN_NATIVE: return IDC_RAD_BUILTIN_NATIVE;
        case DISABLE: return IDC_RAD_DISABLE;
188
        default: return IDC_RAD_BUILTIN_NATIVE;
189
    }
190 191
}

192 193 194 195 196 197 198 199 200 201 202
/* helper for is_builtin_only */
static int compare_dll( const void *ptr1, const void *ptr2 )
{
    const char * const *name1 = ptr1;
    const char * const *name2 = ptr2;
    return strcmp( *name1, *name2 );
}

/* check if dll is recommended as builtin only */
static inline int is_builtin_only( const char *name )
{
203 204 205 206 207 208 209 210 211
    const char *ext = strrchr( name, '.' );

    if (ext)
    {
        if (!strcmp( ext, ".vxd" ) ||
            !strcmp( ext, ".drv" ) ||
            !strcmp( ext, ".tlb" ))
            return TRUE;
    }
212 213 214 215
    return bsearch( &name, builtin_only, sizeof(builtin_only)/sizeof(builtin_only[0]),
                    sizeof(builtin_only[0]), compare_dll ) != NULL;
}

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
/* check if dll should be offered in the drop-down list */
static int show_dll_in_list( const char *name )
{
    const char *ext = strrchr( name, '.' );

    if (ext)
    {
        /* skip 16-bit dlls */
        if (strlen(ext) > 2 && !strcmp( ext + strlen(ext) - 2, "16" )) return FALSE;
        /* skip exes */
        if (!strcmp( ext, ".exe" )) return FALSE;
    }
    /* skip dlls that should always be builtin */
    return !is_builtin_only( name );
}

232
static void set_controls_from_selection(HWND dialog)
233
{
234
    /* FIXME: display/update some information about the selected dll (purpose, recommended load order) maybe? */
235 236
}

237
static void clear_settings(HWND dialog)
238
{
239
    int count = SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0);
240
    int i;
241

242 243 244 245
    WINE_TRACE("count=%d\n", count);
    
    for (i = 0; i < count; i++)
    {
246 247 248
        struct dll *dll = (struct dll *) SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, 0, 0);

        SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_DELETESTRING, 0, 0);
249 250 251
        HeapFree(GetProcessHeap(), 0, dll->name);
        HeapFree(GetProcessHeap(), 0, dll);
    }
252 253
}

254
/* load the list of available libraries from a given dir */
255
static void load_library_list_from_dir( HWND dialog, const char *dir_path, int check_subdirs )
256
{
257
    char *buffer = NULL, name[256];
258 259 260 261 262
    struct dirent *de;
    DIR *dir = opendir( dir_path );

    if (!dir) return;

263 264 265
    if (check_subdirs)
        buffer = HeapAlloc( GetProcessHeap(), 0, strlen(dir_path) + 2 * sizeof(name) + 10 );

266 267 268
    while ((de = readdir( dir )))
    {
        size_t len = strlen(de->d_name);
269
        if (len > sizeof(name)) continue;
270
        if (len > 3 && !strcmp( de->d_name + len - 3, ".so"))
271
        {
272 273
            len -= 3;
            if (len > 4 && !strcmp( de->d_name + len - 4, ".dll.so")) len -= 4;
274 275
            memcpy( name, de->d_name, len );
            name[len] = 0;
276
            if (!show_dll_in_list( name )) continue;
277 278 279 280 281
            SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)name );
        }
        else if (check_subdirs)
        {
            struct stat st;
282
            if (!show_dll_in_list( de->d_name )) continue;
283 284
            sprintf( buffer, "%s/%s/%s.dll.so", dir_path, de->d_name, de->d_name );
            if (!stat( buffer, &st ))
285 286 287 288 289 290 291
            {
                SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)de->d_name );
                continue;
            }
            sprintf( buffer, "%s/%s/%s.so", dir_path, de->d_name, de->d_name );
            if (!stat( buffer, &st ))
            {
292
                SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_ADDSTRING, 0, (LPARAM)de->d_name );
293 294
                continue;
            }
295
        }
296 297
    }
    closedir( dir );
298
    HeapFree( GetProcessHeap(), 0, buffer );
299 300 301 302 303 304
}

/* load the list of available libraries */
static void load_library_list( HWND dialog )
{
    unsigned int i = 0;
305
    const char *path, *build_dir = wine_get_build_dir();
306
    char item1[256], item2[256];
307
    HCURSOR old_cursor = SetCursor( LoadCursorW(0, (LPWSTR)IDC_WAIT) );
308 309 310 311 312 313 314 315 316

    if (build_dir)
    {
        char *dir = HeapAlloc( GetProcessHeap(), 0, strlen(build_dir) + sizeof("/dlls") );
        strcpy( dir, build_dir );
        strcat( dir, "/dlls" );
        load_library_list_from_dir( dialog, dir, TRUE );
        HeapFree( GetProcessHeap(), 0, dir );
    }
317 318

    while ((path = wine_dll_enum_load_path( i++ )))
319
        load_library_list_from_dir( dialog, path, FALSE );
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

    /* get rid of duplicate entries */

    SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_GETLBTEXT, 0, (LPARAM)item1 );
    i = 1;
    while (SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_GETLBTEXT, i, (LPARAM)item2 ) >= 0)
    {
        if (!strcmp( item1, item2 ))
        {
            SendDlgItemMessageA( dialog, IDC_DLLCOMBO, CB_DELETESTRING, i, 0 );
        }
        else
        {
            strcpy( item1, item2 );
            i++;
        }
    }
337
    SetCursor( old_cursor );
338 339
}

340
static void load_library_settings(HWND dialog)
341
{
342
    char **overrides = enumerate_values(config_key, keypath("DllOverrides"));
343 344
    char **p;
    int sel, count = 0;
345

346
    sel = SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
347

348
    WINE_TRACE("sel=%d\n", sel);
349

350 351 352 353 354
    clear_settings(dialog);
    
    if (!overrides || *overrides == NULL)
    {
        set_controls_from_selection(dialog);
355
        disable(IDC_DLLS_EDITDLL);
356 357 358 359
        disable(IDC_DLLS_REMOVEDLL);
        HeapFree(GetProcessHeap(), 0, overrides);
        return;
    }
360

361
    enable(IDC_DLLS_EDITDLL);
362 363 364 365 366
    enable(IDC_DLLS_REMOVEDLL);
    
    for (p = overrides; *p != NULL; p++)
    {
        int index;
367 368
        char *str, *value;
        const char *label;
369
        struct dll *dll;
370

371
        value = get_reg_key(config_key, keypath("DllOverrides"), *p, NULL);
372

373
        label = mode_to_label(string_to_mode(value));
374 375 376 377 378 379
        
        str = HeapAlloc(GetProcessHeap(), 0, strlen(*p) + 2 + strlen(label) + 2);
        strcpy(str, *p);
        strcat(str, " (");
        strcat(str, label);
        strcat(str, ")");
380

381 382
        dll = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dll));
        dll->name = *p;
383
        dll->mode = string_to_mode(value);
384

385 386
        index = SendDlgItemMessageA(dialog, IDC_DLLS_LIST, LB_ADDSTRING, (WPARAM) -1, (LPARAM) str);
        SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_SETITEMDATA, index, (LPARAM) dll);
387

388
        HeapFree(GetProcessHeap(), 0, str);
389

390 391
        count++;
    }
392

393
    HeapFree(GetProcessHeap(), 0, overrides);
394

395 396 397
    /* restore the previous selection, if possible  */
    if (sel >= count - 1) sel = count - 1;
    else if (sel == -1) sel = 0;
398 399

    SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_SETCURSEL, sel, 0);
400

401
    set_controls_from_selection(dialog);
402 403
}

404 405
/* Called when the application is initialized (cannot reinit!)  */
static void init_libsheet(HWND dialog)
406
{
407
    /* clear the add dll controls  */
408
    SendDlgItemMessageW(dialog, IDC_DLLCOMBO, WM_SETTEXT, 1, (LPARAM)emptyW);
409
    load_library_list( dialog );
410
    disable(IDC_DLLS_ADDDLL);
411 412
}

413
static void on_add_combo_change(HWND dialog)
414
{
415
    WCHAR buffer[1024];
416
    int sel, len;
417

418
    SendDlgItemMessageW(dialog, IDC_DLLCOMBO, WM_GETTEXT, sizeof(buffer)/sizeof(WCHAR), (LPARAM) buffer);
419
    /* if lib was chosen from combobox, we receive an empty buffer, check manually */
420 421
    sel=SendDlgItemMessageW(dialog, IDC_DLLCOMBO, CB_GETCURSEL, 0, 0);
    len=SendDlgItemMessageW(dialog, IDC_DLLCOMBO, CB_GETLBTEXTLEN, sel, 0);
422

423
    if (buffer[0] || len>0)
424 425 426
        enable(IDC_DLLS_ADDDLL)
    else
        disable(IDC_DLLS_ADDDLL);
427 428
}

429
static void set_dllmode(HWND dialog, DWORD id)
430
{
431 432 433
    enum dllmode mode;
    struct dll *dll;
    int sel;
434
    const char *str;
435

436
    mode = id_to_mode(id);
437

438
    sel = SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
439
    if (sel == -1) return;
440 441

    dll = (struct dll *) SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, sel, 0);
442

443
    str = mode_to_string(mode);
444
    WINE_TRACE("Setting %s to %s\n", dll->name, str);
445 446

    SendMessageW(GetParent(dialog), PSM_CHANGED, 0, 0);
447
    set_reg_key(config_key, keypath("DllOverrides"), dll->name, str);
448 449

    load_library_settings(dialog);  /* ... and refresh  */
450 451
}

452
static void on_add_click(HWND dialog)
453
{
454 455
    static const char dotDll[] = ".dll";
    char buffer[1024], *ptr;
456 457 458

    ZeroMemory(buffer, sizeof(buffer));

459
    SendDlgItemMessageA(dialog, IDC_DLLCOMBO, WM_GETTEXT, sizeof(buffer), (LPARAM) buffer);
460 461 462 463 464 465 466 467 468
    if (lstrlenA(buffer) >= sizeof(dotDll))
    {
        ptr = buffer + lstrlenA(buffer) - sizeof(dotDll) + 1;
        if (!lstrcmpiA(ptr, dotDll))
        {
            WINE_TRACE("Stripping dll extension\n");
            *ptr = '\0';
        }
    }
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492

    /* check if dll is in the builtin-only list */
    if (!(ptr = strrchr( buffer, '\\' )))
    {
        ptr = buffer;
        if (*ptr == '*') ptr++;
    }
    else ptr++;
    if (is_builtin_only( ptr ))
    {
        MSGBOXPARAMSA params;
        params.cbSize = sizeof(params);
        params.hwndOwner = dialog;
        params.hInstance = GetModuleHandleA( NULL );
        params.lpszText = MAKEINTRESOURCEA( IDS_DLL_WARNING );
        params.lpszCaption = MAKEINTRESOURCEA( IDS_DLL_WARNING_CAPTION );
        params.dwStyle = MB_ICONWARNING | MB_YESNO;
        params.lpszIcon = NULL;
        params.dwContextHelpId = 0;
        params.lpfnMsgBoxCallback = NULL;
        params.dwLanguageId = 0;
        if (MessageBoxIndirectA( &params ) != IDYES) return;
    }

493
    SendDlgItemMessageW(dialog, IDC_DLLCOMBO, WM_SETTEXT, 0, (LPARAM)emptyW);
494 495
    disable(IDC_DLLS_ADDDLL);
    
496
    WINE_TRACE("Adding %s as native, builtin\n", buffer);
497 498

    SendMessageW(GetParent(dialog), PSM_CHANGED, 0, 0);
499
    set_reg_key(config_key, keypath("DllOverrides"), buffer, "native,builtin");
500 501 502

    load_library_settings(dialog);

503
    SendDlgItemMessageA(dialog, IDC_DLLS_LIST, LB_SELECTSTRING, 0, (LPARAM) buffer);
504 505

    set_controls_from_selection(dialog);
506 507
}

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
static INT_PTR CALLBACK loadorder_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static WORD sel;

    switch(uMsg) 
    {
    case WM_INITDIALOG:
        CheckRadioButton(hwndDlg, IDC_RAD_BUILTIN, IDC_RAD_DISABLE, lParam);
        sel = lParam;
        return TRUE;

    case WM_COMMAND:
        if(HIWORD(wParam) != BN_CLICKED) break;
        switch (LOWORD(wParam))
        {
        case IDC_RAD_BUILTIN:
        case IDC_RAD_NATIVE:
        case IDC_RAD_BUILTIN_NATIVE:
        case IDC_RAD_NATIVE_BUILTIN:
        case IDC_RAD_DISABLE:
            sel = LOWORD(wParam);
            return TRUE;
        case IDOK:
            EndDialog(hwndDlg, sel);
            return TRUE;
        case IDCANCEL:
            EndDialog(hwndDlg, wParam);
            return TRUE;
        }
    }
    return FALSE;
}

static void on_edit_click(HWND hwnd)
{
543 544
    INT_PTR ret;
    int index = SendDlgItemMessageW(hwnd, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
545 546 547 548 549 550
    struct dll *dll;
    DWORD id;

    /* if no override is selected the edit button should be disabled... */
    assert(index != -1);

551
    dll = (struct dll *) SendDlgItemMessageW(hwnd, IDC_DLLS_LIST, LB_GETITEMDATA, index, 0);
552
    id = mode_to_id(dll->mode);
553 554 555

    ret = DialogBoxParamW(0, MAKEINTRESOURCEW(IDD_LOADORDER), hwnd, loadorder_dlgproc, id);

556 557 558 559
    if(ret != IDCANCEL)
        set_dllmode(hwnd, ret);
}

560
static void on_remove_click(HWND dialog)
561
{
562
    int sel = SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
563 564 565 566
    struct dll *dll;

    if (sel == LB_ERR) return;

567 568 569 570 571
    dll = (struct dll *) SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETITEMDATA, sel, 0);

    SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_DELETESTRING, sel, 0);

    SendMessageW(GetParent(dialog), PSM_CHANGED, 0, 0);
572
    set_reg_key(config_key, keypath("DllOverrides"), dll->name, NULL);
573 574 575 576

    HeapFree(GetProcessHeap(), 0, dll->name);
    HeapFree(GetProcessHeap(), 0, dll);

577 578
    if (SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0) > 0)
        SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_SETCURSEL, max(sel - 1, 0), 0);
579
    else
580 581
    {
        disable(IDC_DLLS_EDITDLL);
582
        disable(IDC_DLLS_REMOVEDLL);
583
    }
584 585

    set_controls_from_selection(dialog);
586 587 588 589 590 591 592 593
}

INT_PTR CALLBACK
LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_INITDIALOG:
594 595 596 597 598
		init_libsheet(hDlg);
		break;
        case WM_SHOWWINDOW:
                set_window_title(hDlg);
                break;
599 600
	case WM_NOTIFY:
		switch (((LPNMHDR)lParam)->code) {
601 602 603
                case PSN_SETACTIVE:
                    load_library_settings(hDlg);
                    break;
604 605 606 607
		}
		break;
	case WM_COMMAND:
		switch(HIWORD(wParam)) {
608 609 610 611 612 613 614 615 616 617 618

                    /* FIXME: when the user hits enter in the DLL combo box we should invoke the add
                     * add button, rather than the propsheet OK button. But I don't know how to do that!
                     */
                    
                case CBN_EDITCHANGE:
                        if(LOWORD(wParam) == IDC_DLLCOMBO)
                        {
                            on_add_combo_change(hDlg);
                            break;
                        }
619

620 621 622
		case BN_CLICKED:
			switch(LOWORD(wParam)) {
			case IDC_DLLS_ADDDLL:
623 624
                            on_add_click(hDlg);
                            break;
625 626 627
			case IDC_DLLS_EDITDLL:
			    on_edit_click(hDlg);
			    break;
628
			case IDC_DLLS_REMOVEDLL:
629 630
                            on_remove_click(hDlg);
                            break;
631 632
			}
			break;
633
                case LBN_SELCHANGE:
634 635 636 637
                        if(LOWORD(wParam) == IDC_DLLCOMBO)
                            on_add_combo_change(hDlg);
                        else
                            set_controls_from_selection(hDlg);
638
                        break;
639 640 641 642 643 644
		}
		break;
	}

	return 0;
}