Check if an Object exists in VBScript - asp-classic

I'm maintaining a Classic ASP app written in VB Script by an outside company long, long ago.
I have an array of imagefile paths, like so:
dim banners, arrKeys, i
set banners=CreateObject("Scripting.Dictionary")
banners.Add "banner1.jpg", "http://www.somelink.com"
banners.Add "banner2.jpg", "http://www.somelink.com"
banners.Add "banner3.jpg", "http://www.somelink.com"
This will exist ONLY on pages that have banner ads. There is some standard code that iterates through this list in an include file (common to all pages).
If Not banners Is Nothing then
' then loop through the Dictionary and make a list of image links
End if
The problem is that if banners is not instantiated on the page (it's not on all pages), I get a Can't find object error
What's the proper way to check if an object exists in VB Script?

#Atømix: Replace
If Not banners Is Nothing then
and use
If IsObject(banners) Then
Your other code you can then place into an include file and use it at the top of your pages to avoid unnecessary duplication.
#Cheran S: I tested my snippets above with Option Explicit on/off and didn't encounter errors for either version, regardless of whether Dim banners was there or not. :-)

IsObject could work, but IsEmpty might be a better option - it is specifically intended to check if a variable exists or has been initialised.
To summarize:
IsEmpty(var) will test if a variable exists (without Object Explicit), or is initialised
IsNull(var) will test if a variable has been assigned to Null
var Is Nothing will test if a variable has been Set to Nothing, but will throw an error if you try it on something that isn't an object
IsObject(var) will test if a variable is an object (and will apparently still return False if var is Empty).

If a variable is declared, but not initialized, its value will be Empty, which you can check for with the IsEmpty() function:
Dim banners
If IsEmpty(banners) Then
Response.Write "Yes"
Else
Response.Write "No"
End If
' Should result in "Yes" being written
banners will only be equal to Nothing if you explicitly assign it that value with Set banners = Nothing.
You will have problems, though, with this technique if you have Option Explicit turned on (which is the recommendation, but isn't always the case). In that case, if banners hasn't been Dimed and you try to test IsEmpty(banners), you will get a runtime error. If you don't have Option Explicit on, you shouldn't have any problems.
edit: I just saw this related question and answer which might help, too.

Somewhat related is IsMissing() to test if an optional parameter was passed, in this case an object, like this:
Sub FooBar(Optional oDoc As Object)
'if parameter is missing then simulate it
If IsMissing(oDoc) Then Dim oDoc as Object: oDoc = something
...

You need to have at least dim banners on every page.
Don't you have a head.asp or something included on every page?

Neither of IsEmpty, Is Object, IsNull work with the "Option Explicit" Setting, as stealthyninja above has misleadingly answered.
The single way i know is to 'hack' the 'Option Explicit' with the 'On Error Resume Next' setting, as Tristan Havelick nicely does it here:
Is there any way to check to see if a VBScript function is defined?

Related

Iterating through IHtmlElementCollection

I have a VB webapplication that needs to read information from an excisting webpage on the internet. Therefore I use the mshtml library. I read the html into an ihtmldocument3 interface. After that I iterate through an ihtmlelementcollection and everything worked fine in Visual Studio 2010 Debugger. At least, the first time. When I debug the code for the second time, after iterating a few elements, the next elements return nothing and I get an exception. (When I break into the code the ihtmlelementcollection shows 0 items.) When I rename all the variables, it runs properly, but again, only the first time.
Here's the code I use to debug. I have outlined the actual code because that responds into an exception (null reference). Do I need to manually release a collection or something or am I doing something stupid?
'global variable
Private tables as IHTMLElementCollection
...........................................
Dim tableChildren As IHTMLElementCollection = tables(3).children
Dim trElements As IHTMLElementCollection = tableChildren.item(0).getElementsByTagName("tr")
Dim intCount As Integer 'just for debugging purposes
For Each element As IHTMLElement In trElements
intCount += 1 'for debugging purposes
Debug.Print(intCount.ToString & vbNewLine & element.innerHTML)
'strLine1 = element.children(0).innerText
'strLine2 = element.children(1).innerText
'and so on...
Next
I assume that by this point you've already resolved the problem one way or another, but I thought I'd suggest the HtmlAgilityPack. It has the advantage of being written to support just this type of scenario, and (as far as I know) is a native .NET library rather than being COM-based. It might be a better fit for your situation.

QueryString not accepting & - Needs to

I need to be able to handle an HTML encoded ampersand in my .Net code.
So the Url is
http://myite.com/index.aspx?language=en&Refresh=true
There is no way of changing this as it has been generated by something else so this is out of my control.
How can I read the Refresh parameter?
I have tried
HttpUtility.UrlDecode(Request.QueryString("Refresh"))
but my Request.QueryString("Refresh") is actually empty, so this is pointless, as is Uri.EscapeDataString.
This can't be the first time this has happened, but I'm struggling to find a solution, as most people would say use UrlEncoding, but as I said, the Url is out of my control.
& in your query string should be %26.
Since you can't correct the url.
You can read the refresh value as:
Request.QueryString("amp;Refresh");
Note that the developer of the service you are using may correct this in future.
It would be good to be ready for that already.
var refresh = Request.QueryString("amp;Refresh");
if(String.IsNullOrEmpty(refresh))
refresh = Request.QueryString("Refresh");
nunespascal answer pretty much solves your problem. There are some alternate methods.
If its guaranteed that your Refresh parameter is the second key in the QueryStringCollection then you can use Request.QueryString(1)
Another method is to do a Contains on the QueryStringCollection.
If Request.QueryString IsNot Nothing AndAlso Request.QueryString.AllKeys.Count() > 0 Then
Dim refreshKey = Request.QueryString.AllKeys.FirstOrDefault(Function(nv) nv.Contains("Refresh"))
If refreshKey IsNot Nothing Then
Dim refreshValue = Request.QueryString(refreshKey)
End If
End If

Passing a parameter through server.execute?

It is possible to pass a parameter through server.execute?
Fx. I have in my site.asp an IF-scenario where I need functions.asp?a=something&id=123 executed. Is this possible?!
On site.asp:
dim id
id = 123
if b = "hi" then
server.execute("functions.asp?a=something&id=" & id)
else
response.write("No way dude")
end if
On functions.asp
a = request.querystring("a")
id = request.querystring("id")
if a = "something" and cint(id) > 100 then
response.write("Yes way dude")
else
response.write("No way dude")
end if
You can't use querystring in Server.Execute, it's clearly mentioned in the official documentation.
What you can do is much better: you can directly access the variable id defined in site.asp inside functions.asp, and you can also declare and set another variable, a:
--site.asp:
dim id, a
id = 123
a = "something"
server.execute("functions.asp")
--functions.asp
if a = "something" and cint(id) > 100 then
response.write("Yes way dude")
else
response.write("No way dude")
end if
As it creates whole new "scripting environment" the executed file won't have access to the calling code properties, methods or variables, only to the global Request parameters, Session etc.
With this in mind, I fear the most simple way around is using Session variable to pass the value between pages:
Session("id") = 123
Session("a") = "something"
And then:
if Session("a") = "something" and Session("id") > 100 then
response.write("Yes way dude")
else
response.write("No way dude")
end if
This question may be old and resolved, but the best answer doesn't mention everything, and there is information clearly posted on Microsoft.com about it:
Server.Execute Method
The following collections and properties are available to the executed ASP page:
Application variables, even if they are set in the calling page.
Session properties, even if they are set in the calling page.
Server variables and properties, even if they are set in the calling page.
Request collections and properties, even if they are set in the calling page. This includes Form and QueryString data passed to the calling page.
Response collections and properties. The executed .asp file may modify HTTP headers. However, as with any .asp file, if the executed .asp file attempts to modify HTTP headers after it sends a response to the client, it generates an error.
So as you can see, there are 5 ways Microsoft suggests to pass variables through to a Server.Execute method. Before I saw this on Microsoft, the preferred method was Session, as the best answer suggests, since I saw this before the info on Microsoft.com. But after noticing that QueryStrings can be passed from the previous page, I would have to say this beats using Session for passing values. Session would be needed if your application required you adding variables to the executing page.
But passing variables, I would say QueryStrings, and it's easy to apply if your application allows the flexibility. I'm sure you know how to already use querystrings, but in the sense of using it for a Server.Execute method, you can simply do this:
Consider having ASP1.asp and ASP2.asp:
ASP1.asp includes:
Server.Execute("ASP2.asp")
ASP2.asp includes:
Response.Write Request("id")
When you call ASP1.asp?id=123
You will notice that ASP2.asp also see's the same Querystring passed to ASP1.asp, so it would write 123 on the response of ASP1.asp.
That's much less complicated than using a Session for the task.
In short: YES, you can use QueryString values in conjunction with Server.Execute within an ASP.NET page or application.
You can pass dynamic variables in a QueryString argument assembled in the executing ASPX page (page1.aspx), as follows:
Dim intExample1 As Int = 22
Dim strExample2 As String = "hello world"
Server.Execute("page2.aspx?number=" & intExample1 & "&string=" & Server.UrlEncode(strExample2))
In this example, page2.aspx can then reference and use the following values:
Request.QueryString("number")
Request.QueryString("string")
Why not to use #include instead of server.execute?
I looked for a difference and found that in this particular case, using #include is the best solution:
https://en.wikibooks.org/wiki/Active_Server_Pages/Server-Side_Includes#What_it_Does
You need some variables defined in parent page to be used in child, so your solution could be:
dim id, a
id = 123
a = "something"
if b = "hi" then
<!--#include file="functions.asp" -->
else
response.write("No way dude")
end if
On functions.asp
if a = "something" and cint(id) > 100 then
response.write("Yes way dude")
else
response.write("No way dude")
end if
Advantages:
Simply act as it was the same page.
Use of disposal variables instead of Session variables.
Don't show internal variables in main URL.

How to tell whether a variable in ASP has been declared

Let me start off by saying I'm a PHP developer, not an ASP one. (And I really wish ASP had isset().) And I'm working in a live environment so I don't really have an opportunity to do any testing.
All the resources I've found suggest different ways to test for the existence of a variable.
Here's what I'm trying to do:
On SOME pages, I set a variable which holds a value for a robots <meta> tag:
dim dsep_robots
dsep_robots = "nofollow,noindex"
All pages include header.asp. In my header file, I want to test whether dsep_robots has a value and if so, output that value, otherwise, output nothing.
I think that testing whether dsep_robots has a value might look like this:
if not dsep_robots = "" then
'...
end if
Best practices in PHP state that when you're using a variable that may or may not exist, you should always test if (isset($var)) {...} (not doing so will trigger a Notice if the variable doesn't exist).
Is there such a thing in ASP -- i.e. do I really need to test if it exists, or can I simply test if it has a value?
ust by the way, your question isn't about classic ASP, it is a VBScript question. VBScript can occur in scripts outside of ASP. And compilation isn't done in VBScript, because it is an interpreted language. Nevermind.
I think there's some confusion here -- and your question seems to have more to do with uninitialized variables than undeclared variables. For undeclared variables, see below.
For uninitialized, try the function IsEmpty.
For checking for null, try the function IsNull.
dim x
x = 1
dim t
Response.write isempty(x)
Response.write "<br>"
Response.write isempty(t)
Will display:
False
True
Detecting Undeclared Variables
If you include Option Explicit in your header, use of a non-declared variable will cause an runtime error. If your script is not Option Explicit it will not generate an error, and there is no function that will tell you if the variable has been declared or not. This sounds sloppy, and it is, but it was intentional.
The only way you can escape this is to actually set Option Explicit and then trap the error that you will get when you try to use the undeclared variable. If you trap this particular error you will find that it has Err.Number = 500. So, the following will do what you want:
Option Explicit
dim x
On Error Resume Next
Response.Write dsep_robots
If Err.Number > 0 Then
Response.Write Err.Number
end if
Of course, if you set Option Explicit and your code is rife with undeclared variables then you'll get errors being thrown all over the place, so you'd need to set On Error Resume Next at the top of your code so you can successfully ignore it, and only trap it when you want to.
By the way, here's Microsoft's online reference for VBScript:
http://msdn.microsoft.com/en-us/library/d1wf56tt(v=VS.85).aspx
#Jazzerus: I'd recommend putting the code within header.asp into a Sub, something like
Sub outputHeader(ByRef MyTitle, Byref dsep_robots)
'contents of header.asp
End Sub
...and then in your calling pages include header.asp right at the top and use
outputHeader "Title for this page", "value you want dsep_robots to have for page"
If you don't set dsep_robots on that page, then just leave the second parameter blank ("")
Then just checking if the variable is empty or not within the Sub should suffice:
If dsep_robots <> "" Then
Response.Write dsep_robots
End If
What about:
If NOT IsEmpty(myvariable) Then...
that seems to have been working for me.
I use the VarType function to detect if the variable is defined. If the tested variable is not defined, VarType returns the value vbError (10). Interesting to note that the optional parameter cannot be the last parameter else ASP tosses an error.
function Sample(p1,p2,p3,p4)
if VarType(p3) <> vbError then
'do something with p3
end if
end function
ThisWorks=Sample(1,2,,3)
ThisFails=Sample(1,2,3,)

is it possible to issue dynamic include in asp-classic?

I mean, like php'h include...
something like
my_file_to_be_included = "include_me.asp"
-- >
for what I've seen so far, there are a couple of alternatives, but every one of them has some sort of shortcoming...
what I'm trying to figure out is how to make a flexible template system... without having to statically include the whole thing in a single file with a loooooong case statement...
here there are a couple of links
a solution using FileSysmemObject, just lets you include static pages
idem
yet another one
same thing from adobe
this approach uses Server.Execute
but it has some shortcomings I'd like to avoid... seems like (haven't tried yet) Server.Execute code runs in another context, so you can't use it to load a functions your are planning to use in the caller code... nasty...
same thing
I think this one is the same
this looks promising!!!
I'm not sure about it (couldn't test it yet) but it seems like this one dinamycally handles the page to a SSDI component...
any idea???
No you can't do a dyanmic include, period.
Your best shot at this is a server.execute and passing whatever state it needs via a Session variable:-
Session("callParams") = BuildMyParams() 'Creates some sort of string
Server.Execute(my_file_to_be_included)
Session.Contents.Remove("callParams")
Improved version (v2.0):
<%
' **** Dynamic ASP include v.2.0
function fixInclude(content)
out=""
if instr(content,"#include ")>0 then
response.write "Error: include directive not permitted!"
response.end
end if
content=replace(content,"<"&"%=","<"&"%response.write ")
pos1=instr(content,"<%")
pos2=instr(content,"%"& ">")
if pos1>0 then
before= mid(content,1,pos1-1)
before=replace(before,"""","""""")
before=replace(before,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
before=vbcrlf & "response.write """ & before & """" &vbcrlf
middle= mid(content,pos1+2,(pos2-pos1-2))
after=mid(content,pos2+2,len(content))
out=before & middle & fixInclude(after)
else
content=replace(content,"""","""""")
content=replace(content,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
out=vbcrlf & "response.write """ & content &""""
end if
fixInclude=out
end function
Function getMappedFileAsString(byVal strFilename)
Dim fso,td
Set fso = Server.CreateObject("Scripting.FilesystemObject")
Set ts = fso.OpenTextFile(Server.MapPath(strFilename), 1)
getMappedFileAsString = ts.ReadAll
ts.close
Set ts = nothing
Set fso = Nothing
End Function
execute (fixInclude(getMappedFileAsString("included.asp")))
%>
Sure you can do REAL classic asp dynamic includes. I wrote this a while back and it has opened up Classic ASP for me in a whole new way. It will do exactly what you are after, even though people seem to think it isn't possible!
Any problems just let me know.
I'm a bit rusty on classic ASP, but I'm pretty sure you can use the Server.Execute method to read in another asp page, and then carry on executing the calling page. 15Seconds had some basic stuff about it - it takes me back ...
I am building a web site where it would have been convenient to be able to use dynamic includes. The site is all ajax (no page reloads at all) and while the pure-data JSON-returning calls didn't need it, all the different html content for each different application sub-part (window/pane/area/form etc) seems best to me to be in different files.
My initial idea was to have the ajax call be back to the "central hub" main file (that kicks the application off in the first place), which would then know which sub-file to include. Simply including all the files was not workable after I realized that each call for some possibly tiny piece would have to parse all the ASP code for the entire site! And using the Execute method was not good, both in terms of speed and maintenance.
I solved the problem by making the supposed "child" pages the main pages, and including the "central hub" file in each one. Basically, it's a javascript round-trip include.
This is less costly than it seems since the whole idea of a web page is that the server responds to client requests for "the next page" all the time. The content that is being requested is defined in scope by the page being called.
The only drawback to this is that the "web pieces" of the application have to live partly split apart: most of their content in a real named .asp file, but enough of their structure and relationship defined in the main .asp file (so that, for example, a menu item in one web piece knows the name to use to call or load another web piece and how that loading should be done). In a way, though, this is still an advantage over a traditional site where each page has to know how to load every other page. Now, I can do stuff like "load this part (whether it's a whole page or otherwise) the way it wants to be loaded".
I also set it up so each part can have its own javascript and css (if only that part needs those things). Then, those files are included dynamically through javascript only the first time that part is loaded. Then if the part is loaded repeatedly it won't incur an extra overhead.
Just as an additional note. I was getting weird ASCII characters at the top of the pages that were using dynamic includes so I found that using an ADODB.Stream object to read the include file eliminated this issue.
So my updated code for the getMappedFileAsString function is as follows
Function getMappedFileAsString(byVal strFilename)
Dim fso
Set fso = CreateObject("ADODB.Stream")
fso.CharSet = "utf-8"
fso.Open
fso.LoadFromFile(Server.MapPath(strFilename))
getMappedFileAsString = fso.ReadText()
'Response.write(getMappedFileAsString)
'Response.End
Set fso = Nothing
End Function

Resources