Delete Word Document from location - VB UFT - vbscript

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

Related

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.

Using callback functions in vbscript

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.

Check for COM Object Collection Propertys Existence

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

ms access linked image relitive path

I have an Image object.
I have the Picture type set to linked, so I can change the picture if I want.
I have the Picture property set to the picture name.
I would think that access would use relitive addressing and simple looking in the current directory for the image. But it does not and I get an error telling me it cannot find the picture.
Anyone have a solution? (Other than setting the Picture type to embedded or using the full file address?)
Thanks!
Update:
Tried this:
Private Sub Form_Load()
Dim file As String
file = CurrentDb().Name
file = Replace(file, ".mdb", ".bmp")
Me.Image46.Picture = file
End Sub
It works, except I still get the error message. I click O.K. and it works. Just need the error message to go away.
SOLUTION: Use the above code (or the code posted in the answer below) and then set the 'picture type' to "embedded" and then delete the 'picture' field so that it says "(none)".
Save and run.
It should work.
THANKS!
You could set the property on the forms OnLoad event like this
Me.imgMy_image.picture=getDBPath & “mypicture.bmp”
Here is the getDBPath function
Public Function GetDBPath() As String
Dim strFullPath As String
Dim I As Integer
strFullPath = CurrentDb().Name
For I = Len(strFullPath) To 1 Step -1
If Mid(strFullPath, I, 1) = "\" Then
GetDBPath = Left(strFullPath, I)
Exit For
End If
Next
End Function
Before anyone comments yes I know in access 2000 and above you can use currentproject.path but I’m stuck in the land that time forgot so need that custom function, it still works with later versions of access
Current folder depends of the way you open database in Access. At least, if you open it thru "File-Open", current folder changes to the folder of MDB file. But if you open via double-clicking MDB in explorer, it does not.

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