An Outlook Addresses Lookup List
by
, 23-Oct-2011 at 10:32 AM (11193 Views)
This week I had the need for a selection list with e-mail addresses that are stored in my Microsoft Outlook address book(s). So I created one. This blog explains in steps what you need to do to make one as well. The blog is based on version 16.1 of Visual DataFlex. If you do not have that version you cannot create the component.
The Selection List
One of the enhancements of Visual DataFlex 16.1 is a cCJGridPromptList class which offers a selection list that is not getting its data from a Data Dictionary object but from a datasource that you fill yourself. The Microsoft Outlook addresses are data but they cannot be reached via a Data Dictionary Object.
Create a new component in your workspace and use a ModalPanel class for that. Name the dialog oOutlookAddressesLookup and drop a cCJGridPromptList class on the dialog. In the cCJGridPromptList object you should create two columns; one for the display name of the e-mail address and one for the e-mail address itself. Optional you can add a cCJGridColumnRowIndicator object too, for this blog I choose to do so.
By default the selection list lets you select one record but for this example I want to be able to select more than one and thus I set the property pbMultipleSelection to true.Code:Use Windows.pkg Use DFClient.pkg Use cCJGridPromptList.pkg Use cCJGridColumnRowIndicator.pkg Use cCJGridColumn.pkg Use Outlook.pkg Object oOutlookAddressesLookup is a ModalPanel Set Size to 133 292 Set Location to 4 5 Set Border_Style to Border_Thick Set Label to "Select an Outlook contact" Object oSelList is a cCJGridPromptList Set peAnchors to anAll Set Size to 105 280 Set Location to 5 5 Object oCJGridColumnRowIndicator is a cCJGridColumnRowIndicator Set piWidth to 18 End_Object Object oNameColumn is a cCJGridColumn Set piWidth to 200 Set psCaption to "Name" End_Object Object oAddressColumn is a cCJGridColumn Set piWidth to 200 Set psCaption to "E-mail address" End_Object End_Object Object oSelectButton is a Button Set Label to "&Select" Set Location to 115 128 Set peAnchors to anBottomRight Procedure OnClick Send Ok of oSelList End_Procedure End_Object Object oCancelButton is a Button Set Label to "&Cancel" Set Location to 115 182 Set peAnchors to anBottomRight Procedure OnClick Send Cancel of oSelList End_Procedure End_Object Object oSearchButton is a Button Set Label to "Search..." Set Location to 115 236 Set peAnchors to anBottomRight Procedure OnClick Send Search of oSelList End_Procedure End_Object On_Key Key_Alt+Key_S Send KeyAction of oSelectButton On_Key Key_Alt+Key_C Send KeyAction of oCancelButton End_Object
Outlook
Another key player in this blog is Microsoft Outlook. Without having Microsoft Outlook on your machine you cannot get this blog to work. The code you will see will work with Microsoft Outlook 2003, 2007 and 2010 BUT on version 2003 you will get the "someone wants to access..." question. Up from revision 2007 and an installed and alive approved virus scanner the message will no longer appear. So my advise it to use Microsoft Outlook 2007 as minimal version. You can test if Microsoft Outlook is installed with looking in your registry. For this we wrote a function named CheckIfComRegistered. The code for this function is:
You use above function to test if Outlook is installed via:Code:External_Function CLSIDFromString "CLSIDFromString" ole32.dll Pointer lpsz Pointer pclsid Returns Handle External_Function StringFromCLSID "StringFromCLSID" ole32.dll Pointer rclsid Pointer ppsz Returns Integer Function CheckIfComRegistered String sName Returns String String sBuffer sClass sAddress Integer iResult Integer iAddress Move 0 to iAddress Move (Repeat(Character(0),255)) to sBuffer Move (StrToWStr(sName)) to sName Move (CLSIDFromString(AddressOf(sName),AddressOf(sBuffer))) to iResult Move (Repeat(Character(0),255)) to sClass Move (StringFromCLSID(AddressOf(sBuffer),AddressOf(iAddress))) to iResult Move (CopyMemory(AddressOf(sClass),iAddress,76)) to iResult Move (WStrToStr(sClass)) to sClass Function_Return sClass End_Function
The next step around the use of Microsoft Outlook is generating the COM classes. This is easy and can be done from the Visual DataFlex Studio. Go to Create New and the tab-page classes. In there select "Import COM Automation" and click the browse button. Now look for MSOUTL.OLB (at my computer this was in C:\Program Files\Microsoft Office\Office14\MSOUTL.OLB). As you can see from the version I am using Outlook 2010 (version 14). Let the Visual DataFlex Studio generate the class.Code:Get CheckIfComRegistered "Outlook.Application" to sClass Move (sClass = "{0006F03A-0000-0000-C000-000000000046}") to bInstalled
Name Clashes
The class code generator attempt to avoid name clashes by prefixing classnames with cCom and methods with Com. While this is a good attempt it is not good enough if you want to combine multiple parts of Microsoft Office in one environment. Therefor I suggest you make a change in the package that is generated for you. Replace all cCom class names into cOutlook class names. Do this in two steps. Using the search and replace feature in the Visual DataFlex Studio for this. First replace all "Class cCom" with "Class cOutlook" and then the class import instructions via "tocol cCom" with "tocol cOutlook". Using the two steps to avoid that cCom at the wrong places are being replaced.
Addresses, How to Get Them?
How to get to the addresses stored in Outlook? You need to study at the Microsoft Outlook Object Model documentation in MSDN. It learned me that address information (such as e-mail address and name) is stored in an AddressEntry object. An AddressEntry object belongs to a collection of AddressEntries. The AddressEntries collection can be reached via an AddressList object. Note that the object model seems to suggest that AddressEntries belong to AddressLists. An AddressList belongs to a collection of AddressLists. You can get access to the AddressLists via the property AddressLists in a NameSpace object and access to the NameSpace object is via the Application object.
So we need to create the following objects in our code:
To read the addresses you need to make a connection between your Visual DataFlex application and Outlook by sending the message CreateComObject to the oOutlookApplication object. Do this in either the Activating event of the cCJGridPromptList object or in a method started from this event.Code:Object oOutlookApplication is a cOutlookApplication End_Object Object oNameSpace is a cOutlookNameSpace End_Object Object oAddressLists is a cOutlookAddressLists End_Object Object oAddressList is a cOutlookAddressList End_Object Object oAddressEntries is a cOutlookAddressEntries End_Object Object oAddressEntry is a cOutlookAddressEntry End_Object
The other COM objects are connected by retrieving dispatch IDs and assigning them to the Visual DataFlex objects. First we need to get a handle (Dispatch ID) to the NameSpace which we get by adding:Code:Procedure Activating Forward Send Activating Send LoadAddresses End_Procedure Procedure Procedure LoadAddresses Boolean bIsCreated Get IsComObjectCreated of oOutlookApplication to bIsCreated If (not (bIsCreated)) Begin Send CreateComObject of oOutlookApplication End End_Procedure
The only available NameSpace is MAPI. After the dispatch ID is assigned to the oNameSpace object you can talk (send messages) to the oNameSpace object. Messages like Login and the message to retieve the handle to the address lists object.Code:Get ComGetNamespace of oOutlookApplication "MAPI" to vNameSpace Set pvComObject of oNameSpace to vNameSpace
The oAddressLists object is, as mentioned before, a collection of individual address list objects. Therefore we enumerate the addresslist objects via the messages Count and Item.Code:Get psProfileName to sProfileName Get psPassword to sPassword Send ComLogon of oNameSpace sProfileName sPassword True False Get ComAddressLists of oNameSpace to vAddressLists Set pvComObject of oAddressLists to vAddressLists
Each address list has a couple of properties but for this blog is only the AddressEntries property interesting. This property delivers the dispatch ID for the collection of AddressEntry objects, an AddressEntries object.Code:Get ComCount of oAddressLists to iAddressLists For iAddressList from 1 to iAddressLists Get ComItem of oAddressLists iAddressList to vAddressList Set pvComObject of oAddressList to vAddressList
Important to know is that each address entry in the collection of address entries can have its own type. The documentation in MSDN is not clear about what the values exactly are but I found that I am interested in the SMTP entries only.Code:Get ComAddressEntries of oAddressList to vAddressEntries Set pvComObject of oAddressEntries to vAddressEntries Get ComCount of oAddressEntries to iAddressEntries For iAddressEntry from 1 to iAddressEntries Get ComItem of oAddressEntries iAddressEntry to vAddressEntry
At the end of the LoadAddresses routine we tell the grid to load the data from the array we just filled.Code:Get ComType of oAddressEntry to sAddressType If (sAddressType = 'SMTP') Begin Get ComName of oAddressEntry to OutlookAddresses[iElement].sValue[iNameCol] Get ComAddress of oAddressEntry to OutlookAddresses[iElement].sValue[iAddressCol] Increment iElement End
The array is - as usual - an array of tDataSourceRow struct elements. The iNameCol and iAddressCol are integers and their value is retrieved via the piColumnId property of the individual columns of the selection list.Code:Send InitializeData OutlookAddresses
Disconnect OutlookCode:Get piColumnId of oNameColumn to iNameCol Get piColumnId of oAddressColumn to iAddressCol
If you connect to Microsoft Outlook it is fair to disconnect as well when you are ready so in the ClosePromptList message I send a ReleaseComObject message to the oOutlookApplication object.
Calling the Selection ListCode:Procedure ClosePromptList Forward Send ClosePromptList Send ReleaseComObject of oOutlookApplication End_Procedure
For the blog source code I decided the call the selection list from a Form object but you can of course take a different object type if you want. The prompt button mode is turned on, the prompt object is set so that the F4 key can also be used to popup the selection list.
If you think this is it... no you need to perform some extra actions. We do this in the Prompt_Callback event. In there we set the peUpdateMode and the properties to login to Microsoft Outlook.Code:Object oMailToForm is a Form Set Size to 13 242 Set Location to 5 50 Set Label to "To:" Set Label_Col_Offset to 2 Set Label_Justification_Mode to JMode_Right Set Prompt_Object to oOutlookAddressesLookup Set Prompt_Button_Mode to PB_PromptOn End_Object
Code:Procedure Prompt_Callback Integer hPrompt Set psProfileName of hPrompt to 'Outlook' Set psPassword of hPrompt to '<my secret="" password="">' Set peUpdateMode of hPrompt to umPromptCustom Set phmPromptUpdateCallback of hPrompt to (RefProc (DisplaySelectedAddresses)) End_Procedure
Returning Data From the Selection List
The retrieval of the selected addresses is done in the method DisplaySelectedAddresses as you could see in the code above where I assigned the message ID of that routine to the property phmPromptUpdateCallBack. In the procedure we retrieve the selected addresses via the SelectedColumnValues message available in the cCJGridPromptList object.
Because the object used to select the addresses is a single value object (a Form) the addresses are appended to each other separated by a semi-colon.Code:Procedure DisplaySelectedAddresses Handle hoPrompt String[] sSelectedAddresses String sAddresses Integer iUpdateCol iElements iElement Get piUpdateColumn of hoPrompt to iUpdateCol Get SelectedColumnValues of hoPrompt iUpdateCol to sSelectedAddresses Move (SizeOfArray (sSelectedAddresses)) to iElements Decrement iElements For iElement from 0 to iElements If (iElement > 0) Begin Move (sAddresses + ';') to sAddresses End Move (sAddresses - sSelectedAddresses[iElement]) to sAddresses Loop Set Value to sAddresses End_Procedure
Conclusion
Another great enhancement was added to Visual DataFlex that makes it again easier to achieve your goal: Make a successful application for happy customers!</my>