libraries.c 18.1 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 WIN32_LEAN_AND_MEAN
27 28
#include <windows.h>
#include <commdlg.h>
29
#include <wine/library.h>
30 31
#include <wine/debug.h>
#include <stdio.h>
32
#include <dirent.h>
33
#include <assert.h>
34
#include <stdlib.h>
35 36 37
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
38 39 40 41
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

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

WINE_DEFAULT_DEBUG_CHANNEL(winecfg);

47 48 49 50 51 52 53 54 55
/* 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",
56
    "gphoto2.ds",
57
    "icmp",
58 59
    "iphlpapi",
    "kernel32",
60
    "mountmgr.sys",
61 62
    "mswsock",
    "ntdll",
63
    "ntoskrnl.exe",
64
    "opengl32",
65
    "sane.ds",
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    "twain_32",
    "unicows",
    "user32",
    "vdmdbg",
    "w32skrnl",
    "wined3d",
    "winedos",
    "winemp3.acm",
    "wineps",
    "winmm",
    "wintab32",
    "wnaspi32",
    "wow32",
    "ws2_32",
    "wsock32",
};

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

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

99 100
static const WCHAR emptyW[1];

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

108
    len = strlen(in);
109
    out = HeapAlloc(GetProcessHeap(), 0, len + 1);
110 111

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

    /* parse the string */
119 120 121 122 123 124 125 126 127 128
    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;
}
129

130
/* Convert a dllmode to a registry string. */
131
static const char* mode_to_string(enum dllmode mode)
132
{
133 134 135 136 137 138 139
    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 "";
140
        default: return "";
141
    }
142 143
}

144
/* Convert a dllmode to a pretty string for display. TODO: use translations. */
145
static const char* mode_to_label(enum dllmode mode)
146
{
147 148 149 150 151 152 153 154 155 156
    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;
157
    default: return "??";
158 159 160
    }
    if (!LoadStringA( GetModuleHandleA(NULL), id, buffer, sizeof(buffer) )) buffer[0] = 0;
    return buffer;
161
}
162

163 164 165
/* Convert a control id (IDC_ constant) to a dllmode */
static enum dllmode id_to_mode(DWORD id)
{
166 167 168 169 170 171 172 173 174
    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  */
    }
175 176 177 178 179
}

/* Convert a dllmode to a control id (IDC_ constant) */
static DWORD mode_to_id(enum dllmode mode)
{
180 181 182 183 184 185 186
    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;
187
        default: return IDC_RAD_BUILTIN_NATIVE;
188
    }
189 190
}

191 192 193 194 195 196 197 198 199
/* 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 */
200
static inline BOOL is_builtin_only( const char *name )
201
{
202 203 204 205 206 207 208 209 210
    const char *ext = strrchr( name, '.' );

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

215
/* check if dll should be offered in the drop-down list */
216
static BOOL show_dll_in_list( const char *name )
217 218 219 220 221 222 223 224 225 226 227 228 229 230
{
    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 );
}

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

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

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

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

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

    if (!dir) return;

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

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

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

    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 );
    }
316 317

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

    /* 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++;
        }
    }
336
    SetCursor( old_cursor );
337 338
}

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

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

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

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

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

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

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

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

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

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

389 390
        count++;
    }
391

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

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

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

400
    set_controls_from_selection(dialog);
401 402
}

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

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

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

422
    if (buffer[0] || len>0)
423
    {
424
        enable(IDC_DLLS_ADDDLL)
425 426
        SendMessageW(GetParent(dialog), DM_SETDEFID, IDC_DLLS_ADDDLL, 0);
    }
427
    else
428
    {
429
        disable(IDC_DLLS_ADDDLL);
430 431
        SendMessageW(GetParent(dialog), DM_SETDEFID, IDOK, 0);
    }
432 433
}

434
static void set_dllmode(HWND dialog, DWORD id)
435
{
436 437 438
    enum dllmode mode;
    struct dll *dll;
    int sel;
439
    const char *str;
440

441
    mode = id_to_mode(id);
442

443
    sel = SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
444
    if (sel == -1) return;
445 446

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

448
    str = mode_to_string(mode);
449
    WINE_TRACE("Setting %s to %s\n", dll->name, str);
450 451

    SendMessageW(GetParent(dialog), PSM_CHANGED, 0, 0);
452
    set_reg_key(config_key, keypath("DllOverrides"), dll->name, str);
453 454

    load_library_settings(dialog);  /* ... and refresh  */
