Solved

How to persist virtual drives mappings across a reboot?

Posted on 2006-07-14
5
571 Views
Last Modified: 2013-11-18
Hi my dear friends!

I would like to have my virtual drive mappings survive a reboot. Perhaps using the registry?

Regs Paul
0
Comment
Question by:PeterdeB
  • 2
  • 2
5 Comments
 
LVL 26

Accepted Solution

by:
Russell Libby earned 500 total points
ID: 17113133
Paul,

Ok, I checked into this and SetVolumeMountPoint can only be used for physical mount points like \Device\etc and does not work for these virtual mappings we are dealing with. All documentation indicates that these settings are not persisted across a reboot, and I found no good examples of how to handle this.  After some thought, I came up with the following: when we persist the info for the drive, we create an entry in the registry key:

HKCU\Software\Microsoft\Windows\CurrentVersion\Run

with a value of:

VirtualDrives=cmd /c subst X: "Path Setting"

if mutliple drives have been persisted, then the command looks like

VirtualDrives=cmd /c subst X: "Path Setting" & subst X: "Path Setting"  & subst X: "Path Setting"

So when the user logs in, the Run will execute our subst command, and is able to handle mutliple settings because the CMD processor sees the "&" as a command break. The small downside is that the user sees a quick console window during startup, but the upside is that the code can deal with adding/removing these settings directly, and does not rely upon some other process or program to perform the mappings during startup (another alternative).  I have also taken the time to wrap the code into a container object and interface item class, where the container can return useful drive listing information, as well as an array [A]..[Z] of SymbolicDrive items. To reduce your learning curve and implementation time, I also revamped the code I gave you earlier today so that it works on this new unit.

Regards,
Russell

unit SymbolicLink;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  SymbolicLink
//   Author      :  rllibby
//   Date        :  07.14.2006
//   Description :  Symbolic link handling class for Delphi. The code used
//                  within the class relies upon Windows 2000 or higher, and
//                  allows for persisting drives by creating a Run command
//                  that has the list of drives (paths) to SUBST when the PC
//                  restarts and is logged back in.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes;

////////////////////////////////////////////////////////////////////////////////
//   Constants for persisting virtual drives
////////////////////////////////////////////////////////////////////////////////
const
  SYM_PERSIST_KEY   =  'Software\Microsoft\Windows\CurrentVersion\Run';
  SYM_PERSIST_NAME  =  'VirtualDrives';
  SYM_COMMAND       =  'cmd /c ';
  SYM_COMMAND_TAIL  =  '" & ';
  SYM_COMMAND_SEP   =  ' & ';
  SYM_COMMAND_DRIVE =  'subst %s: "';
  SYM_COMMAND_EXEC  =  'subst %s: "%s"';
  SYM_COMMAND_MAX   =  8192;

////////////////////////////////////////////////////////////////////////////////
//   Interface for symbolic drive link
////////////////////////////////////////////////////////////////////////////////
type
  ISymbolicDrive    =  interface(IUnknown)
     ['{6430AC48-3C1A-45AA-A5C5-FE0785756911}']
     // Returns the drive letter only
     function       DriveLetter: Char;
     // Determines if the drive letter is available for use (currently unmapped)
     function       IsAvailable: Boolean;
     // Determines if the drive letter is a virtual mapping for a physical path
     function       IsPath: Boolean;
     // Determines if the drive letter has been persisted to the registry
     function       IsPersisted: Boolean;
     // Persist or remove the registry info for this drive, depeding on the Keep parameter
     procedure      Persist(Keep: Boolean = True);
     // Returns the device of path that a drive is mapped to
     function       GetDevicePath: String;
     // Sets the virtual device path. If already mapped, the current mapping is removed.
     // If the new path is blank, the drive is not remapped.
     procedure      SetDevicePath(Value: String);
     // Property to get / set the device path
     property       DevicePath: String read GetDevicePath write SetDevicePath;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Class type for link container
////////////////////////////////////////////////////////////////////////////////
type
  TSymbolicLinks    =  class(TObject)
  private
     // Private decalarations
     FItems:        TInterfaceList;
     function       IndexFromChar(Value: Char): Integer;
  protected
     // Private decalarations
     function       GetSymbolicDrive(Index: Char): ISymbolicDrive;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     procedure      GetAvailableDriveList(List: TStrings);
     procedure      GetDeviceDriveList(List: TStrings; DriveLetterOnly: Boolean = True);
     procedure      GetVirtualDriveList(List: TStrings; DriveLetterOnly: Boolean = True);
     property       Items[Index: Char]: ISymbolicDrive read GetSymbolicDrive; default;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Global variable
