confdlg.c 18.9 KB
Newer Older
1
/*
2
 * This DLL contains the user interface for the serial driver.
Andreas Mohr's avatar
Andreas Mohr committed
3
 *    a dialog box to configure the specified COMM port
4 5 6 7 8 9 10 11 12 13
 *    an interface to the control panel (??)
 *    functions to load and save default configuration
 *
 * Eventually the 32 bit comm port driver could be moved into here
 * and interfaced to KERNEL32 using the WIN95 or WINNT comm driver interface.
 * This way, different driver DLLS could be written to support other
 * serial interfaces, such as X.25, etc.
 *
 * Basic structure copied from COMCTL32 code.
 *
14
 * Copyright 2000, 2004 Mike McCormack
15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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
28
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 30
 */

31 32
#include "config.h"
#include "wine/port.h"
33
#include "wine/unicode.h"
34

35
#include <string.h>
36
#include <stdarg.h>
37
#include <stdio.h>
38

39
#include "windef.h"
40 41
#include "winbase.h"
#include "winreg.h"
42 43
#include "wingdi.h"
#include "winuser.h"
44
#include "wine/debug.h"
45 46 47
#include "serialui.h"
#include "winerror.h"

48
WINE_DEFAULT_DEBUG_CHANNEL(comm);
49

50
static HMODULE SERIALUI_hModule;
51

52 53
static const WCHAR comW[] = {'c','o','m',0 };

54
/***********************************************************************
55
 * DllMain [Internal] Initializes the internal 'SERIALUI.DLL'.
56 57
 *
 * PARAMS
Andreas Mohr's avatar
Andreas Mohr committed
58
 *     hinstDLL    [I] handle to the DLL's instance
59
 *     fdwReason   [I]
Andreas Mohr's avatar
Andreas Mohr committed
60
 *     lpvReserved [I] reserved, must be NULL
61 62 63 64 65 66
 *
 * RETURNS
 *     Success: TRUE
 *     Failure: FALSE
 */

67
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
68
{
69
    TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved);
70 71 72

    switch (fdwReason) {
	case DLL_PROCESS_ATTACH:
73
            DisableThreadLibraryCalls(hinstDLL);
74
            SERIALUI_hModule = hinstDLL;
75 76 77 78 79 80 81 82
	    break;
    }

    return TRUE;
}


/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
83
 * EnumPropPages (SERIALUI.2)
84 85
 *
 * Called by the device manager to add prop sheets in Control Panel ???
86
 * Pointed to in Win98 registry by
87 88 89 90 91
 * \System\CurrentControlSet\Services\Class\ports\0000\EnumPropPages =
 *  "serialui.dll,EnumPropPages"
 */
typedef LPVOID LPDEVICE_INFO;
typedef LPVOID LPFNADDPROPSHEETPAGE;
92
BOOL WINAPI EnumPropPages(LPDEVICE_INFO pdi, LPFNADDPROPSHEETPAGE pfnAdd, LPARAM lParam )
93 94 95 96 97 98 99 100 101 102 103 104
{
    FIXME("(%p %p %lx)\n",pdi,pfnAdd,lParam);
    return FALSE;
}

/*
 * These data structures are convert from values used in fields of a DCB
 * to strings used in the CommConfigDialog.
 */
typedef struct tagPARAM2STRDATA
{
    DWORD        val;
105
    const CHAR  *name;
106 107 108 109 110 111 112
} PARAM2STRDATA, *LPPARAM2STRDATA;

typedef struct tagPARAM2STR
{
    DWORD         dwSize;
    LPPARAM2STRDATA data;
} PARAM2STR, *LPPARAM2STR;
113
typedef const PARAM2STR *LPCPARAM2STR;
114 115 116 117 118 119 120

static PARAM2STRDATA SERIALUI_Baud2StrData[]={
  {110, "110"}, {300, "300"}, {600, "600"}, {1200, "1200"},
  {2400, "2400"}, {4800, "4800"}, {9600, "9600"}, {14400, "14400"},
  {19200, "19200"}, {38400L, "38400"}, {56000L, "56000"}, {57600L, "57600"},
  {115200L, "115200"}, {128000L, "128000"}, {256000L, "256000"}
};
121
static PARAM2STR SERIALUI_Baud2Str={ ARRAY_SIZE(SERIALUI_Baud2StrData), SERIALUI_Baud2StrData };
122

