I recently wrote an Outlook plug-in (for use in Outlook 2010) for a client that syncs up their user's Outlook databases with a third-party application. This works fine. However, one thing that the client wants to do is to leverage standard copy/paste functionality to duplicate records in Outlook. The issue is that I define a UserProperty for any Outlook record that has been sent to the third-party system (it stores the internal ID from the third-party system). When the client performs a copy/paste, this UserProperty is also copied (which is bad as it creates multiple records with the same third-party ID). I was wondering if there was some way to detect via code that a copy/paste was occurring and to make a modification in the record (removal of this UserProperty) prior to save. As they might perform this on Tasks, Appointments, or Contacts, I would need guidance that would apply across the board.
Per the suggestions below, I attempted to leverage BeforeItemPaste, as seen below:
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
Globals.ThisAddIn.Application.ActiveExplorer().BeforeItemPaste += new Outlook.ExplorerEvents_10_BeforeItemPasteEventHandler(Item_BeforeItemPaste);
}
private void Item_BeforeItemPaste(ref System.Object ClipboardContent,
Microsoft.Office.Interop.Outlook.MAPIFolder Target, ref bool Cancel) {
System.Windows.Forms.MessageBox.Show("Trying to paste");
}
The message appears the very first time I do a copy/paste, and then never again. I tried leveraging Application.Explorers, which theoretically gives me access to all Explorers in Outlook, but that didn't give me access to the individual ones (Appointment, Task, Contact) that I was looking for (there was only one element in that 'array' when I debugged it). Also, I tried to access those specific Explorer items by invoking MAPIFolder.GetExplorer, but this always returned null.
Try to use Explorer.BeforeItemPaste event.
Related
I should create a C# application to manage the mail and attachments that arrive to me on Office 365 (Outlook).
In the app I would like to select from which domains you need to download the mail, based on this the app downloads only the related emails with attachments and shows them to me. This way I can decide what to print or not.
I need this app because I have to record all the projects that come to me from clients, architecture projects and therefore I need to divide everything according to the client.
Can you tell me what is the best way to develop this?
I mean if it is better to create VSTO for Outlook or something else or if there is other way. I would like to start with the right method.
I had thought about installing Outlook on the client, synchronized with Office 365, creating a VSTO that takes care of copying the interested emails (selecting just the domains of interest) and putting attachments in various folders, showing the attachments in an orderly manner and grouped.
Can you suggest me the best method?
I mean at the structural level (how to design system), and not at the code level (which I think I know it).
Thanks so much
You are right, you can develop a VSTO based add-in where you may handle the NewMailEx event of the Application class. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem.
The NewMailEx event fires when a new message arrives in the Inbox and before client rule processing occurs. You can use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item. Use this method with caution to minimize the impact on Outlook performance.
To save the attached files you can use the MailItem.Attachments property which returns an Attachments object that represents all the attachments for the specified item. Use the Attachment.SaveAsFile method which saves the attachment to the specified path.
If TypeName(myItem) = "MailItem" Then
Set myAttachments = myItem.Attachments
'Prompt the user for confirmation
Dim strPrompt As String
strPrompt = "Are you sure you want to save the first attachment in the current item to the Documents folder? If a file with the same name already exists in the destination folder, it will be overwritten with this copy of the file."
If MsgBox(strPrompt, vbYesNo + vbQuestion) = vbYes Then
myAttachments.Item(1).SaveAsFile Environ("HOMEPATH") & "\My Documents\" & _
myAttachments.Item(1).DisplayName
End If
Else
MsgBox "The item is of the wrong type."
End If
End Sub
See Walkthrough: Create your first VSTO Add-in for Outlook to get started quickly.
I have a PushButton in a revit API ribbon and would like to simulate a press on it in order to do some tests (I need a ExternalCommandData object from the currently active document). However I cannot seem to find anything like a PushButton.Click() function.
var panel = Application.CreateRibbonPanel("a", "b")
var buttonData = new PushButtonData(name, name, ApplicationInfo.AddInPath, "TestZone.Commands." + "DefaultCommand");
var button = panel.AddItem(buttonData) as PushButton;
With Application being of course the default UIControlledApplication on the OnStartup function. Anyway to know simulate a button click so that I can obtain an ExternalCommandData object of the currently opened document (In the final version there will be checks to ensure that a document is already open ext.) Or is there another way to obtain an externalCommandData?
Note that this question may require you to know the revit API, I doubt that just knowledge of c# will be enough to answer this.
I had many of the same issues with unit testing Revit - and the other users are right, there is no way to get an ExternalCommandData object without running a command. Fortunately, there's a framework that makes a lot of this possible by automating the startup and running of Revit externally.
https://github.com/DynamoDS/RevitTestFramework
The Dynamo group built this framework to automate their tests, and it offers a lot of great functionality.
Most pertinently for you, it actually exposes a valid ExternalCommandData object
Here is some example code from their framework.
/// <summary>
/// Using the TestModel parameter, you can specify a Revit model
/// to be opened prior to executing the test. The model path specified
/// in this attribute is relative to the working directory.
/// </summary>
[Test]
[TestModel(#"./bricks.rfa")]
public void ModelHasTheCorrectNumberOfBricks()
{
var doc = RevitTestExecutive.CommandData.Application.ActiveUIDocument.Document;
var fec = new FilteredElementCollector(doc);
fec.OfClass(typeof(FamilyInstance));
var bricks = fec.ToElements()
.Cast<FamilyInstance>()
.Where(fi => fi.Symbol.Family.Name == "brick");
Assert.AreEqual(bricks.Count(), 4);
}
RevitTestExecutive.CommandData offers the ExternalCommandData you are looking for.
Note that there's an issue with installing the RTF as an admin on your machine. I recommend installing it to a local directory as a local user so you don't run into Windows UAC issues.
There is no way to obtain an ExternalCommandData object without running a command.
You may be able to use UI Automation to programmatically click on your PushButton. It may be simpler to configure a keyboard shortcut for your command and use UI Automation to simulate the keypresses for you.
See Jeremy Tammik's blog for information on using UI Automation in Revit: http://thebuildingcoder.typepad.com/blog/automation/
You cannot simulate the click to launch an external command, and you cannot create a valid ExternalCommandData object yourself. Only Revit can do that when calling your event handler and providing it with a valid Revit API context in a call-back function, i.e., Revit event handler such as the external command Execute method.
You can however launch an external command from some other valid Revit API context, just as you can a built-in Revit command, using the PostCommand API:
http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.3
I am developing an outlook plugin that will be distributed among several users and I keep getting a message saying that "A program is trying to access e-mail address information stored in outlook."
Is there a way for me to disable this on every machine that downloads my plugin or disable it for my specific plugin? The program is written in c#
Many thanks
the message
Firstly, you should never get that prompt in an Outlook addin as long as you use the Outlook.Application object passed to your addin. Never create a new instance of that object.
To work around that prompt (not that you need to) see http://www.outlookcode.com/article.aspx?id=52 for the detailed list of your options.
I'm trying to run this code:
this.storage =
Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts)
.GetStorage("ws_up_storage", Outlook.OlStorageIdentifierType.olIdentifyByMessageClass);
It runs perfectly well on some machines, but on others it throws this exception:
I also tried other folder names, like olFolderInbox, olFolderCalendar. I've looked at Microsoft docs for this, and it says this error is to be expected if the folder is one of the following:
The folder is a Microsoft Exchange public folder, an Internet Message Access Protocol (IMAP), MSN Hotmail, or a Microsoft SharePoint Foundation folder.
The user permission for the folder is read-only.
The store provider does not support hidden items.
The sad fact is, I do not know how to check which one of these is true and if it's at all possible.
How would I go about determining the cause of the problem and addressing it?
The easiest way to handle all these cases is to try to get a storage. You will need much efforts to implement all cases. The PR_MDB_PROVIDER property allows to identify the store provider, but not all cases such as user permissions for the folder and etc. You can use the try/catch block where you may check the error code - 0x80040102 (MAPI_E_NO_SUPPORT).
However, you may use any low-level property viewers (for example, a free open source tool - MFCMAPI) for exploring Extended MAPI property values. Thus, you may find all the required info about not supported scenarious.
The error 0x80040102 is MAPI_E_NO_SUPPORT, which means the store does not support hidden (associated) items.
Read the PR_MDB_PROVIDER property (DASL name http://schemas.microsoft.com/mapi/proptag/0x34140102) using PropertyAccessor.GetProperty. The returned 16 byte array will be specific for each store kind. E.g. for an Exchange store, it will be pbExchangeProviderPrimaryUserGuid (5494A1C0297F101BA58708002B2A2517). Take a look at any object in the store in question using OutlookSpy (I am its author - click IMessage, IMAPIFolder, or IMsgStore) to see the PR_MDB_PROVIDER property.
I've been searching for a solution for a few days now, I've looked through the MSDN for Interop.Outlook and I think I've found what I need, but can't seem to implement it properly.
Here's the code I've came up with based on something similar I saw in VBA.
class Program
{
Stores allstores = new Stores();
Store store;
static void Main(string[] args)
{
foreach (var store in allstores)
{
MessageBox.Show(store.FilePath);
}
}
}
`
This essentially needs to cycle through a list of computers, and run this code on their outlook(some 2003, some 2007) in order to inventory all connected PST's in each outlook profile. I'm sure there's more code to this, but I can't get this portion to work at all. There seems to be a lack of information on inventorying Outlook data files, most of it is reading e-mails from the mailboxes and not the data file itself.
If someone could shed some light on what I'm overlooking, It'd be greatly appreciated.
EDIT:
I've actually made a working piece of code now, however I have a problem with compatibility. The program works as designed in Office 2010/2007, however it crashes when accessing a 2003 version. I imagine I need to use the Microsoft Office Object 11.0, however I only have Microsoft Office Object 12.0 listed - is there a way to get the 11.0 reference?
This may be of use, pretty thorough object model comparison and development guide.
There is no reason to actually log to any Outlook profiles (which might require an authentication prompt). All the information is already in the profile section in the registry. The exact location is Outlook version specific, and the profile section guids are generated randomly, so the documented profile management API (IProfAdmin etc.) is the way to go, but unfortunately it is Extended MAPI and requires C++ or Delphi.
As of Outlook 2007, Outlook Object Model exposes Namespace.Stores collection and Store.FilePath property, so you can loop through all stores and read the FilePath property for each store (be sure to filter out OST files).
Note that there can be multiple Outlook profiles (as shown in Control Panel | Mail | Show Profiles), but Outlook can only work with one profile at a time, so to use a different profile, you'd need to close Outlook.
If using Redemption is an option (I am its author), it includes ProfMan library (accessible in any language) which will let you extract all PST file locations from all local profiles without actually logging in.:
'Print the path to all the PST files in all profiles
PR_PST_PATH = &H6700001E
set Profiles=CreateObject("ProfMan.Profiles")
for i = 1 to Profiles.Count
set Profile = Profiles.Item(i)
set Services = Profile.Services
Debug.Print "------ Profile: " & Profile.Name & " ------"
for j = 1 to Services.Count
set Service = Services.Item(j)
If (Service.ServiceName = "MSPST MS") or (Service.ServiceName = "MSUPST MS") Then
'there should be only one provider for this service
'but we should really loop through all the providers
Debug.Print Service.Providers.Item(1).ProfSect.Item(PR_PST_PATH)
End If
next
next