vb6, variable not defined for Label using Module - vb6

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

Related

callbyname vb6 using string as argument

I am trying to set some images's visibility to false by using CallByName and a loop through the objects.
here is the code
Private Sub command1Click
dim theobj_str as string
dim ctr as integer
for ctr = 1 to 3
theobj_str = "Images" & ctr
CallByName theobj_str, "Visible", vbLet,False
end for
END SUB
It throws an error "TYPE MISMATCH" on "CallByName **theobj_str**..."
The CallByName takes an object as its first argument. I need to somehow convert the string "theobj_str" into an object. How can I do this ?
The CallByName works fine if I call it like : CallByName Images2, "Visible", vbLet,False
Thanks
If you don't need to use CallByName you could loop through the controls collection and check the type. If the type matches the control you want to hide then you can set it's visible property that way.
The code would look like this:
Private Sub Command_Click()
SetControlVisibility "Image", False
End Sub
Private Sub SetControlVisibility(ByVal controlType As String, ByVal visibleValue As Boolean)
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = controlType Then
ctrl.Visible = visibleValue
End If
Next
End Sub
Doing it this way will allow you to add more image controls to your form without having to remember to change your counts in the for loop.
Hope that helps.

VBScript Function as Parameter, or similar Construct

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.

How to pass the string value to function

I want to pass the string value to function
Function Code
Private Function Assign(Div As String)
sSQL = "Insert into table2 Select * from table1 Where Divi_Code = '" & Div & "'"
Rdoconn.Execute sSQL, rdExecDirect
End Function
Button Click Code
Dim Div as string
Div = "Hai,Howareyou"
Assign Div 'Getting Error as "ByRef arguement type mismatch"
The above code is shwoing error as Getting Error as "ByRef arguement type mismatch"
I tried the following code, and I am not getting any ByRef argument mismatch.
Private Function Assign(Div As String)
ssql = "Insert into table2 Select * from table1 Where Divi_Code = '" & Div & "'"
MsgBox ssql
End Function
Private Sub Command1_Click()
Dim Div As String
Div = "Hai,Howareyou"
Assign Div
End Sub
I am really puzzled why your code is giving you an error. However, there are a couple of things you should change in your code and I'm confident this will resolve your problem:
The function parameter should be declared ByVal. In VB6, parameters are ByRef by default unless specified. This means that the function call could have side-effects in the parent procedure if for any reason the parameter Div is modified. Always use ByVal unless you really need to modify the parameter value:
Private Function Assign(ByVal Div As String)
Is there any reason why you declared Assign as a Function? Are you intending to return a value? If not, you should use Private Sub instead of Private Function (and End Sub at the end). This is equivalent to using the void return type in C. For your reference, you should always define the return type of your functions in VB6, otherwise Variant will be assumed. The return type can be fined in functions using the "As" keyword at the end of the declaration:
Private Function Add(ByVal n1 as Integer, ByVal n2 As Integer) As Integer
One last thing I'd like to add is that you never, ever should concatenate variables to SQL queries like this. At the very least, the code will crash if the name contains an apostrophe (ex: try calling it with "I'm very well"), and at worst, you'll be opening up for SQL injection attacks where someone could use this to run specially crafted queries on your database. While I'm not familiar with RDO, you should check out MSDN - this article mentions how to create parameter queries with RDO.
To call a Function declared as
Private Function AssignDiv(Div As String, Dep As String)
you'd need something like
AssignDiv Div, "WhatEverDep"
Your
Assign Div
is completely wrong.
You changed your declaration to
Private Function AssignDiv(Div As String)
but the function's name is still wrong.
You changed your code again. Now names and parameters match, so if you still get an error, it's not caused by the code you published.
to begin with I don't understand why you are setting you Div value and then passing it into your function, you may as well pass it in directly on the button click, and then also I can't see that sSQL is actually defined as a string, please try the following code:
Public Function Assign(strDiv As String)
Dim sSQL As String
sSQL = "Insert into table2 Select * from table1 Where Divi_Code = '" & strDiv & "'"
Rdoconn.Execute sSQL, rdExecDirect
End Function
And then call from the button click as:
Private Sub Command1_Click()
Call Assign("Hai,Howareyou")
End Sub