123
static PARAM2STRDATA SERIALUI_Parity2StrData[]={
124 125 126
  {NOPARITY,"None"}, {ODDPARITY,"Odd"}, {EVENPARITY,"Even"}, {MARKPARITY,"Mark"},
  {SPACEPARITY,"Space"}
};
127
static PARAM2STR SERIALUI_Parity2Str={ ARRAY_SIZE(SERIALUI_Parity2StrData), SERIALUI_Parity2StrData };
128 129 130 131

static PARAM2STRDATA SERIALUI_Stop2StrData[]={
  {ONESTOPBIT,"1"}, {ONE5STOPBITS,"1.5"}, {TWOSTOPBITS,"2"}
};
132
static PARAM2STR SERIALUI_Stop2Str={ ARRAY_SIZE(SERIALUI_Stop2StrData), SERIALUI_Stop2StrData };
133 134 135 136

static PARAM2STRDATA SERIALUI_Data2StrData[]={
  {5,"5"}, {6,"6"}, {7,"7"}, {8, "8"}, {16,"16"}
};
137
static PARAM2STR SERIALUI_Data2Str={ ARRAY_SIZE(SERIALUI_Data2StrData), SERIALUI_Data2StrData };
138 139 140 141

static PARAM2STRDATA SERIALUI_Flow2StrData[]={
  {0,"None"}, {1,"Hardware (RTS/CTS)"}, {2,"Software (XON/XOFF)"}
};
142
static PARAM2STR SERIALUI_Flow2Str={ ARRAY_SIZE(SERIALUI_Flow2StrData), SERIALUI_Flow2StrData };
143 144 145 146 147 148

/*
 * Add all the fields to a combo box and highlight the current value
 */
static void SERIALUI_AddConfItems(HWND hDlg, DWORD id, LPCPARAM2STR table, DWORD dwVal)
{
149 150
    unsigned int i;
    int n;
151 152 153 154 155 156 157 158 159 160
    HWND hControl = GetDlgItem(hDlg,id);

    if(!hControl)
        return;

    for(i=0; i<table->dwSize; i++)
    {
        n = SendMessageA(hControl, CB_ADDSTRING, 0L, (LPARAM)table->data[i].name);
        if(dwVal == table->data[i].val)
	{
161
            SendMessageA(hControl, CB_SETCURSEL, n, 0);
162 163 164 165 166
	}
    }
}

/*
Austin English's avatar
Austin English committed
167
 * Get the current selection of the given combo box and set a DCB field to
168 169 170 171 172 173 174 175 176 177
 * the value matching that selection.
 */
static BOOL SERIALUI_GetConfItems(HWND hDlg, DWORD id, LPCPARAM2STR table, LPDWORD lpdwVal)
{
    DWORD i;
    CHAR lpEntry[20];
    HWND hControl = GetDlgItem(hDlg,id);

    if( (!hControl) || (!lpdwVal))
    {
178
        TRACE("Couldn't get window handle for item %x\n",id);
179 180 181
        return FALSE;
    }

182
    if(!GetWindowTextA(hControl, &lpEntry[0], sizeof(lpEntry)))
183
    {
184
        TRACE("Couldn't get window text for item %x\n",id);
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
        return FALSE;
    }
    /* TRACE("%ld contains %s\n",id, lpEntry); */

    for(i=0; i<table->dwSize; i++)
    {
        if(!lstrcmpA(table->data[i].name,lpEntry))
	{
            *lpdwVal = table->data[i].val;
            return TRUE;
	}
    }

    return FALSE;
}

/*
 * Both the enumerated values CBR_XXXX and integer baud rates are valid
 * dcb.BaudRate. This code is to convert back and forth between CBR_ style
 * and integers. The dialog box uses integer values.
 */
206
static const DWORD SERIALUI_BaudConvertTable[] =  {
207 208 209 210 211 212 213 214
  CBR_110, 110, CBR_300, 300, CBR_600, 600, CBR_1200, 1200,
  CBR_2400, 2400, CBR_4800, 4800, CBR_9600, 9600, CBR_14400, 14400,
  CBR_19200, 19200, CBR_38400, 38400, CBR_56000, 56000, CBR_57600, 57600,
  CBR_115200, 115200, CBR_128000, 128000, CBR_256000, 256000
};

