Stringify a method call in VBScript - vbscript

I'm currently working every day with QuickTest Professional 11, which uses VBScript behind the scenes. Lately, I've started developing some of my own functions to handle common situations. I'm pretty new to VBscript, most of my programming experience is in C and Python.
I'm trying to implement Python's Try/Except in VBScript, mostly to wrap around actions like clicking links or selecting values from dropdown boxes. Here's what I have so far:
Class cls_ErrorHandler
Private bWasError
Private Sub Class_Initialize()
bWasError = False
End Sub
Private Sub IsErr
If Err.Number <> 0 Then
bWasError = True
Else
bWasError = False
End If
Err.Clear
End Sub
' If the command fails, set bWasError
Public Sub Try(strCommandToTry)
On Error Resume Next
Execute(strCommandToTry)
me.IsErr
On Error Goto 0
End Sub
Public Sub Except(strCommandInCaseOfError)
If bWasError Then
Execute(strCommandInCaseOfError)
End If
bWasError = False
End Sub
End Class
I'd like to be able to write things like this:
Set oErrorHandler = New cls_ErrorHandler
oErrorHandler.Try(Stringify(Browser("Browser Name").Page("Page Name").WebCheckBox("Checkbox Name").Set "ON"
oErrorHander.Except(Stringify(Browser("Browser Name").Page("Page Name").WebButton("Save").Click))
As far as I can tell, there really isn't any nice way to pass a function as an argument to another function in VBScript. The best way seems to be to pass a string containing the name of a function, and then feed that string into Execute() or Eval(). Objects in QuickTest Professional tend to have lots of quotation marks, so escaping them all by hand would make the code unreadable.
In C, I'd use something like this:
#define Stringify(obj) #obj
and it would be done... but nothing like that seems to exist in VBScript. Is there any way to implement this? Is there anything in VBScript that takes an object as its input and returns a string representation of that object's name? Would it be possible to write a DLL in C/C# that would provide this kind of functionality?

You can use GetRef to obtain a function/sub pointer, and call the function/sub using that pointer. See online help, it shows an example for an event handler, but you can refer to any global function or sub with GetRef, and use the variable holding the GetRef return value just like an alias for the sub/function.
Be aware, however, that there are cases you won't be able to cover with this:
You cannot use a GetRef function pointer if the current calling stack contains a
method call that is a function registered to a test object via
RegisterUserFunc.
You cannot call such a test object method from within a routine call
that was adressed via a GetRef function pointer.
Also consider using ExecuteGlobal instead of Execute so the code you pass can set global variables that the ExecuteGlobal caller can access afterwards.

Related

OCIBreak analog in managed ODP.NET

I'm trying to migrate to managed ODP.NET, but I'm having problems with the OCIBreak.
Friend Shared Sub Break(dbConn As OracleConnection)
Dim ContextHandle = DirectCast(dbConn.GetType.GetProperty("ServiceContextHandle", ref.BindingFlags.Instance Or ref.BindingFlags.NonPublic).GetValue(dbConn, Nothing), Runtime.InteropServices.SafeHandle)
Dim ErrorHandle = DirectCast(dbConn.GetType.GetProperty("ErrorHandle", ref.BindingFlags.Instance Or ref.BindingFlags.NonPublic).GetValue(dbConn, Nothing), Runtime.InteropServices.SafeHandle)
OCIBreak(ContextHandle, ErrorHandle)
End Sub
<Runtime.InteropServices.DllImport("oci.dll", CallingConvention:=Runtime.InteropServices.CallingConvention.Cdecl)>
Private Shared Function OCIBreak(Context As Runtime.InteropServices.SafeHandle, ErrorHandle As Runtime.InteropServices.SafeHandle) As Integer
End Function
What I found out, you can partially emulate it using following code.
Private Sub Cancel(conn As OracleConnection)
Using cmd = New OracleCommand("", conn)
cmd.Cancel()
End Using
End Sub
I expect this to work, because OracleCommand.Cancel cancels currently running command, which is not necessarily this one (docs).
And it actually does work this way - but only for SELECT-queries, it doesn't cancel PL/SQL-blocks or stored procedures. It is still possible to cancel those, but only via the corresponding OracleCommand-instance.
Is it possible to achieve somehow? Maybe there is a way to get that currently running command from oracle connection object?

QTP/UFT - Function That Creates WebLink Variables

I am thinking of doing something I am not sure if it is possible - perhaps not the best practice even, but nonetheless want to give it a try.
I have a function library and I have the action in which I do the main scripting, obviously calling functions from the library.
Originally in my action, the code looked something like this:
Code
Browser("Browser").Page("Page").WebElement("Element").Click
I have, however, changed it to this:
Code
Set WebLink = Browser("Browser").Page("Page")
WebLink.WebElement("Element").Click
I did this because i feel that it has a cleaner look with "less" code in each line.
I know that I can code a function to do this:
Public Function myLink(WebLink)
Set WebLink = Browser("Browser").Page("Page")
End Function
Then in my action, I do a call just once at the top:
Code
Call myLink(WebLink)
WebLink.WebElement("Name").HighLight
However, I was thinking that in some instances, I would have different page names, for example:
Code
Browser("Browser").Page("Page1")
Browser("Browser").Page("Page2")
Browser("Browser").Page("Page3")
So perhaps, creating a function that would store my variables and then I am able to call the variables could be an alternative.
My function could look like this:
Code
Public Function myLinks(WebLink)
Dim Pages(): Pages = ("Login","CreateUser","SelectOption","DeleteUser",)
Browser("Browser").Page(Pages())
For Each Elem In Pages
Set WebLink = Browser("Browser").Page(Pages())
End For
End Function
Then in my action, I use it like this:
Code
Call myLinks(WebLink)
Login.WebElement("Element").Click
CreateUser.WebElement("Element").Click
SelectOption.WebElement("Element").Click
DeleteUser.WebElement("Element").Click
I know that what I have is probably illogical but if something like this could perhaps work, I would like to give it a try.
well, if you do not have the Option Explicit enabled at the beginning of your vbs files (which would force you to declare each variable before usage) then you can simply say:
For Each strPage in Pages()
ExecuteGlobal "Set " & strPage & " = Browser(""Browser"").Page(""" & strPage & """)"
Next
This one would create for you Global Variables having as names the Strings in your Pages Array. You do not need any Input Parameter
P.S: A better name would be InitPageObjects - as what you are trying to achieve has nothing to do with Links

VBscript that looks correct but errors 800A09D5

I Have been searching for an answer to this but it looks like my vbscript is correct. The macro is not in a module of the same name "MasterSAP". Can’t see what is wrong, you would think this is simple, but I can’t figure it out.
Dim accessApp
set accessApp = createobject("Access.Application")
accessApp.OpenCurrentDataBase("S:\Salem\SCHED\SeesStuff\Resources\MasterSAPData.accdb")
accessApp.Run "MasterSAP"
AccessApp.Quit
Set accessApp = nothing
It's hard to guess because the rest of the code is missing but you should have a procedure in a standard module that Run() is calling if you don't and you are just passing the name of a macro this error will occur.
You should have your code in a standard module with the following definition;
Public Sub MasterSAP()
...
End Sub
You also want to be careful that this procedure is not placed inside a standard module called MasterSAP as this can also cause the error as both the procedure and the module will share the same namespace.

ASP Issue: Is it possible to pass session variables as parameters to functions in the Global.asa file?

I plan to write the Global.asa file in JavaScript. When a session or application ends, I need changes to be made in a database. Specifically, I need to break up a string and use it in a query in the Global.asa functions. That part doesn't puzzle me at all. Just the part about passing a session variable in as a parameter. I imagine my Global.asa to look something like this:
(Let's say the two variables "variable1" and "variable2" were session variables)
<script language="JScript" runat="Server">
function Application_OnStart() {
}
function Application_OnEnd(variable1) {
}
function Session_OnStart() {
}
function Session_OnEnd(variable2) {
}
</script>
So, I'm not sure if what I'm asking is even feasible. If so, any tips? Keep in mind that I am working in ASP, not ASP.NET. Also, I'm a bit new to using server-side code, so pardon my ignorance.
No, what you suggest is not feasible.
The functions in the global.asa file are callbacks for events. The functions get called, all with no parameters.
You can make use of the Session object to do what you want.
In your code you can set a session variable like this:
session("userid") = 856
In your global.asa you can then use:
variable1 = session("userid")
Have you considered using VBScript for your Global.asa? I've never coded a Global.asa in javascript, but you could code it in VB and use Session or Application level variables:
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
Sub Application_OnStart()
'initialize application level variables
Application("ConnectionString") = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" & Server.mappath("access_db/mydb.mdb")
End Sub
Sub Application_OnEnd()
Application("ConnectionString") = ""
End Sub
Sub Session_OnStart()
'initialize session level variables
Session("UserIP") = Request.ServerVariables("REMOTE_ADDR")
End Sub
Sub Session_OnEnd()
Session("UserIP") = ""
End Sub
Not sure if you can pass a variable into the predefined functions as such, but you can use Session or Application variables anywhere on the website, including within these functions.

Legacy VB6 app throwing type mismatch error during ActiveX create object

I've been tasked with making a change to a legacy VB6 Winform app. What I found is that this app was unnecessarily split up into multiple DLLs (some of the DLL were simply a couple of classes). So, I'm working on consolidating some of the DLLs into the main program but I've run into a problem that I could use some help on.
One of the dlls contained a class called CTest(Test.cls). The main program used it in the following lines of code. strProgId is a string naming another DLL.
Dim objTest As CTest
Set objTest = CreateTestObject(strProgId)
Public Function CreateTestObject(strProgId As String) As Object
10 On Error GoTo ErrorHandler
20 Set CreateTestObject = CreateObject(strProgId)
30 Exit Function
ErrorHandler:
40 UpdateErrorInfo "CreateTestObject", "Globals", strProgId
50 HandleError
End Function
Here are the contents of CTest
Option Explicit
Private m_strName As String
Private m_strDescription As String
Private m_cnnADO As ADODB.Connection
Public Property Get Name() As String
10 Name = m_strName
End Property
Public Property Let Name(strNewName As String)
10 m_strName = strNewName
End Property
Public Property Get Connection() As ADODB.Connection
10 Set Connection = m_cnnADO
End Property
Public Property Set Connection(cnnADO As ADODB.Connection)
10 Set m_cnnADO = cnnADO
End Property
Public Property Get Description() As String
10 Description = m_strDescription
End Property
Public Property Let Description(strNewDescription As String)
10 m_strDescription = strNewDescription
End Property
Public Function Run(ByVal strSTMType As String, _
instInstruments As CInstruments, objResults As CTestResults) As Boolean
End Function
If CTest is still part of a DLL and I have a reference to it in the Main Program, it gets through the CreateTestObject line without an error. If I bring in the class into the main program it throws a type mismatch error.
Any help is appreciated, thank you in advance.
CreateObject will only work with publicly visible COM classes. Therefore, because you've brought CTest into your main program, CreateObject will no longer work and will raise errors just like you describe.
Either
Create the object via Set obj = New CTest
Or just leave the class in a separate DLL? Are you sure there's no other side effects of it being in a separate DLL? No other app using it?
I just solved this one after a day and a half. In my case I invoke the dll twice. The first time it worked and the second time it threw the error above. I have several projects open and each has its' own compatibility setting. For some unexplained reason the second reference to the common dll had compatibility set off. By setting the correct path in the version compatability and setting it to binary compatibility the problem cleared up.
If you're bringing CTest into your main program directly, then you don't need the CreateObject call - just instantiate it the normal way, now that it's part of your program, and it should work fine.

Resources