Windows provides several script engines, most of which are primarily
targeted for web development. Among the engines are JScript, VBScript and
PERL. When a script is run by Windows, it is handled by Windows Script Host
(WSH), which invokes the appropriate script engine.
WSH itself provides access to a range of administrative objects and services
such as network drives, the registry and printer connections. We'll only
look at one particular object in WSH and only to retrieve some limited
information. Feel free to learn more about WSH and what it can do
(especially the Shell object can be useful).
Another part of scripting is the Script Runtime Library which provides
some useful services for any script or application running in Windows. It
contains 3 main objects and we will use one of them to retrieve information
about disk drives via Visual Lisp. The object is called the FileSystemObject.
It provides access to the file system in a way that not only gives
information but also offers standard file manipulation.
The hardest thing about accessing objects outside AutoCAD with Visual
Lisp is not using the VLAX- or VLA-functions. It's knowing where to get the
darn objects! If you never read anything about how to access Excel from
Visual Lisp, would you know that the class name to use is "Excel.Application"?
Or that the type library to import would be "Excel9.olb"? Probably
not.
It's a lot easier to open VBAIDE, find the reference in Tools->References
and check out the path (actually, anything that has to do with objects is a
lot easier in VBA, but don't tell that to anyone).
For accessing simple things like WSH or the Script Runtime Library, we
won't import any type libraries - it's too messy. We'll just create the
objects on the fly and use the generic VLAX-INVOKE-METHOD and VLAX-GET-PROPERTY
functions to access them. To create the objects in the first place, we'll
use VLAX-CREATE-OBJECT.
Let's start it by accessing the Windows Script Host. This only runs on
Windows 98 or higher, but of course you are running at least Windows NT or
2000!
The class name for the WSH object we are interested in is "WScript.Network".
So let's assign that object to a variable:
(setq wscript (vlax-create-object "WScript.Network"))
#<VLA-OBJECT IWshNetwork2 0018ed40>
Now, I haven't figured this out yet, but trying to dump the object on
screen reveals nothing of importance:
Command: (vlax-dump-object wscript T)
; IWshNetwork2: Network Object
; No properties
; Methods supported:
; AddWindowsPrinterConnection (3)
T
It says that the object has no properties and that is not correct. In
fact, here are the properties of a WshNetwork object (again, using the
object browser in VBAIDE can reveal these things much better than Visual
Lisp can):
- ComputerName
- UserDomain
- UserName
The methods are primarily used for mapping network drives and connecting
to network printers. Feel free to look them up (just search for WshNetwork
on the internet). Here we will simply grab the three properties for
informative reasons.
(cond ((setq wscript (vlax-create-object "WScript.Network"))
(setq pcname (vlax-get-property wscript "ComputerName")
pcdom (vlax-get-property wscript "UserDomain")
pcuser (vlax-get-property wscript "UserName"))
)
So far, so good. Let's try to access the FileSystemObject and see what we
can use it for. The class name to access is "Scripting.FileSystemObject"
(you are properly excused if you had to read it to find out):
(setq fso (vlax-create-object "Scripting.FilesystemObject"))
#<VLA-OBJECT IFileSystem3 0cf17148>
Whereas Visual Lisp has some obscure reason not to dump the entire
properties/methods of a WshNetwork object, there is no problem getting the
information from the FileSystemObject:
Command: (vlax-dump-object fso T)
; IFileSystem3: FileSystemObject
; Property values:
; Drives (RO) = #<VLA-OBJECT IDriveCollection 0cf009c8>
; Methods supported:
; BuildPath (2)
; CopyFile (3)
; CopyFolder (3)
; CreateFolder (1)
; CreateTextFile (3)
; DeleteFile (2)
; DeleteFolder (2)
; DriveExists (1)
; FileExists (1)
; FolderExists (1)
; GetAbsolutePathName (1)
; GetBaseName (1)
; GetDrive (1)
; GetDriveName (1)
; GetExtensionName (1)
; GetFile (1)
; GetFileName (1)
; GetFileVersion (1)
; GetFolder (1)
; GetParentFolderName (1)
; GetSpecialFolder (1)
; GetStandardStream (2)
; GetTempName ()
; MoveFile (2)
; MoveFolder (2)
; OpenTextFile (4)
T
That the object only contains a single property is not a mistake. The
Drives property is a collection object that in turn holds information of
each and every disk drive connected to the computer.
The vast amount of methods indicate that here we have a rather clever tool
if it's file manipulation we're looking for. Some of the methods could even
give the impression that the VL engine uses a FileSystemObject for many of
its own routines:
(vlax-invoke-method fso 'CreateFolder "C:\\newdir")
(vl-mkdir "C:\\newdir)
(vlax-invoke-method fso 'GetBaseName "C:\\autoexec.bat")
(vl-filename-base "C:\\autoexec.bat")
(vlax-invoke-method fso 'CopyFile "C:\\somefile.txt" "C:\\somecopy.txt" :vlax-true)
(vl-file-copy "C:\\somefile.txt" "C:\\somecopy.txt")
And so on. Of course, some of the methods in the FileSystemObject aren't
available in Visual Lisp and many of the methods return more useful values
or objects than their VL equivalents.
For our purpose, we won't use any of the methods. We will be humble and only
build a routine to retrieve information about each drive connected to the
PC. For that we need the Drives collection:
(setq drives (vlax-get-property fso 'Drives))
#<VLA-OBJECT IDriveCollection 09529930>
(vlax-dump-object drives T)
; IDriveCollection: Drive Collection Interface
; Property values:
; Count (RO) = 12
; Item (RO) = ...Indexed contents not shown...
; No methods
T
Being a collection object, one would expect at least an Item method. But
the dump says there are no methods. Instead we can use the Item property to
retrieve each drive:
(vlax-get-property drives 'Item "C:")
#<VLA-OBJECT IDrive 0956a138>
What's that, you ask. An argument to a property? Yes, is the short answer
(for the extended answer, go pick up some Visual Basic). The argument is the
drive letter, with or without a colon, or the drive root folder (e.g.
"C:\\"). But what if we don't know all the drive letters of the
drives connected? Well, being a collection, we can use VLAX-FOR:
(vlax-for n drives
(princ (vlax-get-property n 'driveLetter))
(princ " ")
)
A C D E F G H I O P Q R " "
Wowsers!! Cool stuff. Now we know at least one property of the Drive
object, i.e. the kind of object held by the Drives collection. Let's look at
all properties and methods of a Drive object:
(setq myDrive (vlax-get-property drives 'Item "C:"))
#<VLA-OBJECT IDrive 0956a138>
(vlax-dump-object myDrive T)
; IDrive: Drive Interface
; Property values:
; AvailableSpace (RO) = 2118037504
; DriveLetter (RO) = "C"
; DriveType (RO) = 2
; FileSystem (RO) = "NTFS"
; FreeSpace (RO) = 2118037504
; IsReady (RO) = -1
; Path (RO) = "C:"
; RootFolder (RO) = #<VLA-OBJECT IFolder 09558648>
; SerialNumber (RO) = -922880035
; ShareName (RO) = ""
; TotalSize (RO) = 6.98326e+009
; VolumeName = ""
; No methods
T
Ok, there are no methods (the methods of FileSystemObject are actually
ample enough to manipulate files, folders and drives), but there are lots of
properties. There is only one property that is read-write - it's the label
that can also be freely set via Windows Explorer - all the others are
read-only. There is one property, though, that can be used to walk through
the entire disk and as such can be used in a custom built file-manipulation
dialog, for example open or save. It's the RootFolder which gives access to
all other folders - and thereby files - on disk. We won't dwelve at how to
do this because it's a topic by itself (there are many sites around that can
show recursive filesearch techniques). But there are plenty of hurdles to
deal with before we can build a routine to present all of this wonderful
information in a suitable way.
As you may be much too familiar with, the data types used by ActiveX are
not so attractive to work with in Visual Lisp. Properties for a disk drive
show at least three data types that need to be "translated" before
they can be shown in, say, a dialog. They are variants, Boolean and
enumerated values.
The latter are really no trouble by themselves - they are simply returned as
integers - but we have no way of accessing ActiveX constants (unless we
import a type library where they may be defined), so we have to invent a way
to present them in clear text. The FileSystemObject is explained on many
sites (no less by the company who made the object), so the constants are not
hard to get at. You could also enter VBA, connect to Microsoft Scripting
Runtime and check the enums in the object browser. They are:
0 = UnknownType
1 = Removable
2 = Fixed
3 = Remote
4 = CDRom
5 = RAMDisk
In order to convert the integer returned by the DriveType property, we
could simply retrieve a textstring from a list (where n is the integer):
(nth n '("Unknown" "Removable" "Fixed" "Network drive" "CD-Rom" "RAM disk"))
Another, more problematic type, is the variant returned by AvailableSpace,
FreeSpace and TotalSize. Try retrieve one of the properties and see what it
returns:
(setq free (vlax-get-property myDrive 'FreeSpace))
#
Ok, so it's not that problematic. The type 3 indicates that it's a Long.
We simply use VLAX-VARIANT-VALUE to extract the value:
(vlax-variant-value free)
2117771264
Of course, it's probably not a good idea to show it in raw bytes, but
divide it by 1024 and you get kBytes, or divide it by 1024 in the power of 3
to get GigaBytes. The Boolean value of IsReady is in Visual Lisp returned as
:vlax-true or :vlax-false. We could use VL-PRINC-TO-STRING to print the
value directly, but that's not a nice way to present it. Instead we could
use a very simple "convertion" (where n is the Boolean):
(cond ((= n :vlax-true) "Yes")
((= n :vlax-false) "No")
)
Strings are, luckily enough, returned as strings, so we won't bother
doing anything about them. To present the iFolder object, do whatever you
like. The code that follows simply accesses the path and presents it to the
user.
The code that follows?? Next page, dude.