Back to Basics: var != Dim June 25, '08 Comments [36] Posted in Back to Basics | Programming | VB Sponsored By Someone said today after seeing the C# var operator for implicitly typed variables, "Oh, now C# has Dim." Well, not exactly. Not even close. I like what this C# tutorial says about var. ...[var is] a new keyword that means, "I want to declare a variable, but I’m too lazy to write out its type." One way to look at the power of VB's Dim operator is to say, Dim kind of means, "I want to declare a variable but I can't tell you much about how it behaves until much later." Dim lets you do actual late-binding while in C# (today) you do late-binding with reflection. I wanted to find a way to clearly express this in a very visceral sample. A friend wanted to change a value in the CustomDocumentProperties in Word and see that change reflected in an updated field. In the document properties dialog below, you can see there's a "CustomProperty1" that has the value of "Scott" in it. Then in the document, there's a Field that is bound to that property. It's not meant to be a mail merge, but more of a MadLibs kind of a thing for writing applications that update forms and templates within Word or Excel documents. His language of choice is C#, so he started off in C#. He added a reference to the Microsoft.Office.Interop.Word PIA (Primary Interop Assembly) and fought with the system for some hours. After a while, I got enlisted, and after I figured out that the specific COM interface he needed was a late-bound IDispatch interface, we were able to crack with the Reflection. I'm starting to think of Reflection much the way I think about Regular Expressions. If you have to solve your Problem with Reflection, you may just end up with Problems, plural! Notice a few things. First, the need for some of those obvious strings and booleans to be of type Object. Notice all the System.Reflection.Missing.Values passed by reference. Most of all, notice the custom GetCustomPropertyValue and SetCustomPropertyValue that had to use Reflection. ApplicationClass WordApp = new ApplicationClass(); WordApp.Visible = true; object missing = System.Reflection.Missing.Value; object readOnly = false; object isVisible = true; object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc"); Microsoft.Office.Interop.Word.Document aDoc = WordApp.Documents.Open( ref fileName,ref missing,ref readOnly,ref missing, ref missing,ref missing,ref missing,ref missing, ref missing,ref missing,ref missing,ref isVisible, ref missing,ref missing,ref missing,ref missing); aDoc.Activate(); string propertyValue = GetCustomPropertyValue(aDoc, "CustomProperty1"); SetCustomPropertyValue(aDoc, "CustomProperty1", "Hanselman"); foreach (Range r in aDoc.StoryRanges) { r.Fields.Update(); }}public string GetCustomPropertyValue(Document doc, string propertyName){ object oDocCustomProps = doc.CustomDocumentProperties; Type typeDocCustomProps = oDocCustomProps.GetType(); object oCustomProp = typeDocCustomProps.InvokeMember("Item", BindingFlags.Default | BindingFlags.GetProperty, null, oDocCustomProps, new object[] { propertyName }); Type typePropertyValue = oCustomProp.GetType(); string propertyValue = typePropertyValue.InvokeMember("Value", BindingFlags.Default | BindingFlags.GetProperty, null, oCustomProp, new object[] { }).ToString(); return propertyValue;}public void SetCustomPropertyValue(Document doc, string propertyName, string propertyValue){ object oDocCustomProps = doc.CustomDocumentProperties; Type typeDocCustomProps = oDocCustomProps.GetType(); typeDocCustomProps.InvokeMember("Item", BindingFlags.Default | BindingFlags.SetProperty, null, oDocCustomProps, new object[] { propertyName, propertyValue });} There's a great article from 7 (yes SE7EN) years ago on Dr. Dobb's about Invoking COM Components from C# that provided me this diagram. The RCW (Runtime Callable Wrapper) sits in front of the COM Object and makes my reflection calls work. Sure, I could have created some IDL and laid out an IDispatch implementation for these CustomDocumentProperties, but that's getting REALLY involved. Actually, there's supposed to be an implementation for CustomDocumentProperties but the MSDN Sample fails with this: "Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.Office.Core.DocumentProperties'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{2DF8D04D-5BFA-101B-BDE5-00AA0044DE52}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))." Competing with that non-working MSDN sample is this KB article from 2007 that provided the bulk of the yucky, but straightforward reflection code. Why is this a Back to Basics post? Well, two fold. First, COM is Old and it's Basic. Seriously, though, the secondly (and only) reason is that, in my opinion, C# 3.0 is lousy for this kind of late-bound, COM-interop, Office Automation work. Don't believe me? Here's the same code in VB. See the named parameters on the Open()? Notice the late-bound COM stuff just works without Reflection? (I've got Option Strict to Off for this code) Dim WordApp = New Microsoft.Office.Interop.Word.ApplicationClassWordApp.Visible = TrueDim fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\..\..\NewTest.doc")Dim aDoc As Document = WordApp.Documents.Open(FileName:=fileName, ReadOnly:=True, Visible:=True)aDoc.Activate()Dim PROP = "CustomProperty1"Dim propertyValue = aDoc.CustomDocumentProperties(PROP).ValueaDoc.CustomDocumentProperties(PROP).Value = "Hanselman"For Each r As Range In aDoc.StoryRanges r.Fields.Update()Next VB.NET is really well suited for this kind of thing, and my buddy will likely use it in this scenario. I hear this big difference in dynamism will change for the next version of C#. I'll talk to the team and try to get some details or rewrite my C# sample in C#.Next. I've also asked John Lam to help me write this sample in IronRuby. I suppose it'd look nice in PowerShell also. The Point is, and until some future date, var != Dim. The Back to Basics thing to remember is that the language you know might not always be suited for the problem you have. Just because you CAN solve a problem in a certain language doesn't mean that you should. If something REALLY doesn't feel right in a certain language, ask around, perhaps there's another that makes more sense for what you're trying to do. « The Weekly Source Code 29 - Ruby and Sho... | Blog Home | NotNorthwind - Update #1 - All Your Nort... » About Scott Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author. About Newsletter Sponsored By Hosting By