Scripting Objects - Part 1
- See also:
- Scripting Objects - Part 2
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
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)) #<variant 3 2117771264>
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?? See Part 2, dude.