VSTO for Outlook 2010 returning nothing instead of contact? - vb.net

I'm trying to create a right-click menu on the Outlook 2010 contacts view which returns all phone numbers for a contact
I have put in the following XML:
<contextMenu idMso="ContextMenuContactItem">
<button id="MyContextMenuContactItem"
imageMso="AutoDial"
label="Click to dial"
onAction="OnMyButtonClick"/>
</contextMenu>
And this correctly displays the right click item on the contacts list when I right click. I have so far added the following code to the onAction:
Public Sub OnMyButtonClick(ByVal control As Office.IRibbonControl)
Dim card As Office.IMsoContactCard = TryCast(control.Context, Office.IMsoContactCard)
If card Is Nothing Then
MsgBox("Nothing")
Else
MsgBox("We have a card")
End If
End Sub
The problem is here - and I always get nothing. I need to get 'Business Phone' for example ideally.

Here's some C# code that accesses the ContactItem object you've selected in the Contacts Folder. I realize yours is VB, but since they'll both have access to the same object model, it should be just a matter of changing the syntax.
public void OnMyButtonClick(Office.IRibbonControl control)
{
if (control.Context is Outlook.Selection)
{
Outlook.Selection selected = control.Context as Outlook.Selection;
var x = selected.GetEnumerator();
x.MoveNext();
if (x.Current is Outlook.ContactItem)
{
Outlook.ContactItem card = x.Current as Outlook.ContactItem;
Debug.Print(card.FirstName + "'s phone number: " + card.BusinessTelephoneNumber);
}
}
}

Related

Append to meeting invite body with or without Redemption

We are developing an Outlook VSTO add-in.
Right now I am trying to append some information to a meeting invite the user is in the process of composing. I want the content to appear in the body like what clicking the Teams-meeting button would do, where formatted text and links are appended to the end of the body.
Since the content is HTML and the Outlook Object Model does not expose an HTMLBody property for AppointmentItems, I try to set it via Redemption:
// Dispose logic left out for clarity but everything except outlookApplication and outlookAppointment is disposed after use
Application outlookApplication = ...;
AppointmentItem outlookAppointment = ...; // taken from the open inspector
NameSpace outlookSession = outlookApplication.Session;
RDOSession redemptionSession = RedemptionLoader.new_RDOSession();
redemptionSession.MAPIOBJECT = outlookSession.MAPIOBJECT;
var rdoAppointment = (RDOAppointmentItem)redemptionSession.GetRDOObjectFromOutlookObject(outlookAppointment);
string newBody = transform(rdoAppointment.HTMLBody); // appends content right before HTML </body> tag
rdoAppointment.BodyFormat = (int)OlBodyFormat.olFormatHTML;
rdoAppointment.HTMLBody = newBody;
Problem
The Outlook inspector window is not updating with the appended content. If I try to run the code again, I can see the appended content in the debugger, but not in Outlook.
Things I have tried:
Saving the RDOAppointmentItem
Also adding the content to Body property
Using SafeAppointmentItem instead of RDOAppointmentItem; didn't work because HTMLBody is a read-only property there
Setting PR_HTML via RDOAppointment.Fields
Paste the HTML via WordEditor (see below)
Attempt to use WordEditor
Per suggestion I also attempted to insert the HTML via WordEditor:
// Dispose logic left out for clarity but everything except inspector is disposed after use
string htmlSnippet = ...;
Clipboard.SetText(htmlSnippet, TextDataFormat.Html);
Inspector inspector = ...;
Document wordDoc = inspector.WordEditor;
Range range = wordDoc.Content;
range.Collapse(WdCollapseDirection.wdCollapseEnd);
object placement = WdOLEPlacement.wdInLine;
object dataType = WdPasteDataType.wdPasteHTML;
range.PasteSpecial(Placement: ref placement, DataType: ref dataType);
... but I simply receive the error System.Runtime.InteropServices.COMException (0x800A1066): Kommandoen lykkedes ikke. (= "Command failed").
Instead of PasteSpecial I also tried using PasteAndFormat:
range.PasteAndFormat(WdRecoveryType.wdFormatOriginalFormatting);
... but that also gave System.Runtime.InteropServices.COMException (0x800A1066): Kommandoen lykkedes ikke..
What am I doing wrong here?
EDIT: If I use Clipboard.SetText(htmlSnippet, TextDataFormat.Text); and then use plain range.Paste();, the HTML is inserted at the end of the document as intended (but with the HTML elements inserted literally, so not useful). So the general approach seems to be okay, I just can't seem to get Outlook / Word to translate the HTML.
Version info
Outlook 365 MSO 32-bit
Redemption 5.26
Since the appointment is being displayed, work with the Word Object Model - Inspector.WordEditor returns the Document Word object.
Per Dmitrys suggestion, here is a working solution that:
Shows the inserted content in the inspector window.
Handles HTML content correctly with regards to both links and formatting (as long as you stay within the limited capabilities of Words HTML engine).
using System;
using System.IO;
using System.Text;
using Outlook = Microsoft.Office.Interop.Outlook;
using Word = Microsoft.Office.Interop.Word;
namespace VSTO.AppendHtmlExample
{
public class MyExample
{
public void AppendAsHTMLViaFile(string content)
{
// TODO: Remember to release COM objects range and wordDoc and delete output file in a finally clause
Outlook.Inspector inspector = ...;
string outputFolderPath = ...;
string outputFilePath = Path.Combine(outputFolderPath, "append.html");
Word.Document wordDoc = inspector.WordEditor;
File.WriteAllText(outputFilePath, $"<html><head><meta charset='utf-8'/></head><body>{content}</body></html>", Encoding.UTF8);
Word.Range range = wordDoc.Content;
range.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
object confirmConversions = false;
object link = false;
object attachment = false;
range.InsertFile(fileName,
ConfirmConversions: ref confirmConversions,
Link: ref link,
Attachment: ref attachment);
}
}
}

