From 26d0c5d1e26fdb6b5899af98e9fce9fba43bfced Mon Sep 17 00:00:00 2001
From: Roman Dadkov <romand@etersoft.ru>
Date: Fri, 16 Sep 2011 14:10:34 +0400
Subject: [PATCH] winecfg: Tab to control the loading of key drivers (eterbug
 #6916).

---
 po/ru.po                     |   18 ++-
 programs/winecfg/En.rc       |   11 +
 programs/winecfg/Makefile.in |    1 +
 programs/winecfg/Ru.rc       |   11 +
 programs/winecfg/dongleui.c  |  425 ++++++++++++++++++++++++++++++++++++++++++
 programs/winecfg/main.c      |   10 +
 programs/winecfg/resource.h  |   11 +
 programs/winecfg/winecfg.c   |   55 +++++-
 programs/winecfg/winecfg.h   |    9 +
 programs/winecfg/winecfg.rc  |    9 +
 10 files changed, 553 insertions(+), 7 deletions(-)
 create mode 100644 programs/winecfg/dongleui.c

diff --git a/po/ru.po b/po/ru.po
index a7e257a..38030be 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -8636,7 +8636,7 @@ msgstr ""
 msgid "%s      %s      %s      Open resources: %lu\n"
 msgstr "%s      %s         %s      Открытых ресурсов: %lu\n"
 
-#: net.rc:44
+#: net.rc:44  winecfg.rc:90
 msgid "OK"
 msgstr ""
 
@@ -11459,3 +11459,19 @@ msgstr ""
 "[/D | /D:m-d-y] Копирует только новые файлы или те, которые были изменены\n"
 "после указанной даты. Если дата не указана, копирует только\n"
 "те файлы, которые новее в исходной папке.\n"
+
+#: winecfg.rc:33
+msgid "Dongles"
+msgstr "Ключи защиты"
+
+#: winecfg.rc:88
+msgid "Load"
+msgstr "Загружать"
+
+#: winecfg.rc:89
+msgid "Do not load"
+msgstr "Не загружать"
+
+#: winecfg.rc:91
+msgid "Dongle not found"
+msgstr "Ключ не найден"
\ No newline at end of file
diff --git a/programs/winecfg/En.rc b/programs/winecfg/En.rc
index bd51345..7925812 100644
--- a/programs/winecfg/En.rc
+++ b/programs/winecfg/En.rc
@@ -230,3 +230,14 @@ BEGIN
     EDITTEXT         IDC_EDIT_SFPATH,65,195,125,13,ES_AUTOHSCROLL|WS_TABSTOP|WS_DISABLED
     PUSHBUTTON      "B&rowse...",IDC_BROWSE_SFPATH,195,195,50,13,WS_DISABLED
 END
+
+IDD_DONGLECFG DIALOG DISCARDABLE  0, 0, 260, 250
+STYLE WS_CHILD | WS_DISABLED
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX        " Loading dongles drivers ",IDC_STATIC,8,4,244,240
+    CONTROL         "Listview",IDC_LIST_DONGLES,"SysListView32",LVS_REPORT | LVS_AUTOARRANGE | LVS_ALIGNLEFT |
+LVS_SINGLESEL | WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER , 15,18,232,150
+    PUSHBUTTON      "Load",IDC_BUTTON_LOAD,91,172,75,14
+    PUSHBUTTON      "Do not load",IDC_BUTTON_UNLOAD,171,172,75,14
+END
\ No newline at end of file
diff --git a/programs/winecfg/Makefile.in b/programs/winecfg/Makefile.in
index 5d4e499..cb9a664 100644
--- a/programs/winecfg/Makefile.in
+++ b/programs/winecfg/Makefile.in
@@ -7,6 +7,7 @@ C_SRCS = \
 	appdefaults.c \
 	audio.c \
 	drive.c \
+	dongleui.c \
 	drivedetect.c \
 	driveui.c \
 	libraries.c \
diff --git a/programs/winecfg/Ru.rc b/programs/winecfg/Ru.rc
index f337897..969c06b 100644
--- a/programs/winecfg/Ru.rc
+++ b/programs/winecfg/Ru.rc
@@ -205,3 +205,14 @@ BEGIN
     EDITTEXT         IDC_EDIT_SFPATH,65,195,145,13,ES_AUTOHSCROLL|WS_TABSTOP|WS_DISABLED
     PUSHBUTTON      "Обзор",IDC_BROWSE_SFPATH,215,195,30,13,WS_DISABLED
 END
+
+IDD_DONGLECFG DIALOG DISCARDABLE  0, 0, 260, 250
+STYLE WS_CHILD | WS_DISABLED
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX        " Загрузка драйверов ключей ",IDC_STATIC,8,4,244,240
+    CONTROL         "",IDC_LIST_DONGLES,"SysListView32",LVS_REPORT | LVS_AUTOARRANGE |   LVS_ALIGNLEFT |
+    LVS_SINGLESEL | WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER , 15,18,232,150
+    PUSHBUTTON      "Загружать",IDC_BUTTON_LOAD,91,172,75,14
+    PUSHBUTTON      "Не загружать",IDC_BUTTON_UNLOAD,171,172,75,14
+END
\ No newline at end of file
diff --git a/programs/winecfg/dongleui.c b/programs/winecfg/dongleui.c
new file mode 100644
index 0000000..982081d
--- /dev/null
+++ b/programs/winecfg/dongleui.c
@@ -0,0 +1,425 @@
+/*
+ * Dongle management UI code
+ *
+ * Copyright 2009 Alexander Morozov
+ * Copyright 2009 Ilya Shpigor
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#define COBJMACROS
+
+#include <windows.h>
+#include <setupapi.h>
+#include <shellapi.h>
+#include <objbase.h>
+#include <shlguid.h>
+#include <shlwapi.h>
+#include <shlobj.h>
+
+#include <wine/unicode.h>
+#include <wine/debug.h>
+
+#include "winecfg.h"
+#include "resource.h"
+
+#define     LOADSTRING_SIZE     64
+
+WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
+
+static WCHAR status[2][LOADSTRING_SIZE];
+static WCHAR device[2][LOADSTRING_SIZE];
+
+static const char* dongles[] = {"Guardant",
+                                "HASP HL",
+                                "LPT Katran",
+                                "USB Katran",
+                                "Sentinel",
+                                "SmartKey 3"};
+
+static const int count = 6;
+
+static const WCHAR grdkeyW[] = {'G','r','d','K','e','y',0};
+static const WCHAR grdusbW[] = {'G','r','d','U','s','b',0};
+static const WCHAR hardlockW[] = {'H','a','r','d','l','o','c','k',0};
+static const WCHAR akshaspW[] = {'a','k','s','h','a','s','p',0};
+static const WCHAR aksusbW[] = {'a','k','s','u','s','b',0};
+static const WCHAR pkeyW[] = {'P','K','e','y',0};
+static const WCHAR nkeyW[] = {'N','K','e','y',0};
+static const WCHAR upkeyW[] = {'U','P','K','e','y',0};
+static const WCHAR sentinelW[] = {'S','e','n','t','i','n','e','l',0};
+static const WCHAR sntnlusbW[] = {'S','N','T','N','L','U','S','B',0};
+static const WCHAR eusk3usbW[] = {'e','u','s','k','3','u','s','b',0};
+static const WCHAR servicesW[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v','i','c','e','s','\\',0};
+static const WCHAR startW[] = {'S','t','a','r','t',0};
+
+enum dongle_t
+{
+    guardant,
+    hasp,
+    katran_lpt,
+    katran_usb,
+    sentinel,
+    smartkey3
+};
+
+static int file_exists(LPCWSTR filename)
+{
+    HANDLE file;
+
+    WINE_TRACE("%s\n", wine_dbgstr_w(filename));
+    file = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE,
+            FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+            FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file == INVALID_HANDLE_VALUE) return 0;
+    CloseHandle(file);
+    return 1;
+}
+
+static int interface_exists(const GUID *guid)
+{
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail;
+    SP_DEVICE_INTERFACE_DATA intf_data;
+    HDEVINFO set;
+    DWORD size, i = 0;
+    int ret = 0;
+
+    set = SetupDiGetClassDevsW(guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
+    if (set == INVALID_HANDLE_VALUE) return 0;
+    intf_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+    while (!ret && SetupDiEnumDeviceInterfaces(set, NULL, guid, i++, &intf_data))
+    {
+        SetupDiGetDeviceInterfaceDetailW(set, &intf_data, NULL, 0, &size, NULL);
+        detail = HeapAlloc(GetProcessHeap(), 0, size);
+        if (detail == NULL) break;
+        detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+        if (SetupDiGetDeviceInterfaceDetailW(set, &intf_data, detail, size, NULL, NULL) &&
+            file_exists(detail->DevicePath))
+            ret = 1;
+        HeapFree(GetProcessHeap(), 0, detail);
+    }
+    SetupDiDestroyDeviceInfoList(set);
+    return ret;
+}
+
+static int is_loaded(enum dongle_t dongle)
+{
+    static const WCHAR nvkeyntW[] = {'\\','\\','.','\\','n','v','k','e','y','n','t',0};
+    static const WCHAR fentedevW[] = {'\\','\\','.','\\','f','e','n','t','e','d','e','v',0};
+    static const WCHAR pkey0W[] = {'\\','\\','.','\\','p','k','e','y','0',0};
+    static const WCHAR nkey0W[] = {'\\','\\','.','\\','n','k','e','y','0',0};
+    static const WCHAR upkey0W[] = {'\\','\\','.','\\','u','p','k','e','y','0',0};
+    static const WCHAR rnbodrv0W[] = {'\\','\\','.','\\','r','n','b','o','d','r','v','0',0};
+    static const WCHAR skeysusbW[] = {'\\','\\','.','\\','s','k','e','y','s','u','s','b',0};
+    static const WCHAR sntnlusbW[] = {'\\','\\','.','\\','s','n','t','n','l','u','s','b',0};
+    static const WCHAR skeyW[] = {'\\','\\','.','\\','s','k','e','y','-','-','-','-',0};
+    static const WCHAR skey000W[] = {'\\','\\','.','\\','s','k','e','y','-','0','0','0',0};
+    static const GUID hasp_intf = {0x7ab02247, 0x3798, 0x44cd, {0x84,0x72,0x24,0x35,0x64,0x97,0x5f,0x04}};
+    static const GUID grd_intf = {0x46b89760, 0xc784, 0x11d4, {0x81,0xa8,0x00,0x80,0xad,0x87,0xaf,0x5c}};
+
+    switch (dongle)
+    {
+    case guardant:
+        if (file_exists(nvkeyntW) && interface_exists(&grd_intf))
+            return 1;
+        break;
+    case hasp:
+        if (file_exists(fentedevW) && interface_exists(&hasp_intf))
+            return 1;
+        break;
+    case katran_lpt:
+        if (file_exists(pkey0W))
+            return 1;
+        break;
+    case katran_usb:
+        if (file_exists(nkey0W) && file_exists(upkey0W))
+            return 1;
+        break;
+    case sentinel:
+        if (file_exists(rnbodrv0W) && file_exists(skeysusbW) &&
+            file_exists(sntnlusbW))
+            return 1;
+        break;
+    case smartkey3:
+        if (file_exists(skeyW) && file_exists(skey000W))
+            return 1;
+        break;
+    };
+    return 0;
+}
+
+static void set_service_start(LPCWSTR service_name, DWORD start)
+{
+    WCHAR *wpath = HeapAlloc(GetProcessHeap(), 0, sizeof(servicesW) +
+            lstrlenW(service_name) * sizeof(WCHAR));
+
+    if (wpath == NULL) return;
+    lstrcpyW(wpath, servicesW);
+    lstrcatW(wpath, service_name);
+    set_reg_key_dwordW(HKEY_LOCAL_MACHINE, wpath, startW, start);
+    HeapFree(GetProcessHeap(), 0, wpath);
+}
+
+static DWORD get_service_start(LPCWSTR service_name)
+{
+    DWORD start;
+    WCHAR *wpath = HeapAlloc(GetProcessHeap(), 0, sizeof(servicesW) +
+            lstrlenW(service_name) * sizeof(WCHAR));
+
+    if (wpath == NULL) return 0;
+    lstrcpyW(wpath, servicesW);
+    lstrcatW(wpath, service_name);
+    start = get_reg_key_dwordW(HKEY_LOCAL_MACHINE, wpath, startW, 0);
+    HeapFree(GetProcessHeap(), 0, wpath);
+    return start;
+}
+
+static void set_start(enum dongle_t dongle, int start)
+{
+    switch (dongle)
+    {
+    case guardant:
+        set_service_start(grdkeyW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        set_service_start(grdusbW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        break;
+    case hasp:
+        set_service_start(hardlockW, start ? SERVICE_AUTO_START : SERVICE_DISABLED);
+        set_service_start(akshaspW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        set_service_start(aksusbW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        break;
+    case katran_lpt:
+        set_service_start(pkeyW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        break;
+    case katran_usb:
+        set_service_start(nkeyW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        set_service_start(upkeyW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        break;
+    case sentinel:
+        set_service_start(sentinelW, start ? SERVICE_AUTO_START : SERVICE_DISABLED);
+        set_service_start(sntnlusbW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        break;
+    case smartkey3:
+        set_service_start(eusk3usbW, start ? SERVICE_DEMAND_START : SERVICE_DISABLED);
+        break;
+    };
+}
+
+static int get_start(enum dongle_t dongle)
+{
+    switch (dongle)
+    {
+    case guardant:
+        if (get_service_start(grdkeyW) == SERVICE_DEMAND_START &&
+            get_service_start(grdusbW) == SERVICE_DEMAND_START)
+            return 1;
+        break;
+    case hasp:
+        if (get_service_start(hardlockW) == SERVICE_AUTO_START &&
+            get_service_start(akshaspW) == SERVICE_DEMAND_START &&
+            get_service_start(aksusbW) == SERVICE_DEMAND_START)
+            return 1;
+        break;
+    case katran_lpt:
+        if (get_service_start(pkeyW) == SERVICE_DEMAND_START)
+            return 1;
+        break;
+    case katran_usb:
+        if (get_service_start(nkeyW) == SERVICE_DEMAND_START &&
+            get_service_start(upkeyW) == SERVICE_DEMAND_START)
+            return 1;
+        break;
+    case sentinel:
+        if (get_service_start(sentinelW) == SERVICE_AUTO_START &&
+            get_service_start(sntnlusbW) == SERVICE_DEMAND_START)
+            return 1;
+        break;
+    case smartkey3:
+        if (get_service_start(eusk3usbW) == SERVICE_DEMAND_START)
+            return 1;
+        break;
+    };
+    return 0;
+}
+
+static void init_listview_columns(HWND dialog)
+{
+    LVCOLUMNW listColumn;
+    RECT viewRect;
+    int width;
+    WCHAR* empty = {'\0'};
+
+    GetClientRect(GetDlgItem(dialog, IDC_LIST_DONGLES), &viewRect);
+    width = (viewRect.right - viewRect.left)/3;
+
+    listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+    listColumn.cx = (viewRect.right - viewRect.left)/3;
+    listColumn.pszText = empty;
+    SendDlgItemMessageW (dialog, IDC_LIST_DONGLES, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
+
+    width += (viewRect.right - viewRect.left)/3;
+    listColumn.cx = (viewRect.right - viewRect.left)/3;
+    listColumn.pszText = empty;
+    SendDlgItemMessageW (dialog, IDC_LIST_DONGLES, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
+
+    listColumn.cx = (viewRect.right - viewRect.left) - width;
+    listColumn.pszText = empty;
+    SendDlgItemMessageW (dialog, IDC_LIST_DONGLES, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
+}
+
+static void lv_insert_item(HWND dialog, LVITEMW *item)
+{
+    SendDlgItemMessageW(dialog, IDC_LIST_DONGLES, LVM_INSERTITEMW, 0, (LPARAM) item);
+}
+
+static void lv_set_item(HWND dialog, LVITEMW *item)
+{
+    SendDlgItemMessageW(dialog, IDC_LIST_DONGLES, LVM_SETITEMW, 0, (LPARAM) item);
+}
+
+static void lv_set_item_text(HWND dialog, int item, int subItem, WCHAR *text)
+{
+    LVITEMW lvItem;
+    if (item < 0 || subItem < 0) return;
+    lvItem.mask = LVIF_TEXT;
+    lvItem.iItem = item;
+    lvItem.iSubItem = subItem;
+    lvItem.pszText = text;
+    lvItem.cchTextMax = lstrlenW(lvItem.pszText);
+    lv_set_item(dialog, &lvItem);
+}
+
+static int fill_dongles_list(HWND dialog)
+{
+    LVITEMW item;
+    int i;
+
+    SendDlgItemMessage(dialog, IDC_LIST_DONGLES, LVM_DELETEALLITEMS, 0, 0);
+
+    LoadStringW (GetModuleHandle (NULL), IDC_DONGLE_UNLOAD, status[0],
+        LOADSTRING_SIZE);
+    LoadStringW (GetModuleHandle (NULL), IDC_DONGLE_LOAD, status[1],
+        LOADSTRING_SIZE);
+
+    LoadStringW (GetModuleHandle (NULL), IDC_DONGLE_NOTFOUND, device[0],
+        LOADSTRING_SIZE);
+    LoadStringW (GetModuleHandle (NULL), IDC_DONGLE_OK, device[1],
+        LOADSTRING_SIZE);
+
+    for(i = 0; i < count; i++)
+    {
+        item.mask = LVIF_TEXT | LVIF_PARAM;
+        item.iItem = i;
+        item.iSubItem = 0;
+        item.pszText = strdupU2W(dongles[i]);
+        item.cchTextMax = lstrlenW(item.pszText);
+
+        lv_insert_item(dialog, &item);
+        HeapFree(GetProcessHeap(), 0, item.pszText);
+
+        if (get_start(i))
+            lv_set_item_text(dialog, i, 1, status[1]);
+        else
+            lv_set_item_text(dialog, i, 1, status[0]);
+
+        if (is_loaded(i))
+            lv_set_item_text(dialog, i, 2, device[1]);
+        else
+            lv_set_item_text(dialog, i, 2, device[0]);
+    }
+
+    return i;
+}
+
+static int get_list_selection (HWND dialog)
+{
+    return ListView_GetSelectionMark(GetDlgItem(dialog, IDC_LIST_DONGLES));
+}
+
+static void on_load_click (HWND dialog, int num)
+{
+    set_start(num, 1);
+    lv_set_item_text(dialog, num, 1, status[1]);
+    SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
+}
+
+static void on_unload_click (HWND dialog, int num)
+{
+    set_start(num, 0);
+    lv_set_item_text(dialog, num, 1, status[0]);
+    SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
+}
+
+INT_PTR CALLBACK
+DongleDlgProc (HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    int num;
+
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+            init_listview_columns(dialog);
+            fill_dongles_list(dialog);
+            break;
+
+        case WM_COMMAND:
+            switch (LOWORD(wParam))
+            {
+                case IDC_BUTTON_LOAD:
+                    num = get_list_selection(dialog);
+
+                    if (HIWORD(wParam) != BN_CLICKED) break;
+                    if (!get_start(num))
+                        on_load_click(dialog, num);
+                    break;
+                case IDC_BUTTON_UNLOAD:
+                    num = get_list_selection(dialog);
+
+                    if (HIWORD(wParam) != BN_CLICKED) break;
+                    if (get_start(num))
+                        on_unload_click(dialog, num);
+                    break;
+            }
+            break;
+
+        case WM_NOTIFY:
+            switch (LOWORD(wParam))
+            {
+                case IDC_LIST_DONGLES:
+                    {
+                        NMHDR* nhdr;
+
+                        nhdr = (NMHDR*)lParam;
+                        if (nhdr->code != LVN_ITEMACTIVATE)
+                            break;
+
+                        num = ListView_GetSelectionMark(GetDlgItem(dialog, IDC_LIST_DONGLES));
+                        if (get_start(num))
+                            on_unload_click(dialog, num);
+                        else
+                            on_load_click(dialog, num);
+                    }
+                    break;
+            }
+            break;
+    }
+
+    return FALSE;
+}
\ No newline at end of file
diff --git a/programs/winecfg/main.c b/programs/winecfg/main.c
index b7f9331..129dc09 100644
--- a/programs/winecfg/main.c
+++ b/programs/winecfg/main.c
@@ -134,6 +134,16 @@ doPropertySheet (HINSTANCE hInstance, HWND hOwner)
     psp[pg].dwSize = sizeof (PROPSHEETPAGEW);
     psp[pg].dwFlags = PSP_USETITLE;
     psp[pg].hInstance = hInstance;
+    psp[pg].u.pszTemplate = MAKEINTRESOURCEW (IDD_DONGLECFG);
+    psp[pg].u2.pszIcon = NULL;
+    psp[pg].pfnDlgProc = DongleDlgProc;
+    psp[pg].pszTitle =  load_string (IDS_TAB_DONGLES);
+    psp[pg].lParam = 0;
+    pg++;
+
+    psp[pg].dwSize = sizeof (PROPSHEETPAGEW);
+    psp[pg].dwFlags = PSP_USETITLE;
+    psp[pg].hInstance = hInstance;
     psp[pg].u.pszTemplate = MAKEINTRESOURCEW (IDD_AUDIOCFG);
     psp[pg].u2.pszIcon = NULL;
     psp[pg].pfnDlgProc = AudioDlgProc;
diff --git a/programs/winecfg/resource.h b/programs/winecfg/resource.h
index 24eaec4..6fe8e24 100644
--- a/programs/winecfg/resource.h
+++ b/programs/winecfg/resource.h
@@ -242,3 +242,14 @@
 
 #define IDC_WINVER_LAUNCH            8439
 
+
+/* Dongles tab */
+#define IDS_TAB_DONGLES              8435
+#define IDD_DONGLECFG                8436
+#define IDC_LIST_DONGLES             8437
+#define IDC_DONGLE_LOAD              8438
+#define IDC_DONGLE_UNLOAD            8439
+#define IDC_BUTTON_LOAD              8440
+#define IDC_BUTTON_UNLOAD            8441
+#define IDC_DONGLE_OK                8442
+#define IDC_DONGLE_NOTFOUND          8443
\ No newline at end of file
diff --git a/programs/winecfg/winecfg.c b/programs/winecfg/winecfg.c
index cdf3a84..16d494a 100644
--- a/programs/winecfg/winecfg.c
+++ b/programs/winecfg/winecfg.c
@@ -103,15 +103,15 @@ WCHAR* load_string (UINT id)
  * not. Caller is responsible for releasing the result.
  *
  */
-static WCHAR *get_config_key (HKEY root, const WCHAR *subkey, const WCHAR *name, const WCHAR *def)
+static void *get_config_key (HKEY root, const WCHAR *subkey, const WCHAR *name, const void *def, DWORD type)
 {
     LPWSTR buffer = NULL;
     DWORD len;
     HKEY hSubKey = NULL;
     DWORD res;
 
-    WINE_TRACE("subkey=%s, name=%s, def=%s\n", wine_dbgstr_w(subkey),
-               wine_dbgstr_w(name), wine_dbgstr_w(def));
+    WINE_TRACE("subkey=%s, name=%s, def=%p, type=%d\n", wine_dbgstr_w(subkey),
+               wine_dbgstr_w(name), def, type);
 
     res = RegOpenKeyExW(root, subkey, 0, MAXIMUM_ALLOWED, &hSubKey);
     if (res != ERROR_SUCCESS)
@@ -119,7 +119,10 @@ static WCHAR *get_config_key (HKEY root, const WCHAR *subkey, const WCHAR *name,
         if (res == ERROR_FILE_NOT_FOUND)
         {
             WINE_TRACE("Section key not present - using default\n");
-            return def ? strdupW(def) : NULL;
+            if (!def) return NULL;
+            if (type == REG_SZ) return strdupW(def);
+            if (type == REG_DWORD) return memdup(def, sizeof(DWORD));
+            return NULL;
         }
         else
         {
@@ -132,7 +135,12 @@ static WCHAR *get_config_key (HKEY root, const WCHAR *subkey, const WCHAR *name,
     if (res == ERROR_FILE_NOT_FOUND)
     {
         WINE_TRACE("Value not present - using default\n");
-        buffer = def ? strdupW(def) : NULL;
+        if (def)
+        {
+            if (type == REG_SZ) buffer = strdupW(def);
+            else if (type == REG_DWORD) buffer = memdup(def, sizeof(DWORD));
+        }
+
 	goto end;
     } else if (res != ERROR_SUCCESS)
     {
@@ -274,7 +282,7 @@ WCHAR *get_reg_keyW(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR
     }
 
     /* no, so get from the registry */
-    val = get_config_key(root, path, name, def);
+    val = get_config_key(root, path, name, def, REG_SZ);
 
     WINE_TRACE("returning %s\n", wine_dbgstr_w(val));
 
@@ -318,6 +326,41 @@ char *get_reg_key(HKEY root, const char *path, const char *name, const char *def
     return szRet;
 }
 
+DWORD get_reg_key_dwordW(HKEY root, const WCHAR *path, const WCHAR *name, DWORD def)
+{
+    struct list *cursor;
+    struct setting *s;
+    DWORD *val, ret = 0;
+
+    WINE_TRACE("path=%s, name=%s, def=%d\n", wine_dbgstr_w(path),
+               wine_dbgstr_w(name), def);
+
+    /* check if it's in the list */
+    LIST_FOR_EACH( cursor, settings )
+    {
+        s = LIST_ENTRY(cursor, struct setting, entry);
+
+        if (root != s->root) continue;
+        if (lstrcmpiW(path, s->path) != 0) continue;
+        if (!s->name) continue;
+        if (lstrcmpiW(name, s->name) != 0) continue;
+
+        if (s->value) ret = *(DWORD *)s->value;
+        WINE_TRACE("found %s:%s in settings list, returning %d\n",
+                   wine_dbgstr_w(path), wine_dbgstr_w(name), ret);
+        return ret;
+    }
+
+    /* no, so get from the registry */
+    val = get_config_key(root, path, name, &def, REG_DWORD);
+    ret = *val;
+    HeapFree(GetProcessHeap(), 0, val);
+
+    WINE_TRACE("returning %d\n", ret);
+
+    return ret;
+}
+
 /**
  * Used to set a registry key.
  *
diff --git a/programs/winecfg/winecfg.h b/programs/winecfg/winecfg.h
index 1959409..5729c5e 100644
--- a/programs/winecfg/winecfg.h
+++ b/programs/winecfg/winecfg.h
@@ -53,6 +53,7 @@ extern WCHAR* current_app; /* NULL means editing global settings  */
 
 void set_reg_keyW(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR *value);
 void set_reg_key_dwordW(HKEY root, const WCHAR *path, const WCHAR *name, DWORD value);
+DWORD get_reg_key_dwordW(HKEY root, const WCHAR *path, const WCHAR *name, DWORD def);
 WCHAR *get_reg_keyW(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR *def);
 
 void set_reg_key(HKEY root, const char *path, const char *name, const char *value);
@@ -82,6 +83,7 @@ void set_window_title(HWND dialog);
 /* Window procedures */
 INT_PTR CALLBACK GraphDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 INT_PTR CALLBACK DriveDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK DongleDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 INT_PTR CALLBACK DriveEditDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 INT_PTR CALLBACK AppDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 INT_PTR CALLBACK LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
@@ -149,6 +151,13 @@ static inline WCHAR *strdupU2W(const char *unix_str)
     return unicode_str;
 }
 
+static inline void *memdup(const void *data, size_t len)
+{
+    void *ptr = HeapAlloc(GetProcessHeap(), 0, len);
+    if (ptr) return memcpy(ptr, data, len);
+    return NULL;
+}
+
 static inline char *get_text(HWND dialog, WORD id)
 {
     HWND item = GetDlgItem(dialog, id);
diff --git a/programs/winecfg/winecfg.rc b/programs/winecfg/winecfg.rc
index bbd7842..11371f4 100644
--- a/programs/winecfg/winecfg.rc
+++ b/programs/winecfg/winecfg.rc
@@ -30,6 +30,7 @@ BEGIN
     IDS_TAB_APPLICATIONS    "Applications"
     IDS_TAB_DLLS            "Libraries"
     IDS_TAB_DRIVES          "Drives"
+    IDS_TAB_DONGLES         "Dongles"
     IDS_CHOOSE_PATH         "Select the Unix target directory, please."
     IDS_HIDE_ADVANCED       "Hide &Advanced"
     IDS_SHOW_ADVANCED       "Show &Advanced"
@@ -82,6 +83,14 @@ BEGIN
     IDS_NO_DRIVE_C              "You don't have a drive C. This is not so great.\n\nRemember to click 'Add' in the Drives tab to create one!\n"
 END
 
+STRINGTABLE DISCARDABLE
+BEGIN
+    IDC_DONGLE_LOAD             "Load"
+    IDC_DONGLE_UNLOAD           "Do not load"
+    IDC_DONGLE_OK               "OK"
+    IDC_DONGLE_NOTFOUND         "Dongle not found"
+END
+
 STRINGTABLE
 BEGIN
     IDS_ACCEL_FULL              "Full"
-- 
1.7.6.1
