Include some kind of "header"-file to VBScript [duplicate] - vbscript

VBScript doesn't appear to have a way to include a common file of functions.
Is there a way to achieve this?

You can create a (relatively) small function in each file that you want to include other files into, as follows:
sub includeFile (fSpec)
dim fileSys, file, fileData
set fileSys = createObject ("Scripting.FileSystemObject")
set file = fileSys.openTextFile (fSpec)
fileData = file.readAll ()
file.close
executeGlobal fileData
set file = nothing
set fileSys = nothing
end sub
and then use it to include specific files - these are executed as if they were inline.
includeFile "commonapi.vbi"
includeFile "dbcalls.vbi"
It basically opens the file, reads the entire contents into a string, then executes that string. There's no error handling on the I/O calls since this sort of stuff is usually done once on program start, and you want to fail if there's a problem including it.
Note that the includeFile function can be compressed to:
Sub includeFile(fSpec)
With CreateObject("Scripting.FileSystemObject")
executeGlobal .openTextFile(fSpec).readAll()
End With
End Sub
Or even to (if you're not adverse to long lines):
Sub includeFile(fSpec)
executeGlobal CreateObject("Scripting.FileSystemObject").openTextFile(fSpec).readAll()
End Sub

The "Windows Script Host" framework (if ya want to call it that), offers an XML wrapper document that adds functionality over regular vbs files. One of which is the ability to include external script files of both the VBscript and Jscript flavors. I never got very deep into it, but I think it would do what you're wanting to do.
http://msdn.microsoft.com/en-us/library/15x4407c(VS.85).aspx
You can include JavaScript, VBScript, or modules of other WScript script languages.
Example WSF file:
<job id="IncludeExample">
<script language="JavaScript" src="sprintf.js"/>
<script language="VBScript" src="logging.vbs"/>
<script language="VBScript" src="iis-queryScriptMaps.vbs"/>
</job>
If the above file is called "iis-scriptmaps.wsf", run it this way with cscript.exe:
cscript.exe iis-scriptmaps.wsf

I know this is an old thread but I post my answer anyway so others can learn what I have learnt about VBS and WSF files by "trial and error" :
So to have the same functionality as in other languages you can create one WSF file and include all of your VBS libs there, including the main program.
Something like this :
<job id="MainProg">
<script language="VBScript" src="Constants.vbs"/>
<script language="VBScript" src="FileFunctions.vbs"/>
<script language="VBScript" src="SendMail.vbs"/>
<script language="VBScript" src="LoggingFunctions.vbs"/>
<script language="VBScript" src="MainProgram.vbs"/>
<script language="VBScript">
' Here we call the main program
MainProgram()
</script>
</job>
In Constants.vbs collect all constants you want to use later and in the other VBS files define your functions. In your main program file MainProgram.vbs, create a sub called MainProgram() and write your program there.
In this subroutine, you can use all of the constants and functions defined in the other VBS files.
For example :
sub MainProgram()
' Local variables
Dim strMessage, strSendTo, strSubject
' OpenFile is a function from FileFunctions.vbs
strMessage = OpenFile("C:\Msg\message.html")
strSendTo = "email.address#yourdomain.com"
strSubject = "Daily report - " & date
' SendMessage is a function from SendMail.vbs
' cFrom and cServer are constants from Constants.vbs
SendMessage(cFrom, strSendTo, strSubject, strMessage, cServer)
' Logger is a function from LoggingFunctions.vbs
Logger("Daily report sent - " & now())
end sub
Hope you get the idea and I could help some people write better VBS apps :)

Building on the accepted answer, here's an include sub procedure which takes a path relative to the script location instead of the working directory:
Sub include( relativeFilePath )
Set fso = CreateObject("Scripting.FileSystemObject")
thisFolder = fso.GetParentFolderName( WScript.ScriptFullName )
absFilePath = fso.BuildPath( thisFolder, relativeFilePath )
executeGlobal fso.openTextFile( absFilePath ).readAll()
End Sub
Note the you can additionally use . and .. parts in your path to include files in parent folders, etc. and it will not matter where you launch the script from. Example:
include "..\Lib\StringUtilities.vbs"

Is this VBScript being used locally, or served classic ASP style?
If its classic ASP, you can use SSI todo it:
<!-- #include virtual="/PathTo/MyFile.vbs" -->

You can use the ExecuteGlobal function to run arbitrary VBS code in the global namespace. An example can be found here : http://www.source-code.biz/snippets/vbscript/5.htm

IIS 5 and up also allow a script tag for including other files from an ASP file. (Is your VBScript an ASP page or a Windows script?) Here's an example:
<script language="VBScript" runat="server" src="include.asp"></script>
The behavior and rules are a bit different from server-side includes. Note: I have never actually tried using this syntax from classic ASP.