static BOOL SERIALUI_MakeBaudDword(LPDWORD lpdwBaudRate)
{
215
    unsigned int i;
216

217
    for(i=0; i<ARRAY_SIZE(SERIALUI_BaudConvertTable); i+=2)
218 219 220 221 222 223 224 225 226 227 228 229
    {
        if(*lpdwBaudRate == SERIALUI_BaudConvertTable[i])
        {
            *lpdwBaudRate = SERIALUI_BaudConvertTable[i+1];
            return TRUE;
        }
    }
    return FALSE;
}

static BOOL SERIALUI_MakeBaudEnum(LPDWORD lpdwBaudRate)
{
230
    unsigned int i;
231

232
    for(i=0; i<ARRAY_SIZE(SERIALUI_BaudConvertTable); i+=2)
233 234 235 236 237 238 239 240 241 242 243 244
    {
        if(*lpdwBaudRate == SERIALUI_BaudConvertTable[i+1])
        {
            *lpdwBaudRate = SERIALUI_BaudConvertTable[i];
            return TRUE;
        }
    }
    return FALSE;
}

typedef struct tagSERIALUI_DialogInfo
{
245
    LPCWSTR lpszDevice;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    LPCOMMCONFIG lpCommConfig;
    BOOL bConvert; /* baud rate was converted to a DWORD */
    DWORD dwFlowControl; /* old flow control */
} SERIALUI_DialogInfo;

static void SERIALUI_DCBToDialogInfo(HWND hDlg, SERIALUI_DialogInfo *info)
{
    DWORD dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl;
    LPDCB lpdcb = &info->lpCommConfig->dcb;

    /* pass integer pointers to SERIALUI_ dialog config fns */
    dwBaudRate    = lpdcb->BaudRate;
    dwStopBits    = lpdcb->StopBits;
    dwParity      = lpdcb->Parity;
    dwByteSize    = lpdcb->ByteSize;

    /* map flow control state, if it looks normal */
    if((lpdcb->fRtsControl == RTS_CONTROL_HANDSHAKE) ||
264
       (lpdcb->fOutxCtsFlow)) {
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        dwFlowControl = 1;
    } else if(lpdcb->fOutX || lpdcb->fInX) {
        dwFlowControl = 2;
    } else {
        dwFlowControl = 0;
    }

    info->bConvert = SERIALUI_MakeBaudDword(&dwBaudRate);

    SERIALUI_AddConfItems( hDlg, IDC_BAUD, &SERIALUI_Baud2Str ,dwBaudRate);
    SERIALUI_AddConfItems( hDlg, IDC_STOP, &SERIALUI_Stop2Str ,dwStopBits);
    SERIALUI_AddConfItems( hDlg, IDC_PARITY, &SERIALUI_Parity2Str ,dwParity);
    SERIALUI_AddConfItems( hDlg, IDC_DATA, &SERIALUI_Data2Str ,dwByteSize);
    SERIALUI_AddConfItems( hDlg, IDC_FLOW, &SERIALUI_Flow2Str, dwFlowControl );

    info->dwFlowControl = dwFlowControl;
}

static void SERIALUI_DialogInfoToDCB(HWND hDlg, SERIALUI_DialogInfo *info)
{
    DWORD dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl;
    LPDCB lpdcb = &info->lpCommConfig->dcb;

    SERIALUI_GetConfItems( hDlg, IDC_BAUD, &SERIALUI_Baud2Str, &dwBaudRate);
    SERIALUI_GetConfItems( hDlg, IDC_STOP, &SERIALUI_Stop2Str, &dwStopBits);
    SERIALUI_GetConfItems( hDlg, IDC_PARITY, &SERIALUI_Parity2Str, &dwParity);
    SERIALUI_GetConfItems( hDlg, IDC_DATA, &SERIALUI_Data2Str, &dwByteSize);
    SERIALUI_GetConfItems( hDlg, IDC_FLOW, &SERIALUI_Flow2Str, &dwFlowControl );
293

294
    TRACE("baud=%d stop=%d parity=%d data=%d flow=%d\n",
295
          dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl);
296

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    lpdcb->BaudRate = dwBaudRate;
    lpdcb->StopBits = dwStopBits;
    lpdcb->Parity   = dwParity;
    lpdcb->ByteSize = dwByteSize;

    /* try not to change flow control if the user didn't change it */
    if(info->dwFlowControl != dwFlowControl)
    {
        switch(dwFlowControl)
        {
        case 0:
            lpdcb->fOutxCtsFlow = FALSE;
            lpdcb->fOutxDsrFlow = FALSE;
            lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
            lpdcb->fOutX        = FALSE;
            lpdcb->fInX         = FALSE;
            lpdcb->fRtsControl  = RTS_CONTROL_DISABLE;
            break;
        case 1: /* CTS/RTS */
            lpdcb->fOutxCtsFlow = TRUE;
            lpdcb->fOutxDsrFlow = FALSE;
            lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
            lpdcb->fOutX        = FALSE;
            lpdcb->fInX         = FALSE;
            lpdcb->fRtsControl  = RTS_CONTROL_HANDSHAKE;
            break;
        case 2:
            lpdcb->fOutxCtsFlow = FALSE;
            lpdcb->fOutxDsrFlow = FALSE;
            lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
            lpdcb->fOutX        = TRUE;
            lpdcb->fInX         = TRUE;
            lpdcb->fRtsControl  = RTS_CONTROL_DISABLE;
            break;
        }
    }

    if(info->bConvert)
        SERIALUI_MakeBaudEnum(&lpdcb->BaudRate);
}

