Search Outlook Global Address List Asynchronously - vb.net

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 "------------"

Related

Skip a line if runtime exceeds certain time limit for a single code - VBA

Im creating a form where all credentials are automatically populated by anyone who accesses it. However I am facing difficulties trying to get the user's email address.
Set olApp = CreateObject("Outlook.Application")
Set olNS = olApp.GetNamespace("MAPI")
UserEmail = olNS.CurrentUser.AddressEntry.GetExchangeUser.PrimarySmtpAddress
This one line could be taking >1 minute of some user's PC and few seconds on other's.
Is there a way to skip this if its taking more than 5 seconds for example?
I'm thinking of applying application.ontime but dont know how to go from there.
VBA is not designed for running multithreaded calls. If you want to optimize performance of your Outlook VBA solutions you may consider creating a COM add-in instead. See Chapter 12: Migrating VBA Solutions to VSTO for more information.
Before accessing the address entry object I'd suggest getting the Address property value. It returns a string representing the e-mail address of the Recipient. If it corresponds to an SMTP address then you may skip unnecessary calls.

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.

Determine currently active Outlook store in vba / wsh vbs

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.

How to get user-Information from domino-server via vba

I writing a macro for ms word, that starts when the document is loaded and fills a few fields with user information (tel,email etc.). I want this information to come form our domino-servers, as they are our primary database.
I have tried using notes objects in vba to establish a session on the server, but that requires that user to keep their notes-client open(as far as I know), so thats not an option.
Currently I am trying to get the information via ldap request(the domino-server runs the ldap-service), however I cant find information on how the code and search string has to look like.
The last site I tried was this one (http://www.selfadsi.org/bind.htm), using the "Bind using special credentials" variant.
But I am getting a protocol error.(the string proably needs to be adjusted for connecting to domino, I tried "LDAP://[Server-FQN]/dc=DE" hoping it would show me all users registered as C=DE, but it did not work.)
I have also searched through IBMs Support Site and Knowledge-Center, but could not find the info I need.
If anyone could provide me an example on how to connect to the domino ldap and pull the information for a user, I would be very grateful.
There are two different sets of Notes objects available to you in VBA. There are OLE objects, which you access this way:
Set Session = CreateObject("Notes.NotesSession")
And there are COM objects, which you access this way:
Set Session = CreateObject("Lotus.NotesSession")
The key differences between them are that OLE objects require that the Notes client must be running and they also include the "front-end" classes like Notes.NotesUIWorkspace that can actually drive the Notes client, while the COM objects do not require that the Notes client is running (though either it, or the Domino server, must be installed) and it does not provide the front-end classes.
If you want to stick with the LDAP approach, the first thing you should probably do is download an LDAP client. I like the SoftTerra LDAP browser, but I've also heard that a lot of people like the Apache Directory Studio. Browsing the tree is really the best way to figure out what queries will get you the info you need.
s mentioned in the commment on Richards answer, I also tried finding the LDAP-String using a LDAP-Browser. Doing that and some testing with ADODB Objects I was able to come up with the following code:
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand = CreateObject("ADODB.Command")
Set objCommand.ActiveConnection = objConnection
objCommand.CommandText = "SELECT UID, mail FROM 'LDAP://[Server-FQN]:389 'WHERE ObjectClass='dominoPerson'"
Set objRecordSet = objCommand.Execute
While Not objRecordSet.EOF
varMail = objRecordSet.Fields("mail").Value
Debug.Print varMail(0)
objRecordSet.MoveNext
Wend
Which extracts the email-adress of all our users using anonymous access and prints the result into the debug window.
Now all I have to do is to adjust the WHERE-Statement to get the data I want per user. :-)

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.