I want to simulate WindowsKey + L (The short cut to lock the console) in visual basic and bind it to a function. So when this function is called it will lock the console.
Can I do this?
Simulating the hotkey is the wrong approach. All you need to do is call the LockWorkStation function. This has the same result as pressing Ctrl+Alt+Del and selecting "Lock Workstation", or using the Win+L hotkey, except that you can do it programmatically through code.
To call this function from a VB application, you'll need to write a declaration, like so:
Private Declare Function LockWorkStation Lib "user32.dll" () As Long
You'll want to place that declaration at the top of your module file, before any procedures are defined. Then, inside one of the procedures, you can call the function. For example:
Private Sub LockComputer()
LockWorkStation
End Sub
Even better code would check the return value of LockWorkStation for an error code. A return value of 0 indicates an error. The standard way of checking for Win32 errors in VB, Err.LastDllError, will give you more information about what exactly went wrong.
Related
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
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.
I'm trying to make an update script for windows7 in vbscript
when invoking IUpdateSearcher::BeginSearch how do I pass the callback to ISearchCompletedCallback::Invoke Method?
I'm just clueless on this points:
do I need an function or sub or does this a custom object with an invoke method(and how to create)
how I need to pass the callback
is it even possible in vbscript (if not what is a good next step?)
Thanks
I've never tried it, but I'd look at the ConnectObject Method.
This article about scripting events might also be useful.
So maybe something like this (complete guess):
Set objSession = CreateObject("Microsoft.Update.Session")
Set objSearcher = objSession.CreateUpdateSearcher
WScript.ConnectObject objSearcher, "searcherCallBack_"
objSearcher.BeginSearch ...
sub searcherCallBack_Invoke()
' handle the callback
end sub
I'd also suggest reading Guidelines for Asynchronous WUA Operations to make sure that you clean up after yourself.
that link also mentions using Windows Script Host, so it should definitely be possible to do this, though unless you need it to be asynchronous, the synchronous methods wouls probably be a easier.
I am currently maintaining some code, which is likely to be refactored soon. Before that happens, I want to make the standard error handling code, which is injected by an Add-In, more efficient and take up less space. One thing that annoys me is that every module has a constant called m_ksModuleName that is used to construct a big string, which is then rethrown from the error handler so we can trace the error stack. This is all template code, i.e., repetitive, but I could easily strip it down to a procedure call. Now, I have fixed the code so that you can pass the Me reference to the procedure - but you can't do that for the BAS modules. Nor can you access the project name (the part which would be passed as part of a ProgramID, for instance) - although you get given it when you raise an error yourself.
All these strings are contained in the EXE, DLL or OCX - believe me, I've used a debugger to find them. But how can I access these in code?
AFAIK there's no way to get the name of a BAS module in code. The usual solution is to use a module-level constant as in Mike's answer.
AFAIK the only way to get the ProgID (programmatic ID, Project Name in project properties dialog) is to raise an error in a BAS module, trap it, and read the Err.Source.
It's all quite a hassle, and that's why we don't usually bother including the module name or the ProgID in our standard error handlers. We "roll our own" call stack, with the names of the routines. That's always enough information to find out which modules are involved. Routines in BAS modules usually have unique names, right?
Something like this, and you can add this automatically with the free MZTools VB6 add-in.
Sub / Function whatever
On Error Goto Handler
do some stuff
Exit Sub / Function
Handler:
Err.Raise Err.Number, "(function_name)->" & Err.source, Err.Description
End Sub
Every top-level routine in a DLL or OCX has a similar error handler but also includes App.ExeName so we can tell when errors cross component boundaries.
I'm not sure of an easy way to programmatically get the name of the module that you are in. The usual solution is to set a variable at the top of each method to the name of the module, and then it is available to the error handler for use in logging:
'In MyModule.bas'
Public Sub Foo()
Const MODULE_NAME As String = "MyModule"
On Error GoTo ErrorHandler
' Code here '
Exit Sub
ErrorHandler:
LogError Err.Number, Err.Description, MODULE_NAME
End Sub
If you are using an add-in such as MZTools, you have it generate this boilerplate code for you.
As for getting the current component name, you can access this using App.EXEName (despite the name, this works for other project types such as DLL's). This value is pulled from the Project Name field in the project's properties (Project -> Properties) when running in the IDE, and from the name of the compiled binary file (minus the file extension) when running outside the IDE.
Whenever we load a VB project it will call Initialize event of a User Control ( if there is any in the project). My problem is that is that I have some code in UserControl_Initialize that will try to create instances of other COM objects. On my build machine those controls are not registered. One option is to move the code to some other method other than Initialize but I want to know if there is a better solution? Somewhere I found that we may have a check to see if the calling application is VB editor then skip the initialization code...
You can use:
If Not Me.DesignMode Then
...
End If
An other solution we used was a little function which can be used globally:
Public Function IsRuntime() as Boolean
On Error Goto NotRuntime
Debug.Print(1 / 0)
IsRuntime = True
Exit Function
NotRuntime:
IsRuntime = False
End If
Don't know if it is syntactically well formed, but the idea should be clear:
Only in the IDE the debug statement is called.
This only happens if your project was saved with the form designer open: this means that at startup the form is displayed (maybe in the background) and consequently, all controls on it need to be initialized. Hence, your user control initializer is called if this control is used on the form.
To prevent this, simply save the project with the form designer closed.