Determine currently active Outlook store in vba / wsh vbs - vba

I'm using JavaScript and the Windows Scripting Host to work with Outlook items.
This has been straightforward enough, until one of our users pointed out that the script does not work because he has two Exchange profiles configured in Outlook.
The one the script should be working on is in the second profile, but if I access, e.g. the Outlook categories master list, this is loaded from the standard profile.
The answer is easy enough, I just need to address the correct Outlook store object. My script needs to determine, which store is being accessed in the currently active Outlook Explorer, and I could not find a native function for this.
Given an outlook application object var ol = new ActiveXObject( "Outlook.Application" ), I can natively determine the currently active Outlook explorer by simply calling ol.[ActiveExplorer()][4]; But the session object, through which stores are accessed, does not offer an equivalent function.
I've come up with a workaround, by comparing the explorer's caption
ol.ActiveExplorer().Caption // "Inbox - email#address.com - Outlook"
with a loop over the stores' display names
ol.Session.Stores.Item(counter).DisplayName // "email#address.com"
So I do have working code:
function ActiveStore(olApplication) {
var ActiveExplorerCaption=olApplication.ActiveExplorer().Caption;
for (var storeCounter=1; storeCounter<=olApplication.Session.Stores.Count; storeCounter++) {
var storeDisplayName=olApplication.Session.Stores.Item(storeCounter).DisplayName;
if (ActiveExplorerCaption.indexOf(storeDisplayName)!=-1) return olApplication.Session.Stores.Item(storeCounter);
}
return "undefined";
};
var ol = new ActiveXObject( "Outlook.Application" );
WScript.Echo("The Outlook store currently active in an explorer is called "+ActiveStore(ol).DisplayName);
But, I'm wondering:
will this code always work, i.e. are this variables always set up in this way, or just in the installations I can work with
isn't there a better way to determine the currently active store?

Since you have ActiveExplorer, you also have Explorer.CurrentFolder and thus Folder.StoreID. Then use Namespace.GetStoreFromID using that StoreID value.

Related

Search Outlook Global Address List Asynchronously