you can definately use the WSF script tag in cscript:
<script language="VBScript" src="ADOVBS.INC"/>
If you use ADOVBS.inc for an ADODB access make sure to remove the
<% %>
tags from ADOVBS.INC.

Related

Expected Statement error when using multiple parameters in VBS

I am working on making a simple vbs that will open and close an excel file and save as it closes. This is to update the values inside the excel file after they are modified by a separate python script, and I need to do it this way for many dumb reasons outside my control.
I don't have a lot of experience with VBS but what I researched about this, I don't see the error in my code. However, I get an error whenever I try to run the code below. There error is:
Line: 7
Char: 39
Error: Expected statement
Code: 800A0400
Source: Microsoft VBScript compilation error
Any help would be appreciated, thanks!
dim fso
set fso = CreateObject("Scripting.FileSystemObject")
if(fso.FileExists("C:\Users\Public\Documents\Templates\testfile.xlsx")) then
set xlapp = createobject("Excel.Application")
xlapp.Workbooks.Open "C:\Users\Public\Documents\Templates\testfile.xlsx"
xlapp.ActiveWorkbook.SaveAs Filename:="C:\Users\Public\Documents\Templates\testfile_test.xlsx", FileFormat:=51, ConflictResolution:=2
xlapp.ActiveWorkbook.Close SaveChanges:=True
end if
If you are using standalone VBS, the following lines won't work because VBS isn't VBA. VBA supports Named Argument Syntax in function calls whereas its not supported by VBS
xlapp.ActiveWorkbook.SaveAs Filename:="C:\Users\Public\Documents\Templates\testfile_test.xlsx", FileFormat:=51, ConflictResolution:=2
xlapp.ActiveWorkbook.Close SaveChanges:=True
You have to provide the arguments to the function. So Function ArgName:=ArgValue becomes Function ArgValue
xlapp.ActiveWorkbook.SaveAs "C:\Users\Public\Documents\Templates\testfile_test.xlsx",51,,,,,,2
xlapp.ActiveWorkbook.Close True

external VBScript in a HTA

I am trying to get my HTA to load a script from an external location to save me releasing new HTA files, some of it can be loaded dynamically as I continue to add new functions to it. I can get it to work when I am using IIS to host the script file locally on my PC. But when I am trying to host it externally (I have only tried GitHub so far) I get the following error:
Line: 1
Char: 1
Error: "Type mismatch: 'Hello'"
Code: 0
URL:
I have something like:
<html>
<HEAD>
<title>HTA Test</title>
<HTA:APPLICATION
SCROLL="yes"
SINGLEINSTANCE="yes"
WINDOWSTATE="maximize"
>
<script language="VBScript" src="http://localhost/HtaUpdates/script.txt"></script>
<SCRIPT Language=vbscript>
Sub Window_OnLoad
setTimeout "Hello", 100, "VBScript"
End Sub
</SCRIPT>
</HEAD>
<BODY>
</BODY>
</html>
and in the script.txt on the server:
Sub Hello
MsgBox("Hello.")
End Sub
I have two recommendations for you.
Firstly, the reason why the script isn't loading is because you're specifying the script as type 'txt'. This means when you serve the script over your local/remote web server, the MIME type for the script will be 'text/plain' which won't be accepted. If you rename it to be a proper 'vbs' extension, IIS will serve the file with the MIME type 'text/vbscript' which is what the HTA is expecting.
Additionally, using setTimeout in this way is fragile because it requires that your script contents are loaded successfully within 100ms. If the network is slow, the call will fail. It's better to simply make the call to Hello at the end of the script that you are requesting over the network. You could repurpose the setTimeout call to check if the script has loaded successfully within e.g. 10 seconds (by looking for a variable, maybe?) and show an error message if it hasn't. Note that in my solution below, I have put the main code before the script tag, so that the global variable loaded is visible to the remote script.
One final warning: While testing this, I found that the HTA was caching the VBS file so when I made changes to script.vbs, they weren't taking effect when I reloaded the application. You'd need to either...
configure the web server to set an Expires header so that these files don't get cached
write some code in your HTA file to request the script with a 'nonce' value after it, to override the cache. For example requesting http://localhost/script.vbs?nonce=[RANDOM NUMBER], using VBScript's Randomize and Rnd functions. Requesting a static file with a nonce like this will still work on IIS because the parameters are ignored - it only cares that the file exists.
test.hta:
<html>
<HEAD>
<title>HTA Test</title>
<HTA:APPLICATION SCROLL="yes" SINGLEINSTANCE="yes" WINDOWSTATE="maximize">
<SCRIPT Language=vbscript>
Dim loaded : loaded = False
Sub CheckLoaded()
If loaded Then
MsgBox("Yes, the script loaded.")
Else
MsgBox("Oh dear! The script hasn't loaded.")
End If
End Sub
Sub Window_OnLoad
setTimeout "CheckLoaded", 1000, "VBScript"
End Sub
</SCRIPT>
<script language="VBScript" src="http://localhost/script.vbs"></script>
</HEAD>
<BODY>
</BODY>
</html>
script.vbs:
loaded = True
MsgBox("Hello.")