455 456
}

457
static void on_add_click(HWND dialog)
458
{
459 460
    static const char dotDll[] = ".dll";
    char buffer[1024], *ptr;
461 462 463

    ZeroMemory(buffer, sizeof(buffer));

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

    /* 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;
    }

498
    SendDlgItemMessageW(dialog, IDC_DLLCOMBO, WM_SETTEXT, 0, (LPARAM)emptyW);
499
    disable(IDC_DLLS_ADDDLL);
500 501
    SendMessageW(GetParent(dialog), DM_SETDEFID, IDOK, 0);

502
    WINE_TRACE("Adding %s as native, builtin\n", buffer);
503 504

    SendMessageW(GetParent(dialog), PSM_CHANGED, 0, 0);
505
    set_reg_key(config_key, keypath("DllOverrides"), buffer, "native,builtin");
506 507 508

    load_library_settings(dialog);

509
    SendDlgItemMessageA(dialog, IDC_DLLS_LIST, LB_SELECTSTRING, 0, (LPARAM) buffer);
510 511

    set_controls_from_selection(dialog);
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 543 544 545 546 547 548
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)
{
549 550
    INT_PTR ret;
    int index = SendDlgItemMessageW(hwnd, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
551 552 553 554 555 556
    struct dll *dll;
    DWORD id;

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

557
    dll = (struct dll *) SendDlgItemMessageW(hwnd, IDC_DLLS_LIST, LB_GETITEMDATA, index, 0);
558
    id = mode_to_id(dll->mode);
559 560 561

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

562 563 564 565
    if(ret != IDCANCEL)
        set_dllmode(hwnd, ret);
}

566
static void on_remove_click(HWND dialog)
567
{
568
    int sel = SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCURSEL, 0, 0);
569 570 571 572
    struct dll *dll;

    if (sel == LB_ERR) return;

573 574 575 576 577
    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);
578
    set_reg_key(config_key, keypath("DllOverrides"), dll->name, NULL);
579 580 581 582

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

583 584
    if (SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_GETCOUNT, 0, 0) > 0)
        SendDlgItemMessageW(dialog, IDC_DLLS_LIST, LB_SETCURSEL, max(sel - 1, 0), 0);
585
    else
586 587
    {
        disable(IDC_DLLS_EDITDLL);
588
        disable(IDC_DLLS_REMOVEDLL);
589
    }
590 591

    set_controls_from_selection(dialog);
592 593 594 595 596 597 598 599
}

INT_PTR CALLBACK
LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_INITDIALOG:
600 601 602 603 604
		init_libsheet(hDlg);
		break;
        case WM_SHOWWINDOW:
                set_window_title(hDlg);
                break;
605 606
	case WM_NOTIFY:
		switch (((LPNMHDR)lParam)->code) {
607 608 609
                case PSN_SETACTIVE:
                    load_library_settings(hDlg);
                    break;
610 611 612 613
		}
		break;
	case WM_COMMAND:
		switch(HIWORD(wParam)) {
614
                case CBN_EDITCHANGE:
615 616 617 618 619 620 621 622 623 624 625
                    if (LOWORD(wParam) == IDC_DLLCOMBO)
                        on_add_combo_change(hDlg);
                    break;
                case CBN_SETFOCUS:
                    if (LOWORD(wParam) == IDC_DLLCOMBO)
                        on_add_combo_change(hDlg);
                    break;
                case CBN_KILLFOCUS:
                    if (LOWORD(wParam) == IDC_DLLCOMBO)
                        SendMessageW(GetParent(hDlg), DM_SETDEFID, IDOK, 0);
                    break;
626 627 628
		case BN_CLICKED:
			switch(LOWORD(wParam)) {
			case IDC_DLLS_ADDDLL:
629 630
                            on_add_click(hDlg);
                            break;
631 632 633
			case IDC_DLLS_EDITDLL:
			    on_edit_click(hDlg);
			    break;
634
			case IDC_DLLS_REMOVEDLL:
635 636
                            on_remove_click(hDlg);
                            break;
637 638
			}
			break;
639
                case LBN_SELCHANGE:
640 641 642 643
                        if(LOWORD(wParam) == IDC_DLLCOMBO)
                            on_add_combo_change(hDlg);
                        else
                            set_controls_from_selection(hDlg);
644
                        break;
645 646 647 648 649 650
		}
		break;
	}

	return 0;
}