I want to;
Search the Global Address List of MS Outlook
Using an input, filterStr
To retrieve all items asynchronously that match a criteria satisfying
Contact first name starts with filterStr or contact last name starts with filterStr
Display the contacts as they become available in some sort of list
I am developing a MS Outlook add-in with Visual Studio 2017 using VB.net. (c# code examples are wellcome). I need this add-in to be able to search contacts like MS Skype for Business does, through its "Find Someone" field of "RICHEDIT60W"
I have tried the following so far;
Use "RICHEDIT60W" of MS Skype for Business (the search field) in my form
Could not find documentation
Use the "RichEdit20WPT" of MS Outlook (the "TO:" field in new e-mail compose)
Could not find documentation
Get the "Global Address List" through Session.GetGlobalAddressList
Succeeded, but looping through more than 50k items is too slow
Use the System.DirectorySearcher
This does not always work. When I'm connected to my corporate network through cable, I can create the object. When I'm connected through my home wireless, I get the error: "specified domain does not exist or could not be contacted"
I did try using a DirectoryEntry object initialized with;
name.surname.corporation.com where my e-mail address is name.surname#corporation.com, this time I get an "unspecified error"
I tried using ADODB to connect as given in https://learn.microsoft.com/en-us/windows/desktop/ad/example-code-for-searching-for-users#visual-basic-example
This also gives me an error on line 42 (root = GetObject("LDAP://rootDSE")
QUESTION: How can I do any of the following;
Get DirectorySearcher to work
Get ADODB method to work
Use the existing RichEdit controls of either Outlook or Skype for Business
?
I solved it like this;
Getting the GAL itself and the AddressEntries is fast enough, no problem there
I'm looping through the AddressEntries only once, during initialization and getting only the Nameproperties of each to a List(Of String), which takes around 3 seconds
When I need to search for an entry, I'm using Linq to query the list with IndexOf function, which takes about 150ms max to get all matches, and the items of the list are immediately accessible
This suited my needs because
- It works through my home network and even when I don't have a connection (I assume Outlook is cacheing the GAL)
- Querying using Linq is fast enough to facilitate searching as the user types
On the low (Extended MAPI - C++ or Delphi only) level, you would need to apply PR_ANR MAPI restriction: that is what Outlook uses when it resolves a name and displays the list of ambiguous matches. You can run that code on a separate thread, but it won't return matches one at a time - you will get the whole set back.
If Extended MAPI is not an option, you can use Redemption (I am its author) and its RDOSession.AddressBook.GAL.ResolveNamesEx method:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set AdrrEntries = Session.AddressBook.GAL.ResolveNameEx("John")
Debug.Print AdrrEntries.Count & " names were returned by ResolveNameEx:"
Debug.Print "------------"
for each AE in AdrrEntries
Debug.Print AE.Name
next
Debug.Print "------------"

What is the VBA property for: Server folder contains x items?

I like to compare the amount of items in each folder on the Exchange server with the amount of items in my local Outlook file.
In Outlook I can use the following to get the item count per folder
Dim Folder As Outlook.MAPIFolder
Set Folder = objNS.Folders("xxx").Folders("Inbox")
Debug.Print Folder.Items.Count
How do I get the item count on the server like in the following screenshot?
To find out the number of items in a server (online) folder, you will need to open that folder in the online mode.
In Extended MAPI (C++ or Delphi) you would need to use the MAPI_NO_CACHE bit when calling IMAPISession::OpenEntry - you can play with that bit in OutlookSpy (I am its author): click IMAPIFolder button, select PR_ENTRYID property, right click, select IMAPISession::OpenEntry, make sure MAPI_NO_CACHE is checked. One you do that, you can retrieve the count either from the IMAPITable contents table or by reading the PR_CONTENT_COUNT (0x36020003) MAPI property from the folder itself.
Outlook Object Model will not let you override the caching mode on the per folder/message level - the whole profile must have the right connection mode.
In case of languages other than C++ or Delphi, you can use Redemption (I am also its author) - its versions of GetFolderFromID, GetMessageFromID, etc. allow to pass flags to be used by IMAPISession::OpenEntry.
MAPI_NO_CACHE = &H0200
MAPI_BEST_ACCESS = &H0010
set OomFolder = Application.ActiveExplorer.CurrentFolder
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set RdoFolder = Session.GetFolderFromID(OomFolder.EntryID, , MAPI_NO_CACHE Or MAPI_BEST_ACCESS)
MsgBox "Number of items in the online folder: " & RdoFolder.Items.Count

Read fields in Lotus Notes Documents Through VBA

I am trying to write VBA code that will read data from fields in a Lotus Notes document. Currently, I am able to read data using the FieldGetText method, however this only works when I have the document open. I need to be able to loop through documents without opening them, as there are several hundred. I need to be able to read these same fields from many documents, but cannot figure out how to loop through them. My code currently is:
Set LotusNotes = CreateObject("Notes.NotesUiWorkspace")
Set CurrentDoc = LotusNotes.CurrentDocument
While Not (CurrentDoc Is Nothing)
' Affectation of data
DueDate = CurrentDoc.FieldGetText("RevDueDate")
DueTime = CurrentDoc.FieldGetText("RevDueTime")
DateClosed = CurrentDoc.FieldGetText("DateClosed")
Wend
I understand that this uses a Front End object. I was able to use a Back End object that could loop through documents (without them being open), however the data (for the dates, specifically) did not match the field text from the documents. That code looked like this:
Set LotusNotes = CreateObject("Notes.NotesSession")
Set db = LotusNotes.GetDatabase("")
Set view = db.GetView(view_name)
view.AutoUpdate = False
Set columnview = view.AllEntries
Set doc = view.GetFirstDocument
While Not (doc Is Nothing)
revDate = doc.GetItemValueDateTimeArray("RevDueDate")
revDate = doc.RevDueDate
Set doc = view.GetNextDocument(doc)
Wend
Basically, I'm just wondering if it's possible to loop through multiple files using the NotesUIWorkspace class that I tried first, or if it's possible to somehow use FieldGetText in the NotesWorkspace class instead.
Any help is appreciated, thanks.
You're using the Notes "front-end" classes rooted at Notes.NotesUIWorkspace. These are OLE classes, which means that they need the Notes client to be running and they work on the open document. There are also back-end classes rooted at Notes.NotesSession. These are also OLE classes, so they still need the Notes client to be running but they can access other documents (and other databases, servers, etc.). There is also a set of back-end classes rooted at Lotus.NotesSession. Note the different prefix. These are COM classes, so they do not need the Notes client to be running - though it does have to be installed and configured, and your code will have to provide the user's password since the client won't prompt for it.
You can find documentation and examples for the NotesSession class here. Down near the bottom of the page, you'll find links to information about using them via OLE or COM. You can find a bunch of examples based on using the COM classes here, including one that traverses the documents in a view in database. Apart from the inital setup of the session, it would be the same if you use OLE.
It is possible, and also better to use the 'Back end' object Notes.NotesSession. So try this:
Set doc = view.GetFirstDocument
While Not (doc Is Nothing)
'Check if you have the field in the document
if doc.HasItem("RevDueDate") Then
revDate = doc.getFirstItem("RevDueDate").Text
End If
Set doc = view.GetNextDocument(doc)
Wend
It is also possible to use Notes.NotesUiWorkspaceobject by opening every document in the client, getting the fields data and closing the document but I would highly recommend NOT TO make it like this as there is high possibility that you will crash the Notes client, if you need to loop on more documents.

Open Outlook mail Item using EntryID, StoreID, and / or PR_ENTRYID

NOTE: I'm using VBA and Office 2007. (I would use C#, but the project parameters don't allow this)
I'm attempting to find some method in Outlook, or an API, that will allow me to open an Outlook mail item by providing either the Outlook EntryID or the MAPI "PR_ENTRYID" property from an Access Database. I have found many references to said code, but I have never seen anyone actually post a solution. I have attempted in include references to mapi32.dll and OLMAPI32.dll, but I get the following error: "Can't add a reference to the specified file." I'm guessing this is because those dll's are meant for .NET.
Any help you can give would be greatly appreciated.
Use Namespace.GetItemFromID. Note the second parameter (store id) is optional. You can omit it if the store in question was already touched by Outlook is in the current session. If not, Outlook will raise the "unknown entry id" exception. If the store entry id is specified, Outlook will open it first, and the store provider will have a chance to register its entry ids with the MAPI system.
set App = CreateObject("Outlook.Application")
set NS = App.GetNamespace("MAPI")
NS.Logon
set Msg = NS.GetItemFromID(EntryID)
MsgBox Msg.Subject
For C#:
var ns = OutlookApp.GetNamespace("MAPI");
var item = ns.GetItemFromID(entryId) as MailItem;
Where OutlookApp has Microsoft.Office.Interop.Outlook._Application type.

Get Inboxes from Outlook

I configured two Exchange accounts in Outlook 2010, however I cant find out how to get to Inbox of the second account. Session.GetDefaultFolder() always return the first one.
Even enumerating Session.Accounts, finding the right account and calling Session.Account(found one).Store.GetDefaultFolder() returns wrong Inbox (from the default exchange account, not the secondary).
Does this show you all the available Inboxes?
Sub LoopThroughInboxes
Dim ol As Outlook.Application
Dim ns As Outlook.NameSpace
Dim i As Long
Set ol = Outlook.Application
Set ns = ol.GetNamespace("MAPI")
For i = 1 To ns.Folders.Count
Debug.Print ns.Folders(i).Name
Next i
If so then ns.Folders(i).Folders("Inbox") will get you the Inbox for each mailbox.
To Go to Mapix library
Mapix library link as given below
Mapix library for C++/MFC
Note: This Library valid for Inbox emails in MS Outlook
Use Store.GetDefaultFolder instead of Namespace.GetDefaultFolder.
Note that Store.GetDefaultFolder was added in Outlook 2010. In the earlier versions of Outlook use Extended MAPI (C++ or Delphi) or Redemption (I am its author) - RDOStore.GetDefaultFolder.
Maybe you have long given up on this question, but here goes...
I've had this same problem before and I solved it by adding the Outlook Account Management API. Unfortunately for you, this a c++ oriented API. (My addin was already developed in c++)
Furthermore, the OOM (Outlook Object Model) which VBA and the .NET addins use has poor (if any) support for multiple accounts. By adding to exchange accounts, you have essentially added multiple accounts to your profile.
So, You might have to go down a level, using MAPI with c++ and then hook in the Outlook Account Management API. It's a lot of work, but that's exactely what I did and it worked like a charm.
Also, here is an example:
http://www.codeproject.com/KB/IP/IOlkAccountManager.aspx
I guess this is an old one, but somebody might need it one day.
Here is code to iterate all "Sent Mail" folders in Outlook. (I think this will only work for Outlook 2010 and newer).
MSOutlook._NameSpace ns = Globals.ThisAddIn.Application.GetNamespace("MAPI");
var accounts = ns.Accounts;
foreach (MSOutlook.Account account in accounts)
{
try
{
// You might want to test if DeliveryStore is null, in case this account is not an Exchange account
MSOutlook.MAPIFolder sentFolder = account.DeliveryStore.GetDefaultFolder(MSOutlook.OlDefaultFolders.olFolderSentMail);
if(sentFolder != null)
{
SentItems = sentFolder.Items;
SentItems.ItemAdd += LogMethods.Items_Sent_ItemAdd;
}
}
catch (Exception e)
{
BaseClass.log.Log(LoggLevel.Warning, e.Message);
}
}