Archive
Getting a list of mapped network drives
Continuing the theme of mapped network drives (see earlier post about how to connect a network drive by code), here is another class that is quite helpful. In short, it simply queries the operative system for mapped network paths and their drive letter.
Very simply to use: just create an instance, call the refresh method (this is called automatically by the constructor) and use the properties to traverse the list.
TQTXNetworkDriveList = Class(TObject) Private FObjects: TStringList; Function GetCount:Integer; Function GetItem(index:Integer):String; Function GetDriveLetter(index:Integer):String; Function GetMapPath(index:Integer):String; Public Property Count:Integer read GetCount; Property DriveLetter[index:Integer]:String read GetDriveLetter; Property MapPath[index:Integer]:String read GetMapPath; Property Items[index:Integer]:String read GetItem;default; Public Procedure Refresh; function toString:String;override; Procedure AfterConstruction;override; Constructor Create; Destructor Destroy;Override; End; //########################################################################## // TQTXNetworkDriveList //########################################################################## Constructor TQTXNetworkDriveList.Create; Begin inherited; FObjects:=TStringList.Create; end; Destructor TQTXNetworkDriveList.Destroy; Begin FObjects.free; inherited; end; Procedure TQTXNetworkDriveList.AfterConstruction; Begin inherited; Refresh; end; function TQTXNetworkDriveList.toString:String; begin result:=FObjects.Text; end; Function TQTXNetworkDriveList.GetDriveLetter(index:Integer):String; Begin result:=FObjects.Names[index] + ':'; end; Function TQTXNetworkDriveList.GetMapPath(index:Integer):String; Begin result:=FObjects.Values[FObjects.Names[index]]; end; Function TQTXNetworkDriveList.GetCount:Integer; Begin result:=FObjects.Count; end; Function TQTXNetworkDriveList.GetItem(index:Integer):String; Begin result:=GetDriveLetter(index) + getMapPath(index); end; Procedure TQTXNetworkDriveList.Refresh; const CNT_DRIVEMASK = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var i: integer; mPath: string; mLen: DWord; mDrive: String; Begin FObjects.Clear; for i:=1 to length(CNT_DRIVEMASK) do Begin mDrive:=CNT_DRIVEMASK[i] + ':'; mLen := MAX_PATH; SetLength(mPath,MAX_PATH); If NO_ERROR=WNetGetConnection(PChar(mDrive),PChar(mPath),mLen) then FObjects.Values[CNT_DRIVEMASK[i]]:=copy(mPath,1,mLen); end; end;
Map network path by code
Ever wanted to map a network path by code? I mean, instead of mapping a network path to a drive letter, like you can do with using Windows Explorer. What If you could map a known network location without any other program or even Windows Explorer knowing about it? Well, this is what my little class does.
This class came about many years ago while I was coding a collection of services meant to receive, process and transport data to various places in a huge network. The win32 services all ran in the background (like services do) and there was no way to ensure that a specific network path was mapped and ready to use.
Is it useful?
Let me demonstrate with a simple task. You are hired to create a win32 service that accepts data on a listening Indy server. This data should be processed and stored in 3 locations on the network (a huge network). The user account that executes your service will have access rights for these 3 locations, but there is one problem — the locations can change without any warning – and the network entrypoint-paths are stored in the registry only.
So, how do you solve this? You can’t hardcode the target locations (you should avoid hardcoding as much as possible at all times), you can’t keep mapping network paths to drive-letters, because it will be a complete mess, and since stuff can change at any time — you need to map the path for each call to the service to be safe.
The answer is to map the network paths by code, with no drive letter involved. And here is a mini-class that does this!
TQTXNetworkPath = Class(TObject) Private FHostName: String; FRemotePath: String; FUser: String; FPassword: String; FConnected: Boolean; FOwned: Boolean; FLastError: String; FFailed: Boolean; FUNCData: packed array[0..4096] of char; FURI: String; protected procedure ClearLastError; Procedure setLastError(aValue:String); Public Property Active:Boolean read FConnected; Property HostName:String read FHostName; Property NetworkPath:String read FRemotePath; Property LastError:String read FLastError; Property Failed:Boolean read FFailed; Function getRelativePath(aFilename:String):String; Function Connect(aHostName:String; aNetworkPath:String; const aUsername:String=''; const aPassword:String=''):Boolean; Procedure Disconnect(const aForce:Boolean=False); Destructor Destroy;override; End; //########################################################################## // TQTXNetworkPath //########################################################################## Destructor TQTXNetworkPath.Destroy; Begin if FConnected then Disconnect(True); inherited; end; function TQTXNetworkPath.Connect(aHostName, aNetworkPath: String; const aUsername, aPassword: String): Boolean; var mNet: TNetResource; mRes: Cardinal; mTxt: String; begin clearLastError; result:=False; aHostName:=trim(aHostName); aNetworkPath:=trim(aNetworkPath); if length(aHostName)>0 then Begin (* Already connected? Disconnect and force closing of all files *) if FConnected then Disconnect(true); (* Build complete network path *) if length(aNetworkPath)>0 then FURI:='\\' + aHostname else FURI:=format('\\%s\%s',[aHostName,aNetworkPath]); (* Initialize UNC path *) ZeroMemory(@FUNCData,SizeOf(FUNCData)); Move(pointer(@FURI[1])^,pointer(@FUNCData)^,length(FURI) * SizeOf(char) ); (* initialize network resource data *) ZeroMemory(@mNet,SizeOf(mNet)); mNet.dwScope:=RESOURCE_GLOBALNET; mNet.dwType:=RESOURCETYPE_DISK; mNet.dwDisplayType:=RESOURCEDISPLAYTYPE_GENERIC; mNet.dwUsage:=RESOURCEUSAGE_CONNECTABLE; mNet.lpRemoteName:=@FUNCData; (* Now attempt to connect the sucker *) mRes:=WNetAddConnection2(mNet, pchar(aPassword), pchar(aUserName),CONNECT_TEMPORARY); if mRes<>NO_ERROR then Begin ZeroMemory(@FUNCData,SizeOf(FUNCData)); setLength(FURI,0); Case mRes of ERROR_ACCESS_DENIED: mTxt:='Access_denied:'; ERROR_ALREADY_ASSIGNED: mTxt:='Already assigned:'; ERROR_BAD_DEV_TYPE: mTxt:='Bad device type:'; ERROR_BAD_DEVICE: mTxt:='Bad device:'; ERROR_BAD_NET_NAME: mTxt:='Bad network Name:'; ERROR_BAD_PROFILE: mTxt:='Bad profile:'; ERROR_BAD_PROVIDER: mTxt:='Bad provider:'; ERROR_BUSY: mTxt:='Busy:'; ERROR_CANCELLED: mTxt:='Canceled:'; ERROR_CANNOT_OPEN_PROFILE: mTxt:='Cannot_open_profile:'; ERROR_DEVICE_ALREADY_REMEMBERED:mTxt:='Device_already_remembered:'; ERROR_EXTENDED_ERROR: mTxt:='Extended error:'; ERROR_INVALID_PASSWORD: mTxt:='Invalid password:'; ERROR_NO_NET_OR_BAD_PATH: mTxt:='No_Net_or_bad_path:'; ERROR_NO_NETWORK: mTxt:='No_Network:'; end; setLastError(Format('Failed to connect [%s], %s', [mTxt,SysErrorMessage(mRes)])); end else Begin FOwned:=True; FConnected:=True; FHostName:=aHostName; FUser:=aUserName; FPassword:=aPassword; FRemotePath:=aNetworkPath; end; end else setLastError('Failed to connect, invalid hostname error'); end; procedure TQTXNetworkPath.Disconnect(const aForce: Boolean); var mRes: Integer; mCount: Integer; Begin (* Close network connection if connected *) If (FConnected=True) and (FOwned=True) then Begin (* Attempt to close the connection *) mRes:=WNetCancelConnection(@FUNCData,aForce); (* Since files can be open temporarily, we try to wait a little before we re-try to close again. A maximum of 100 attempts is set *) if mRes=ERROR_OPEN_FILES then Begin mCount:=0; While mRes=ERROR_OPEN_FILES do Begin inc(mCount); if mCount=100 then break; Sleep(100); mRes:=WNetCancelConnection(@FUNCData,aForce); end; end; FConnected:=False; FOwned:=False; setLength(FHostName,0); setLength(FRemotePath,0); setLength(FUser,0); setLength(FPassword,0); setLength(FURI,0); end; end; procedure TQTXNetworkPath.ClearLastError; Begin FFailed:=False; setLength(FLastError,0); end; Procedure TQTXNetworkPath.setLastError(aValue:String); Begin FLastError:=trim(aValue); FFailed:=Length(FLastError)>0; end; Function TQTXNetworkPath.getRelativePath(aFilename:String):String; Begin if FConnected then result:=FURI + aFilename else result:=aFilename; end;
How does it work?
While the class should be self-explanatory (and remember, if you are to lazy to use MSDN to read about what WinAPI calls do, then you really should work harder to be a good programmer), here are a few words about the workings.
First, the relative path stuff.
As you probably know, network paths are not like ordinary local paths. A local fixed path can be something like:
C:\MyFolder\Nextfolder\ThirdFolder\Somefile.dat
A network path is more abstract and designed to be relative to a mount-point:
$MOUNTPOINT\MyFolder\Nextfolder\ThirdFolder\Somefile.dat
In many cases the mountpoint ($MOUNTPOINT above) doesnt even have to be included, you can reference the machine name directly, like this:
\\somePC\shared folders\myshared folder\Somefile.dat
In other words, when working with paths that can change at all times, you really want to work exclusively with relative paths. Directories that are relative to the folder you are mounting.
When using the class above, the first method you want to use is the Connect() method. As you see it accepts two parameters: the name of the machine (server), followed by the network path you want to use. It also has two optional parameters for username and password. Note: The username and password is for the account you want to mount the drive for. It doesnt have to be your current account. In some network setups it can be wise to isolate access via proxy accounts to better control who has access to what.
Anyways, this network path is your entry-point (mount point) and whatever files you operate on are, naturally, relative to that. To know the relative path for a file, use the method getRelativePath().
Really not more to say about the class. It takes care of everything and is compatible with findFirst/findNext file-searching (all things normal windows and pascal). Once mounted, the mountpoint can be used anywhere in your process.
Hexlicense component suite almost ready
I am proud to announce the nearby release of my license tracking software for Delphi and C++ builder. The software is published under Fuerza development and will be available for purchase shortly.
The suite is compatible with Delphi 7 – XE5, 32 and 64 bit. It is written in standard Delphi and is thus platform independent – with no dependencies or external libraries.
Introduction
You have a killer application and want to get it onto the marketplace. But how will you deal with serial numbers? How will do handle trial periods? How will you generate serial numbers in bulk, which online vendors need in order to sell your product?
This is where the license manager for Delphi comes in. Turning your application into a trial version is now a matter of adding some components, setting a few properties – and then deciding what to disable in your code during the trial. With plenty of events and straight forward mechanisms, the license manager component package will save you a lot of time!
What does it do?
Our components is about serial number generation, validation and management of trial periods from within your Delphi application. How you store the license data (file and registry storage components ship with the package) is up to you. If you want to handle the license data yourself, just drop a THexOwnerLicenseStorage component and take charge via the event handlers.
This model gives you the benefit of not having to deal with the low-level stuff (like encryption, data signatures, date and time checking, file validation etc). You are not boxed into “our” way of doing things.
If you have a custom server solution or ordering system online, you can easily extend serial-number validation by calling your server to check usage. You can also use the license generator to create as many serial numbers (based on a root key) as you wish. These lists can be uploaded to your webshop which takes care of delivery to your customers.
Components
The entire package consists of five non-visual components and the license generator application. Starting to use the package is a matter of dropping the desired components on a form, starting the generator application, generating a root key and a bulk of serial numbers, pasting the root-key into your code and setting the trial mode. That is more or less all you need to do for the most basic setup. It is highly recommended that you disable some features of your application when running under trial. This will encourage users to buy your application to enjoy it in full.
THexLicense is the central component. It allows you to define the trial mode (fixed time range, day trial, run trial) and it’s values. You can also set it to automatically start on loading, which means you don’t have to manually call the component for it to initialize. This component must be connected to a THexSerialNumber component and a THexLicenseStorage adapter.
THexSerialNumber deals exclusively with the serial number. In order for this component to do its work it must be connected to a THexSerialMatrix component.
THexSerialMatrix is the component that handles the “root key”. You provide the root-key via an event handler. The root key is generated with the serial-number generator application that ships with the package (you can also write your own).
THexLicenseStorage is a base component from which THexFileLicenseStorage, THexRegistryStorage and THexOwnerStorage adapters inherit. This is where reading and writing of the actual license data is handled. As the names imply the registry and file adapters are pre-configured to store data in either the windows registry or on disk. If you want to handle the storage yourself (which most people prefer) you can drop a THexOwnerStorage component on your form or datamodule, and take charge via the OnReadData, OnWriteData and OnDataExists events.
THexSerialGenerator is the component used by the generator application to generate serial numbers in bulk. It exists as a component so it can be easily added server-side if you already have an existing ordering system online.
Recent
The vatican vault
- March 2023
- February 2023
- December 2022
- October 2022
- January 2022
- October 2021
- March 2021
- November 2020
- September 2020
- July 2020
- June 2020
- April 2020
- March 2020
- February 2020
- January 2020
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- September 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012
You must be logged in to post a comment.