Check for COM Object Collection Propertys Existence - vb6

I have been pondering this one for a while. I am programatically (in VB6) sifting through Outlook PSTs, indexing items that are in them. I have happened on to one that is a little corrupted and this is where I am having difficulty. My program attaches the PST and starts drilling down through the folders (olFolder). These Outlook created objects are supposed to have a collection that would normally hold sub folders (appropriately named Folders).
During the execution of my code I recursively call the process folder function to process all folders in the current folder, but I have one that does not have a collection. This causes an exception because I am checking the count of folders in the collection and there is nothing to check. I understand how to check for the existence of an object, but I am having a hard time checking for the existence of a collection in and object.
Update
The expression:
If Not fl.Folders Is Nothing Then
ends up throwing the exception too. The exception that I am getting is the following:
Could not complete the operation because the service provider does not support it.
This is only when trying to access this corrupted folder which appears to have a Folders Collection Property that is FUBARed.
Final
Alright; in this case I am just going to put in some in-line error handling.
If Not Err.Number = -2147221246 Then
'Do the thing with the other thing
End If
Err.Clear

If you are talking about checking whether obj.Coll.Count = 0, then see if obj.Coll Is Nothing.

well when i work web services on vb6 i have check some cases if property exist. i used that method.
It is slow but i hope that help you.
Public Function HasProperty(ByRef obj As Object, ByVal nameProperty As String) As Boolean
On Local Error GoTo hasProperty_Error
Dim Result
Result = CallByName(obj, nameProperty, VbGet)
hasProperty = True
hasProperty_Done:
Exit Function
hasProperty_Error:
If Err.Number = 438 Then
hasProperty = False
End If
Resume hasProperty_Done
End Function

Related

Delete Word Document from location - VB UFT

As suggested in the title, it looks like a simple question but i didn't find any solution so far.
So i would like to delete a Word document from a file system, share, file explorer; so any location.
I didn't find a way to deal with that concern.
I've tried simple DeleteFile function but it seems it doesn't handle word file.
Function DeleteAFile(filespec)
Set fso = CreateObject("Scripting.FileSystemObject")
fso.DeleteFile(filespec)
End Function
The same i tried creating a oWord object ( an instance of a COM component ) but there is no delete method or event available.
Set oWord = CreateObject("Word.Application")
oWord.'No method nor event for delete action
So i'm blocked.
Is someone having a solution, it would be helpful.
In VBA or VB.Net the simple command is:
Kill(Path and Filename)
Based on additional information provided in the comments that only VBScript can now be used. You should consider two factors. The files you are trying to delete are Read Only and you have to then use the Force option on the DeleteFile method. The other is that your routine is receiving some error condition and thus the DeleteFile method is being stopped. You should add error checking to your routine.
For further information on the FileDelete method see the following:
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/deletefile-method

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.

QTP - if object exists in object repository

In QTP is there any way in the code to check to see if a specific object exists in the object repository. I have tried the following code:
If JavaWindow(className).JavaDialog(dialogName).Exist Then
doThisStuff
Else
doThisStuffInstead
End If
But from what I have gleamed from the Internets, this is similar to a isVisible method, only resulting in true if the specified object is currently visible. When I use the above code I receive a "JavaDialog object was not found in the Object Repository." Is there a method or any way to prevent this very error and check to see if the object does indeed exist?
Thank you for your time
I'm not sure what you're trying to accomplish here, one typically knows if an object exists in the object repository before using it. The doubt is usually whether there is a corresponding control in the AUT (Application Under Test).
If you really face the situation that sometimes the object is in the repository and sometimes it isn't (I can think of several ways for this to happen but none of them make much sense) then you can use VBScript's error handling mechanism.
On Error Resume Next ' Turn off error handling
' Just check if object is in repository, there's no need to do anything with it
Dim Exists: Exists=JavaWindow(className).JavaDialog(dialogName).Exist
If Err.Number <> 0 Then
doThisStuff 'Exists is still empty
Else
doThisStuffInstead ' Exists is properly set
End If
On Error Goto 0 ' Resume regular error handling
So, from the error you get, either the dialog that appears is different from the one you've stored in the repository or you don't have it there.
Have you checked it is really present in the Repository? You can try to just locate this element button.
Using the method of "if object not in the repository - skip the step" is not really a good idea. 1. Why would you want to skip the test/part of the test if the object was not saved in the repository?
2. If it's not there, so you need to make sure to store it.
I would assume that this "missing" object might have some values by which it's matched to the object from the repository different from test to test. You can tune the "matching" mechanism by manually setting the values by which you want QTP to locate it.

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.

How do I access the names of VB6 modules from code?

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.

Resources