When is "Set" required in lotus script when setting an environment variable - syntax

I've inherited a nightmare of a project from the days when I can only assume LOC was an acceptable KPI in lotus script. I'm working on refactoring repetitive code after I've been asked to make a change to how our company's apps work. The code below illustrates something I don't understand about the syntax of Lotus Script -- I'm getting an error in the domino designer that on the "Control = UIDoc.Document....." line that A "Set" is required. Looking at existing code, sometimes Set is used for assignment, sometimes it is not. I'm trying to read the lotus script doc's, but haven't yet found an explanation of the difference. Any change someone could illuminate me on when Set should be used and when it should not? If it helps I have a strong C# background with some minor experience in interpreted languages like JS and PHP
Class DeploymentType
Private ProductName As String
Private ControlSuffix As String
Private TypeHeader As String
Private Control As NotesRichTextItem
Sub new (Product As String, Suffix As String, Header As String, UIDoc As NotesUIDocument)
'These three assignments do not show an error without set
ProductName = Product
ControlSuffix = Suffix
TypeHeader = Header
'This assignment shows an error unless I prepend it with "Set"
Set Control = UIDoc.Document.GetFirstItem(ProductName + ControlSuffix)
End Sub

The set keyword has nothing to do with environment variables. The UIDoc.Document method returns an object in the NotesDocument class. Set is required for assigning object references. Assignments to scalar variables with simple built-in data types (string, integer, boolean, byte, etc.) do not require Set.
Use Set for objects from the Notes product classes, any OLE or COM classes or any classes that you define in LotusScript itself.

Related

Is there a way for VB6 to throw a type mismatch error when assigning a variable of the same type?

I am trying to figure out the cause of a type mismatch error, but I don't understand the reason it is even being thrown. I am assigning to a variable of the same type that is being assigned from.
Begin bxControls.starTable m_tblClaims 'Defined at the top of the form file
Dim objTable As PVDataTable5.DataTable
Set objTable = m_tblClaims.Table
Begin PVDataTable5.DataTable dtTable 'Defined at the top of the .ctl file
'Property inside of bxControls.starTable
Public Property Get Table() As Object
Attribute Table.VB_MemberFlags = "40"
Set Table = dtTable
End Property
As you can see, the assignment taking place should not be having a type mismatch since they are the same type. Any help would be awesome. TIA
Try this
Dim objTable As PVDataTable5.DataTable
Set objTable = m_tblClaims.Table.Object
Note that this might work in IDE but fail when compiled.
The story of user-controls is mostly of complete lies from the IDE. For instance all user-control properties/methods are called late-bound, even if the IDE intellisense gives look like the callsite is early-bound.
For each user-control type that is currently loaded in the IDE (as part of a project group for instance) the IDE creates surrogate date-type with merged original control properties/methods and some VB supplied ones (like Visible etc.) which are coming from VBControlExtender class.
That is why Dim objTable As PVDataTable5.DataTable sometimes is not declaration of PVDataTable5.DataTable data-type but of the surrogate PVDataTable5.DataTable from an OCA file, that is when the user-control is loaded in the IDE.
The only sane way to pass references to VB6 user-controls that works both if the user-control is loaded and when referenced in compiled OCX is to pass VBControlExtender instead and use its Object property to access the wrapped reference.

ADODB.Parameters error '800a0e7c' Parameter object is improperly defined. Inconsistent or incomplete information was provided