////////////////////////////////////////////////////////////////////////////////
var
  SymbolicLinks:    TSymbolicLinks;

implementation

////////////////////////////////////////////////////////////////////////////////
//   Protected implementation for symbolic drive interface
////////////////////////////////////////////////////////////////////////////////
type
  TSymbolicDrive    =  class(TInterfacedObject, ISymbolicDrive)
  private
     // Private declarations
     FDrive:        Char;
     function       IndexFromChar(Value: Char): Integer;
  protected
     // Protected declarations
     function       DriveLetter: Char;
     function       IsAvailable: Boolean;
     function       IsPath: Boolean;
     function       IsPersisted: Boolean;
     procedure      Persist(Keep: Boolean = True);
     function       GetDevicePath: String;
     procedure      SetDevicePath(Value: String);
  public
     // Public declarations
     constructor    Create(Drive: Char);
     property       DevicePath: String read GetDevicePath write SetDevicePath;
  end;

//// Utility functions /////////////////////////////////////////////////////////
procedure AddDrive(cDrive: Char; lpszPath: PChar; lpszCommand: PChar);
begin

  // Check path
  if Assigned(lpszPath) then
  begin
     // Add base command if needed
     if (lpszCommand^ = #0) then StrCopy(lpszCommand, SYM_COMMAND);
     // Push past the command
     Inc(lpszCommand, Length( SYM_COMMAND));
     // Is this the first exec command
     if (lpszCommand^ = #0) then
        // Add the new command
        StrCopy(lpszCommand, PChar(LowerCase(Format(SYM_COMMAND_EXEC, [cDrive, lpszPath]))))
     else
     begin
        // Append the new command
        StrCat(lpszCommand, SYM_COMMAND_SEP);
        StrCat(lpszCommand, PChar(LowerCase(Format(SYM_COMMAND_EXEC, [cDrive, lpszPath]))));
     end;
  end;

end;

function RemoveDrive(cDrive: Char; lpszCommand: PChar): Boolean;
var  lpszExec:      PChar;
     lpszTail:      PChar;
begin

  // Set default result
  result:=False;

  // Check command
  if (StrLComp(lpszCommand, SYM_COMMAND, Length(SYM_COMMAND)) = 0) then
  begin
     // Skip the command portion
     Inc(lpszCommand, Length(SYM_COMMAND));
     // Scan for drive command
     lpszExec:=StrPos(lpszCommand, PChar(LowerCase(Format(SYM_COMMAND_DRIVE, [cDrive]))));
     // Check command exec pointer
     if Assigned(lpszExec) then
     begin
        // Drive is found, now remove it
        result:=True;
        // Get tail end of exec command
        lpszTail:=StrPos(lpszExec, SYM_COMMAND_TAIL);
        // Now check for first, middle, last or only
        if (lpszExec = lpszCommand) then
        begin
           // First or only
           if Assigned(lpszTail) then
           begin
              // First item
              Inc(lpszTail, Length(SYM_COMMAND_TAIL));
              StrCopy(lpszExec, lpszTail);
           end
           else
              // Remove only item
              lpszExec^:=#0;
        end
        else
        begin
           // Middle or last item
           if Assigned(lpszTail) then
           begin
              // Middle item
              Inc(lpszTail, Length(SYM_COMMAND_TAIL));
              StrCopy(lpszExec, lpszTail);
           end
           else
           begin
              // Last item, need to back up to remove the preceding ' & '
              if (lpszExec > (lpszCommand + Length(SYM_COMMAND_SEP))) then Dec(lpszExec, Length(SYM_COMMAND_SEP));
              // Truncate
              lpszExec^:=#0;
           end;
        end;
        // Check command string now, is there anything left?
        if (lpszCommand^ = #0) then
        begin
           // Drop back
           Dec(lpszCommand, Length(SYM_COMMAND));
           // Truncate
           lpszCommand^:=#0;
        end;
     end;
  end;

end;

//// TSymbolicDrive ////////////////////////////////////////////////////////////
function TSymbolicDrive.IsPersisted: Boolean;
var  hSymKey:       HKEY;
     lpszSymCmd:    PChar;
     dwSize:        DWORD;
begin

  // Open th registry key
  if (RegOpenKey(HKEY_CURRENT_USER, SYM_PERSIST_KEY, hSymKey) = ERROR_SUCCESS) then
  begin
     // Resource protection
     try
        // Set buffer size to use
        dwSize:=SYM_COMMAND_MAX;
        // Allocate memory for string read
        lpszSymCmd:=AllocMem(dwSize);
        // Resource protection
        try
           // Query for the virtual drive command
           if (RegQueryValueEx(hSymKey, SYM_PERSIST_NAME, nil, nil, PByte(lpszSymCmd), @dwSize) = ERROR_SUCCESS) then
              // Scan command string for the exec command for this drive
              result:=RemoveDrive(FDrive, StrLower(lpszSymCmd))
           else
              // Failed to get value
              result:=False;
        finally
           // Free memory
           FreeMem(lpszSymCmd);
        end;
     finally
        // Close the key
        RegCloseKey(hSymKey);
     end;
  end
  else
     // Not persisted
     result:=False;

end;

procedure TSymbolicDrive.Persist(Keep: Boolean = True);
var  hSymKey:       HKEY;
     lpszSymCmd:    PChar;
     dwSize:        DWORD;
begin

  // Has to be mapped to a path
  if IsPath and (RegCreateKey(HKEY_CURRENT_USER, SYM_PERSIST_KEY, hSymKey) = ERROR_SUCCESS) then
  begin
     // Resource protection
     try
        // Set buffer size to use
        dwSize:=SYM_COMMAND_MAX;
        // Allocate memory for string read and parsing
        lpszSymCmd:=AllocMem(dwSize);
        // Resource protection
        try
           // Query for the virtual drive command
           RegQueryValueEx(hSymKey, SYM_PERSIST_NAME, nil, nil, PByte(lpszSymCmd), @dwSize);
           // Remove the drive
           RemoveDrive(FDrive, StrLower(lpszSymCmd));
           // Check for persistence
           if Keep then AddDrive(FDrive, PChar(GetDevicePath), lpszSymCmd);
           // Write the value back
           if (lpszSymCmd^ = #0) then
              // Remove the value
              RegDeleteValue(hSymKey, SYM_PERSIST_NAME)
           else
              // Update the value
              RegSetValueEx(hSymKey, SYM_PERSIST_NAME, 0, REG_SZ, lpszSymCmd, Succ(StrLen(lpszSymCmd)));
        finally
           // Free memory
           FreeMem(lpszSymCmd);
        end;
     finally
        // Close the key
        RegCloseKey(hSymKey);
     end;
  end;

end;

function TSymbolicDrive.DriveLetter: Char;
begin

  // Return the drive letter
  result:=FDrive;

end;

function TSymbolicDrive.IsAvailable: Boolean;
begin

  // Get drive bits and check drive state
  result:=((GetLogicalDrives and (1 shl IndexFromChar(FDrive))) = 0);

end;

function TSymbolicDrive.IsPath: Boolean;
var  szPath:        String;
begin

  // Check availability
  if IsAvailable then
     // Not linked
     result:=False
  else
  begin
     // Get the device path
     szPath:=GetDevicePath;
     // Check device path
     result:=(Length(szPath) >= 2) and (szPath[1] in ['A'..'Z', 'a'..'z']) and (szPath[2] = ':');
  end;

end;

function TSymbolicDrive.GetDevicePath: String;
var  lpszPath:      Array [0..MAX_PATH] of Char;
     lpszReturn:    PChar;
     dwSize:        DWORD;
begin

  // Allocate space for the result
  SetLength(result, MAX_PATH);

  // Query the DOS device
  dwSize:=QueryDosDevice(PChar(String(FDrive+':')), @lpszPath, MAX_PATH);

  // Set the length of the result
  if (dwSize > 0) then
  begin
     // Get pointer to string
     lpszReturn:=@lpszPath;
     // Check for path setting
     if (StrLComp(lpszReturn, '\??\', 4) = 0) or (StrLComp(lpszReturn, '\\.\', 4) = 0) then Inc(lpszReturn, 4);
     // Convert to string result
     SetString(result, lpszReturn, StrLen(lpszReturn));
  end
  else
     // Failed to get path
     SetLength(result, 0);

end;

procedure TSymbolicDrive.SetDevicePath(Value: String);
begin

  // Check for change
  if not(CompareText(Value, GetDevicePath) = 0) then
  begin
     // Delete the symbolic link if its a path
     if IsPath then
     begin
        // Delete link
        DefineDosDevice(DDD_REMOVE_DEFINITION, PChar(String(FDrive+':')), nil);
        // Remove persistence
        Persist(False);
     end;
     // Check for new path setting
     if IsAvailable and (Length(Value) > 0) and ((GetFileAttributes(PChar(Value)) and faDirectory) = faDirectory) then
     begin
        // Check for \ starting the path, which is an indicator for raw type
        if (Value[1] = '\') then
           DefineDosDevice(DDD_RAW_TARGET_PATH, PChar(String(FDrive+':')), PChar(Value))
        else
           DefineDosDevice(0, PChar(String(FDrive+':')), PChar(Value));
     end;
  end;

end;

function TSymbolicDrive.IndexFromChar(Value: Char): Integer;
begin

  // Convert drive letter into an index
  if (Value in ['a'..'z']) then
     result:=Ord(Value)-97
  else if (Value in ['A'..'Z']) then
     result:=Ord(Value)-65
  else
     result:=(-1);

end;

constructor TSymbolicDrive.Create(Drive: Char);
begin

  // Perform inherited
  inherited Create;

  // Set drive letter
  FDrive:=Drive;

end;

//// TSymbolicLinks ////////////////////////////////////////////////////////////
procedure TSymbolicLinks.GetDeviceDriveList(List: TStrings; DriveLetterOnly: Boolean = True);
var  cIndex:        Char;
begin

  // Check list
  if Assigned(List) then
  begin
     // Lock
     List.BeginUpdate;
     // Resource protection
     try
        // Clear list
        List.Clear;
        // Walk all drives
        for cIndex:='A' to 'Z' do
        begin
           if not(Items[cIndex].IsAvailable) and not(Items[cIndex].IsPath) then
           begin
              if DriveLetterOnly then
                 List.Add(cIndex+':')
              else
                 List.Add(cIndex+':='+Items[cIndex].DevicePath);
           end;
        end;
     finally
        // Unlock
        List.EndUpdate;
     end;
  end;

end;

procedure TSymbolicLinks.GetVirtualDriveList(List: TStrings; DriveLetterOnly: Boolean = True);
var  cIndex:        Char;
begin

  // Check list
  if Assigned(List) then
  begin
     // Lock
     List.BeginUpdate;
     // Resource protection
     try
        // Clear list
        List.Clear;
        // Walk all drives
        for cIndex:='A' to 'Z' do
        begin
           if not(Items[cIndex].IsAvailable) and Items[cIndex].IsPath then
           begin
              if DriveLetterOnly then
                 List.Add(cIndex+':')
              else
                 List.Add(cIndex+':='+Items[cIndex].DevicePath);
           end;
        end;
     finally
        // Unlock
        List.EndUpdate;
     end;
  end;

end;

procedure TSymbolicLinks.GetAvailableDriveList(List: TStrings);
var  cIndex:        Char;
begin

  // Check list
  if Assigned(List) then
  begin
     // Lock
     List.BeginUpdate;
     // Resource protection
     try
        // Clear list
        List.Clear;
        // Walk all drives
        for cIndex:='A' to 'Z' do
        begin
           if Items[cIndex].IsAvailable then List.Add(cIndex+':');
        end;
     finally
        // Unlock
        List.EndUpdate;
     end;
  end;

end;

function TSymbolicLinks.GetSymbolicDrive(Index: Char): ISymbolicDrive;
begin

  // Return interface
  result:=FItems[IndexFromChar(Index)] as ISymbolicDrive;

end;

function TSymbolicLinks.IndexFromChar(Value: Char): Integer;
begin

  // Convert drive letter into an index
  if (Value in ['a'..'z']) then
     result:=Ord(Value)-97
  else if (Value in ['A'..'Z']) then
     result:=Ord(Value)-65
  else
     result:=(-1);

end;

constructor TSymbolicLinks.Create;
var  cIndex:        Char;
begin

  // Perform inherited
  inherited Create;

  // Create interface list
  FItems:=TInterfaceList.Create;

  // Walk all drive letters
  for cIndex:='A' to 'Z' do FItems.Add(TSymbolicDrive.Create(cIndex));

end;

destructor TSymbolicLinks.Destroy;
begin

  // Resource protection
  try
     // Free the interface list
     FItems.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

initialization

  // Create global links
  SymbolicLinks:=TSymbolicLinks.Create;

finalization

  // Free the links
  FreeAndNil(SymbolicLinks);

end.

-- updated code --

-----------------
unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, Add, Remove, SymbolicLink;

type
  TMainForm         = class(TForm)
     btnAdd:        TButton;
     btnRemove:     TButton;
     lvVirtual:     TListView;
     procedure      FormCreate(Sender: TObject);
     procedure      btnAddClick(Sender: TObject);
     procedure      btnRemoveClick(Sender: TObject);
  private
     // Private declarations
     procedure      LoadVirtualDrives;
  public
     // Public declarations
  end;

var
  MainForm:            TMainForm;

implementation
{$R *.DFM}

procedure TMainForm.LoadVirtualDrives;
var  cIndex:        Char;
     szDrive:       String;
     szPath:        String;
begin

  lvVirtual.Items.BeginUpdate;
  try
     lvVirtual.Items.Clear;
     for cIndex:='A' to 'Z' do
     begin
        if SymbolicLinks[cIndex].IsPath then
        begin
           with lvVirtual.Items.Add do
           begin
              Caption:=SymbolicLinks[cIndex].DriveLetter+':';
              SubItems.Add(SymbolicLinks[cIndex].DevicePath);
           end;
        end;
     end;
  finally
     lvVirtual.Items.EndUpdate;
  end;

end;

procedure TMainForm.FormCreate(Sender: TObject);
begin

  LoadVirtualDrives;
 
end;

procedure TMainForm.btnAddClick(Sender: TObject);
begin

  AddForm.Startup;
  if (AddForm.ShowModal = mrOK) and (AddForm.Drive > #0) then
  begin
     SymbolicLinks[AddForm.Drive].DevicePath:=AddForm.txtPath.Text;
     SymbolicLinks[AddForm.Drive].Persist(True);
  end;
  LoadVirtualDrives;

end;

procedure TMainForm.btnRemoveClick(Sender: TObject);
begin

  RemoveForm.Startup;
  if (RemoveForm.ShowModal = mrOK) and (RemoveForm.Drive > #0) then
  begin
     SymbolicLinks[RemoveForm.Drive].DevicePath:=EmptyStr;
  end;
  LoadVirtualDrives;

end;

end.

-----------------

unit Add;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, FileCtrl, ActiveX, CommDlg, ShlObj, SymbolicLink;

type
  TAddForm          =  class(TForm)
     cboDrives:     TComboBox;
     txtPath:       TEdit;
     btnBrowse:     TButton;
     btnOK:         TButton;
     btnCancel:     TButton;
     procedure      FormCreate(Sender: TObject);
     procedure      cboDrivesChange(Sender: TObject);
     procedure      txtPathChange(Sender: TObject);
     procedure      btnBrowseClick(Sender: TObject);
  private
     // Private declarations
     procedure      UpdateState;
  public
     // Public declarations
     procedure      Startup;
     function       Drive: Char;
  end;

var
  AddForm:          TAddForm;

implementation
{$R *.DFM}

procedure TAddForm.Startup;
begin

  SymbolicLinks.GetAvailableDriveList(cboDrives.Items);
  if (cboDrives.Items.Count > 0) then cboDrives.ItemIndex:=0;
  UpdateState;

end;

function TAddForm.Drive: Char;
begin

  if (cboDrives.ItemIndex < 0) then
     result:=#0
  else
     result:=cboDrives.Items[cboDrives.ItemIndex][1];

end;

procedure TAddForm.UpdateState;
begin

  btnOK.Enabled:=(cboDrives.ItemIndex >= 0) and (Length(txtPath.Text) > 0) and DirectoryExists(txtPath.Text);

end;

procedure TAddForm.FormCreate(Sender: TObject);
begin

  Startup;

end;

procedure TAddForm.cboDrivesChange(Sender: TObject);
begin

  UpdateState;
 
end;

procedure TAddForm.txtPathChange(Sender: TObject);
begin

  UpdateState;
 
end;

function BrowseForFolderCallBack(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall;
var  lpPath:        Array [0..MAX_PATH] of Char;
begin

  // Handle callback message
  case uMsg of
     // Init
     BFFM_INITIALIZED  :  SendMessage(Wnd, BFFM_SETSELECTION, 1, lpData);
     // Selection changed
     BFFM_SELCHANGED   :
     begin
        // Clear buffer
        ZeroMemory(@lpPath, SizeOf(lpPath));
        // Convert pidl to path
        if SHGetPathFromIDList(PItemIDList(lParam), lpPath) then StrPCopy(@lpPath, ExtractFileName(lpPath));
        // Set status text
        SendMessage(Wnd, BFFM_SETSTATUSTEXT, 0, Integer(@lpPath));
     end;
  end;

  // Return zero for all messages
  result:=0;

end;

procedure TAddForm.btnBrowseClick(Sender: TObject);
var  pidlContext:   PItemIDList;
     lpBrowse:      TBrowseInfo;
     lpTitle:       Array [0..512] of Char;
     lpFolder:      Array [0..MAX_PATH] of Char;
     pvMalloc:      IMalloc;
begin

  // Clear buffers
  ZeroMemory(@lpBrowse, SizeOf(lpBrowse));
  ZeroMemory(@lpTitle, SizeOf(lpTitle));

  // Copy params over to static buffers
  StrPLCopy(@lpFolder, txtPath.Text, SizeOf(lpFolder));
  if (StrLen(@lpFolder) = 2) then StrCat(lpFolder, '\');
  StrPLCopy(@lpTitle, Application.Title, SizeOf(lpTitle));

  // Set browse info params
  lpBrowse.hwndOwner:=Handle;
  lpBrowse.pszDisplayName:=@lpFolder;
  lpBrowse.lpszTitle:=@lpTitle;
  lpBrowse.ulFlags:=BIF_RETURNONLYFSDIRS or BIF_STATUSTEXT;
  lpBrowse.lpfn:=@BrowseForFolderCallBack;
  lpBrowse.lParam:=Integer(@lpFolder);

  // Browse for folder
  pidlContext:=SHBrowseForFolder(lpBrowse);

  // Check result
  if Assigned(pidlContext) then
  begin
     // Get the path from the pidl
     if SHGetPathFromIDList(pidlContext, lpFolder) then
     begin
        // Update path
        txtPath.Text:=lpFolder;
        // Get malloc to release memory
        if (SHGetMalloc(pvMalloc) = S_OK) then
        begin
           // Free memory
           pvMalloc.Free(pidlContext);
           // Release the interface
           pvMalloc:=nil;
        end;
     end;
  end;

end;

end.

-----------------

unit Remove;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, SymbolicLink;

type
  TRemoveForm       = class(TForm)
     cboDrives:     TComboBox;
     txtPath:       TEdit;
     btnOK:         TButton;
     btnCancel:     TButton;
     procedure      cboDrivesChange(Sender: TObject);
  private
     // Private declarations
     procedure      UpdateState;
  public
     // Public declarations
     procedure      Startup;
     function       Drive: Char;
  end;

var
  RemoveForm:       TRemoveForm;

implementation
uses Main;
{$R *.DFM}

procedure TRemoveForm.UpdateState;
begin

  // Update text window
  if (cboDrives.ItemIndex < 0) then
     txtPath.Text:=EmptyStr
  else
     txtPath.Text:=SymbolicLinks.Items[cboDrives.Items[cboDrives.ItemIndex][1]].GetDevicePath;

  // Update the ok button
  btnOK.Enabled:=not(cboDrives.ItemIndex < 0);

end;

function TRemoveForm.Drive: Char;
begin

  if (cboDrives.ItemIndex < 0) then
     result:=#0
  else
     result:=cboDrives.Items[cboDrives.ItemIndex][1];

end;

procedure TRemoveForm.Startup;
begin

  SymbolicLinks.GetVirtualDriveList(cboDrives.Items);
  if (cboDrives.Items.Count > 0) then cboDrives.ItemIndex:=0;
  UpdateState;

end;

procedure TRemoveForm.cboDrivesChange(Sender: TObject);
begin

  UpdateState;

end;

end.

0
 
LVL 9

Expert Comment

by:sun4sunday
ID: 17113137
If you are doing mapping manually there is an option in hte Map Net work drive   "reconnect at login." select that.
It will hold the mapped drive after reboot.

sun4sunday
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17113191
sun4sunday,

If we were dealing with network drives, then your comment would apply. We are dealing with drive mappings that are mapped using DefineDosDevice to path locations (aka subst via the kernel API).  

Russell
0
 

Author Comment

by:PeterdeB
ID: 17127535
Russell,

If we were dealing with your code then we can be pretty sure things work!! Just tested and it does what it should do exactly!! :)

Best regards,

Paul
0
 
LVL 9

Expert Comment

by:sun4sunday
ID: 17162317
Yes, Russel, it applied only for the network drives, not for the Virtual Drivers.
Thank you for pointing out this

sun4sunday
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

SASS allows you to treat your CSS code in a more OOP way. Let's have a look on how you can structure your code in order for it to be easily maintained and reused.
The Delta outage: 650 cancelled flights, more than 1200 delayed flights, thousands of frustrated customers, tens of millions of dollars in damages – plus untold reputational damage to one of the world’s most trusted airlines. All due to a catastroph…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now