/***********************************************************************
 * SERIALUI_ConfigDialogProc
 *
 * Shows a dialog for configuring a COMM port
 */
343
static INT_PTR CALLBACK SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
344
{
345
    WCHAR szTitle[128], format[40];
346 347 348 349 350 351 352 353
    SERIALUI_DialogInfo *info;

    switch (uMsg)
    {
    case WM_INITDIALOG:
        info = (SERIALUI_DialogInfo*) lParam;
        if(!info)
            return FALSE;
354
        SetWindowLongPtrW(hWnd, DWLP_USER, lParam);
355 356
        GetWindowTextW(hWnd, format, ARRAY_SIZE(format));
        snprintfW(szTitle, ARRAY_SIZE(szTitle), format, info->lpszDevice);
357
        SetWindowTextW(hWnd, szTitle);
358 359 360 361 362 363 364
        SERIALUI_DCBToDialogInfo(hWnd, info);
        return TRUE;

    case WM_COMMAND:
    {
        WORD wID = LOWORD(wParam);

365
        info = (SERIALUI_DialogInfo *) GetWindowLongPtrW(hWnd, DWLP_USER);
366 367 368 369 370 371
        if(!info)
            EndDialog(hWnd,0);
        switch (wID)
        {
        case IDOK:
            SERIALUI_DialogInfoToDCB(hWnd,info);
372
            EndDialog(hWnd, ERROR_SUCCESS);
373 374
            return TRUE;
        case IDCANCEL:
375
            EndDialog(hWnd, ERROR_CANCELLED);
376 377 378 379 380
            return TRUE;
/* test code for Get/SetDefaultCommConfig begins */
        case ID_GETDEFAULT:
            {
                DWORD r,dwConfSize = sizeof (COMMCONFIG);
381
                r = GetDefaultCommConfigW(info->lpszDevice,
382 383 384 385 386 387 388 389 390 391
                          info->lpCommConfig, &dwConfSize);
                if(!r)
                    MessageBoxA(hWnd,"Failed","GetDefaultCommConfig",MB_OK);
            }
            SERIALUI_DCBToDialogInfo(hWnd, info);
            break;
        case ID_SETDEFAULT:
            {
                DWORD r;
                SERIALUI_DialogInfoToDCB(hWnd,info);
392
                r = SetDefaultCommConfigW(info->lpszDevice,
393 394 395 396 397 398 399 400 401 402 403 404 405
                          info->lpCommConfig, sizeof (COMMCONFIG));
                if(!r)
                    MessageBoxA(hWnd,"Failed","GetDefaultCommConfig",MB_OK);
            }
            break;
/* test code for Get/SetDefaultCommConfig ends */
        }
    }
    default:
        return FALSE;
    }
}

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
static LPWSTR SERIALUI_strdup( LPCSTR str )
{
    DWORD len;
    LPWSTR strW;

    if (!str)
        return NULL;
    len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
    strW = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
    MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len );
    return strW;
}

static VOID SERIALUI_strfree( LPWSTR strW )
{
421
    HeapFree( GetProcessHeap(), 0, strW );
422 423
}

424
/***********************************************************************
425
 * drvCommConfigDialogW (SERIALUI.@)
426
 *
427 428
 * Show a dialog for configuring a Serial Port.
 *
429
 */
