Can anyone tell me how to read the custom Field value of the outlook using c#
Right now i tried "UserProperties" and "ItemProperties". Both are throwing error. My sample code as follows
Microsoft.Office.Interop.Outlook.Application f = new Microsoft.Office.Interop.Outlook.Application();
NameSpace outlookNS = f.GetNamespace("MAPI");
MAPIFolder inboxFolder = outlookNS.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
foreach (object obj in inboxFolder.Items)
{
MailItem item = obj as MailItem;
if (item != null)
{
Console.WriteLine(item.UserProperties["test"].Value);
Console.WriteLine(item.ItemProperties["test"].Value);
}
}
Thanks in advance
This answer has been rewritten following experiments with Outlook.
My C# is good enough for me to know what you are doing but I have not tried to access Outlook from C# yet.
Both Items and Item are used within the Outlook model. I do not know it you can use item in the way you are attempting.
UserProperties would throw an error if user property "test" did not exist. Below I show how to test for existence. Are you adding a user property and forgetting to save the amended mail item?
The following shows access to user properties from Outlook VBA. I would expect the InterOp model to be as similar as the syntax allows. Important differences of which I know:
Set is not required with C#.
Nothing is the VBA equivalent of null.
Variables:
FolderItem is an item within a folder that has been tested to be of class olMail.
UserProp is of type UserProperty.
InxUP is of type integer.
The following adds a user property with name "TestProp" and type olText, sets its value to "Value for TestProp" and saves the amended mail item. Without the Save, the previous statements have no effect.
With FolderItem
Set UserProp = .UserProperties.Add("TestProp", olText)
UserProp.Value = "Value for TestProp"
.Save
End with
The following outputs to the immediate window the name and value of every user property against the mail item.
For InxUP = 1 To .UserProperties.Count
Debug.Print " User prop " & InxUP & _
.UserProperties(InxUP).Name & " " & _
.UserProperties(InxUP).Value
Next
The following checks that user property "TestProp" exists and, if so, outputs its value to the immediate window.
Set UserProp = .UserProperties.Find("TestProp")
If Not UserProp Is Nothing Then
Debug.Print " TestProp " & .UserProperties("TestProp").Value
End If
Hope this helps
This reply might be a bit late for you. But I came across the same problem as the original poster (need to extract values from User-Defined Fields (Custom fields) of Outlook).
Someone somewhere shared a VBA macro which did the job. So I went around looking for a way to execute Outlook macro VBA in powershell. As it turns out Outlook stopped supporting /autorun of macro after version 2003 SP2. I nearly switched from Outlook 2019 to Outlook 2003 just for the /autorun. Then it occurred to me Outlook 2003 isn't as good with showing multiple calendars side by side as Outlook 2019. So I decided to keep going with 2019 afterall. Cut long story short, I massaged the VBA macro to run in Powershell and it worked for me. I basically added in a few dollar signs in front of things, and removed some stuff here and there, and then it just worked. I hope it works for everyone else too who is still looking for answer:
Function Get-ContactUDF
{
Add-Type -assembly "Microsoft.Office.Interop.Outlook" -ErrorAction Stop -ErrorVariable "OutlookError"
$Outlook = New-Object -comobject Outlook.Application -ErrorAction stop -ErrorVariable "ApplicationError"
$objNameSpace = $Outlook.GetNameSpace("MAPI")
$objContacts = $objNameSpace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderContacts)
$objContact = $objContacts.Items.Find("[FirstName] = ""John""")
$objProperty = $objContact.UserProperties.Find("Favorite Character in Lord of the Rings")
$objProperty.Value
}
Just in case the code is not clear. This powershell function will look for the Contact whose FirstName is "John", and then find the value under the custom field called "Favorite Character in Lord of the Rings" and print out the value (for example Frodo). To run it, ofcourse you just do this under powershell:
Get-ContactUDF
By the way if you just want to print values from default fields (those built-in fields that came with Outlook eg. Firstname, Surname, Birthday, phone number, spouse name, title etc), then it's like the following:
Function Get-OutlookContacts
{
Add-Type -assembly "Microsoft.Office.Interop.Outlook" -ErrorAction Stop -ErrorVariable "OutlookError"
$Outlook = New-Object -comobject Outlook.Application -ErrorAction stop -ErrorVariable "ApplicationError"
$namespace = $Outlook.GetNameSpace("MAPI")
$contactObject = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderContacts)
}
Call this function by just typing:
Get-OutlookContacts
in Powershell
THIS WILL LIST EVERYTHING IN THE CONTACTS DATABASE:
$contactObject.Items
THIS WILL LIST ALL OBJECTS FIRSTNAME, LASTNAME, BIRTHDAY, EMAILADDRESS, MOBILE IN THE CONTACTS DATABASE:
$contactObject.Items | Select-Object -Property FirstName, LastName, Birthday, Email1Address, MobileTelephoneNumber
ISSUE this next command to grab objects and list only for where the firstname object equals Rambo:
$contactObject.Items | Select-Object -Property FirstName, LastName, Birthday, Email1Address, MobileTelephoneNumber | where-object {$_.Firstname -eq "Rambo" }
Hope these examples are clear. I use Outlook 2019 on Windows 10 totally offline mode (no cloud, no 365). If you use 365 then the code above probably needs modifications.
I also remember other people asking for a way to extract Outlook Calendar events using Powershell. If I come across that question again I'll post the answer. Basically the same drill. I read some VB code from somewhere and then turned it into Powershell script. You guys can do it yourself if you like. Just add in dollar signs and remove the DIM lines.
Related
I am simply trying to connect to a different SharePoint site. This code works perfectly with a specific site but when using a different site with the correct permissions to the site and site list, I receive the following error:
format-default: The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.
If the user has full control on the SharePoint list as well as the SharePoint site, why isn't the connection initialized?
I have stepped through the code line by line to see what is being stored to each variable and find that the code throws the error at the following line:
$sharepointlistitems = Get-ListItems -Context $Context -ListTitle $ListName
Here is the code from start to finish for accessing the site. The commented out site works perfectly with the code as is:
[Reflection.Assembly]::LoadFile(([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client").location))
[Reflection.Assembly]::LoadFile(([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.runtime").location))
$admincreds = Get-Credential
$SPCredentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($admincreds.UserName, $admincreds.Password)
######### Create Sharepoint List Functions ######################################################################
#Function to get items that are not processed
Function Get-ListItems([Microsoft.SharePoint.Client.ClientContext]$Context, [String]$ListTitle) {
$list = $Context.Web.Lists.GetByTitle($ListTitle)
$qry = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery()
#In future query by status to avoid slowing down of program
$items = $list.GetItems($qry)
$Context.Load($items)
$Context.ExecuteQuery()
return $items
}
#Function to update item status
Function Set-ListItemByID([Microsoft.SharePoint.Client.ClientContext]$Context, [String]$ListTitle, [int]$ItemId, [string]$Status) {
$list = $Context.Web.Lists.GetByTitle($ListTitle)
$listitem = $list.GetItemById($ItemId)
$listitem["AutoAccountCreationStatus"] = $Status
$listitem.Update();
$Context.ExecuteQuery()
}
########################## Step 1: Read from SharePoint List and add to an array ###########################################
#Sets Site, sets list of interest, creates array
#"https://<sitename>.sharepoint.com/sites/Home/TeamSite"
$SiteUrl = "https://<sitename>.sharepoint.com/sites/InfTech"
$ListName = "New Users"
#Setup the context
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
$Context.Credentials = $SPCredentials
#Getting items from list
$sharepointlistitems = Get-ListItems -Context $Context -ListTitle $ListName
$sharepointlistitems = $sharepointlistitems | where {$_['AutoAccountCreationStatus'] -eq 'Received'}```
I use Power GUI Script editor to debug PowerShell script usually, we could monitor the variables, it's helpful for troubleshooting.
Or, you could use PnP PowerShell instead,demo
I am trying get some C# to attach to an open reply-email (triggered manually by user), on the already running instance of Outlook (opened manually by user). The code should identify the open reply email, edit the subject line and body of the email and send the email.
The problem is that I get as far as identifying the running instance of Outlook and assigning it to an object using one of the Marshal methodsoutApp = Marshal.GetActiveObject("Outlook.Application") as Application, but then I cannot cast it to a MailItem type in order to manipulate its elements e.g. the subject line, body, etc...something like MailItem mailItem = (MailItem)outApp.CreateItem((OlItemType.olMailItem)); throws an invalid cast exception at runtime.
Apologies if I am wrong, but could not find a single example close to this exact sequence of events, one of the closer ones is this post c# outlook open existing instance and reply to email
but then it goes a whole different way. There are tons of posts on how to use the Microsoft.Office.Interop.Outlook to OPEN and then use an instance of Outlook, but hardly anything (that I could find) on how to use an open instance. Any help is appreciated, thank you.
EDIT 08102019:
The code is used from an RPA platform, so there is no risk of it being picked up as malware. The "user" is just a virtual user on an account with purpose-made permissions and a controlled environment...sorry, nothing dark here :-). Anyway, here is the code I am using at the moment which creates a new instance and saves it to drafts in Outlook. It is not what I set out to do, as I explained above, this is just a temporary fix:
OutlookApp outlookApp = new OutlookApp();
MailItem mailItem = (MailItem)outlookApp.CreateItem(OlItemType.olMailItem);
mailItem.To = "test#test.com";
mailItem.Subject = "Test Email Generation";
mailItem.HTMLBody = "<html><body>This is the body of the email.</strong>.<br/> This is another line in the body of the email.</body></html>";
mailItem.Display(false);
System.Threading.Thread.Sleep(3000);
mailItem.Close(OlInspectorClose.olSave);
Marshal.ReleaseComObject(outlookApp);
To get the opened mail item in the inspector window you need:
Use the ActiveInspector method to get an instance of the Inspector class.
The Inspector.CurrentItem property returns an Object representing the current item being displayed in the inspector.
Set any properties like Subject, Body, Recipients and etc.
To get the inline response in the Explorer window you need to use the Explorer.ActiveInlineResponse property which returns an item object representing the active inline response item in the explorer reading pane.
I'm trying to develop a snippet in C # code that enables the "voting option" function of Outlook.
This code will be used by a platform called Blue Prism.
The "vote" function of Outlook is in the Microsoft.Office.Interop.Outlook namespace, so I need to import it using C#, but I dont have enough knowledge to develop this.
I tried to do something like this but it is giving an error.
Here is the code:
public class program {
[DllImport(#"C:\Program Files\Blue Prism Limited\Blue Prism Automate\Microsoft.Office.Interop.Outlook.dll", EntryPoint = "VotingOptions")]
public static extern string Outlook(uint type);
static void Main()
{
// Create the Outlook application.
Outlook.Application oApp = new Outlook.Application();
oApp.VotingOption = "Yes; No";
}
}
So, can someone help me?
The VotingOptions property belongs to the MailItem class , not Outlook Application. Voting options on messages are used to give message recipients a list of choices and to track their responses. To create voting options programmatically, set a string that is a semicolon-delimited list of values for the VotingOptions property of a MailItem object. The values for the VotingOptions property will appear under the Vote command in the Respond group in the ribbon of the received message.
private void OrderPizza()
{
Outlook.MailItem mail = (Outlook.MailItem)Application.CreateItem(
Outlook.OlItemType.olMailItem);
mail.VotingOptions = “Cheese; Mushroom; Sausage; Combo; Veg Combo;”
mail.Subject = “Pizza Order”;
mail.Display(false);
}
Also you may find the C# app automates Outlook (CSAutomateOutlook) sample project helpful, it shows how to automate Outlook in C#.
Com objects aren't accessed through DLLImport. They're accessed using references. From the sample Eugene linked:
Create a Console application and reference the Outlook Primary Interop Assembly (PIA). To reference the Outlook PIA, right-click the project file and click the "Add Reference..." button. In the Add Reference dialog, navigate to the .NET tab, find Microsoft.Office.Interop.Outlook 12.0.0.0 and click OK.
Now you'll have access to the Microsoft.Office.Interop.Outlook object.
If you are using Blue Prism then rather than having to specify DLL references you may also chose to go the GetObject or CreateObject way, you will be able to interact with Outlook just like Blue Prism does with Excel. The drawback of this approach is that you have to use VB.NET (unless I am mistaken) and that you will not be able to use text representation of enum values (so for OlItemType you will not be able to use olMailItem but only its numeric value, which is 0).
Please note that Blue Prism has released a new version recently (6.3) and with it a new VBO for interaction with Outlook. It's nothing revolutionary, but it may provide some insight.
Is it possible to determine the Exchange Server ItemID for a MailItem (the selected Item in the active explorer)? The solution I am working on has an Outlook AddIn component and another component that accesses mail items through EWS.
I have code similar to the below in my Outlook addin:
Outlook.Explorer ActiveExplorer = Globals.ThisAddIn.Application.ActiveExplorer();
object selectedItem = ActiveExplorer.Selection[1];
Outlook.MailItem selectedEmail = selectedItem as Outlook.MailItem;
In this way I can access certain properties of the email but it is important to the workings of the overall solution that the property values are exactly the same as those returned by EWS. For example, if the property returned a time, it would be important that the time matched down to the millisecond.
If I had the ItemID I could bind to and work with the Item (from within the addin) using something like the below.
Item myItem = Item.Bind(MyExchangeService, MyItemID);
On a whim I have tried binding to MailItem.EntryID but I got a malformed ID error (which didn't surprise me). I have been trying to determine if the Exchange ID was available through MailItem.PropertyAccessor.GetProperty but I am not really familiar with accessing properties in this way and haven't had any luck so far.
Thoughts?
I came across the following Stack Overflow post which didn't exactly answer my question but changed my focus to converting the EntryID into the EWS ID rather than finding the EWS ID.
Exchange ItemID differs from GlobalAppointmentID for Outlook AddIn
With this new angle I was able to find the following site which directly addressed my issue.
https://bernhardelbl.wordpress.com/2013/04/15/converting-entryid-to-ewsid-using-exchange-web-services-ews/
I have posted the code here in full in case the link gets broken.
string ConvertHexEntryIdToEwsId(ExchangeService esb, string sID, string strSMTPAdd)
{
AlternateId objAltID = new AlternateId();
objAltID.Format = IdFormat.HexEntryId;
objAltID.Mailbox = strSMTPAdd;
objAltID.UniqueId = sID;
AlternateIdBase objAltIDBase = esb.ConvertId(objAltID, IdFormat.EwsId);
AlternateId objAltIDResp = (AlternateId)objAltIDBase;
return objAltIDResp.UniqueId;
}
I wish to create multiple mailcontacts (external Contacts) in the GAL in Microsoft Online by running Powershell command from C#. The code below works, but is very slow and takes about 15-20 min to run for 400 mailcontacts.
foreach(EmailAdressVM emailAddressVM in emailList.emailAddresses1)
{
//Create New MailContact.
Pipeline pplNewMailContact = runspace.CreatePipeline();
Command cmdNewMailContact = new Command("New-MailContact");
cmdNewMailContact.Parameters.Add("Name", emailAddressVM.sExternalEmailAddress);
cmdNewMailContact.Parameters.Add("Displayname", emailAddressVM.sFullName.Trim());
cmdNewMailContact.Parameters.Add("Lastname", emailAddressVM.sLastName.Trim());
cmdNewMailContact.Parameters.Add("Firstname", emailAddressVM.sFirstName.Trim());
cmdNewMailContact.Parameters.Add("ExternalEmailAddress", emailAddressVM.sExternalEmailAddress.Trim());
pplNewMailContact.Commands.Add(cmdNewMailContact);
pplNewMailContact.Invoke();
pplNewMailContact.Stop();
pplNewMailContact.Dispose();
}
I am guessing that this is slow since I create a new Pipeline for every new mailcontact that is added and there has to be a more eficient way of doing this since running...
import-csv <filename> | ForEach {
new-mailcontact -name $_.emailaddress -displayname $_.FullName -lastname $_.lastname -firstname $_.firstname -externalemailaddress $_.emailaddress -alias $_.alias
}
...is much faster.
I have found some references after many hours of searching the web that you can do something similar to using a CSV when running Powershell commands from C#, i.e. send a list (or array) of values to a command (in this case the "new-mailcontact" command). But, I have not found any good example of how to send more than one value to a command and I need to supply many values (for example: -name $.emailAddress -displayname $.FullName, etc.) to the "new-mailcontact" command.
Is it possible to send a list (or array) in a similar way as the "import-csv" command (when using regular powershell) and will this be faster, or is there an evan better way? Would I get better performance if I use Powershell 3 instead of 1 (as I am using now).
Please provide working sample code i C#!
Please note that I cannot save a CSV file to disk and the execute powershell from CMD since I do not have write access to disk and that I do not think that I can run an entire script remotely (since remote scripting probably is disabled on Exchange Online).
The biggest reason I would think is because for each address you are creating a new Powershell instance and you are not multithreaded.
You code looks something like this from above:
Foreach email address{
Declare a new Powershell process
Add attributes to call later
Start Powershell and pipe stuff in
Close Powershell instance
}
I think you would be better off creating the Powershell instance / pipe once and then sending each object into it. More along the lines of:
Create PS Pipe
Foreach email address{
PS.SendArguments(Email, Name, DN, etc.);
}
I am not in an environment to get something working or tested right now, so hopefully this gives you at least most of what you need...