Item_Load Event: Outlook.ActiveExplorer().Selection is not null but cannot be casted to Selection (throws Exception) why?

I try to develop an Outlook addin. I registered on the Item_Load event with this code:
Application.ItemLoad += Item_Load;
In the event handler I use this code:
[...]
Outlook.Explorer explorer = this.Application.ActiveExplorer();
if (explorer != null)
{
if (explorer.Selection == null)
{
return;
}
int count = explorer.Selection.Count;
if (count > 0)
{
Outlook.Selection selection = explorer.Selection[1];
This throws an Exception as the explorer.Select[1] does not deliver an Outlook.Selection? How is this possible as Outlook.Explorer.Selection[] is defined as:https://learn.microsoft.com/de-de/office/vba/api/outlook.explorer.selection so it should deliver a Selection?
The Selection property returns a Selection object that contains the item or items that are selected in the explorer window. So the code should like this:
Outlook.Selection selection = explorer.Selection;
But if you use an indexer you get an instance of Outlook items:
Outlook.MailItem mail = explorer.Selection[1] as Outlook.MailItem;
Remember that the ItemLoad event provides an instance of the loaded Outlook item as a parameter to the event handler. So, there is no need to get the currently selected items.

Looking for a specific tag

I had a previous question regarding a way to access a proxy card from within a web page when using Chrome. I'm now attempting to instead use a vb.Net application with an embedded CefSharp object. I have the code I need to access the proxy card (thanks to Smart Card API), but I need an easy way to indicate that this is even an option. My thought is to:
Put an otherwise empty element on the web page (such as <div id='smartcard' />)
Inside Visual Basic, monitor the contents of the page for this <div />
If the <div /> is found, make sure the card reader is detected. If so, add some text (and maybe an image) to its contents indicating the the card can be scanned
Once a card scan is detected, put the value from the card into a form element and POST it
It seems likely to me that I'm going to have to use some combination of JavaScript and vb.net code, but I'm so new to CefSharp that I really have no idea where to start.
Thanks in advance for all your help.
Not being a C# programmer, I looked at the information on the General Usage guide many times and still didn't really understand it. That said, I think I've been able to get this project off the ground. In addition to the CefSharp project, I'm also using the non-free Smart Card API from CardWerk.
Below is some snippets of what I did.
VB.Net
Imports CefSharp
Imports Subsembly ' For the SmartCard namespace
Class MainWindow
Private WithEvents CardManager As SmartCard.CardTerminalManager
Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
browser.Address = "https://jake-dev7.local/trainingmatrix/"
Debug.Print(SmartCard.SMARTCARDAPI.API_VERSION)
CardManager = SmartCard.CardTerminalManager.Singleton
CardManager.Startup(True)
End Sub
Private Sub browser_LoadingStateChanged(sender As Object, e As LoadingStateChangedEventArgs) Handles browser.LoadingStateChanged
Dim script As String
If Not e.IsLoading Then
If CardManager.SlotCount Then
script = "if ($('#proxcard').length) { proxcard_init() }"
browser.GetMainFrame().ExecuteJavaScriptAsync(script)
End If
End If
End Sub
Protected Sub InsertedEvent(ByVal aSender As Object, ByVal aEventArgs As SmartCard.CardTerminalEventArgs) Handles CardManager.CardInsertedEvent
Dim aCard As SmartCard.CardHandle
Dim nActivationResult As SmartCard.CardActivationResult
Dim iFacilityCode As Integer
Dim iCardID As Integer
' There's a bunch of code here taken from the sample code that came
' with the SmartCard API from CardWerk to pull the facility code and
' card id out of the prox card.
If iFacilityCode <> 0 And iCardID <> 0 Then
Dim script As String
script = "if ($('#proxcard').length) { proxcard_scan(" & iFacilityCode & ", " & iCardID & ") }"
browser.GetMainFrame().ExecuteJavaScriptAsync(script)
End If
End Sub
End Class
JavaScript
(This is in a .js file that is loaded by the web page. This page can also be loaded in Chrome, Firefox, IE, etc and these functions will never be run which keeps this utility usable for computers that don't have the custom .exe and card reader).
// These proxcard_* functions are called via our parent application
// (CefSharp object embeded in a vb.Net assembly)
function proxcard_init() {
$('#proxcard').html("<div class='or'>- OR -</div><div><img src='proxcard.jpg'><br>Scan your card</div>");
}
function proxcard_scan(facilityID, cardID) {
var vars = {
facilityID: facilityID,
cardID: cardID
};
if ($('form#adduser').length) {
// We're on the add user page. Check to see if this card matches somebody.
$.post('httprequest.php?type=get-emp-from-prox', vars, function(data) {
if (data && data.number) {
// Update UI and backend form fields. If everything validates, submit the form
} else {
// Clear UI and backend form fields that pertain to user ID
alert('Card not found');
}
}, 'json');
} else if ($('form#update').length) {
// Deal with the update form
}
}
In my actual code, I have multiple else if statements for dealing with different forms where I allow a card to be scanned. They are not included to keep this from getting out of hand :).
Please Note: This is not intended to be the entire project or all the code needed to process prox cards using CefSharp. My hope is that it will be enough to help somebody else.

Infopath 2010 > is multiple selection list box allowed for Browser

I may be missing something. I have a Multiple Selection List box in a Section. See attached pic. When I right click on the grpApplications and copy XPath it gives me /my:myFields/my:grpAccessOfficeEquipment/my:grpApplications when I right click for XPath on the fldApplicatins it gives me /my:myFields/my:grpAccessOfficeEquipment/my:grpApplications/my:fldApplications
In the code (code posted below) I am referencing the /my:myFields/my:grpAccessOfficeEquipment/my:grpApplications/my:fldApplications and I get no value. but I used /my:myFields/my:grpAccessOfficeEquipment/my:grpApplications I get a value for selected item in the listbox.
But I am still getting error NullReferenceException "object reference not set to an instance of an object" on string appName line. The AppID looks like this "\n\t\t\t262265264143\n\t\t" instead of whole number in debug. but when I click on the binocular is shows 262265264143 but with spaces/lines before and after this
XPathNavigator MainDS = MainDataSource.CreateNavigator();
XPathNodeIterator iterApp = MainDS.Select("/my:myFields/my:grpAccessOfficeEquipment/my:grpApplications", NamespaceManager);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
while (iterApp.MoveNext())
{
string AppID = iterApp.Current.Value;
//int lookupID = Convert.ToInt16(value);
XPathNavigator secAPPDS = DataSources["SupportEscalationList"].CreateNavigator();
string appName = secAPPDS.SelectSingleNode("/dfs:myFields/dfs:dataFields/d:SharePointListItem_RW[#ID = '" + AppID + "']/#ApplicationProcess", NamespaceManager).Value;
sb.Append(appName);
item["ApplicationProcess"] = sb.ToString(); // because applicationprocess column in target sp list is single text type column
}
Says here multiple selection list boxes are supported in 2010 browser forms but not 2007 browsers forms. Hope that helps.
http://office.microsoft.com/en-us/infopath-help/infopath-2010-features-unavailable-in-web-browser-forms-HA101732796.aspx

VSTO - Outlook 2007 - Display form before send message?

I'm new to Outlook add-in programming and not sure if this is possible:
I want to display a pop-up form (or selection) and ask for user input at the time they click Send. Basically, whenever they send out an email (New or Reply), they will be asked to select a value in a dropdown box (list items from a SQL database, preferrably).
Base on their selection, a text message will be appended to the mail's subject.
I did my research and it looks like I should use Form Regions but I'm not sure how can I display a popup/extra form when user click Send.
Also, it looks like Form Regions can be used to extend/replace current VIEW mail form but can I use it for CREATE NEW form?
Thanks for everybody's time.
You can probably add the Item Send event handler in the ThisAddIn Internal Startup method and then in the Item Send Event, call the custom form (a windows form).
In the below sample I call a custom windows form as modal dialog before the email item is send and after the send button is clicked.
private void InternalStartup()
{
this.Application.ItemSend += new ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel)
{
if (Item is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem currentItem = Item as Microsoft.Office.Interop.Outlook.MailItem;
Cancel = true;
Forms frmProject = new ProjectForm();;
DialogResult dlgResult = frmProject.ShowDialog();
if (dlgResult == DialogResult.OK)
System.Windows.Forms.SendKeys.Send("%S"); //If dialog result is OK, save and send the email item
else
Cancel = false;
currentItem.Save();
currentItem = null;
}
}