I had been working on this issue lately where customer was using Outlook 2003 to create custom actions. It is not very commonly used thing so you might not know about it, at least I was not very much aware about it.
To do that, you have to open a form in design mode and go to Actions tab, hit the New action button down in the bottom left corner.
Give it the name of your choice and select “Appointment” type form from “Standard Forms” template. It will look something like this..
Publish this form and send it to your self.
Once sent, it will come up like this to your inbox, see the “CustomAction” button (this is the name that you provided)
Once you click this button, it should open a new Appointment form like when you do a forward or reply button, but instead of doing that it shows an error.
Error: "Could not complete the operation. One or more parameter values are not valid"
I found this problem to be related with build number Outlook 2003 11.8313.xxxx (SP3) and it does not reproduce with the standard install of Outlook 2003 SP3 (build 11.8169.8172).
So who caused it? Looks like that a latest security update has caused this issue.
Security Update for Microsoft Office Outlook 2003 (KB973705)
I have tried to uninstall this update and it started to work just fine, installed it back and it breaks again.
Since Outlook 2003 is out of commercial support lifecycle, you may or may not get an hotfix for this. If you have a business justification and this issue is causing trouble to you then please do not hesitate to contact me.
If we ever decide to fix it, then I will update this post and update you all.
PS.: The only possible workaround, though highly not recommended, is to uninstall the security update but be aware of the risks of doing so.
Happy Debugging!!!
I have been working lately with this customer who was trying to create new signature files under %APPDATA%\Microsoft\Signatures folder which will be used by Outlook 2007 (SP2) but what we discovered is that whenever they try to use it, it used to crash Outlook.
This was the content of the HTML Signature file that we were using.
<HTML>
<BODY>
<IMG SRC="http://www.server.com/logo.jpg" />
</BODY>
</HTML>
Let’s save this HTML file to %APPDATA%\Microsoft\Signatures\CrashMe.htm
Now next thing you need to do is make sure that Outlook’s Mail Format is HTML
Next you need to make sure that “Typing replaces selected text” is unchecked under Tools->Options->Mail Format->Editor Options->Advanced
Once you done it all, now we load a new email item and insert our signature into the body and just try to save the message, and it will crash the Outlook.
Root Cause:
I am not very much sure but its not Outlook’s fault at all, Outlook is just a victim and its Word libraries at fault which are used to Edit the email message.
Workaround:
Since there is no solution at this moment, here are your workarounds.
1) Create the signature file using Outlook’s Signature Designer
2) Do not use HTML as default format for emails, either use RTF or Text
3) Do not uncheck the ‘Typing replaces selected text’ option, keep it selected
The word team is working on it and I shall update you all when I have some more information on this.
Happy Debugging.
I am not talking about some Friday night dinner or the latest buff around the corner. I am talking about the “Include” tab on Outlook 2007’s Ribbon UI.
Recently I got this case where customer developed a custom email form but he was not able to use the “Include” tab to add attachments to the email, as you can see from the following screenshot.
All the buttons were grayed-out, but why? Customer had no clue, well that’s why he called Microsoft, isn’t it?
I tried to reproduce it locally, but first I opened up the custom form which customer was using and the initial discovery was that there was no message body included to it. That ring some bells to me, but I ignored that for the moment and tried to dig if there is any property that I can set to disable that Include tab but there were none. I also checked if there was any code behind, but there was none.
Now that I know what was present in form, and what was not, I decided to start from scratch and created a new form. And discovered the following.
The issue here is that the customer has deleted the standard “Message” field (which is used to write the email content) from the message, this field is also used to insert attachments, business cards, calendar & other stuff by Outlook.
Repro Steps
1) Open a new email inspector
2) Go to Developers Tab
3) Hit Design Mode
4) Delete the Message body
5) Run the Form and you will see the “Include” as grayed out.
Good now we know that was the culprit, but I do not need that message body on my form.. how do I fix it?
Workaround
1) We need to add the Message Body back to the form
2) Open the form in Design Mode again and drag & drop the “Message” back to the form
3) Now you might not want that “Message Body” to be visible, but if you set the “Message Body” visible state
to false then you will again not be able to add attachments as the body is not visible.
4) Another workaround, you need to keep the visible state as visible but make body’s height & width set to 0 to hide it from screen
Bingo!! That was it.. yeah now it works.
..but what a minute..why?
Overall, that's a general design decision they made a long time ago. That first page of the message form has a lot of dependencies on commands, etc. So once you do a "simple" thing like delete the body control, you run some big risks. It's up to the form developer to really scope all of that out, disable other commands if they need to, etc.
Happy Debugging!!!
It’s been 3 very long and cherished years of my life as I complete my 3 years of career at Microsoft India GTSC, based out of Bangalore, India.
A Bachelor in Computer Sciences was just a degree I acquired to certify my geek level. I was much more interested in practical knowledge than theoretical and decided to work a freelance along during my final years of graduation. After 4 years of working in different roles in IT industry, as an independent consultant, system administrator , business analyst or software engineer, I got the opportunity to work with the Industry leader and decided to join Microsoft India GTSC & Developer Messaging Team in May 2006 as Support Engineer.
Joining Microsoft was the beginning of my career in the real sense of the word, working with great minds & mentors brought me up to this level. Exchange has always been my favorite, seeing it grow from 5.5 to E14 and being part of this growth brings me immense pleasure. I am very excited to take up the role of Support Escalation Engineer and hoping to continue my legacy of providing the best and exceeding the limits.
Happy Coding Debugging!!!
First off there is no justification why you should be using Cdosys directly, you should use System.Web.Mail if you are still on .Net 1.1 otherwise you should upgrade to System.Net.Mail.
Anyways, the problem I want to mention is when you use CDOSYS.dll directly from .Net 1.1 or 2.0 or even 3.5, the problem is with memory management.
Take this sample code..
Function SendMail()
Dim iMsg As New CDO.Message
With iMsg
.To = "test@server.com"
.From = "test@server.com"
.Subject = "Hello, This is a test mail !"
.HTMLBody = "<b>Hi</b>, <i>This is a test <u>HTML</u> mail !</i>"
.AddAttachment("d:\attachment.txt")
End With
iMsg = Nothing
End Function
Looks short and sweet like there is nothing wrong with this, think again!
This code is holding the reference to File Handle and not going to release it even after you set iMsg = Nothing
The solution here is to release the COM Object (CDOSYS) properly by calling GC.Collect() followed by GC.WaitForPendingFinalizers() but outside of this code block.
Now consider this sample…
Function SafeSendMail()
SendMail()
Gc.Collect()
GC.WaitForPendingFinalizers()
End Function
Function SendMail()
Dim iMsg As New CDO.Message
With iMsg
.To = "test@server.com"
.From = "test@server.com"
.Subject = "Hello, This is a test mail !"
.HTMLBody = "<b>Hi</b>, <i>This is a test <u>HTML</u> mail !</i>"
.AddAttachment("d:\attachment.txt")
End With
iMsg = Nothing
End Function
This will make sure that the CDOSYS is collected as soon as you leave the SendMail block otherwise you won’t be able to access the file for writing/deleting it.
Happy Debugging!!!
First thing first, you should understand that this is NOT SUPPORTED & NOT RECOMMENDED solution by Microsoft.
This is just a proof of concept that OWA 2007 can be auto login, if you know the username & password of target account.
The approach is simple and took me little reverse engineering or OWA’s login page. Owa login page being HTML does a form submit and post the username/password along with few hidden variables and upon receiving those parameters owaauth.dll generate a cookie, assign it to the session and redirect you to your mailbox. How easy, you may say.. but how you can submit those parameters programmatically.
This is how its done…
<script>
function LoginToOWA (server,domain,username,password) {
var url = "https://" + server + "/exchweb/bin/auth/owaauth.dll";
var p = {destination:'https://' + server + '/exchange',flags:'0',forcedownlevel:'0',trusted:'0',isutf8:'1',username:domain + '\\' + username,password:password};
var myForm = document.createElement("form");
myForm.method="post" ;
myForm.action = url ;
for (var k in p) {
var myInput = document.createElement("input") ;
myInput.setAttribute("name", k) ;
myInput.setAttribute("value", p[k]);
myForm.appendChild(myInput) ;
}
document.body.appendChild(myForm) ;
myForm.submit() ;
document.body.removeChild(myForm) ;
}
</script>
<body onload="javascript:LoginToOWA('owa.exchange.com','domain','username','password');">
<h3>Please wait while redirecting to OWA...</h3>
</Body>
Happy Coding!!!
First thing first, this is just for demonstration purpose and might *not* be a supported solution by Microsoft. The setting we are reading from are hidden messages and not documented anywhere. I had to reverse engineer it for demonstration purpose.
Objective: To get OOF State of any user in your organization.
Challenges: You might ask me why not use GetUserOofSettings to query user’s OOF status with the help of Exchange Impersonation? This is due to the limitation of EWS’s GetUserOofSettings/SetUserOofSettings methods as they don’t support Impersonation.
Workaround: Directly reading the hidden messages from Mailbox Root where this Flag & OOF message is stored. Since we are read-only we should be good to do so, that’s my assumption.
We need to first make a FindItem call to the mailbox root and search for a message with ItemClass = “IPM.Microsoft.OOF.UserOOFSettings”. Assuming that there will be only one message with that message class at any given time, we get its ItemId and make a GetItem call to read the message body. The message body is plain text XML with following structure
For OOF enabled…
<UserOofSettings>
<OofState >Enabled</OofState>
</UserOofSettings>
For OOF disabled…
<UserOofSettings>
<OofState >Disabled</OofState>
</UserOofSettings>
There is also a state called SCHEDULED, please refer to the below mentioned article to know more about it
Reference: http://msdn.microsoft.com/en-us/library/aa580465.aspx
This code sample can do the job for you.
using System;
using GetOOFState.MyEWS;
namespace GetOOFState
{
class Program
{
static void Main(string[] args)
{
ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.Url = "https://server/ews/exchange.asmx";
esb.Credentials = new System.Net.NetworkCredential("ServiceAccount", Password", "Domain");
esb.ExchangeImpersonation = new ExchangeImpersonationType();
esb.ExchangeImpersonation.ConnectingSID = new ConnectingSIDType();
esb.ExchangeImpersonation.ConnectingSID.PrimarySmtpAddress = "username@domain.com";
FindItemType fit = new FindItemType();
fit.ItemShape = new ItemResponseShapeType();
fit.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
fit.Restriction = new RestrictionType();
IsEqualToType itemMessageClass = new IsEqualToType();
PathToUnindexedFieldType msgClassProp = new PathToUnindexedFieldType();
msgClassProp.FieldURI = UnindexedFieldURIType.itemItemClass;
ConstantValueType msgClassValue = new ConstantValueType();
msgClassValue.Value = "IPM.Microsoft.OOF.UserOOFSettings";
itemMessageClass.Item = msgClassProp;
itemMessageClass.FieldURIOrConstant = new FieldURIOrConstantType();
itemMessageClass.FieldURIOrConstant.Item = msgClassValue;
fit.Restriction = new RestrictionType();
fit.Restriction.Item = itemMessageClass;
DistinguishedFolderIdType rootFolder = new DistinguishedFolderIdType();
rootFolder.Id = DistinguishedFolderIdNameType.root;
fit.ParentFolderIds = new BaseFolderIdType[] { rootFolder };
FindItemResponseType firt = esb.FindItem(fit);
string itemID = ((ArrayOfRealItemsType)((FindItemResponseMessageType)firt.ResponseMessages.Items[0]).RootFolder.Item).Items[0].ItemId.Id;
GetItemType git = new GetItemType();
ItemIdType iit = new ItemIdType();
iit.Id = itemID;
git.ItemIds = new BaseItemIdType[] { iit };
git.ItemShape = new ItemResponseShapeType();
git.ItemShape.BaseShape = DefaultShapeNamesType.IdOnly;
PathToUnindexedFieldType []propsToFetch = new PathToUnindexedFieldType[1];
propsToFetch[0] = new PathToUnindexedFieldType();
propsToFetch[0].FieldURI = UnindexedFieldURIType.itemBody;
git.ItemShape.AdditionalProperties = propsToFetch;
GetItemResponseType girt = esb.GetItem(git);
ItemType itemOOF = ((ItemType)(((ItemInfoResponseMessageType)girt.ResponseMessages.Items[0]).Items.Items[0]));
System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();
xDoc.LoadXml(itemOOF.Body.Value);
Console.WriteLine("OOF State: {0}", xDoc.GetElementsByTagName("OofState")[0].InnerText);
}
}
}
Happy Coding!!!
This is going to be a very simple sample of how you can send an email with custom properties set and also change the message class so that it render on outlook as Custom Form.
Prerequisites: A custom form must be published with same class name to render the email properly.
How it works…
Step 1) It drop an email in the drafts folder of user
Step 2) Patches the properties to set Message Class & other properties, even including with special characters in name like Spaces and ‘/’
Step 3) Drop the mail to DavMailSubmissionURL to send it
Code:
Imports System.Net
Imports System.IO
Imports System.Web
Module Module1
Const strServer As String = "Exchange_Server_Name"
Const strMailbox As String = "Target_Mailbox"
Const strFrom As String = "from@domain.com"
Const strTo As String = "to@domain.com"
Const strSubject As String = "Custom form email - sent using WebDAV"
Const strBody As String = "<B>Hello There,</B><BR> Hope you are doing good.<BR>Good Bye!!!"
Const strMessageClass As String = "IPM.Note.TestMessageClass"
Const strDomain As String = "DomainNameHere"
Const strUsername As String = "UsernameToLoginWith"
Const strPassword As String = "Password"
Dim objCDO As New CDO.Message
Dim strMIME As String
Dim sQuery As String = ""
Dim sUri As Uri = New Uri("http://" & strServer & "/Exchange/" + strMailbox & "/Drafts/" & Guid.NewGuid().ToString() & ".eml")
Dim sDavSubmissionURI As Uri = New Uri("http://" & strServer & "/Exchange/" + strMailbox & "/%23%23DavMailSubmissionURI%23%23/")
Dim DavRequest As HttpWebRequest = DirectCast(WebRequest.Create(sUri), HttpWebRequest)
Dim myCred As New NetworkCredential((strDomain & "\" & strUsername), strPassword)
Dim myCredentialCache As New CredentialCache()
Dim ByteQuery As Byte()
Dim QueryStream As Stream
Dim DavResponse As HttpWebResponse
Dim iStatCode As Integer
Dim sStatus As String
Dim strm As Stream
Dim sr As StreamReader
Dim sText As String
Sub Main()
Console.WriteLine("Preparing message...")
PrepairMessage()
Console.WriteLine("Creating message in draft folder...")
CreateMessageInDraft()
Console.WriteLine("Updating message class & other properties...")
SetMessageProperties()
Console.WriteLine("Sending message...")
SendMessage()
Console.WriteLine("Message Sent")
End Sub
Sub PrepairMessage()
With objCDO
.From = strFrom
.To = strTo
.Subject = strSubject
.HTMLBody = strBody
strMIME = .GetStream.ReadText
End With
sQuery = strMIME
End Sub
Sub CreateMessageInDraft()
myCredentialCache.Add(sUri, "Basic", myCred)
DavRequest.Credentials = myCredentialCache
DavRequest.KeepAlive = False
DavRequest.Headers.Set("Translate", "f")
DavRequest.ContentType = "message/rfc822"
DavRequest.ContentLength = sQuery.Length
'Set the request timeout to 5 minutes.
DavRequest.Timeout = 300000
' Set the request method.
DavRequest.Method = "PUT"
' Store the data in a byte array.
ByteQuery = System.Text.Encoding.UTF8.GetBytes(sQuery)
DavRequest.ContentLength = ByteQuery.Length
QueryStream = DavRequest.GetRequestStream()
' write the data to be posted to the Request Stream
QueryStream.Write(ByteQuery, 0, ByteQuery.Length)
QueryStream.Close()
QueryStream = Nothing
' Send the request and get the response.
DavResponse = DirectCast(DavRequest.GetResponse(), HttpWebResponse)
' Get the Status code.
iStatCode = CInt(DavResponse.StatusCode)
sStatus = iStatCode.ToString()
Console.WriteLine("Status Code: " & sStatus.ToString())
' Read the response stream.
strm = DavResponse.GetResponseStream()
sr = New StreamReader(strm)
sText = sr.ReadToEnd()
Console.WriteLine("Response: " & sText)
' Close the stream.
strm.Close()
End Sub
Sub SetMessageProperties()
Dim sPropPatch As String = "<?xml version=""1.0""?>" + _
"<g:propertyupdate xmlns:g=""DAV:"">" + _
"<g:set>" + _
"<g:prop>" + _
"<d:outlookmessageclass xmlns:d=""http://schemas.microsoft.com/exchange/"">" & strMessageClass & "</d:outlookmessageclass>" + _
"<NewCustomProperty>" & "CustomProperty1" & "</NewCustomProperty>" + _
"</g:prop>" + _
"</g:set>" + _
"</g:propertyupdate>"
DavRequest = Nothing
DavResponse = Nothing
DavRequest = DirectCast(WebRequest.Create(sUri), HttpWebRequest)
DavRequest.Credentials = myCredentialCache
DavRequest.KeepAlive = False
DavRequest.Headers.Set("Translate", "f")
DavRequest.ContentType = "text/xml"
DavRequest.ContentLength = sQuery.Length
'Set the request timeout to 5 minutes.
DavRequest.Timeout = 300000
' Set the request method.
DavRequest.Method = "PROPPATCH"
' Store the data in a byte array.
ByteQuery = System.Text.Encoding.UTF8.GetBytes(sPropPatch)
DavRequest.ContentLength = ByteQuery.Length
QueryStream = DavRequest.GetRequestStream()
' write the data to be posted to the Request Stream
QueryStream.Write(ByteQuery, 0, ByteQuery.Length)
QueryStream.Close()
QueryStream = Nothing
' Send the request and get the response.
DavResponse = DirectCast(DavRequest.GetResponse(), HttpWebResponse)
' Get the Status code.
iStatCode = CInt(DavResponse.StatusCode)
sStatus = iStatCode.ToString()
Console.WriteLine("Status Code: " & sStatus.ToString())
' Read the response stream.
strm = DavResponse.GetResponseStream()
sr = New StreamReader(strm)
sText = sr.ReadToEnd()
Console.WriteLine("Response: " & sText)
' Close the stream.
strm.Close()
DavResponse = Nothing
DavRequest = Nothing
End Sub
Sub SendMessage()
DavRequest = Nothing
DavResponse = Nothing
DavRequest = DirectCast(WebRequest.Create(sUri), HttpWebRequest)
DavRequest.Credentials = myCredentialCache
'Set the request timeout to 5 minutes.
DavRequest.Timeout = 300000
' Set the request method.
DavRequest.Method = "MOVE"
DavRequest.Headers.Add("Destination", sDavSubmissionURI.ToString())
' Send the request and get the response.
DavResponse = DirectCast(DavRequest.GetResponse(), HttpWebResponse)
' Get the Status code.
iStatCode = CInt(DavResponse.StatusCode)
sStatus = iStatCode.ToString()
Console.WriteLine("Status Code: " & sStatus.ToString())
' Read the response stream.
strm = DavResponse.GetResponseStream()
sr = New StreamReader(strm)
sText = sr.ReadToEnd()
Console.WriteLine("Response: " & sText)
' Close the stream.
strm.Close()
DavResponse = Nothing
DavRequest = Nothing
End Sub
End Module
Note: I will update the sample to include custom properties with special characters soon… last edited: 5/23/09 4:19 AM
Good news for all .Net developers working with Exchange or willing to work with Exchange but have zero experience in the domain.
Exchange Team had been working so hard to reduce your efforts and improve your efficiency.
Check out these videos to see what EWS Managed API is capable of
http://channel9.msdn.com/pdc2008/BB46/
http://msexchangeteam.com/archive/2009/03/24/450892.aspx
Keep a watch on here as it will go live in few hours, be the first one to grab it - http://msdn.microsoft.com/exchange
I have been working on different SearchFolders cases lately where customers were trying to create SearchFolder using EWS. Working on those issues has uncovered a limitation of Outlook (I tested it on 2007, but may exist with earlier versions as well)
I created the search folder using the following XML
<?xml version="1.0"?>
<CreateFolderType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ParentFolderId xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<DistinguishedFolderId Id="searchfolders" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</ParentFolderId>
<Folders xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<SearchFolder xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
<DisplayName>Employee Search</DisplayName>
<SearchParameters Traversal="Deep">
<Restriction>
<And>
<IsNotEqualTo>
<ExtendedFieldURI DistinguishedPropertySetId="PublicStrings" PropertyName="EmployeeID" PropertyType="String" />
<FieldURIOrConstant>
<Constant Value="" />
</FieldURIOrConstant>
</IsNotEqualTo>
<Exists>
<ExtendedFieldURI DistinguishedPropertySetId="PublicStrings" PropertyName="CustomPropertyName" PropertyType="String" />
</Exists>
</And>
</Restriction>
<BaseFolderIds>
<DistinguishedFolderId Id="inbox" />
</BaseFolderIds>
</SearchParameters>
</SearchFolder>
</Folders>
</CreateFolderType>
I have created an email with custom property and it has some value in there to meet the criteria. The search folder is created and visible in the Outlook but I still do not see any entries under that folder. I also tried with just "Exists" criteria for the custom property but still it does not list the item. Then I created another SearchFolder with specific words in subject and itemClass = IPM.NOTE and that works fine… surprised!!
I went ahead and tried to test the Search Folder from OWA, to further add to my surprise it was not even listed in OWA… strange!! So then I dig into support.microsoft.com and found the following KB - http://support.microsoft.com/kb/831400
Symptoms: When you create Search Folders in Microsoft Office Outlook 2007 and Microsoft Office Outlook 2003in a Microsoft Exchange profile, the Search Folders do not appear in Microsoft Outlook Web Access.
Cause: This behavior occurs when you use Outlook in Cached Exchange Mode. The local copy of your mailbox maintains a Finder folder. This folder is not synchronized with the Finder folder that is in your Microsoft Exchange mailbox.
Resolution: Switch to online mode, expand search folders, let it sync with server and then you should be able to see.
I followed the resolution steps and now I can see the items in OWA & also in Outlook… relief!!
Happy Debugging…
Here is a quick sample if you are looking at creating a search folder using EWS which will look for Custom Property.
Here we are looking for a custom property named “EmployeeID” and make sure that the property exist and does not have a blank value.
public static BaseFolderType CreateCustomPropSearchFolder()
{
//Base folder type
FolderIdType folderID = new FolderIdType();
//create the request that will create a new searchfolder under the finder directory
CreateFolderType folderType = new CreateFolderType();
// inherited type from FolderType
SearchFolderType[] folderArray = new SearchFolderType[1];
folderArray[0] = new SearchFolderType();
folderArray[0].SearchParameters = new SearchParametersType();
// Go a Deep traversal and search every folder inside parent folder
folderArray[0].SearchParameters.Traversal = SearchFolderTraversalType.Deep; // deep traversal
folderArray[0].SearchParameters.TraversalSpecified = true; // must set it to true otherwise traversal will have no effect
//Define the base folder to search under, in this case its Inbox
folderArray[0].SearchParameters.BaseFolderIds = new DistinguishedFolderIdType[1];
DistinguishedFolderIdType dType = new DistinguishedFolderIdType();
dType.Id = new DistinguishedFolderIdNameType();
dType.Id = DistinguishedFolderIdNameType.inbox; // we are looking at Inbox only
folderArray[0].SearchParameters.BaseFolderIds[0] = dType;
folderArray[0].SearchParameters.Restriction = new RestrictionType();
PathToExtendedFieldType CustomPropPath = new PathToExtendedFieldType();
CustomPropPath.DistinguishedPropertySetId = DistinguishedPropertySetType.PublicStrings;
CustomPropPath.DistinguishedPropertySetIdSpecified = true;
CustomPropPath.PropertyName = "EmployeeID";
CustomPropPath.PropertyType = MapiPropertyTypeType.String;
ExistsType existsType = new ExistsType();
existsType.Item = CustomPropPath;
// First part where the
IsNotEqualToType isNotEqualType = new IsNotEqualToType();
isNotEqualType.FieldURIOrConstant = new FieldURIOrConstantType();
//Actual value of property that we are looking at
ConstantValueType customPropertyValue = new ConstantValueType();
customPropertyValue.Value = string.Empty;
isNotEqualType.FieldURIOrConstant.Item = customPropertyValue;
isNotEqualType.Item = CustomPropPath;
// Create a AND EXPRESSION & add two condition to it
AndType andTypeExpressions = new AndType();
andTypeExpressions.Items = new SearchExpressionType[] {isNotEqualType,existsType };
folderArray[0].SearchParameters.Restriction.Item = andTypeExpressions;
folderArray[0].DisplayName = "Employee Search"; // Give your search folder a unique name
folderType.Folders = folderArray;
TargetFolderIdType targetFolder = new TargetFolderIdType();
//Create the searchfolder under the Finder Folder
DistinguishedFolderIdType searchFolder = new DistinguishedFolderIdType();
searchFolder.Id = DistinguishedFolderIdNameType.searchfolders; // Saving it under searchfolders root
targetFolder.Item = searchFolder;
folderType.ParentFolderId = targetFolder;
// Uncomment the following lines of code to make it a hidden search folder, can be consumed by other programs and not visible to users
// folderArray[0].ExtendedProperty = new ExtendedPropertyType[1];
// folderArray[0].ExtendedProperty[0] = new ExtendedPropertyType();
// folderArray[0].ExtendedProperty[0].ExtendedFieldURI = new PathToExtendedFieldType();
// folderArray[0].ExtendedProperty[0].ExtendedFieldURI.PropertyTag="0x10F4"; //PR_ATTR_HIDDEN
// folderArray[0].ExtendedProperty[0].ExtendedFieldURI.PropertyType = MapiPropertyTypeType.Boolean;
// folderArray[0].ExtendedProperty[0].Item = "true";
//Create the search folder
CreateFolderResponseType createFolderResponse = esb.CreateFolder(folderType);
//Return the newly created search folder
FolderInfoResponseMessageType folderInfo = (FolderInfoResponseMessageType)createFolderResponse.ResponseMessages.Items[0];
return folderInfo.Folders.Length == 0 ? null : folderInfo.Folders[0];
}
Ever copied a DLL from one system to another and registered it using RegSvr32.exe? or in other words ever created a DLL hell situation? Its easy and dirty at the same time to copy-paste DLL between systems and register them using regsvr32.exe
Often you copy-paste, register DLL from temporary folders or may be desktop and then later delete the DLL or move to other folder without unregistering/re-registering it. Stop right there!!! You are creating a problem for yourself.
Every time you register a DLL, if its self-registering DLL, will create entries in System’s Registry with its current location, ProgID, CLSID along with other information. This information is used by programs when they are doing late binding to find the DLL and load in memory.
If you move/delete a DLL without unregistering/re-registering the DLL you are leaving with an entry behind in the Registry which is pointing to “Invalid Path”. In this case any program which is using that DLL will fail and you may not realize what’s happening.
As a first step in a “Could not load <Image>/Error loading <Image>” error scenario I would always go to registry and verify that the DLL is registered properly. It could be tedious task and risky as well. You may just edit a registry entry by mistake and break other functionality as well.
I have come up with a VBScript which gives you the ease to quickly verify the Image path & current version on the basic of ProgID or CLSID. This script will prompt you to enter either ProgID or CLSID and give you the desired information.
Here we are looking for PROGID of CDOSYS.DLL
…and here are the results.
Similarly you could use CLSID to do reverse lookup for ProgID & Image Path

Here is the code that I used in this script…
On Error Resume Next
Dim strProgId
Dim strDescription
Dim strCLSID
Dim strInProcServer
Dim strCurVer
Dim strCurVerCLSID
Dim strCurVerInProcServer
Dim strVersionIndependentProgID
Dim oShell
Set oShell = CreateObject("WScript.Shell")
Dim strOutput
strProgId = UCase(Trim(InputBox("Enter the ProgID/CLSID:","ProgID Tool")))
If Len(strProgId)<=0 Then
Msgbox "Invalid ProgID/CLSID"
Else
If Left(strProgId,1) = "{" Then strProgId = oShell.RegRead("HKEY_CLASSES_ROOT\CLSID\" & strProgId & "\ProgID\")
strDescription = oShell.RegRead("HKEY_CLASSES_ROOT\" & strProgId & "\")
strCLSID = oShell.RegRead("HKEY_CLASSES_ROOT\" & strProgId & "\CLSID\")
strCurVer = oShell.RegRead("HKEY_CLASSES_ROOT\" & strProgId & "\CurVer\")
strInProcServer = oShell.RegRead("HKEY_CLASSES_ROOT\CLSID\" & strCLSID & "\InprocServer32\")
strVersionIndependentProgID = oShell.RegRead("HKEY_CLASSES_ROOT\CLSID\" & strCLSID & "\VersionIndependentProgID\")
strOutput = strOutput & "ProgID: " & strProgId & vbCrLf
strOutput = strOutput & "Description: " & strDescription & vbCrLf
strOutput = strOutput & "Current Version: " & strCurVer & vbCrLf
strOutput = strOutput & "VersionIndependentProgID: " & strVersionIndependentProgID & vbCrLf & vbCrLf
strOutput = strOutput & strProgId & " CLSID: " & strCLSID & vbCrLf
strOutput = strOutput & strProgId & " InProcServer: " & strInProcServer & vbCrLf & vbCrLf
If (Len(strCurVer)>0) Then
strCurVerCLSID = oShell.RegRead("HKEY_CLASSES_ROOT\" & strCurVer & "\CLSID\")
strCurVerInProcServer = oShell.RegRead("HKEY_CLASSES_ROOT\CLSID\" & strCurVerCLSID & "\InprocServer32\")
strOutput = strOutput & strCurVer & " CLSID: " & strCurVerCLSID & vbCrLf
strOutput = strOutput & strCurVer & " InProcServer: " & strCurVerInProcServer & vbCrLf
End If
Msgbox strOutput,,"ProgId Tool"
End If
Happy debugging….
This is my first attempt to write a Powershell sample to my readers. In this sample we will give Send-As, Receive-As, ms-Exch-Store-Admin rights to an active directory user. We will be piping two commands here… Get-MailboxServer and the result will go to Add-AdPermission.
You may give same permissions using Exchange Power Shell Window
| C:\>Get-MailboxServer –Identity MSGEX07 | Add-AdPermission –User UserA –AccessRights GenericRead, GenericWrite –ExtendedRights Send-As, Receive-As, ms-Exch-Store-Admin |
Here is the sample…
using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
namespace PowershellSample
{
class Program
{
static void Main(string[] args)
{
RunspaceConfiguration config = RunspaceConfiguration.Create();
PSSnapInException warning;
// Load Exchange PowerShell snap-in.
config.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out warning);
if (warning != null) throw warning;
using (Runspace thisRunspace = RunspaceFactory.CreateRunspace(config))
{
try
{
thisRunspace.Open();
using (Pipeline thisPipeline = thisRunspace.CreatePipeline())
{
//Please change parameter values.
Command cmdGetMailbox = new Command("Get-MailboxServer");
Command cmdAddAdPermission = new Command("Add-AdPermission");
cmdGetMailbox.Parameters.Add("Identity", @"MSGEX07");
cmdAddAdPermission.Parameters.Add("User", "UserA");
cmdAddAdPermission.Parameters.Add("AccessRights", new string[] { "GenericRead", "GenericWrite" });
cmdAddAdPermission.Parameters.Add("ExtendedRights", new string[] { "Send-As", "Receive-As", "ms-Exch-Store-Admin" });
thisPipeline.Commands.Add(cmdGetMailbox);
thisPipeline.Commands.Add(cmdAddAdPermission);
try
{
thisPipeline.Invoke();
}
catch (Exception exx)
{
Console.Write("Error: " + exx.ToString());
}
// Check for errors in the pipeline and throw an exception if necessary.
if (thisPipeline.Error != null && thisPipeline.Error.Count > 0)
{
StringBuilder pipelineError = new StringBuilder();
pipelineError.AppendFormat("Error calling cmdLets...");
foreach (object item in thisPipeline.Error.ReadToEnd())
{
pipelineError.AppendFormat("{0}\n", item.ToString());
}
throw new Exception(pipelineError.ToString());
}
}
}
finally
{
thisRunspace.Close();
}
}
}
}
}
I have seen lots of confusion lately and people asking this question in various forums. I do not have an official statement yet on this and waiting for a response from Exchange SDK documentation owners.
Here is my understanding, I may be wrong here.
According to http://www.microsoft.com/downloadS/details.aspx?familyid=47720D33-FA5A-4AF4-A8EB-FA39695CBAD1&displaylang=en, the Exchange 2007 SP1 SDK does not list Windows 2008 as supported platform.
It does not says it’s NOT supported but it also does not mention that whether its supported or not. Usually something which is NOT documented as “not supported” is assumed to be supported :-)
Let’s take an example of “Exchange 2007 SP1” - http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=44c66ad6-f185-4a1d-a9ab-473c1188954c
The download page does not mention that it’s supported on Windows 2008 while it is the only version of Exchange so far which is supported on Windows 2008
Source: http://technet.microsoft.com/en-us/library/bb232170.aspx
Exchange 2007 and Windows Server 2008
The RTM version of Exchange 2007 cannot be installed on Windows Server 2008. However, Exchange 2007 SP1 is supported for installation on Windows Server 2008. Although you cannot install the RTM version of Exchange 2007 on Windows Server 2008, the RTM version of Exchange 2007 will be supported for use with Windows Server 2008 directory servers.
Exchange Server 2003 Service Pack 2 (SP2) is similarly supported. Exchange Server 2003 cannot be installed on a computer running Windows Server 2008, but Exchange 2003 will be supported for use with Windows Server 2008 directory servers.
Be aware of the following when combining Microsoft Exchange with Windows Server 2008:
- Exchange 2007 RTM and all previous versions of Microsoft Exchange are not supported for installation on a computer running Windows Server 2008.
- Exchange 2007 SP1 can be installed on a computer running Windows Server 2008.
- Management tools for Exchange 2007 RTM and all previous versions of Microsoft Exchange are not supported for installation on a computer running Windows Server 2008 or Windows Vista.
- Management tools for Exchange 2007 SP1 can be installed on a computer running Windows Server 2008 or Windows Vista.
With all that into consideration, it is safe to assume that if you are running with Exchange Sp1 Management Tools on a Windows 2008 machine you are under support boundaries to install and use SDK. I will update this document once I have more information along with facts & confirmation from documentation team.
Stay tuned…
EDIT: UPDATED ON 3/18/2009
I have got confirmation from product group that its a document bug and nothing more or less. So we DO support Exchange 2007 SP1 SDK on Windows Server 2008.