VBsscript (.vbs) code gets cleared/removed

At the top of most of my VBscripts (.vbs files) I have the following code:
Option Explicit
Const ForReading = 1
Dim objFSO, objFile
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("Z:\somepath\somefile.vbs", ForReading)
Execute objFile.ReadAll()
This code allows me to use another vbs file as a library. In this case somefile.vbs would be my library and have all my subs and functions defined that are called from the script the above code is called from (I call this the calling script).
This issue: Every once in a while, one of the scripts seems to delete the code in Z:\somepath\somefile.vbs (the library script read by the calling script).
I think this because if a wscript.exe is listed in my Task Manager Processes tab and I restore the Z:\somepath\somefile.vbs file from a backup location, almost immediately, when I open Z:\somepath\somefile.vbs again, there is no code in that file. But if I kill the wscript.exe process, the file is fine. I can't reproduce the behavior because it only occurs when our network has a hiccup of some kind (I think).
My first thought is that the create setting is wrong when I use this line:
Set objFile = objFSO.OpenTextFile("Z:\somepath\somefile.vbs", ForReading)
But according to this link, the default create value should be false:
https://msdn.microsoft.com/en-us/library/aa265347(v=vs.60).aspx
Note, coincidentally, I am also using objFile and objFSO variables in the file somefile.vbs for things that aren't related to what I am doing in the calling script. For example, the objFile in the somefile.vbs file has a completely different name and location and is created this way:
Set objFile = objFSO.OpenTextFile("z:\differentpath\differentname.vbs", ForAppending, True)
I am guessing this is the issue, but I don't understand it. Can someone shed some light on this? Is the create or append setting getting reset in the calling script? How does that work?
Not knowing what else to do I have change the variable names in the somefile.vbs file to oFSO, oFile and in the calling script they are still objFSO, objFile. I also changed the line of code in the calling script to include false for the create setting like this:
Set objFile = objFSO.OpenTextFile("Z:\somepath\somefile.vbs", ForReading,false)
Going out on a limb (since you posted only partial code) I'm going to assume that you don't explicitly close your library script after reading, so your main script keeps the file open until it terminates. Either add a line objFile.Close after the Execute statement, or (better yet) change
Set objFile = objFSO.OpenTextFile("Z:\somepath\somefile.vbs", ForReading)
Execute objFile.ReadAll()
to
code = objFSO.OpenTextFile("Z:\somepath\somefile.vbs").ReadAll
Execute code
or just
Execute objFSO.OpenTextFile("Z:\somepath\somefile.vbs").ReadAll
so that the file is automatically closed after being read.
Learned from https://ss64.com/vb/execute.html
Execute takes a group of statements and executes them in local scope, ExecuteGlobal executes them in global scope.
However, if the same Execute statement is invoked outside of a procedure (i.e., in global scope), not only does it inherit everything in global scope, but it can also be called from anywhere, since its context is global.
Does the issue remain if you call the Execute form a procedure?

VBscript Unable to Run Shell Command

I have set up the following Sub to run shell commands quickly and easily.
This script works for the login scripts at my company without fail.
I am currently developing a script to add a large batch of users to our domain.
When I used this Sub in my new script I receive an error saying that the file cannot be found.
I have tried using the fix in this stackoverflow post, but I recieve the same error even with this code.
VBScript WScript.Shell Run() - The system cannot find the file specified
The part I find puzzling is that this sub works just fine when run from the netlogon folder of our domain controller.
What am I doing wrong?
Thanks,
Sub runcommand(strCommand)
Dim objWshShell, intRC
set objWshShell = WScript.CreateObject("WScript.Shell")
intRC = objWshShell.Run(strCommand, 0, TRUE)
call reportError(intRC,strCommand)
set objWshShell = nothing
end Sub
function reportError(intRC, command)
if intRC <> 0 then
WScript.Echo "Error Code: " & intRC
WScript.Echo "Command: " & command
end if
end function
The previous values for strCommand had no spaces and were very straightforward. Your new script is passing more complex variables to your Sub so you need additional conditional handling, as Alex K. pointed out in his Collusion (i.e., "Comment/Solution") above. Alex K.'s sample above is perfect, so, being a Point Pimp tonight, will post it as the solution:
objWshShell.Run("cmd /k echo Hello World", 1, TRUE)