430 431
DWORD WINAPI drvCommConfigDialogW(LPCWSTR lpszName, HWND hWndParent, LPCOMMCONFIG lpCommConfig)
{
432
    SERIALUI_DialogInfo info;
433
    INT res;
434 435 436 437 438 439

    info.lpCommConfig  = lpCommConfig;
    info.lpszDevice    = lpszName;
    info.bConvert      = FALSE;
    info.dwFlowControl = 0;

440 441
    if ((!lpCommConfig) || (!lpszName))
        return ERROR_INVALID_PARAMETER;
442

443 444 445 446 447 448 449
    if (lpCommConfig->dwSize < sizeof(COMMCONFIG))
        return ERROR_INSUFFICIENT_BUFFER;

    if (!lpszName[0])
        return ERROR_BADKEY;

    res = DialogBoxParamW( SERIALUI_hModule,
450
                           MAKEINTRESOURCEW(IDD_SERIALUICONFIG),
451
                           hWndParent,
452
                           SERIALUI_ConfigDialogProc,
453
                           (LPARAM)&info);
454 455

    return (res == -1) ? GetLastError() : res ;
456 457
}

458 459 460
/***********************************************************************
 * drvCommConfigDialogA (SERIALUI.@)
 */
461
DWORD WINAPI drvCommConfigDialogA(LPCSTR lpszName, HWND hWndParent, LPCOMMCONFIG lpCommConfig)
462 463
{
    LPWSTR strW = SERIALUI_strdup( lpszName );
464
    DWORD r = drvCommConfigDialogW( strW, hWndParent, lpCommConfig );
465 466 467 468 469 470 471 472 473 474 475
    SERIALUI_strfree( strW );
    return r;
}

static const WCHAR lpszCommKey[] = {
    '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','\\',
    'C','l','a','s','s','\\','P','o','r','t','s',0
};
static const WCHAR lpszDCB[]     = {'D','C','B',0};
476 477

/***********************************************************************
478
 * drvSetDefaultCommConfigW (SERIALUI.@)
479 480 481 482 483
 *
 * Used by Win98 KERNEL to set the default config for a COMM port
 * FIXME: uses the wrong registry key... should use a digit, not
 *        the comm port name.
 */
484 485 486
BOOL WINAPI drvSetDefaultCommConfigW(
	LPCWSTR lpszDevice, LPCOMMCONFIG lpCommConfig, DWORD dwSize)
{
487
    HKEY hKeyReg=0, hKeyPort=0;
488
    WCHAR szKeyName[100];
489
    DWORD r,dwDCBSize;
490
    static const WCHAR fmt[] = {'%','s','\\','%','s',0 };
491

492
    TRACE("%p %p %x\n",lpszDevice,lpCommConfig,dwSize);
493 494 495 496 497 498 499

    if(!lpCommConfig)
        return FALSE;

    if(dwSize < sizeof (COMMCONFIG))
        return FALSE;

500
    r = RegConnectRegistryW(NULL, HKEY_LOCAL_MACHINE, &hKeyReg);
501 502 503
    if(r != ERROR_SUCCESS)
        return FALSE;

504
    snprintfW(szKeyName, ARRAY_SIZE(szKeyName), fmt, lpszCommKey, lpszDevice);
505
    r = RegCreateKeyW(hKeyReg, szKeyName, &hKeyPort);
506 507 508
    if(r == ERROR_SUCCESS)
    {
        dwDCBSize = sizeof (DCB);
509 510
        r = RegSetValueExW( hKeyPort, lpszDCB, 0, REG_BINARY,
                            (LPBYTE)&lpCommConfig->dcb,dwDCBSize);
511
        TRACE("write key r=%d\n",r);
512 513 514 515 516 517 518 519 520
        RegCloseKey(hKeyPort);
    }

    RegCloseKey(hKeyReg);

    return (r==ERROR_SUCCESS);
}

/***********************************************************************
521 522 523 524 525 526 527 528 529 530 531 532 533
 * drvSetDefaultCommConfigA (SERIALUI.@)
 */
BOOL WINAPI drvSetDefaultCommConfigA(
	LPCSTR lpszDevice, LPCOMMCONFIG lpCommConfig, DWORD dwSize)
{
    LPWSTR strW = SERIALUI_strdup( lpszDevice );
    BOOL r = drvSetDefaultCommConfigW( strW, lpCommConfig, dwSize );
    SERIALUI_strfree( strW );
    return r;
}

