2 * Drive management UI code
4 * Copyright 2003 Mark Westcott
5 * Copyright 2003 Mike Hearn
6 * Copyright 2004 Chris Morgan
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* - Support for devices(not sure what this means) */
26 /* - Various autodetections */
37 #include <wine/debug.h>
48 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
50 typedef struct drive_entry_s
61 static BOOL updatingUI
= FALSE
;
62 static drive_entry_t
* editDriveEntry
;
63 static int lastSel
= 0; /* the last drive selected in the property sheet */
64 drive_entry_t drives
[26]; /* one for each drive letter */
66 int getDrive(char letter
)
68 return (toupper(letter
) - 'A');
71 BOOL
addDrive(char letter
, char *targetpath
, char *label
, char *serial
, uint type
)
73 int driveIndex
= getDrive(letter
);
75 if(drives
[driveIndex
].in_use
)
78 WINE_TRACE("letter == '%c', unixpath == '%s', label == '%s', serial == '%s', type == %d\n",
79 letter
, targetpath
, label
, serial
, type
);
81 drives
[driveIndex
].letter
= toupper(letter
);
82 drives
[driveIndex
].unixpath
= strdup(targetpath
);
83 drives
[driveIndex
].label
= strdup(label
);
84 drives
[driveIndex
].serial
= strdup(serial
);
85 drives
[driveIndex
].type
= type
;
86 drives
[driveIndex
].in_use
= TRUE
;
91 /* frees up the memory associated with a drive and returns the */
92 /* pNext of the given drive entry */
93 void freeDrive(drive_entry_t
*pDrive
)
95 free(pDrive
->unixpath
);
99 pDrive
->in_use
= FALSE
;
102 void setDriveLabel(drive_entry_t
*pDrive
, char *label
)
104 WINE_TRACE("pDrive->letter '%c', label = '%s'\n", pDrive
->letter
, label
);
106 pDrive
->label
= strdup(label
);
109 void setDriveSerial(drive_entry_t
*pDrive
, char *serial
)
111 WINE_TRACE("pDrive->letter '%c', serial = '%s'\n", pDrive
->letter
, serial
);
112 free(pDrive
->serial
);
113 pDrive
->serial
= strdup(serial
);
116 void setDrivePath(drive_entry_t
*pDrive
, char *path
)
118 WINE_TRACE("pDrive->letter '%c', path = '%s'\n", pDrive
->letter
, path
);
119 free(pDrive
->unixpath
);
120 pDrive
->unixpath
= strdup(path
);
123 BOOL
copyDrive(drive_entry_t
*pSrc
, drive_entry_t
*pDst
)
127 WINE_TRACE("pDst already in use\n");
131 if(!pSrc
->unixpath
) WINE_TRACE("!pSrc->unixpath\n");
132 if(!pSrc
->label
) WINE_TRACE("!pSrc->label\n");
133 if(!pSrc
->serial
) WINE_TRACE("!pSrc->serial\n");
135 pDst
->unixpath
= strdup(pSrc
->unixpath
);
136 pDst
->label
= strdup(pSrc
->label
);
137 pDst
->serial
= strdup(pSrc
->serial
);
138 pDst
->type
= pSrc
->type
;
144 BOOL
moveDrive(drive_entry_t
*pSrc
, drive_entry_t
*pDst
)
146 WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc
->letter
, pDst
->letter
);
148 if(!copyDrive(pSrc
, pDst
))
150 WINE_TRACE("copyDrive failed\n");
158 int refreshDriveDlg (HWND dialog
)
161 int doesDriveCExist
= FALSE
;
168 /* Clear the listbox */
169 SendMessageA(GetDlgItem(dialog
, IDC_LIST_DRIVES
), LB_RESETCONTENT
, 0, 0);
171 for(i
= 0; i
< 26; i
++)
177 /* skip over any unused drives */
178 if(!drives
[i
].in_use
)
181 if(drives
[i
].letter
== 'C')
182 doesDriveCExist
= TRUE
;
184 titleLen
= snprintf(title
, 0, "Drive %c:\\ %s", 'A' + i
,
186 titleLen
++; /* add a byte for the trailing null */
188 title
= malloc(titleLen
);
190 /* the %s in the item label will be replaced by the drive letter, so -1, then
191 -2 for the second %s which will be expanded to the label, finally + 1 for terminating #0 */
192 snprintf(title
, titleLen
, "Drive %c:\\ %s", 'A' + i
,
195 WINE_TRACE("title is '%s'\n", title
);
197 /* the first SendMessage call adds the string and returns the index, the second associates that index with it */
198 itemIndex
= SendMessageA(GetDlgItem(dialog
, IDC_LIST_DRIVES
), LB_ADDSTRING
,(WPARAM
) -1, (LPARAM
) title
);
199 SendMessageA(GetDlgItem(dialog
, IDC_LIST_DRIVES
), LB_SETITEMDATA
, itemIndex
, (LPARAM
) &drives
[i
]);
205 WINE_TRACE("loaded %d drives\n", driveCount
);
206 SendDlgItemMessage(dialog
, IDC_LIST_DRIVES
, LB_SETSEL
, TRUE
, lastSel
);
208 /* show the warning if there is no Drive C */
209 if (!doesDriveCExist
)
210 ShowWindow(GetDlgItem(dialog
, IDS_DRIVE_NO_C
), SW_NORMAL
);
212 ShowWindow(GetDlgItem(dialog
, IDS_DRIVE_NO_C
), SW_HIDE
);
214 /* disable or enable controls depending on whether we are editing global vs app specific config */
215 if (appSettings
== EDITING_GLOBAL
) {
216 WINE_TRACE("enabling controls\n");
217 enable(IDC_LIST_DRIVES
);
218 enable(IDC_BUTTON_ADD
);
219 enable(IDC_BUTTON_REMOVE
);
220 enable(IDC_BUTTON_EDIT
);
221 enable(IDC_BUTTON_AUTODETECT
);
224 WINE_TRACE("disabling controls\n");
225 disable(IDC_LIST_DRIVES
);
226 disable(IDC_BUTTON_ADD
);
227 disable(IDC_BUTTON_REMOVE
);
228 disable(IDC_BUTTON_EDIT
);
229 disable(IDC_BUTTON_AUTODETECT
);
237 /******************************************************************************/
238 /* The Drive Editing Dialog */
239 /******************************************************************************/
240 #define DRIVE_MASK_BIT(B) 1<<(toupper(B)-'A')
247 static code_desc_pair type_pairs
[] = {
248 {DRIVE_FIXED
, "Local hard disk"},
249 {DRIVE_REMOTE
, "Network share" },
250 {DRIVE_REMOVABLE
, "Floppy disk"},
251 {DRIVE_CDROM
, "CD-ROM"}
253 #define DRIVE_TYPE_DEFAULT 1
256 void fill_drive_droplist(long mask
, char currentLetter
, HWND hDlg
)
262 char sName
[4] = "A:";
264 for( i
=0, count
=0, selection
=-1, next_letter
=-1; i
<= 'Z'-'A'; ++i
) {
265 if( mask
& DRIVE_MASK_BIT('A'+i
) ) {
269 index
= SendDlgItemMessage( hDlg
, IDC_COMBO_LETTER
, CB_ADDSTRING
, 0, (LPARAM
) sName
);
271 if( toupper(currentLetter
) == 'A' + i
) {
275 if( i
>= 2 && next_letter
== -1){ /*default drive is first one of C-Z */
283 if( selection
== -1 ) {
284 selection
= next_letter
;
287 SendDlgItemMessage( hDlg
, IDC_COMBO_LETTER
, CB_SETCURSEL
, selection
, 0 );
290 #define BOX_MODE_CD_ASSIGN 1
291 #define BOX_MODE_CD_AUTODETECT 2
292 #define BOX_MODE_NONE 3
293 #define BOX_MODE_NORMAL 4
294 void enable_labelserial_box(HWND dialog
, int mode
)
296 WINE_TRACE("mode=%d\n", mode
);
298 case BOX_MODE_CD_ASSIGN
:
299 /* enable(IDC_RADIO_AUTODETECT); */
300 enable(IDC_RADIO_ASSIGN
);
301 disable(IDC_EDIT_DEVICE
);
302 disable(IDC_BUTTON_BROWSE_DEVICE
);
303 enable(IDC_EDIT_SERIAL
);
304 enable(IDC_EDIT_LABEL
);
305 enable(IDC_STATIC_SERIAL
);
306 enable(IDC_STATIC_LABEL
);
309 case BOX_MODE_CD_AUTODETECT
:
310 /* enable(IDC_RADIO_AUTODETECT); */
311 enable(IDC_RADIO_ASSIGN
);
312 enable(IDC_EDIT_DEVICE
);
313 enable(IDC_BUTTON_BROWSE_DEVICE
);
314 disable(IDC_EDIT_SERIAL
);
315 disable(IDC_EDIT_LABEL
);
316 disable(IDC_STATIC_SERIAL
);
317 disable(IDC_STATIC_LABEL
);
321 disable(IDC_RADIO_AUTODETECT
);
322 disable(IDC_RADIO_ASSIGN
);
323 disable(IDC_EDIT_DEVICE
);
324 disable(IDC_BUTTON_BROWSE_DEVICE
);
325 disable(IDC_EDIT_SERIAL
);
326 disable(IDC_EDIT_LABEL
);
327 disable(IDC_STATIC_SERIAL
);
328 disable(IDC_STATIC_LABEL
);
331 case BOX_MODE_NORMAL
:
332 disable(IDC_RADIO_AUTODETECT
);
333 enable(IDC_RADIO_ASSIGN
);
334 disable(IDC_EDIT_DEVICE
);
335 disable(IDC_BUTTON_BROWSE_DEVICE
);
336 enable(IDC_EDIT_SERIAL
);
337 enable(IDC_EDIT_LABEL
);
338 enable(IDC_STATIC_SERIAL
);
339 enable(IDC_STATIC_LABEL
);
344 /* This function produces a mask for each drive letter that isn't currently used. Each bit of the long result
345 * represents a letter, with A being the least significant bit, and Z being the most significant.
347 * To calculate this, we loop over each letter, and see if we can get a drive entry for it. If so, we
348 * set the appropriate bit. At the end, we flip each bit, to give the desired result.
350 * The letter parameter is always marked as being available. This is so the edit dialog can display the
351 * currently used drive letter alongside the available ones.
353 long drive_available_mask(char letter
)
361 for(i
= 0; i
< 26; i
++)
363 if(!drives
[i
].in_use
) continue;
364 result
|= (1 << (toupper(drives
[i
].letter
) - 'A'));
368 if (letter
) result
|= DRIVE_MASK_BIT(letter
);
370 WINE_TRACE( "finished drive letter loop with %lx\n", result
);
374 void advancedDriveEditDialog(HWND hDlg
, BOOL showAdvanced
)
376 #define ADVANCED_DELTA 120
379 static BOOL got_initial_ok_position
= FALSE
;
381 static RECT windowpos
; /* we only use the height of this rectangle */
382 static BOOL got_initial_window_position
= FALSE
;
384 static RECT current_window
;
390 if(!got_initial_ok_position
)
393 GetWindowRect(GetDlgItem(hDlg
, ID_BUTTON_OK
), &okpos
);
396 ScreenToClient(hDlg
, &pt
);
397 okpos
.right
+= (pt
.x
- okpos
.left
);
398 okpos
.bottom
+= (pt
.y
- okpos
.top
);
401 got_initial_ok_position
= TRUE
;
404 if(!got_initial_window_position
)
406 GetWindowRect(hDlg
, &windowpos
);
407 got_initial_window_position
= TRUE
;
414 text
= "Hide Advanced";
418 offset
= ADVANCED_DELTA
;
419 text
= "Show Advanced";
422 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC_TYPE
), state
);
423 ShowWindow(GetDlgItem(hDlg
, IDC_COMBO_TYPE
), state
);
424 ShowWindow(GetDlgItem(hDlg
, IDC_BOX_LABELSERIAL
), state
);
425 ShowWindow(GetDlgItem(hDlg
, IDC_RADIO_AUTODETECT
), state
);
426 ShowWindow(GetDlgItem(hDlg
, IDC_RADIO_ASSIGN
), state
);
427 ShowWindow(GetDlgItem(hDlg
, IDC_EDIT_LABEL
), state
);
428 ShowWindow(GetDlgItem(hDlg
, IDC_EDIT_DEVICE
), state
);
429 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC_LABEL
), state
);
430 ShowWindow(GetDlgItem(hDlg
, IDC_BUTTON_BROWSE_DEVICE
), state
);
431 SetWindowPos(GetDlgItem(hDlg
, ID_BUTTON_OK
),
433 okpos
.left
, okpos
.top
- offset
, okpos
.right
- okpos
.left
,
434 okpos
.bottom
- okpos
.top
,
437 /* resize the parent window */
438 GetWindowRect(hDlg
, ¤t_window
);
443 windowpos
.right
- windowpos
.left
,
444 windowpos
.bottom
- windowpos
.top
- offset
,
447 /* update the button text based on the state */
448 SetWindowText(GetDlgItem(hDlg
, IDC_BUTTON_SHOW_HIDE_ADVANCED
),
453 void refreshDriveEditDialog(HWND dialog
) {
459 int i
, selection
= -1;
466 fill_drive_droplist( drive_available_mask( editDriveEntry
->letter
), editDriveEntry
->letter
, dialog
);
469 path
= editDriveEntry
->unixpath
;
471 WINE_TRACE("set path control text to '%s'\n", path
);
472 SetWindowText(GetDlgItem(dialog
, IDC_EDIT_PATH
), path
);
473 } else WINE_WARN("no Path field?\n");
476 type
= editDriveEntry
->type
;
478 for(i
= 0; i
< sizeof(type_pairs
)/sizeof(code_desc_pair
); i
++) {
479 SendDlgItemMessage(dialog
, IDC_COMBO_TYPE
, CB_ADDSTRING
, 0,
480 (LPARAM
) type_pairs
[i
].sDesc
);
481 if(type_pairs
[i
].sCode
== type
){
486 if( selection
== -1 ) selection
= DRIVE_TYPE_DEFAULT
;
487 SendDlgItemMessage(dialog
, IDC_COMBO_TYPE
, CB_SETCURSEL
, selection
, 0);
488 } else WINE_WARN("no Type field?\n");
491 /* removeable media properties */
492 label
= editDriveEntry
->label
;
494 SendDlgItemMessage(dialog
, IDC_EDIT_LABEL
, WM_SETTEXT
, 0,(LPARAM
)label
);
495 } else WINE_WARN("no Label field?\n");
497 /* set serial edit text */
498 serial
= editDriveEntry
->serial
;
500 SendDlgItemMessage(dialog
, IDC_EDIT_SERIAL
, WM_SETTEXT
, 0,(LPARAM
)serial
);
501 } else WINE_WARN("no Serial field?\n");
503 /* TODO: get the device here to put into the edit box */
504 device
= "Not implemented yet";
506 SendDlgItemMessage(dialog
, IDC_EDIT_DEVICE
, WM_SETTEXT
, 0,(LPARAM
)device
);
507 } else WINE_WARN("no Device field?\n");
509 selection
= IDC_RADIO_ASSIGN
;
510 if ((type
== DRIVE_CDROM
) || (type
== DRIVE_REMOVABLE
)) {
513 selection
= IDC_RADIO_AUTODETECT
;
514 enable_labelserial_box(dialog
, BOX_MODE_CD_AUTODETECT
);
517 selection
= IDC_RADIO_ASSIGN
;
518 enable_labelserial_box(dialog
, BOX_MODE_CD_ASSIGN
);
523 enable_labelserial_box(dialog
, BOX_MODE_NORMAL
);
524 selection
= IDC_RADIO_ASSIGN
;
527 CheckRadioButton( dialog
, IDC_RADIO_AUTODETECT
, IDC_RADIO_ASSIGN
, selection
);
534 /* storing the drive propsheet HWND here is a bit ugly, but the simplest solution for now */
535 static HWND driveDlgHandle
;
537 void onEditChanged(HWND hDlg
, WORD controlID
) {
538 WINE_TRACE("controlID=%d\n", controlID
);
540 case IDC_EDIT_LABEL
: {
541 char *label
= getDialogItemText(hDlg
, controlID
);
542 if(!label
) label
= strdup("");
543 setDriveLabel(editDriveEntry
, label
);
544 refreshDriveDlg(driveDlgHandle
);
545 if (label
) free(label
);
548 case IDC_EDIT_PATH
: {
549 char *path
= getDialogItemText(hDlg
, controlID
);
550 if (!path
) path
= strdup("fake_windows"); /* default to assuming fake_windows in the .wine directory */
551 WINE_TRACE("got path from control of '%s'\n", path
);
552 setDrivePath(editDriveEntry
, path
);
556 case IDC_EDIT_SERIAL
: {
557 char *serial
= getDialogItemText(hDlg
, controlID
);
558 if(!serial
) serial
= strdup("");
559 setDriveSerial(editDriveEntry
, serial
);
560 if (serial
) free (serial
);
563 case IDC_EDIT_DEVICE
: {
564 char *device
= getDialogItemText(hDlg
,controlID
);
565 /* TODO: handle device if/when it makes sense to do so.... */
566 if (device
) free(device
);
567 refreshDriveDlg(driveDlgHandle
);
573 /* edit a drive entry */
574 INT_PTR CALLBACK
DriveEditDlgProc (HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
577 static BOOL advanced
= FALSE
;
581 EndDialog(hDlg
, wParam
);
584 case WM_INITDIALOG
: {
585 enable_labelserial_box(hDlg
, BOX_MODE_NORMAL
);
586 advancedDriveEditDialog(hDlg
, advanced
);
587 editDriveEntry
= (drive_entry_t
*)lParam
;
588 refreshDriveEditDialog(hDlg
);
592 switch (LOWORD(wParam
)) {
594 if (HIWORD(wParam
) != CBN_SELCHANGE
) break;
595 selection
= SendDlgItemMessage( hDlg
, IDC_COMBO_TYPE
, CB_GETCURSEL
, 0, 0);
596 if( selection
== 2 || selection
== 3 ) { /* cdrom or floppy */
597 if (IsDlgButtonChecked(hDlg
, IDC_RADIO_AUTODETECT
))
598 enable_labelserial_box(hDlg
, BOX_MODE_CD_AUTODETECT
);
600 enable_labelserial_box(hDlg
, BOX_MODE_CD_ASSIGN
);
603 enable_labelserial_box( hDlg
, BOX_MODE_NORMAL
);
605 editDriveEntry
->type
= type_pairs
[selection
].sCode
;
608 case IDC_COMBO_LETTER
: {
609 int item
= SendDlgItemMessage(hDlg
, IDC_COMBO_LETTER
, CB_GETCURSEL
, 0, 0);
611 SendDlgItemMessage(hDlg
, IDC_COMBO_LETTER
, CB_GETLBTEXT
, item
, (LPARAM
) newLetter
);
613 if (HIWORD(wParam
) != CBN_SELCHANGE
) break;
614 if (newLetter
[0] == editDriveEntry
->letter
) break;
616 WINE_TRACE("changing drive letter to %c\n", newLetter
[0]);
617 moveDrive(editDriveEntry
, &drives
[getDrive(newLetter
[0])]);
618 editDriveEntry
= &drives
[getDrive(newLetter
[0])];
619 refreshDriveDlg(driveDlgHandle
);
623 case IDC_BUTTON_BROWSE_PATH
:
627 case IDC_RADIO_AUTODETECT
: {
629 WINE_FIXME("Implement autodetection\n");
630 enable_labelserial_box(hDlg
, BOX_MODE_CD_AUTODETECT
);
631 refreshDriveDlg(driveDlgHandle
);
635 case IDC_RADIO_ASSIGN
:
638 edit
= getDialogItemText(hDlg
, IDC_EDIT_LABEL
);
639 if(!edit
) edit
= strdup("");
640 setDriveLabel(editDriveEntry
, edit
);
643 serial
= getDialogItemText(hDlg
, IDC_EDIT_SERIAL
);
644 if(!serial
) serial
= strdup("");
645 setDriveSerial(editDriveEntry
, serial
);
648 /* TODO: we don't have a device at this point */
649 /* setDriveValue(editWindowLetter, "Device", NULL); */
650 enable_labelserial_box(hDlg
, BOX_MODE_CD_ASSIGN
);
651 refreshDriveDlg(driveDlgHandle
);
655 case IDC_BUTTON_SHOW_HIDE_ADVANCED
:
656 advanced
= (advanced
== TRUE
) ? FALSE
: TRUE
; /* toggle state */
657 advancedDriveEditDialog(hDlg
, advanced
);
661 EndDialog(hDlg
, wParam
);
665 if (HIWORD(wParam
) == EN_CHANGE
) onEditChanged(hDlg
, LOWORD(wParam
));
671 void onAddDriveClicked(HWND hDlg
) {
672 /* we should allocate a drive letter automatically. We also need some way to let the user choose the mapping point,
673 for now we will just force them to enter a path automatically, with / being the default. In future we should
674 be able to temporarily map / then invoke the directory chooser dialog. */
676 char newLetter
= 'C'; /* we skip A and B, they are historically floppy drives */
677 long mask
= ~drive_available_mask(0); /* the mask is now which drives aren't available */
679 while (mask
& (1 << (newLetter
- 'A'))) {
681 if (newLetter
> 'Z') {
682 MessageBox(NULL
, "You cannot add any more drives.\n\nEach drive must have a letter, from A to Z, so you cannot have more than 26", "", MB_OK
| MB_ICONEXCLAMATION
);
686 WINE_TRACE("allocating drive letter %c\n", newLetter
);
688 if(newLetter
== 'C') {
689 addDrive(newLetter
, "fake_windows", "System Drive", "", DRIVE_FIXED
);
691 addDrive(newLetter
, "/", "", "", DRIVE_FIXED
);
694 refreshDriveDlg(driveDlgHandle
);
696 DialogBoxParam(NULL
, MAKEINTRESOURCE(IDD_DRIVE_EDIT
), NULL
, (DLGPROC
) DriveEditDlgProc
, (LPARAM
) &(drives
[getDrive(newLetter
)]));
699 void onDriveInitDialog(void)
701 char *pDevices
, *pDev
;
708 /* setup the drives array */
709 pDev
= pDevices
= malloc(512);
710 ret
= GetLogicalDriveStrings(512, pDevices
);
712 /* make all devices unused */
713 for(i
= 0; i
< 26; i
++)
715 drives
[i
].letter
= 'A' + i
;
716 drives
[i
].in_use
= FALSE
;
723 CHAR volumeNameBuffer
[512];
725 CHAR serialNumberString
[256];
726 DWORD maxComponentLength
;
727 DWORD fileSystemFlags
;
728 CHAR fileSystemName
[128];
732 char targetpath
[256];
734 *pDevices
= toupper(*pDevices
);
736 WINE_TRACE("pDevices == '%s'\n", pDevices
);
738 volumeNameBuffer
[0] = 0;
740 retval
= GetVolumeInformation(pDevices
,
742 sizeof(volumeNameBuffer
),
747 sizeof(fileSystemName
));
750 WINE_TRACE("GetVolumeInformation() for '%s' failed, setting serialNumber to 0\n", pDevices
);
755 WINE_TRACE("serialNumber: '0x%lX'\n", serialNumber
);
757 /* build rootpath for GetDriveType() */
758 strncpy(rootpath
, pDevices
, sizeof(rootpath
));
759 pathlen
= strlen(rootpath
);
760 /* ensure that we have a backslash on the root path */
761 if((rootpath
[pathlen
- 1] != '\\') &&
762 (pathlen
< sizeof(rootpath
)))
764 rootpath
[pathlen
] = '\\';
765 rootpath
[pathlen
+ 1] = 0;
768 strncpy(simplepath
, pDevices
, 2); /* QueryDosDevice() requires no trailing backslash */
770 QueryDosDevice(simplepath
, targetpath
, sizeof(targetpath
));
772 snprintf(serialNumberString
, sizeof(serialNumberString
), "%lX", serialNumber
);
773 WINE_TRACE("serialNumberString: '%s'\n", serialNumberString
);
774 addDrive(*pDevices
, targetpath
, volumeNameBuffer
, serialNumberString
, GetDriveType(rootpath
));
776 ret
-=strlen(pDevices
);
777 pDevices
+=strlen(pDevices
);
779 /* skip over any nulls */
780 while((*pDevices
== 0) && (ret
))
789 WINE_TRACE("found %d drives\n", i
);
795 void applyDriveChanges(void)
799 CHAR targetpath
[256];
801 CHAR volumeNameBuffer
[512];
803 DWORD maxComponentLength
;
804 DWORD fileSystemFlags
;
805 CHAR fileSystemName
[128];
811 /* add each drive and remove as we go */
812 for(i
= 0; i
< 26; i
++)
814 defineDevice
= FALSE
;
816 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
819 if(QueryDosDevice(devicename
, targetpath
, sizeof(targetpath
)))
824 /* if we found a drive and have a drive then compare things */
825 if(foundDrive
&& drives
[i
].in_use
)
827 char newSerialNumberText
[256];
829 volumeNameBuffer
[0] = 0;
831 WINE_TRACE("drives[i].letter: '%c'\n", drives
[i
].letter
);
833 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
834 retval
= GetVolumeInformation(devicename
,
836 sizeof(volumeNameBuffer
),
841 sizeof(fileSystemName
));
844 WINE_TRACE(" GetVolumeInformation() for '%s' failed\n", devicename
);
845 WINE_TRACE(" Skipping this drive\n");
847 continue; /* skip this drive */
850 snprintf(newSerialNumberText
, sizeof(newSerialNumberText
), "%lX", serialNumber
);
852 WINE_TRACE(" current path: '%s', new path: '%s'\n",
853 targetpath
, drives
[i
].unixpath
);
854 WINE_TRACE(" current label: '%s', new label: '%s'\n",
855 volumeNameBuffer
, drives
[i
].label
);
856 WINE_TRACE(" current serial: '%s', new serial: '%s'\n",
857 newSerialNumberText
, drives
[i
].serial
);
859 /* compare to what we have */
860 /* do we have the same targetpath? */
861 if(strcmp(drives
[i
].unixpath
, targetpath
) ||
862 strcmp(drives
[i
].label
, volumeNameBuffer
) ||
863 strcmp(drives
[i
].serial
, newSerialNumberText
))
866 WINE_TRACE(" making changes to drive '%s'\n", devicename
);
869 WINE_TRACE(" no changes to drive '%s'\n", devicename
);
871 } else if(foundDrive
&& !drives
[i
].in_use
)
873 /* remove this drive */
874 if(!DefineDosDevice(DDD_REMOVE_DEFINITION
, devicename
, drives
[i
].unixpath
))
876 WINE_ERR("unable to remove devicename of '%s', targetpath of '%s'\n",
877 devicename
, drives
[i
].unixpath
);
881 WINE_TRACE("removed devicename of '%s', targetpath of '%s'\n",
882 devicename
, drives
[i
].unixpath
);
884 } else if(drives
[i
].in_use
) /* foundDrive must be false from the above check */
889 /* adding and modifying are the same steps */
897 char driveValue
[256];
899 /* define this drive */
900 /* DefineDosDevice() requires that NO trailing slash be present */
901 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
902 if(!DefineDosDevice(DDD_RAW_TARGET_PATH
, devicename
, drives
[i
].unixpath
))
904 WINE_ERR(" unable to define devicename of '%s', targetpath of '%s'\n",
905 devicename
, drives
[i
].unixpath
);
909 WINE_TRACE(" added devicename of '%s', targetpath of '%s'\n",
910 devicename
, drives
[i
].unixpath
);
912 /* SetVolumeLabel() requires a trailing slash */
913 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
914 if(!SetVolumeLabel(devicename
, drives
[i
].label
))
916 WINE_ERR("unable to set volume label for devicename of '%s', label of '%s'\n",
917 devicename
, drives
[i
].label
);
921 WINE_TRACE(" set volume label for devicename of '%s', label of '%s'\n",
922 devicename
, drives
[i
].label
);
926 /* Set the drive type in the registry */
927 if(drives
[i
].type
== DRIVE_FIXED
)
929 else if(drives
[i
].type
== DRIVE_REMOTE
)
930 typeText
= "network";
931 else if(drives
[i
].type
== DRIVE_REMOVABLE
)
933 else /* must be DRIVE_CDROM */
937 snprintf(driveValue
, sizeof(driveValue
), "%c:", toupper(drives
[i
].letter
));
939 retval
= RegOpenKey(HKEY_LOCAL_MACHINE
,
940 "Software\\Wine\\Drives",
943 if(retval
!= ERROR_SUCCESS
)
945 WINE_TRACE(" Unable to open '%s'\n", "Software\\Wine\\Drives");
948 retval
= RegSetValueEx(
954 strlen(typeText
) + 1);
955 if(retval
!= ERROR_SUCCESS
)
957 WINE_TRACE(" Unable to set value of '%s' to '%s'\n",
958 driveValue
, typeText
);
961 WINE_TRACE(" Finished setting value of '%s' to '%s'\n",
962 driveValue
, typeText
);
967 /* Set the drive serial number via a .windows-serial file in */
968 /* the targetpath directory */
969 snprintf(filename
, sizeof(filename
), "%c:\\.windows-serial", drives
[i
].letter
);
970 WINE_TRACE(" Putting serial number of '%ld' into file '%s'\n",
971 serialNumber
, filename
);
972 hFile
= CreateFile(filename
,
977 FILE_ATTRIBUTE_NORMAL
,
981 WINE_TRACE(" writing serial number of '%s'\n", drives
[i
].serial
);
984 strlen(drives
[i
].serial
),
995 WINE_TRACE(" CreateFile() error with file '%s'\n", filename
);
999 /* if this drive is in use we should free it up */
1000 if(drives
[i
].in_use
)
1002 freeDrive(&drives
[i
]); /* free up the string memory */
1009 DriveDlgProc (HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1012 drive_entry_t
*pDrive
;
1016 onDriveInitDialog();
1019 switch (LOWORD(wParam
)) {
1020 case IDC_LIST_DRIVES
:
1021 /* double click should open the edit window for the chosen drive */
1022 if (HIWORD(wParam
) == LBN_DBLCLK
)
1023 SendMessageA(hDlg
, WM_COMMAND
, IDC_BUTTON_EDIT
, 0);
1025 if (HIWORD(wParam
) == LBN_SELCHANGE
) lastSel
= SendDlgItemMessage(hDlg
, IDC_LIST_DRIVES
, LB_GETCURSEL
, 0, 0);
1028 case IDC_BUTTON_ADD
:
1029 onAddDriveClicked(hDlg
);
1032 case IDC_BUTTON_REMOVE
:
1033 if (HIWORD(wParam
) != BN_CLICKED
) break;
1034 nItem
= SendDlgItemMessage(hDlg
, IDC_LIST_DRIVES
, LB_GETCURSEL
, 0, 0);
1035 pDrive
= (drive_entry_t
*)SendDlgItemMessage(hDlg
, IDC_LIST_DRIVES
, LB_GETITEMDATA
, nItem
, 0);
1037 refreshDriveDlg(driveDlgHandle
);
1040 case IDC_BUTTON_EDIT
:
1041 if (HIWORD(wParam
) != BN_CLICKED
) break;
1042 nItem
= SendMessage(GetDlgItem(hDlg
, IDC_LIST_DRIVES
), LB_GETCURSEL
, 0, 0);
1043 pDrive
= (drive_entry_t
*)SendMessage(GetDlgItem(hDlg
, IDC_LIST_DRIVES
), LB_GETITEMDATA
, nItem
, 0);
1044 DialogBoxParam(NULL
, MAKEINTRESOURCE(IDD_DRIVE_EDIT
), NULL
, (DLGPROC
) DriveEditDlgProc
, (LPARAM
) pDrive
);
1047 case IDC_BUTTON_AUTODETECT
:
1053 case WM_NOTIFY
: switch(((LPNMHDR
)lParam
)->code
) {
1054 case PSN_KILLACTIVE
:
1055 WINE_TRACE("PSN_KILLACTIVE\n");
1056 SetWindowLong(hDlg
, DWL_MSGRESULT
, FALSE
);
1059 applyDriveChanges();
1060 SetWindowLong(hDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
1063 driveDlgHandle
= hDlg
;
1064 refreshDriveDlg (driveDlgHandle
);