How do I include a common file in VBScript (similar to C #include)?

VBScript doesn't appear to have a way to include a common file of functions.
Is there a way to achieve this?
You can create a (relatively) small function in each file that you want to include other files into, as follows:
sub includeFile (fSpec)
dim fileSys, file, fileData
set fileSys = createObject ("Scripting.FileSystemObject")
set file = fileSys.openTextFile (fSpec)
fileData = file.readAll ()
file.close
executeGlobal fileData
set file = nothing
set fileSys = nothing
end sub
and then use it to include specific files - these are executed as if they were inline.
includeFile "commonapi.vbi"
includeFile "dbcalls.vbi"
It basically opens the file, reads the entire contents into a string, then executes that string. There's no error handling on the I/O calls since this sort of stuff is usually done once on program start, and you want to fail if there's a problem including it.
Note that the includeFile function can be compressed to:
Sub includeFile(fSpec)
With CreateObject("Scripting.FileSystemObject")
executeGlobal .openTextFile(fSpec).readAll()
End With
End Sub
Or even to (if you're not adverse to long lines):
Sub includeFile(fSpec)
executeGlobal CreateObject("Scripting.FileSystemObject").openTextFile(fSpec).readAll()
End Sub
The "Windows Script Host" framework (if ya want to call it that), offers an XML wrapper document that adds functionality over regular vbs files. One of which is the ability to include external script files of both the VBscript and Jscript flavors. I never got very deep into it, but I think it would do what you're wanting to do.
http://msdn.microsoft.com/en-us/library/15x4407c(VS.85).aspx
You can include JavaScript, VBScript, or modules of other WScript script languages.
Example WSF file:
<job id="IncludeExample">
<script language="JavaScript" src="sprintf.js"/>
<script language="VBScript" src="logging.vbs"/>
<script language="VBScript" src="iis-queryScriptMaps.vbs"/>
</job>
If the above file is called "iis-scriptmaps.wsf", run it this way with cscript.exe:
cscript.exe iis-scriptmaps.wsf
I know this is an old thread but I post my answer anyway so others can learn what I have learnt about VBS and WSF files by "trial and error" :
So to have the same functionality as in other languages you can create one WSF file and include all of your VBS libs there, including the main program.
Something like this :
<job id="MainProg">
<script language="VBScript" src="Constants.vbs"/>
<script language="VBScript" src="FileFunctions.vbs"/>
<script language="VBScript" src="SendMail.vbs"/>
<script language="VBScript" src="LoggingFunctions.vbs"/>
<script language="VBScript" src="MainProgram.vbs"/>
<script language="VBScript">
' Here we call the main program
MainProgram()
</script>
</job>
In Constants.vbs collect all constants you want to use later and in the other VBS files define your functions. In your main program file MainProgram.vbs, create a sub called MainProgram() and write your program there.
In this subroutine, you can use all of the constants and functions defined in the other VBS files.
For example :
sub MainProgram()
' Local variables
Dim strMessage, strSendTo, strSubject
' OpenFile is a function from FileFunctions.vbs
strMessage = OpenFile("C:\Msg\message.html")
strSendTo = "email.address#yourdomain.com"
strSubject = "Daily report - " & date
' SendMessage is a function from SendMail.vbs
' cFrom and cServer are constants from Constants.vbs
SendMessage(cFrom, strSendTo, strSubject, strMessage, cServer)
' Logger is a function from LoggingFunctions.vbs
Logger("Daily report sent - " & now())
end sub
Hope you get the idea and I could help some people write better VBS apps :)
Building on the accepted answer, here's an include sub procedure which takes a path relative to the script location instead of the working directory:
Sub include( relativeFilePath )
Set fso = CreateObject("Scripting.FileSystemObject")
thisFolder = fso.GetParentFolderName( WScript.ScriptFullName )
absFilePath = fso.BuildPath( thisFolder, relativeFilePath )
executeGlobal fso.openTextFile( absFilePath ).readAll()
End Sub
Note the you can additionally use . and .. parts in your path to include files in parent folders, etc. and it will not matter where you launch the script from. Example:
include "..\Lib\StringUtilities.vbs"
Is this VBScript being used locally, or served classic ASP style?
If its classic ASP, you can use SSI todo it:
<!-- #include virtual="/PathTo/MyFile.vbs" -->
You can use the ExecuteGlobal function to run arbitrary VBS code in the global namespace. An example can be found here : http://www.source-code.biz/snippets/vbscript/5.htm
IIS 5 and up also allow a script tag for including other files from an ASP file. (Is your VBScript an ASP page or a Windows script?) Here's an example:
<script language="VBScript" runat="server" src="include.asp"></script>
The behavior and rules are a bit different from server-side includes. Note: I have never actually tried using this syntax from classic ASP.
you can definately use the WSF script tag in cscript:
<script language="VBScript" src="ADOVBS.INC"/>
If you use ADOVBS.inc for an ADODB access make sure to remove the
<% %>
tags from ADOVBS.INC.

Resources