I could not figure out whats the problem is
Sub Reportstart(oEvent As Object)
Dim oFeld As Object
Dim oForm As Object
Dim oDocument As Object
Dim oDocView As Object
Dim Arg()
oField = oEvent.Source.Model
oForm = oField.Parent
sURL = oForm.DataSourceName
oDocument = StarDesktop.loadComponentFromURL(sURL, "C:\Users\Nameless\Desktop\Latest.odb", 0, Arg() )
oDocView = oDocument.CurrentController.Frame.ContainerWindow
oDocView.Visible = False
oDocument.getCurrentController().connect
Wait(100)
oDocument.ReportDocuments.getByName("report_student").open
oDocument.close(True)
End Sub'
The error is BASIC runtime error.
Argument is not optional.
Reportstart requires an argument oEvent, and the way you executed it, the subroutine was not given any argument.
The macro was designed to be called from an event handler of a control, for example, the Execute action of a push button on a Base form. Perhaps you executed the subroutine from the LibreOffice Basic IDE instead.
Related: https://ask.libreoffice.org/en/question/192344/argument-is-not-optional/
Related
I'm trying to put together tests in HP Unified Functional Testing
the way a programmer would.
For those unaware, the tool uses VBScript as its driver.
Because I want to use data from the same DataTable across multiple UFT actions
-- and because the Global table already has a different set of data on it
-- I want to retrieve data from an external file.
UFT happily supports this function.
My current plan is that, depending on which test I'm running,
I will iterate through only a range of rows in that table.
This is the script I've come up with:
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet "PersonFile.xlsx", 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstPersonIndex")
lastRow = Parameter("LastPersonIndex")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
' begin payload
MsgBox(DataTable.Value("LastName", dtLocalSheet))
' end payload
Next
End if
I don't want to have to repeat all this boilerplate
every time I want to use this pattern.
I'd really like to have something like:
In a Function Library:
sub LoopThroughSheetAnd(sheetFile, doThis)
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet sheetFile, 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstRow")
lastRow = Parameter("LastRow")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
call doThis()
Next
End if
end sub
In the original action...
sub Payload1()
MsgBox(DataTable.Value("LastName", dtLocalSheet))
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload1
In a separate action, 3 or 4 steps later...
sub Payload2()
' compare the data against another data source
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload2
The above code doesn't work in VBScript.
A type mismatch error is thrown
as soon as we try to pass Payload1 as a parameter.
How could one reasonably pull this off in VBScript?
Bonus points if the answer also works in UFT.
You can pass functions as parameters with the GetRef() function. Here's a utility map function, like you'd find in JavaScript that accepts an array and calls a function for each element of the array:
Sub Map(a, f)
Dim i
For i = 0 To UBound(a)
' Call a function on each element and replace its value with the function return value
a(i) = f(a(i))
Next
End Sub
Map MyArray, GetRef("SomeFunc")
Now you could write SomeFunc so that it operates on a value and returns an updated value:
Function SomeFunc(i)
SomeFunc = i + 1
End Function
This works fine. map calls SomeFunc using the function "pointer" we passed to it.
You could do something similar with your LoopThroughStreetAnd function:
LoopThroughStreetAnd "PersonFile.xlsx", GetRef("Payload2")
The standard way of callbacks in VBScript uses GetRef, as in this demo.
When using objects, you can wrap a call to a method in an object, and then you can pass the object. (This is approximately what happens in other languages already, you just have to do it manually in VBScript.)
The only issue is that any method called this way has to be Public.
I would use a naming scheme of something like "Func1", "Func2", "Action1", "Action2", etc., depending on the arity of the functions and whether they return values or not.
Dim s : Set s = New Something : s.Run
Class Something
Public Sub HowToPassMe(pValue)
WScript.Echo pValue
End Sub
Public Sub Run
Dim action : Set action = New Action1Wrapper
Set action.Target = Me
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
End Sub
End Class
Class SomethingElse
Public Sub DoSomethingElse(pAction1)
pAction1.Action1("something")
End Sub
End Class
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Public Sub Action1(p1)
mTarget.HowToPassMe(p1)
End Sub
End Class
Using Execute, Action1Wrapper can also be written something like the following. You can also write a factory class for easier use.
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Private mName
Public Property Let Name(value) : mName = value : End Property
Public Sub Action1(p1)
Execute "mTarget." & mName & "(p1)"
End Sub
End Class
Class Action1Factory_
Public Function Create(pTarget, pName)
Dim a1 : Set a1 = New Action1Wrapper
Set a1.Target = pTarget
a1.Name = pName
Set Create = a1
End Function
End Class
Dim Action1Factory : Set Action1Factory = New Action1Factory_
Used as:
Dim action : Set action = Action1Factory.Create(Me, "HowToPassMe")
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
And as I write the question, my memory gets jogged,
and I begin researching a "feature" I once discovered.
This fails to work in the context of HP UFT,
but if you're running cscript, or working with Classic ASP,
you can either declare a function late, or replace a previous declaration,
to change how it works.
VBScript lets you declare the same function or subroutine
multiple times in a program.
It treats the last declaration as the correct one.
You can get around this in cscript and ASP by physically separating
the different versions of the function,
so that one doesn't get clobbered by the other.
You'll have to be careful not to put the two anywhere near each other,
or you(r successor) might have an aneurysm trying to debug the outcome.
Honestly, you're probably better served refactoring your code some other way.
Now, with the disclaimers out of the way,
the following example is for use with cscript or wscript.
Code
Since this won't work in UFT anyway, I'll write from scratch.
In WrapperSub.vbs:
' Sub WrapperSub_Payload doesn't exist in this file.
' It must be declared by the calling file or the program will crash.
Sub WrapperSub()
wscript.echo("This begins the wrapper.")
WrapperSub_Payload
wscript.echo("This ends the wrapper.")
End Sub
In WrapperSubUseA.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload A.")
End Sub
WrapperSub
In WrapperSubUseB.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload B.")
End Sub
WrapperSub
Output
>cscript wrappersubusea.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload A.
This ends the wrapper.
>cscript wrappersubuseb.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload B.
This ends the wrapper.
Note that if a placeholder for WrapperSub_Payload
were declared in the source file,
that placeholder would always execute instead of the intended subroutine.
This is probably due to ExecuteGlobal
executing after the current file is parsed,
causing the placeholder to load after the local declaration.
When you try this in UFT --
placing the contents of WrapperSub.vbs in a function library --
the function library rightfully ignores the caller's scope.
It will then fail because WrapperSub_Payload doesn't exist in scope.
I have written a COM library on C++.
There is only 1 function in this library.
STDMETHODIMP TMyCodeBitmapGenerateImpl::GenerateMyFile(BSTR ATextToEncode,
BSTR APathToSaveFile)
Library name is MyCode_COM_Library
Library is registered in the system.
trying
DataString = "data123"
Filename = "D:\img.bmp"
with CreateObject("MyCode_COM_Library.TMyCodeBitmapGenerateImpl")
.GenerateMyFile(DataString,Filename)
end with
I'm getting error Object Required
Trying again
dim ws
dim wd
Set ws = CreateObject("MyCode_COM_Library.TMyCodeBitmapGenerateImpl")
Set wd = ws.GenerateMyFile(datastring, FileName)
same error.
In VB in References i added this library
Dim wd
Dim wa As MyCodeBitmapGenerate
datastring = "data123"
FileName = "D:\img123.bmp"
wd = wa.GenerateMyFile(datastring, FileName) ' the function returns int
After that the compilator selects
.GenerateMyFile
and giving error Expected Function or Variable
Please help me to connect VB and my COM library.
Thank You.
If i'm adding library in References and writing such code in VB than all OK
Private Sub Command1_Click()
Dim azclib As AztecCodeBitmapGenerate
ds = "12343445454243442Data1234567890"
fp = "D:\imgDS.bmp"
Set azclib = New AztecCodeBitmapGenerate
azclib.GenerateAztecFile ds, fp
End Sub
But if i'm trying to write this code in VBScript like
Dim dt
Dim fp
Dim we, ws
dt = "123456789"
fp = "C:\imgDT.bmp"
Set we = CreateObject("AztecCode_COM_Library.TAztecCodeBitmapGenerateImpl")
Call we.GenerateAztecFile(dt, fp)
then i have an error Object Required - 800A01A8 on Call we.GEnerateAztecFile (dt,fp).
I think that the object "we" is not created but i'm not sure
Sorry to ask such a dumb question.. but for the life of me i cant get it.. i have searched EVERYWHERE... This is a Re-Creation of my code that gives the same error. This is the most basic example i could re-create.
I dont understand why i have to declare a Label ?? (or an object)
What I am trying to accomplish is use my main form to call all the modules.
This is the FORM
'frmMain.frm
Option Explicit
Public Sub btnOpen_Click()
GetNum
End Sub
This is the MODULE
'modGet.bas
Option Explicit
Public Sub GetNum()
Dim a As String
Dim b As String
a = "hello"
b = "world"
-> Label1.Caption = a 'ERROR, Compile Error, Variable not Defined. (vb6)
Label2.Caption = b
End Sub
YES, i have a form, with a Button named 'btnOpen', i have 2 Labels named 'Label1' & 'Label2'
If i ADD..
Dim Label1 As Object 'in MODULE
i get a different error..
ERROR '91' Object Variable or With block variable not set
IF I put everything in 1 FORM, it works..(but i want to use separate modules)
I Commented out 'OPTION EXPLICIT' ... same error.
In another Test, i got the error for a TextBox..
TextBox1.Text = x
Once i get the answer for this, i can apply it for everything... I'm sure it's simple too and imma feel stupid. :-(
One of my Main Things is Querying WMI, and i get the ERROR '91' for the Label (This is in a For Each Loop) .. But its the same error, its like its makin me Declare Objects..(using Modules)
Label1.Caption = objItem.Antecedent
If Someone Could PLEASE Help me...
Use
form1.label1.caption = a
But make sure form1 is loaded
You get the error because Label1 and Label2, and your other controls for that matter do not exist in the scope of modGet.bas. They can only be referenced (the properties accessed or set), from with the form. The different error you get when you add Dim Label1 As Object is caused because an you defined Label1 as an Object, not as a Label, and an object does not have a Caption property. Unless you have a good reason for putting the GetNum sub in a .bas module move it into the form and it should work.
I modified the second example. It will modify the strings passed into it in a way that when execution passes back to the form you can assign the strings to your textboxes. I am against modifying controls on a form from another module because it goes against the idea of encapsulation.
'modGet.bas
Option Explicit
Public Function GetHello() As String
Dim strHello As String
strHello = "Hello"
GetHello = strHello
End Function
'frmMain.frm
'Option Explicit
Public Sub btnOpen_Click()
Label1.Caption = GetHello()
End Sub
Something a little different.
'MyModule.bas
Public Sub HelloWorld ByRef Value1 As String, ByVal Value2 As String)
On Error GoTo errHelloWorld
Value1 = "Hello"
Value2 = "World"
Exit Sub
errHelloWorld:
' deal with the error here
End Sub
'frmMain.frm
Option Explicit
Private Sub frmMain_Load()
Dim strText1 As String
Dim strText2 As String
HelloWorld(strText1, strText2)
Text1.Text = strText1
Text2.Text = strText2
End Sub
I also added basic error handling in the second example
I am new to QTP, just started using it. I have written one class definition in some functional library and also created a test as under:
Class ExcelFileReader
Public default Function Init(pathToExcel)
Dim objFSO
Dim result
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(pathToExcel) Then
Rem File Found
Dim objExcel
Set objExcel = CreateObject("Excel.Application")
objExcel.Workbooks.open(pathToExcel)
Else
REM File not found
result = vbOk
While result <> vbCancel
result = Msgbox ("Unable to Locate the file", 5, "Error")
Wend
ExitAction(1)
End If
End Function
End Class
Test:
Dim objExcelReader : Set objExcelReader = New ExcelFileReader
objExcelReader.Init("D:\mytest.xlsx")
I have associated the functional library with the test but still I am getting an error at line number 2 in test stating class definition not found. Also if I copy complete code in the same file "test" then the things are working as intended.
Thanks in advance :)
Classes have local scope in your library. You have to construct them with a public function to make them publicly available:
Public Function new_ExcelFileReader()
Set new_ExcelFileReader = new ExcelFileReader
End Function
Class ExcelFileReader
Sub Class_Initialize
MsgBox "Present!"
End Sub
End Class
And in your other library:
Dim objExcelReader : Set objExcelReader = New_ExcelFileReader
objExcelReader.Init("D:\mytest.xlsx")
Protip: You can pass initialization parameters into your constructor function.
EDIT
On request: how to pass constructor parameters. Just add them to your constructor function:
Public Function new_ExcelFileReader2(filepath, sheetname)
Set new_ExcelFileReader2 = new ExcelFileReader
new_ExcelFileReader2.Init(filepath, sheetname)
End Function
' And the call:
Set myExcelFileReader = new_ExcelFileReader2("C:\temp\tempExcel.xlsx", "sheet1")
In my implementation I have sometimes the same object, but that gets 'configured' by multiple contructor functions. In your case you could have a new_ExcelFileReader, a new_CSVFileReader and a new_TabDelimitedReader all pointing to the same object but configured differently.
Another way to fancy up your code is to return the object (with the me keyword) by the init function. This will result in code like this:
Class ExcelFileReader
private filepath_
public function Init(filepath)
filepath_ = filepath
Set Init = me
end function
End Class
Set myExcelFileReader = new ExcelFileReader.Init("C:\temp\tmpExcel.xlsx")
With a constructor function you can use it by just returning the object and then calling the Init function.
Public Function new_ExcelFileReader() ' this is the same as the first function
Set new_ExcelFileReader = new ExcelFileReader
End Function
Set myExcelFileReader = new_ExcelFileReader.Init("C:\temp\tmpExcel.xlsx")
How can you obtain the Type (the name as a string is sufficient) of an Object in VB6 at runtime?
i.e. something like:
If Typeof(foobar) = "CommandButton" Then ...
/EDIT: to clarify, I need to check on Dynamically Typed objects. An example:
Dim y As Object
Set y = CreateObject("SomeType")
Debug.Print( <The type name of> y)
Where the output would be "CommandButton"
I think what you are looking for is TypeName rather than TypeOf.
If TypeName(foobar) = "CommandButton" Then
DoSomething
End If
Edit: What do you mean Dynamic Objects? Do you mean objects created with
CreateObject(""), cause that should still work.
Edit:
Private Sub Command1_Click()
Dim oObject As Object
Set oObject = CreateObject("Scripting.FileSystemObject")
Debug.Print "Object Type: " & TypeName(oObject)
End Sub
Outputs
Object Type: FileSystemObject
TypeName is what you want... Here is some example output:
VB6 Code:
Private Sub cmdCommand1_Click()
Dim a As Variant
Dim b As Variant
Dim c As Object
Dim d As Object
Dim e As Boolean
a = ""
b = 3
Set c = Me.cmdCommand1
Set d = CreateObject("Project1.Class1")
e = False
Debug.Print TypeName(a)
Debug.Print TypeName(b)
Debug.Print TypeName(c)
Debug.Print TypeName(d)
Debug.Print TypeName(e)
End Sub
Results:
String
Integer
CommandButton
Class1
Boolean
I don't have a copy of VB6 to hand, but I think you need the
Typename()
function... I can see it in Excel VBA, so it's probably in the same runtime. Interestingly, the help seems to suggest that it shouldn't work for a user-defined type, but that's about the only way I ever do use it.
Excerpt from the help file:
TypeName Function
Returns a String that provides information about a variable.
Syntax
TypeName(varname)
The required varname argument is a
Variant containing any variable except
a variable of a user-defined type.
This should prove difficult, since in VB6 all objects are COM (IDispatch) things. Thus they are only an interface.
TypeOf(object) is class probably only does a COM get_interface call (I forgot the exact method name, sorry).