/***********************************************************************
 * drvGetDefaultCommConfigW (SERIALUI.@)
534 535 536 537 538
 *
 * Used by Win9x KERNEL to get the default config for a COMM port
 * FIXME: uses the wrong registry key... should use a digit, not
 *        the comm port name.
 */
539
DWORD WINAPI drvGetDefaultCommConfigW(
540 541
	LPCWSTR lpszDevice, LPCOMMCONFIG lpCommConfig, LPDWORD lpdwSize)
{
542
    HKEY hKeyReg, hKeyPort;
543
    WCHAR szKeyName[100];
544
    DWORD r,dwSize,dwType;
545
    static const WCHAR fmt[] = {'%','s','\\','%','s',0 };
546

547
    TRACE("(%s, %p, %p) *lpdwSize: %u\n", debugstr_w(lpszDevice), lpCommConfig, lpdwSize, lpdwSize ? *lpdwSize : 0);
548

549 550 551
    if ((!lpszDevice) || (!lpCommConfig) || (!lpdwSize)) {
        return ERROR_INVALID_PARAMETER;
    }
552

553 554 555 556
    if (*lpdwSize < sizeof (COMMCONFIG)) {
        *lpdwSize = sizeof (COMMCONFIG);
        return ERROR_INSUFFICIENT_BUFFER;
    }
557

558
    /* only "com1" - "com9" is allowed */
559
    r = ARRAY_SIZE(comW);       /* len of "com\0" */
560 561
    lstrcpynW(szKeyName, lpszDevice, r);    /* simulate a lstrcmpnW */
    r--;
562 563

    if( lstrcmpiW(szKeyName, comW) ||
564 565 566
        (lpszDevice[r] < '1') || (lpszDevice[r] > '9') || lpszDevice[r+1]) {
        return ERROR_BADKEY;
    }
567 568 569 570 571

    *lpdwSize = sizeof (COMMCONFIG);
    memset(lpCommConfig, 0 , sizeof (COMMCONFIG));
    lpCommConfig->dwSize = sizeof (COMMCONFIG);
    lpCommConfig->wVersion = 1;
572
    lpCommConfig->dwProviderSubType = PST_RS232;
573

574
    r = RegConnectRegistryW(NULL, HKEY_LOCAL_MACHINE, &hKeyReg);
575
    if(r != ERROR_SUCCESS) return r;
576

577
    snprintfW(szKeyName, ARRAY_SIZE(szKeyName), fmt, lpszCommKey, lpszDevice);
578
    r = RegOpenKeyW(hKeyReg, szKeyName, &hKeyPort);
579 580 581 582
    if(r == ERROR_SUCCESS)
    {
        dwSize = sizeof (DCB);
        dwType = 0;
583 584
        r = RegQueryValueExW( hKeyPort, lpszDCB, NULL,
                             &dwType, (LPBYTE)&lpCommConfig->dcb, &dwSize);
585

586
        RegCloseKey(hKeyPort);
587 588 589 590 591
        if ((r!=ERROR_SUCCESS) || (dwType != REG_BINARY) || (dwSize != sizeof(DCB))) {
            RegCloseKey(hKeyReg);
            return ERROR_INVALID_PARAMETER;
        }

592 593 594 595 596 597 598 599 600 601 602
    }
    else
    {
        /* FIXME: default to a hardcoded commconfig */
        lpCommConfig->dcb.DCBlength = sizeof(DCB);
        lpCommConfig->dcb.BaudRate = 9600;
        lpCommConfig->dcb.fBinary = TRUE;
        lpCommConfig->dcb.fParity = FALSE;
        lpCommConfig->dcb.ByteSize = 8;
        lpCommConfig->dcb.Parity = NOPARITY;
        lpCommConfig->dcb.StopBits = ONESTOPBIT;
603
        return ERROR_SUCCESS;
604 605 606 607
    }

    RegCloseKey(hKeyReg);

608
    return r;
609
}
610 611 612 613

/***********************************************************************
 * drvGetDefaultCommConfigA (SERIALUI.@)
 */
614
DWORD WINAPI drvGetDefaultCommConfigA(
615 616 617
	LPCSTR lpszDevice, LPCOMMCONFIG lpCommConfig, LPDWORD lpdwSize)
{
    LPWSTR strW = SERIALUI_strdup( lpszDevice );
618
    DWORD r = drvGetDefaultCommConfigW( strW, lpCommConfig, lpdwSize );
619 620 621
    SERIALUI_strfree( strW );
    return r;
}