DirExist for OneDrive synchronized Sharepoint directories - vba

Nowadays you can work on synchronized sharepoint content via OneDrive even when you are not linked to your company network via e.g. VPN, but logged in to Onedrive via your company account. Strange thing is that the DIR function in that disconnected situation returns an empty value for a directory that certainly exists. I see solutions trying to create mapped drives, but it can be done simpler.
I'll answer this question myself.

Robert, this is exactly your code but a bit more dense.
The With statement takes care of the object reference and ensures its destroyed when it reaches the End With block.
Public Function DirExists(ByVal path As String) As Boolean
On Error Resume Next
With CreateObject("Scripting.FileSystemObject")
DirExists = Not .GetFolder(path) Is Nothing
End With
On Error GoTo 0
End Function
This is nothing more than a different approach on how to write the particular function. There's absolutely nothing wrong with your example.

This is my solution:
Function MyDirExists(ByVal myPath As String) As Boolean
'Dir() doesn't work on directories in synchronized sharepoint
'that appear in your OneDrive folders
'Let's use the FileSystem instead.
'Use Late binding as not everyone has the library FileSystemObject included
Dim objFSO As Object 'Late binding
Dim objfolder As Object 'Late binding
Set objFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
'if directory does not exist GetFolder raises an error
'and we happily use that fact
Set objfolder = objFSO.GetFolder(myPath)
If Not objfolder Is Nothing Then
MyDirExists = True 'Default return value is False
End If
On Error GoTo 0
'Clean up objects
Set objFSO = Nothing
Set objfolder = Nothing
End Function

Related

how to check whether Scripting Runtime is available