I'm primarily an PHP developer, but I have some old ASP one of our previous developers made that broke and I can't figure out how to fix it. We have a program that sends some variables to a listener page that compares that data to registration codes an msSQL database and then lets the program know if the registration code is valid.
I'm getting the following error where
.Parameters.Append .CreateParameter("#code", adVarChar, 1, 50, x)
is line 134:
ADODB.Parameters error '800a0e7c'
Parameter object is improperly defined. Inconsistent or incomplete information was provided.
/checkregistrationpro.asp, line 134
I've already specified any named constants in an include file that I have not included in the code, so it isn't to do with that.
My Connection String (I've already verified that these settings are right):
set conn = Server.CreateObject("ADODB.Connection")
set cmd = Server.CreateObject("ADODB.Command")
sConnString = "Provider=sqloledb; Data Source=MYDATASOURCE; Initial Catalog=MYCATALOG; User ID=MYUSERID; Password='MYPASSWORD';"
conn.Open sConnString
My Code:
...
Function BlockInjectCode(StrVal)
BlockInjectCode = Replace(StrVal,"--","")
BlockInjectCode = Replace(BlockInjectCode,"'","")
BlockInjectCode = Replace(BlockInjectCode,"""","")
if instr(lcase(BlockInjectCode),"<") > 0 then
BlockInjectCode = ""
end if
End Function
x = BlockInjectCode(Request.QueryString("rid"))
uid = BlockInjectCode(Request.QueryString("uid"))
chkcode = BlockInjectCode(Request.QueryString("Code"))
CheckPro = BlockInjectCode(Request.QueryString("pro"))
CheckProProd = BlockInjectCode(Request.QueryString("prod"))
CheckProMac = BlockInjectCode(Request.QueryString("mac"))
MacAdd = CheckProMac
CodeValid = False
if x <> "" and uid <> "" then
'-- Get information about this registration code.
sqlStr = "select * from MYTABLE where Code = ? and IsValid = 1"
set cmdCodes = Server.CreateObject("ADODB.Command")
Set cmdCodes.ActiveConnection = Conn
cmdCodes.CommandText = sqlStr
with cmdCodes
.Parameters.Append .CreateParameter("#code", adVarChar, 1, 50, x)
end With
Set rsCodes = cmdCodes.execute
...
Common occurrence is likely you do not have adVarChar defined, so the CreateParameter() method is "improperly defined".
This is just an example of one of the many named constants found in the ADODB Library. A messy approach to dealing with this is to just define the value yourself something like;
Const adVarChar = 200
The problem with this approach is you then have to define all the named constants which can be a headache. Another approach is to just skip the whole named constants thing and just use the integer values, so the call would be;
.Parameters.Append .CreateParameter("#code", 200, 1, 50, x)
However this isn't easy to read although it looks as though you are doing this already with the ParameterDirectionEnum value of 1 which is the named constant adParamInput in the ADODB Library. Regardless I wouldn't recommend this approach.
A slightly better approach is to use an #include directive so that it includes in the calling page all the named constant definitions you could want and this is how most do it. Microsoft provide a pre defined file for this very purpose with IIS (or possibly the MDAC library installation I'm not sure off the top of my head) known as adovbs.inc or adovbs.asp, by including this file your page would have access to all the named constant definitions within.
The reason for all this is VBScript doesn't support type libraries, so in a Client scenario defining them yourself or copying and pasting from the adovbs.inc file is your only option. However in a Server scenario we still have the power of IIS which lets us do some funky things.
Wouldn't it be nice if the Type Library could just be added once and you don't have to worry about it?, no annoying constants to have to define? Let's face it they already exist in the Type Library so why can't we get them from there? Well turns out we can thanks to the METADATA directive.
Here is an example;
<!--
METADATA
TYPE="TypeLib"
NAME="Microsoft ActiveX Data Objects 2.5 Library"
UUID="{00000205-0000-0010-8000-00AA006D2EA4}"
VERSION="2.5"
-->
The beauty of this approach is you can use for any Type Library (ideally exposed to COM) and you can define in one page, or add it into the global.asa to have defined across the entire Web Application.
With this approach you are then safe to use code like;
.Parameters.Append .CreateParameter("#code", adVarChar, adParamInput, 50, x)
Useful Links
VBScript ADO Programming
Answer to ASP 3.0 Declare ADO Constants w/out Including ADOVBS.inc
Answer to Passing Parameters to a Stored Procedure using ASP
Using METADATA to Import DLL Constants
In the places where you are defining adVarChar, and other ADO magic numbers, get rid of them completely.
Instead add this to the top of your global.asa (create one if it doesn't) exist.
<!--METADATA TYPE="TypeLib" file="C:\Program Files (x86)\Common Files\System\ado\msado15.dll" -->
This will import the msado15 type library which already has all of those magic numbers defined. It will pull in all the defines for you automatically. Meaning you won't have to define adVarChar etc yourself, the metadata tag will import them automatically for you. Credit to #Lankymart for this as his comment lead me to discovering this and figuring it out, and it works great.
Now, turn create a test asp page that's empty and add some test code to it with option explicit on.
<%
Option Explicit
Response.Write "adVarChar: " & adVarChar
%>
If you get an error that adVarChar is not defined, then msado15.dll is not on the server. So you will need to contact godaddy etc to have them tell you the path to whatever version of MSADO is on there and change your metadata tag to reference the right version.

Creating a view and agent in multiple databases

The problem I am encountering is that some of the messages are not accessible by the user ID file, I would like to skip these files instead of the agent crashing out. The error message received is as follows:
Using the view approach if this happened I was able to delete the document temporarily and re-run the agent but if there is a way to skip documents it would be a great help.
Thanks for the help guys.
Ok I have amended the code to a point where I am almost comfortable with it.
Sub Initialize
Dim s As New notessession
Dim db As notesdatabase
Dim view As notesview
Dim doc As notesdocument
Dim nextdoc As notesdocument
Set db = s.currentdatabase
If view Is Nothing Then
Set view = db.CreateView("Encrypted",{Encrypt="1"})
End If
Set doc = view.getfirstdocument
On Error Goto ErrorHandler
While Not doc Is Nothing
nextDocument:
Set nextdoc = view.getnextdocument(doc)
'The below loop is mandatory to ensure that all $File entries are unecrypted
Forall i In doc.items
If i.isencrypted Then
i.isencrypted=False
End If
End Forall
'Must have at least 1 field encrypted in order to call Encrypt method
Dim temp As New NotesItem(doc,"tempjunk","temp")
temp.IsEncrypted=True
Call doc.encrypt
Call doc.save(True, False)
'This portion can now remove the fields relative to encrypting the
'single token encrypted field.
Call doc.removeitem("$Seal")
Call doc.removeitem("$SealData")
Call doc.removeitem("SecretEncryptionKeys")
Call doc.removeitem("Encrypt")
Call doc.removeItem("tempjunk")
Call doc.save(True, False)
Set doc = nextdoc
Wend
Exit Sub
ErrorHandler:
On Error Resume nextDocument
Exit Sub
End Sub
The error handling is not playing nice;
On Error Resume nextDocument is showing up as an error.
I have tried suppressing all of the error warnings which seems to attempt to strip the encryption but I think they body of the messages is being destroyed as a result.
It is no problem to create an agent in a container database and let that agent access documents in all "target" databases and modify them accordingly - No need to copy that agent to all databases.
Only restriction: If the databases are on another server, then on the server security tab of the target server you have to enter the server with the container database as trusted server.
AND: If your agent runs longer than the allowed maximum run time for agents on the server, then it will be killed prematurely.
There is no need to create views in the target databases, you can use NotesDatabase.Search() to get the corresponding documents in the databases...
You can create views by copying them from another database. Say you create a view "Encrypted" in your db with the agent.
Then add a piece of code to get a handle of this view as a NotesDocument:
Dim dbThis As NotesDatabase
Dim viewTemplate As NotesView
Dim docView As NotesDocument
Set dbThis = s.currentDatabase
Set viewTemplate = dbThis.getView("Encrypted")
Set docView = dbThis.Getdocumentbyunid(viewTemplate.Universalid)
In the agent loop, test if view Encrypted exists, if not copy the "view template":
Set view = db.getview("Encrypted")
If view Is Nothing Then
Call docView.Copytodatabase(db)
Set view = db.getview("Encrypted")
End If
Finally, if you insist, a similar procedure might be used to copy the agent to all databases, but for me the idea of running the agent in one db sounds better.
Edited: In the view of full disclosure - of course you can create a view (I guess that was the original question).
If view Is Nothing Then
Set view = db.Createview("Encrypted", {Encrypt="1"})
End If
Or do one-shot dbSearch suggested by Torsten, with a good re-mark of Richard - if you intend to run your code several times - say if encrypted documents might get created again or re-encrypted, rather go for the view.
My method is a bit old fashioned (pre-dates availability of createView) and works well if you need more than selection formula, so you can pre-build a complicated view for re-use.
Performance-wise: whatever method you will choose either creating view using createView or copying from other db or doing dbSearch there is going to be a certain slow-down while the view gets built or dbSearch executes. Karl-Henry's approach will avoid this search/view build, but will be relatively slow if there are not many encrypted documents.
Whichever method you choose - here is a small tip to boost performance. Make your loops like this to release memory as you go; for example, assuming Karl-Henry's approach:
Dim doc1 as NotesDocument
Set doc = col.GetFirstDocument()
Do Until doc Is Nothing
Set doc1 = col.GetNextDocument(doc)
formname = doc.GetItemValue("Form")(0)
If IsElement(exclude(formname))=False Then
Call RemoveEncryption(doc) '*** Your function to remove encryption
End If
' releasing memory when processing thousands of documents improves performance and avoids crashes
Delete doc
Set doc = doc1
Loop
Now again, as you are talking only about migration (so one shot) of 20+ databases, the speed or implementation details should not be that critical.
If you have to process all (or almost all) documents in each database, you can use db.AllDocuments. It is more efficient than using db.Search() with an #All formula.
If you want to exclude certain documents, perhaps based on the form name, I would build a list of forms to exclude, and then use IsElement to check each document being processed against that list.
Dim exclude List As Boolean
exclude("FormA")=True
exclude("FormB")=True
Set col = db.AllDocuments
Set doc = col.GetFirstDocument()
Do Until doc Is Nothing
formname = doc.GetItemValue("Form")(0)
If IsElement(exclude(formname))=False Then
Call RemoveEncryption(doc) '*** Your function to remove encryption
End If
Set doc = col.GetNextDocument(doc)
Loop
Something like that. By the way, you can create the list as any data type. I just choose Boolean as it is a small data type, and that it makes the code easier to read. The IsElement() function just check if the element exists, it does not use the value you set.
You would wrap the code above in a function and call it once per database.
Appended answer, based on additional info in original question:
That should not be hard, just add error handling to your code.
Before you start to loop throung the document:
On Error Goto errHandler
Before you get the next document in the loop:
nextDocument:
At the end of your code:
Exit Sub
errHandler:
Resume nextDocument
End Sub
Try that.

vbscript - attempt to automate updating upgrade table at compile time gives error object required

I am trying to automate a process to update a wise for windows package at compile time to update the productcode, package code, productversion, and the update table versionmax values.
I can successfully update the property table and the summary information for the Package Code. However, the update table is giving me problems. There are several entries in this table.
I can enumerate the existing entries, but I cannot update the existing entries. Each time I attempt to I get an error "Exception: Object required 'project.WTables(...).WRows.Row(...)'
The syntax I am using is identical to the preceding calls to update the ProductCode, ProductVersion, and Package Code.
I am using vbscript.
I am passing in the product version and full path to the wise wsi project file. In the UpdateUpgradeTable function aUpgradeGuids populate correctly with a collection of upgrade codes as I expect. All I want to do now is to change the VersionMax value for each of these codes to the current wiseProductVersion. However, vbscript seems to have some problem with the object WRows.Row(a(i)), which is an actual GUID.
I have looked at other sample scripts in the automation section of help and it does support the use of variables, and it does not look like the variables require quotes like straight text does. I am not even sure what it means, object required. Project is definitely an object. I have confirmed that. I have even tried this by inserting straight text inside the function but it still fails. I have tried it with tblProperty.WRows.Row(...) but it fails the same way. I have tried calling it with a good Upgrade Code outside of the For loop right after Set tblProperty = ... and it still fails. This type of structure works in all previous tables. It has me puzzled. Any ideas? I think this may transcend just Wise for Windows and is perhaps generic to msi as well so I am going to post under MSI too.
I have a sub main that looks like this:
Dim wiseInstallDir: wiseInstallDir = WScript.Arguments(0)
Dim wiseProductVersion: wiseProductVersion = WScript.Arguments(1)
Sub main()
Set wise = CreateObject("WfWi.Document")
Dim nResult
nResult = wise.Open (wiseInstallDir)
UpdateProductVersion wise
... 'other functions
UpdateUpgradeTable wise
wise.Save wiseInstallDir
End Sub
'This function works properly
Sub UpdateProductVersion(project)
Set tblProperty = project.WTables("Property")
tblProperty.WRows.Row("ProductVersion").WColumns("Value").Data=wiseProductVersion
End Sub
Sub UpdateUpgradeTable(project)
Set tblProperty = project.WTables("Upgrade")
Dim tmpRow,count: count = 0
Dim aUpgradeGuids: Set aUpgradeGuids = CreateObject("Scripting.Dictionary")
Const UPGRADECODE = 0
For Each tmpRow In tblProperty.WRows
Dim tRow: tRow=tmpRow.Key
Dim values: values=Split(tRow, ",")
aUpgradeGuids.Add count,values(0)
count=count + 1
Next
a = aUpgradeGuids.Items
For i = 0 To aUpgradeGuids.Count - 1
project.WTables("Upgrade")._
WRows.Row(a(i)).WColumns("VersionMax").Data=wiseProductVersion
Next
End Sub
main
EDIT:
'updated UpdateUpgradeTable
Sub UpdateUpgradeTable(project)
Set tblProperty = project.WTables("Upgrade")
Dim tmpRow
'Get the UpgradeCode for each current record and change the VersionMax value for that record.
For Each tmpRow In tblProperty.WRows
Dim tRow: tRow=tmpRow.Key
Dim values: values=Split(tRow, ",")
tblProperty.WRows.Row(values(0)).WColumns("VersionMax").Data=wiseProductVersion
Next
End Sub
I think I know the answer. I still do not have it working 100% but I found additional documentation for the Row method stating for tables with multiple key columns, commas separate key values. I have inserted the value(0), value(1), ... but I am now getting a different error. I will update once I get this working.
The upgrade table has 5 primary key values.
<!-- language: lang-vbs -->
Set rows = tblProperty.WRows
Set row = tblProperty.WRows.Row(values(UPGRADECODE), values(VERSIONMIN), values(VERSIONMAX), values(LANGUAGE), values(ATTRIBUTES))
tblProperty.WRows.Row(values(UPGRADECODE),values(VERSIONMIN),values(VERSIONMAX),values(LANGUAGE),values(ATTRIBUTES)).WColumns("VersionMax").Data=wiseProductVersion
Note that the first Set rows=... returns a valid object with its corresponding methods.
The second line fails with the message:
Exception: Wrong number of arguments or invalid property assignment: 'tblProperty.WRows.Row'
Below are the 5 primary keys for the upgrade table.
values (UPGRADECODE)
values (VERSIONMIN)
values (VERSIONMAX)
values (LANGUAGE)
values (ATTRIBUTES)
Is there another way to update the upgrade table? Is there something I am missing?
According to documentation the WRows object is a child of WTable. And it is returning a valid object for me. It has the following method:
WRow Row(BSTR strKeyValue): returns a WRow given its key value. For tables with multiple key columns, commas separate key values.
Row() is the default method.
However, my comma separated key values construct above is failing and I am not sure where to turn now. Has anyone ever worked with this before and had it working?

using WebEdit .Type property cannot be used

I have a piece of code in using QTP for a webpage
Browser("Sarit_2").Page("Sarit").WebEdit("frm_vendor_address1").Set dtVAdd1
You will observe, I used a 'Set' property. In reality, I wanted to use a 'Type' property because soon I will write script to check field limit and .Set property throws an error when I try to set a value larger than the field length. But Type property(I used it in win32 app), it types as much as the field length but at least doesn't throw an error I can't handle.
QTP in WebEdit or this particular WebEdit does not allow me to choose .Type. How come it allowed me in the case of SWFfield?
Any suggestions?
"Type" is a method used for Windows application, Delphi, Java, swf etc... But it is not used for Web application.
For web application we have to use "Set" method. Therefore above piece of code accepts 'Set' method. This also tells why you can not use 'Type' method with web based object.
If you want to count the field limit, you can object spy on the webedit-> save its max length value to any variable-> at run time store max length of that web edit field to variable2 using 'Browser("...").Page("..."). .... .GetROProperty("max length"). Now compare the two variable with if statement.
The WebEdit test object is helpfully trying to prevent you from setting an invalid value into the edit field.
You obviously don't want this help the question I'm not clear on is what you're trying to accomplish. If you want to set the first part of the string up to maxlength you can do something like this:
Public Function SetLimit(ByRef test_object, ByRef Value)
max = test_object.GetROProperty("max length")
If Len(Value) > max Then
test_object.Set Left(Value, max)
Else
test_object.Set Value
End If
End Function
RegisterUserFunc "WebEdit", "Set", "SetLimit"
You could use the WScript shell object to send keystrokes to the WebEdit after focussing it (by clicking into it). Like shown in http://h30499.www3.hp.com/t5/Quality-Center-Support-and-News/QTP-cannot-input-value-to-Webedit-field/td-p/4206627#.UkRMyIbIYyg:
Set WshShell = CreateObject("WScript.Shell")
Browser("Sarit_2").Page("Sarit").WebEdit("frm_vendor_address1").Click
WshShell.SendKeys(dtVAdd1)
Set WshShell = Nothing
Note that dtVAdd1's value must then conform to the format SendKeys expects -- as documented in (for example) http://msdn.microsoft.com/en-us/library/8c6yea83(v=vs.84).aspx.
Alternatively, you could use the undocumented Mercury.DeviceReplay interface to send the keystrokes. This is easily googleable.
As we know WebEdit does not supports Type method, we can use alternate approach like
1.SendKeys
Set objClick = CreateObject("WScript.Shell")
Browser("Sarit_2").Page("Sarit").WebEdit("frm_vendor_address1").Click
objClick.SendKeys "dtVAdd1"
Set objClick = Nothing
http://www.ufthelp.com/2013/02/sending-keyboard-strokes-in-uft-115.html
2.Using Native Object properties
Browser("Sarit_2").Page("Sarit").WebEdit("frm_vendor_address1").object.value ="dtVAdd1"
but this is almost similar to Set method
3.Using Advance Run settings in UFT
Tools->options->GUI Testing ->Web->Advance->Run Settings->Replay Type ->Mouse

Resources