Visual Studio Macro to Paste similar to String.Format

I'd like to be able to cut/copy a string like "<strong>{0}</strong>" for example.
I'd like to then select some code such as "Hello, World" and then invoke a macro which will result in "<strong>Hello, World</strong>".
How could you do this?
Update: WHY do I want to do this?
I could just make a macro or shortcut to add something specific like a <strong> tag to a selection. However, my idea to create any sort of "surround with" paste behavior on the fly.
Fairly often, I paste in a list of fields or properties. So from somewhere else I get
PersonID
FirstName
LastName
And just as an example, I know I want to set those up as
FieldName = dataRow("FieldName").Value
With my magic macro, I could select the following and press CTRL+C to get it in my clipboard:
{0} = dataRow("{0}").Value
Then all I have to do is go line by line and apply my magic paste.
Fun little project.
Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Public Module StringFormatModule
Private clipText As String
Public Property ClipboardText() As String
Get
RunThread(AddressOf GetClipboardText)
Return clipText
End Get
Set(ByVal value As String)
clipText = value
RunThread(AddressOf CopyToClipboard)
End Set
End Property
Private Function RunThread(ByVal fct As Threading.ThreadStart)
Dim thread As New Threading.Thread(fct)
thread.ApartmentState = Threading.ApartmentState.STA
thread.Start()
thread.Join()
End Function
Private Sub GetClipboardText()
clipText = My.Computer.Clipboard.GetText()
End Sub
Private Sub CopyToClipboard()
My.Computer.Clipboard.SetText(clipText)
End Sub
Sub FormatSelectedTextWithCopiedText()
Dim formatString As String
formatString = ClipboardText
Dim token As String
Dim selectedText As TextSelection
selectedText = DTE.ActiveDocument.Selection
token = selectedText.Text
selectedText.Text = String.Format(formatString, token)
End Sub
End Module
I borrowed the clipboard code from here.
This does work. I tested it on a text file, copy your formatstring into the clipboard (ctrl-c), highlight the text you want to format, and then run the macro (i just doubleclicked it from the macro explorer but you could make a keyboard shortcut).
Wouldn't it be better to define a macro that added the 'strong' tags around the selected text? Then you could assign it to Ctrl+B or whatever.
Having to select both chunks of text and invoking the macro twice seems too much like hard work to me.
(maybe you need to explain why you want to do this)
Instead of {0}, I use &. Assign macro to Ctrl+Q and you are all set!
' Wraps the current selection with the specified text. Use the & character as the anchor for the selected text.
Public Sub WrapSelection()
Dim selection As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
DTE.UndoContext.Open("Wrap Selection")
Try
Dim sInput As String = InputBox("Wrap(&&, state)")
If Len(sInput) > 0 Then
Dim sContent As String = selection.Text
Dim iStart As Integer = InStr(sInput, "&") - 1
Dim iEnd As Integer = InStrRev(sInput, "&")
selection.Insert(sInput.Substring(0, iStart) + sContent + sInput.Substring(iEnd), vsInsertFlags.vsInsertFlagsContainNewText)
'selection.Insert(sInput.Substring(iEnd), vsInsertFlags.vsInsertFlagsInsertAtEnd)
End If
Catch ex As Exception
DTE.UndoContext.SetAborted()
MsgBox(ex.Message)
Finally
'If an error occured, then need to make sure that the undo context is cleaned up.
'Otherwise, the editor can be left in a perpetual undo context
DTE.UndoContext.Close()
End Try
End Sub

VB6 Runtime Type Retrieval

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).

Resources