Is there any way in VBA to determine whether the Scripting-Runtime aka scrrun.dll is disabled on a users system (here's a link on how to do that)?
I know, this is a very rare case, but it could be the case for exactly one client. There is another thread here but it's a little different.
Would you just go something like this?
Dim fso As Object
On Error Resume Next
Set fso = CreateObject("Scripting.FileSystemObject")
On Error Goto 0
If fso Is Nothing then _
MsgBox "Scripting runtime is not available on this system."
Yes, I would use this approach, it's as short as it can be:
Public Function ScriptingRuntimeAvailable() As Boolean
On Error Resume Next
With CreateObject("Scripting.FileSystemObject"): End With
ScriptingRuntimeAvailable = Err.Number = 0
End Function

Setting a folder object through a string describing its path

In MS Outlook (2016) I am working on a VBA procedure to more quickly archive certain, individually selected e-mails into certain folders in my e-mail archive.
I have a procedure that does the trick when I address the target folder manually:
'[...]
Dim MoveToFolder As Outlook.MAPIFolder
'[...]
Set MoveToFolder = ns.Folders("Archive").Folders("Projekte-Archiv").Folders("P03_NetRef")
'[...]
With this the procedure knows what folder to move pre-selected e-mail to.
Now my problem:
I am trying to set the "MoveToFolder" folder object through a string variable giving it all the necessary data.
Why do I want to do this: Handing over the folder data as a string variable would allow me to use the same procedure for as many folders in as many hierarchy levels I want.
Here is what I came up with, using the CType function:
'[...]
Dim MoveToFolder As Outlook.MAPIFolder
'[...]
Set MoveToFolder = CType("ns.Folders(""Archive"").Folders(""Projekte-Archiv"").Folders(""P03_NetRef"")", Outlook.MAPIFolder)
'[...]
(The idea is of course in a next step to insert the string through a variable, not in plain writing like the example.)
This does not work. The object type 'Outlook.MAPIFolder' results in an error on compiling ("method or data object not found").
Later insight
As I understood later on, the CType() function is not available in VBA (as opposed to VB.net).
Untested:
Set MoveToFolder = GetFolder(ns, "Archive|Projekte-Archiv|P03_NetRef")
A function to parse the path:
Function GetFolder(root, fpath)
Dim f As Object
Dim arr, i
arr = Split(fpath, "|")
For i = 0 To UBound(arr)
If i = 0 Then
Set f = root.Folders(arr(i))
Else
Set f = f.Folders(arr(i))
End If
Next i
Set GetFolder = f
End Function

Create Object for handling word on the fly (VBA)

I'm using VBA for creating an instance of word currently by adding a reference to the library, but this cause an issue, because there existing some machines without word.
This cause instantly a runtime error at startup on these machines. It's not possible to catch this error.
However, I try to create an object in VBA on the fly by something like this
Dim oWshShell As WshShell
Set oWshShell = New WshShell
' *** TEST REGESTRY
Dim tmp As String
tmp = oWshShell.RegRead("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\15.0\Word\Options\PROGRAMDIR")
Debug.Print tmp
tmp = tmp + "winword.exe"
Dim oWord As Object
Set oWord = Shell(tmp)
But my problem is that oWord is not an Object of Word.Application. So how to handle this?
Would be nice to get available all functionalities like with Word.Application.
You don't have to use a shell, use COM factory directly:
Function openWordApp() As Object
On Error Resume Next
Set openWordApp = CreateObject("Word.Application")
If openWordApp Is Nothing Then
msgBox "Word not installed on this machine"
Else
openWordApp.Visible = True
End If
End Function
In the caller, check if the returned value Is Nothing.

How to automate saving an inbox message to a folder when closing after reading - Outlook 2010 VBA

I want to keep this in VBA. I'm seeking info on how to work around the following issue.
I get this error:
The item's properties and methods cannot be used inside this event procedure. MS has stopped people being able to use the .Close, .Move and .Delete methods in the Inspector_Close event.
I've seen suggestions to use threading to run a delayed macro, but can't find help on this, and suspect it may not be available in VBA.
My code is as follows:
Private Sub objInspector_Close()
Dim objNS As NameSpace
Dim objFolder As MAPIFolder
'On Error Resume Next
Set objNS = Application.Session
If Not mailSent Then
If objInspector.CurrentItem.Class = olMail Then
'Mail inspector is closing
If objInspector.CurrentItem.Parent = "Inbox" Then
Set objFolder = objNS.PickFolder
If Not objFolder Is Nothing And IsInDefaultStore(objFolder) _
And objFolder.DefaultItemType = olMailItem Then
Set objInspector.CurrentItem.Move = objFolder
End If
End If
End If
Else
mailSent = False
End If
Set objFolder = Nothing
Set objNS = Nothing
End Sub
The global mailSent Boolean is there to prevent this event code executing when I send / close an email.
The error occurs on Set objInspector.CurrentItem.Move = objFolder.
Is there a way for me to delay this until the event ends or perhaps to set some flags on the email item and then run a macro over all emails in my inbox later to move them to folders.
I work on multiple projects and maintain multiple email folders and am looking for ways to automate my email management. I've seen other pages where folder names are derived from email subjects but I don't want to do that.
Thanks for your help.
You may consider adding a user property which can mark the message for moving etc. Then you can use the Find/FindNext or Restrict methods for searching marked items. You can read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also you can use the GetTable method of the Folder class which obtains a Table object that contains items filtered by Filter.
As you probably know the Outlook object model can't be used from another threads. You need to use a low-level API - Extended MAPI which supports secondary threads. Or any other third-party wrappers around that API, for example - Redemption.
You could abandon the idea of using a trigger and move "manually"
Option Explicit
Private Sub MoveCurrentItem()
Dim objNS As Namespace
Dim objFolder As folder
Dim currItem As Object
Dim uPrompt As String
Set objNS = Application.Session
On Error Resume Next
Set currItem = ActiveInspector.currentItem
On Error GoTo 0
If currItem Is Nothing Then GoTo ExitRoutine
If currItem.Class = olMail Then
If currItem.Sent Then ' reading not composing
If currItem.Parent = "Inbox" Then
Set objFolder = objNS.PickFolder
If Not objFolder Is Nothing And IsInDefaultStore(objFolder) _
And objFolder.DefaultItemType = olMailItem Then
currItem.Move objFolder
End If
End If
End If
End If
ExitRoutine:
Set currItem = Nothing
Set objFolder = Nothing
Set objNS = Nothing
End Sub

Determining whether an existing Outlook instance is open

After reading how to use automation to send a message, I'm unclear of whether it's possible to avoid opening a new instance of Outlook if I already have one opened. If so, I'm unsure of how to search for examples determining whether an existing Outlook instance is open.
-----Including the suggestion--------
I have the following snippet, but I found that I can't create the instance properly. I'm basically following this example. I'm either getting this screenshot, or the error of "User-defined type not defined." Any suggestions?
Sub Example()
'Dim w As Outlook.Application
Const ERR_APP_NOTRUNNING As Long = 429
On Error Resume Next
' Handle Microsoft outlook
Set w = GetObject(, "Outlook.Application")
If Err = ERR_APP_NOTRUNNING Then
'Set w = New Outlook.Application
Set w = CreateObject("Outlook.Application")
End If
End Sub
I know this question has been answered, but I thought I'd add that applications like Outlook (and I believe PowerPoint as well) are single-instance applications -- there is no need to determine if Outlook is already open because you can only have one copy of Outlook running.
http://msdn.microsoft.com/en-us/library/aa164542(v=office.10).aspx
If you need to instantiate Outlook, simply use CreateObject to create the instance; if Outlook is already running, your object reference will point to the existing instance. If not, you will create the class. Binding (late or early) is irrelevant.
For example, let's say Outlook isn't running. We can use this code to create the instance:
Sub testOutlook()
Dim olApp As Object ' Outlook.Application
Set olApp = CreateObject("Outlook.Application")
MsgBox (olApp2 Is Nothing)
End Sub
This will print "False" because we created the instance.
Let's say Outlook IS running. We can use this code to verify that using GetObject and CreateObject will refer to the existing instance:
Sub testOutlook()
Dim olApp As Object ' Outlook.Application
Dim olApp2 As Object ' Outlook.Application
Set olApp = GetObject(, "Outlook.Application")
MsgBox (olApp Is Nothing)
Set olApp2 = CreateObject("Outlook.Application")
MsgBox (olApp2 Is Nothing)
MsgBox "Same object? " & (olApp Is olApp2)
End Sub
This will print "False" (existing instance), "False" (our alleged "new instance"), but the last message box is "True" because the new instance is actually the same object as the existing instance.
So what do we do if we don't know if Outlook is running or not? As demonstrated above, CreateObject either created a new instance (if one didn't exist, as in the first example) or hooked the existing instance if Outlook was already open (as in the second example).
I see in your question that you commented out
'Dim w As Outlook.Application
presumably because this gives you the "User-defined type not defined" error.
This is likely because you have not set a reference to the Outlook library in your Excel-VBA project. This is done as follows: Tools > References > check "Microsoft Outlook xx.x Object Library". Then you can write this
Dim w As Outlook.Application
Set w = New Outlook.Application
' or,
'Set w = CreateObject("Outlook.Application")
which, by the way, results in compile-time (or "early") binding. And gives you the Outlook object intellisense.
Alternatively, you can omit setting the reference and declare w as a generic object and let it bind at run-time
Dim w As Object
Set w = CreateObject("Outlook.Application")
but runtime (or "late") binding is less efficient.
Do whatever feels best -- I'm going to go ahead and venture that chances are, you won't notice the difference in efficency. I'm a recent convert to the early-binding thing, really just because of the intellisense.
EDIT So you've created a new Outlook application, but you can't see it. If you look in the Windows task manager, you'll see that the process is there, running -- but it's just not showing on the screen. Unfortunately, some brilliant engineer at Microsoft decided that Outlook shouldn't have a Visible property like Word or Excel do, so we have to use an awkward workaround. Open one of the special folders e.g. the Inbox like this:
Dim w As Outlook.Application
Dim wInbox As Outlook.MAPIFolder
Set w = New Outlook.Application
Set wInbox = w.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
wInbox.Display 'This makes Outlook visible
Set w = GetObject(, "Outlook.Application")
this should get running instance, if none is running catch error and do CreateObject
If you like, use this.
This is not a perfect solution, but you can open Outlook App when it's not be opened.
Function OpenOutlookApp(isSend As Boolean) As Boolean
' If it has opened, return true.
' my office version is 2016.
Dim oApp As Object
On Error GoTo ErrorHandle
On Error Resume Next
Set oApp = GetObject(, "Outlook.Application")
On Error GoTo 0
If oApp Is Nothing Then
Set oApp = CreateObject("Outlook.Application")
oApp.GetNamespace("MAPI").GetDefaultFolder(6).Display
End If
If isSend Then Call SendAndReceiveOutlookMail(False)
OpenOutlookApp = True
GoTo NonErrorHandle
ErrorHandle:
NonErrorHandle:
On Error GoTo 0
End Function
Sub SendAndReceiveOutlookMail(isQuit As Boolean)
Dim oApp As New Outlook.Application
On Error Resume Next
Call oApp.Session.LogOn("Outlook", "")
Call oApp.Session.SendAndReceive(True)
If isQuit Then oApp.Quit
Set oApp = Nothing
On Error GoTo 0
End Sub