MarkNethercott
asked on
VB. NET: Excel BuiltinProperties - DirectCast fails
The following fails with;
An unhandled exception of type 'System.InvalidCastExcepti on' occurred in resultsoutput.dll
Additional information: Specified cast is not valid.
In general terms the code is constructed as follows;
ModuleA
Public xlWb As Excel.Workbook
ModuleB
SubA
Dim prps As Microsoft.Office.Core.Docu mentProper ties
prps = DirectCast(xlWb.BuiltinDoc umentPrope rties, Microsoft.Office.Core.Docu mentProper ties)
This is using;
Framework 1.1
Excel 2002 SP3
Reference to COM Microsoft Excel 10.0 Object Library (Version 1.4)
An unhandled exception of type 'System.InvalidCastExcepti
Additional information: Specified cast is not valid.
In general terms the code is constructed as follows;
ModuleA
Public xlWb As Excel.Workbook
ModuleB
SubA
Dim prps As Microsoft.Office.Core.Docu
prps = DirectCast(xlWb.BuiltinDoc
This is using;
Framework 1.1
Excel 2002 SP3
Reference to COM Microsoft Excel 10.0 Object Library (Version 1.4)
ASKER
Thanks Tom. I've tried your suggestion & it still fails. With option strict on, the compiler doesn't allow late binding.
I've been following I've been following the article by Ken Getz
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_vsto2003_ta/html/ExcelObj.asp
which suggests the code should work as I've constructed it .
Ken's article refers to Excel 2003, but all the objects he references are also in the Microsoft Excel 10 COM object. I wondered whether my errors were caused by a bug in the 2002 COM or I've missed something in my code.
I've been following I've been following the article by Ken Getz
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_vsto2003_ta/html/ExcelObj.asp
which suggests the code should work as I've constructed it .
Ken's article refers to Excel 2003, but all the objects he references are also in the Microsoft Excel 10 COM object. I wondered whether my errors were caused by a bug in the 2002 COM or I've missed something in my code.
I think you will need to use Reflection to access the properties:
The following iterates through the BuiltInDocumentProperties collection and displays the name and value of each property in a MessageBox.
Dim objwb As Excel.Workbook
Dim objapp As Excel.Application
Dim result As Object
Dim val As String
Dim name As String
Dim count As Integer
Dim i As Integer
objapp = New Excel.Application()
objapp.Visible = True
objwb = objapp.Workbooks.Open("c:\ Test.xls")
'Get properties count
count = Int32.Parse(objwb.BuiltinD ocumentPro perties.Ge tType().In vokeMember ("Count", BindingFlags.GetProperty, Nothing, objwb.BuiltinDocumentPrope rties, New Object() {}).ToString)
For i = 1 To count '(Count property is not zero-based index)
'Get Document Property at specified index
result = objwb.BuiltinDocumentPrope rties.GetT ype().Invo keMember(" Item", BindingFlags.GetProperty, Nothing, objwb.BuiltinDocumentPrope rties, New Object() {i})
'Get Property Name
name = result.GetType().InvokeMem ber("Name" , BindingFlags.GetProperty, Nothing, result, New Object() {}).ToString
Try
'Get value for Property (some properties will throw an exception if you try to access their property)
val = result.GetType().InvokeMem ber("", BindingFlags.GetProperty, Nothing, result, New Object() {}).ToString
Catch ex As Exception
val = "None"
End Try
'Show Property name and Value
MsgBox(name.ToString & ": " & val.ToString)
Next
If you want to access specific Properties by name then replace the 'result = objwb.BuiltinDocumentPrope rties.GetT ype().Invo keMember(" Item", BindingFlags.GetProperty, Nothing, objwb.BuiltinDocumentPrope rties, New Object() {i})' line with something like the following:
result = objwb.BuiltinDocumentPrope rties.GetT ype().Invo keMember(" Item", BindingFlags.GetProperty, Nothing, objwb.BuiltinDocumentPrope rties, New Object() {"Author"})
Hope this helps.
Tom.
The following iterates through the BuiltInDocumentProperties collection and displays the name and value of each property in a MessageBox.
Dim objwb As Excel.Workbook
Dim objapp As Excel.Application
Dim result As Object
Dim val As String
Dim name As String
Dim count As Integer
Dim i As Integer
objapp = New Excel.Application()
objapp.Visible = True
objwb = objapp.Workbooks.Open("c:\
'Get properties count
count = Int32.Parse(objwb.BuiltinD
For i = 1 To count '(Count property is not zero-based index)
'Get Document Property at specified index
result = objwb.BuiltinDocumentPrope
'Get Property Name
name = result.GetType().InvokeMem
Try
'Get value for Property (some properties will throw an exception if you try to access their property)
val = result.GetType().InvokeMem
Catch ex As Exception
val = "None"
End Try
'Show Property name and Value
MsgBox(name.ToString & ": " & val.ToString)
Next
If you want to access specific Properties by name then replace the 'result = objwb.BuiltinDocumentPrope
result = objwb.BuiltinDocumentPrope
Hope this helps.
Tom.
ASKER
Thanks Tom for all your work, this is really useful.
I'm still finding my way through 'Reflection' and am struggling to take your code to the next level where I can set a value for a property.
I'm still finding my way through 'Reflection' and am struggling to take your code to the next level where I can set a value for a property.
ASKER
Here's my attempt so far;
The property I'm trying to set can have a variety of types, which is why I evaluate 'TheType'. Because I know the properties I'm trying to set in advance, I can make sure I pass a property value of the correct type.
Some properties are in the Buitlin set whilst others are in the custom set.
Calling routine ...
SetProperty(xlWb, "Comment", CommentString, False)
SetProperty(xlWb, "Company", "Company", False)
SetProperty(xlWb, "Client", Customer, True)
SetProperty(xlWb, "Date Completed", Now(), True)
SetProperty(xlWb, "Language", "English", True)
SetProperty(xlWb, "Owner", "ThisCompany", True)
SetProperty(xlWb, "Status", "Release", True)
SetProperty(xlWb, "Source", "Analysis Engine: " & GlobalVariableSet.analysis EngineNo, True)
SetProperty(xlWb, "Document Number", "1", True)
...
Sub SetProperty(ByVal xlWB As Excel.Workbook, ByVal PName As String, ByVal PValue As Object, _
ByVal isPropCustom As Boolean)
'This function sets the value of a property.
'The property is added if it does not exist.
'
'The parameters passed to the function are as follows:
'
'WorkbookName A string containing the name of the workbook whose property is to be set.
' If missing, the ActiveWorkbook is used.
'PName A string containing the name of the property
'PValue A variant containing the value of the property
'PropCustom A boolean indicating whether the property is a Custom Document Property.
Dim TheType As Long
Dim result As Object
Dim count As Integer
Dim name As String
If isPropCustom Then
count = Int32.Parse(xlWB.CustomDoc umentPrope rties.GetT ype().Invo keMember(" Count", BindingFlags.GetProperty, _
Nothing, xlWB.CustomDocumentPropert ies, New Object() {}).ToString)
Else
count = Int32.Parse(xlWB.BuiltinDo cumentProp erties.Get Type().Inv okeMember( "Count", BindingFlags.GetProperty, _
Nothing, xlWB.BuiltinDocumentProper ties, New Object() {}).ToString)
End If
Select Case VarType(PValue)
Case vbBoolean
TheType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeBo olean
Case vbDate
TheType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeDa te
Case vbDouble, vbLong, vbSingle, vbCurrency
TheType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeFl oat
Case vbInteger
TheType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeNu mber
Case vbString
TheType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeSt ring
Case Else
TheType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeSt ring
End Select
'Set Property
'xlWB.BuiltinDocumentPrope rties.GetT ype().Invo keMember(" Item", BindingFlags.SetProperty, _
Nothing, xlWB.BuiltinDocumentProper ties, New Object() {PName}) = PValue
End Sub
The property I'm trying to set can have a variety of types, which is why I evaluate 'TheType'. Because I know the properties I'm trying to set in advance, I can make sure I pass a property value of the correct type.
Some properties are in the Buitlin set whilst others are in the custom set.
Calling routine ...
SetProperty(xlWb, "Comment", CommentString, False)
SetProperty(xlWb, "Company", "Company", False)
SetProperty(xlWb, "Client", Customer, True)
SetProperty(xlWb, "Date Completed", Now(), True)
SetProperty(xlWb, "Language", "English", True)
SetProperty(xlWb, "Owner", "ThisCompany", True)
SetProperty(xlWb, "Status", "Release", True)
SetProperty(xlWb, "Source", "Analysis Engine: " & GlobalVariableSet.analysis
SetProperty(xlWb, "Document Number", "1", True)
...
Sub SetProperty(ByVal xlWB As Excel.Workbook, ByVal PName As String, ByVal PValue As Object, _
ByVal isPropCustom As Boolean)
'This function sets the value of a property.
'The property is added if it does not exist.
'
'The parameters passed to the function are as follows:
'
'WorkbookName A string containing the name of the workbook whose property is to be set.
' If missing, the ActiveWorkbook is used.
'PName A string containing the name of the property
'PValue A variant containing the value of the property
'PropCustom A boolean indicating whether the property is a Custom Document Property.
Dim TheType As Long
Dim result As Object
Dim count As Integer
Dim name As String
If isPropCustom Then
count = Int32.Parse(xlWB.CustomDoc
Nothing, xlWB.CustomDocumentPropert
Else
count = Int32.Parse(xlWB.BuiltinDo
Nothing, xlWB.BuiltinDocumentProper
End If
Select Case VarType(PValue)
Case vbBoolean
TheType = Microsoft.Office.Core.MsoD
Case vbDate
TheType = Microsoft.Office.Core.MsoD
Case vbDouble, vbLong, vbSingle, vbCurrency
TheType = Microsoft.Office.Core.MsoD
Case vbInteger
TheType = Microsoft.Office.Core.MsoD
Case vbString
TheType = Microsoft.Office.Core.MsoD
Case Else
TheType = Microsoft.Office.Core.MsoD
End Select
'Set Property
'xlWB.BuiltinDocumentPrope
Nothing, xlWB.BuiltinDocumentProper
End Sub
You're on the right lines:
To set the value of the Property you do need to use the SetProperty Binding flag, but you need to reference the DocumentProperty item in the DocumentProperties collection first and then set its Value property.
So:
result = xlWB.BuiltinDocumentProper ties.GetTy pe().Invok eMember("I tem", BindingFlags.GetProperty, _
Nothing, xlWB.BuiltinDocumentProper ties, New Object() {PName})
This returns the data type DocumentProperty, which you can then access its methods and properties, so the next line of code sets the property value:
result.GetType().InvokeMem ber("Value ", BindingFlags.SetProperty, Nothing, result, New Object() {pvalue})
(You don't need to specify "Value", as it is the default property - you can just pass a "")
If the invocation fails, then an exception will be thrown.
Tom.
To set the value of the Property you do need to use the SetProperty Binding flag, but you need to reference the DocumentProperty item in the DocumentProperties collection first and then set its Value property.
So:
result = xlWB.BuiltinDocumentProper
Nothing, xlWB.BuiltinDocumentProper
This returns the data type DocumentProperty, which you can then access its methods and properties, so the next line of code sets the property value:
result.GetType().InvokeMem
(You don't need to specify "Value", as it is the default property - you can just pass a "")
If the invocation fails, then an exception will be thrown.
Tom.
ASKER
This works perfectly for the BuiltinDocumentProperties, but I can't get it to work for the CustomDocumentProperties.
If I use your method to count the number of CustomDocumentProperties, I get a zero return (count=0). This suggest to me that although the custom properties are listed if you look (using 'Properties/Custom' in the application) to get them defined I'll need to add them in as new.
I've managed to determine the properties and methods for the CustomDocumentProperties using
Dim minfos() As MemberInfo = GetType(Excel.CustomProper ties).GetM embers
For Each mi As MemberInfo In minfos
Console.WriteLine("{0} - {1}", mi.Name, mi.MemberType)
Next
This shows up an Add method
The first bit I'm stuck on is how do I
a) find what the correct syntax is for the add method
b) implement it using the 'Reflection' method.
I expect that the add method will want (Name, Type and Value) and that I'll need to pass it an array.
So, where you've used InvokeMember("Value", ... New Object() {pvalue}) above, (if I've started to understand 'Reflection') I'd expect to change this to;
InvokeMember("Add", ... New Object() {PName, PType, PValue}
Though I'd need to find out what sort of Type declaration the method is expecting - I don't know whether 'TheType' I've declared above should work.
If I use your method to count the number of CustomDocumentProperties, I get a zero return (count=0). This suggest to me that although the custom properties are listed if you look (using 'Properties/Custom' in the application) to get them defined I'll need to add them in as new.
I've managed to determine the properties and methods for the CustomDocumentProperties using
Dim minfos() As MemberInfo = GetType(Excel.CustomProper
For Each mi As MemberInfo In minfos
Console.WriteLine("{0} - {1}", mi.Name, mi.MemberType)
Next
This shows up an Add method
The first bit I'm stuck on is how do I
a) find what the correct syntax is for the add method
b) implement it using the 'Reflection' method.
I expect that the add method will want (Name, Type and Value) and that I'll need to pass it an array.
So, where you've used InvokeMember("Value", ... New Object() {pvalue}) above, (if I've started to understand 'Reflection') I'd expect to change this to;
InvokeMember("Add", ... New Object() {PName, PType, PValue}
Though I'd need to find out what sort of Type declaration the method is expecting - I don't know whether 'TheType' I've declared above should work.
ASKER
With thansks to Francesco Balena, I've found the following
Dim mi As MethodInfo = GetType(Excel.CustomProper ties).GetM ethod("Add ")
Console.Write(mi.Name & "(")
For Each pi As ParameterInfo In mi.GetParameters
If pi.Position > 0 Then Console.Write(", ")
Dim direction As String = "ByVal"
If pi.IsOut Then direction = "ByRef"
Console.Write("{0} {1} as {2}", direction, pi.Name, pi.ParameterType.FullName)
Next
Console.WriteLine(")")
To find the parameters required in the 'Add' method
Dim mi As MethodInfo = GetType(Excel.CustomProper
Console.Write(mi.Name & "(")
For Each pi As ParameterInfo In mi.GetParameters
If pi.Position > 0 Then Console.Write(", ")
Dim direction As String = "ByVal"
If pi.IsOut Then direction = "ByRef"
Console.Write("{0} {1} as {2}", direction, pi.Name, pi.ParameterType.FullName)
Next
Console.WriteLine(")")
To find the parameters required in the 'Add' method
ASKER
This says the 'Add' method is = Add(ByVal Name as System.String, ByVal Value as System.Object)
But I can't figure out how to add it in.
But I can't figure out how to add it in.
ASKER
I think I've finally distilled it out to use the 'InvokeMethod' as ...
xlWB.CustomDocumentPropert ies.GetTyp e().Invoke Member("Ad d", BindingFlags.InvokeMethod, _
Nothing, xlWB.CustomDocumentPropert ies, New Object() {PName, PValue})
but the command still fails with;
"An unhandled exception of type 'System.Reflection.TargetI nvocationE xception' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation."
xlWB.CustomDocumentPropert
Nothing, xlWB.CustomDocumentPropert
but the command still fails with;
"An unhandled exception of type 'System.Reflection.TargetI
Additional information: Exception has been thrown by the target of an invocation."
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Many thanks -
For future reference, the final function came out as;
Sub SetProperty(ByVal xlWB As Excel.Workbook, ByVal PName As String, ByVal PValue As Object, _
ByVal isPropCustom As Boolean)
'This function sets the value of a property.
'
'The parameters passed to the function are as follows:
'
'xlWB A reference to the the workbook whose property is to be set.
'PName A string containing the name of the property
'PValue A variant containing the value of the property
'PropCustom A boolean indicating whether the property is a Custom Document Property.
Dim result As Object
Dim PType As Long
Dim isLinkToContent As Boolean = False
Dim LinkSource As Object = Nothing
Select Case VarType(PValue)
Case vbBoolean
PType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeBo olean
Case vbDate
PType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeDa te
Case vbDouble, vbLong, vbSingle, vbCurrency
PType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeFl oat
Case vbInteger
PType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeNu mber
Case vbString
PType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeSt ring
Case Else
PType = Microsoft.Office.Core.MsoD ocProperti es.msoProp ertyTypeSt ring
End Select
If isPropCustom Then
xlWB.CustomDocumentPropert ies.GetTyp e().Invoke Member("Ad d", BindingFlags.InvokeMethod, _
Nothing, xlWB.CustomDocumentPropert ies, New Object() {PName, isLinkToContent, PType, PValue, LinkSource})
Else
result = xlWB.BuiltinDocumentProper ties.GetTy pe().Invok eMember("I tem", BindingFlags.GetProperty, _
Nothing, xlWB.BuiltinDocumentProper ties, New Object() {PName})
result.GetType().InvokeMem ber("Value ", BindingFlags.SetProperty, Nothing, result, New Object() {PValue})
End If
End Sub
As an aside, I was down a bit of a dead end as my little bit of code (above) to get all the parameters of the 'Add' method didn't return all the parameters I needed.
For future reference, the final function came out as;
Sub SetProperty(ByVal xlWB As Excel.Workbook, ByVal PName As String, ByVal PValue As Object, _
ByVal isPropCustom As Boolean)
'This function sets the value of a property.
'
'The parameters passed to the function are as follows:
'
'xlWB A reference to the the workbook whose property is to be set.
'PName A string containing the name of the property
'PValue A variant containing the value of the property
'PropCustom A boolean indicating whether the property is a Custom Document Property.
Dim result As Object
Dim PType As Long
Dim isLinkToContent As Boolean = False
Dim LinkSource As Object = Nothing
Select Case VarType(PValue)
Case vbBoolean
PType = Microsoft.Office.Core.MsoD
Case vbDate
PType = Microsoft.Office.Core.MsoD
Case vbDouble, vbLong, vbSingle, vbCurrency
PType = Microsoft.Office.Core.MsoD
Case vbInteger
PType = Microsoft.Office.Core.MsoD
Case vbString
PType = Microsoft.Office.Core.MsoD
Case Else
PType = Microsoft.Office.Core.MsoD
End Select
If isPropCustom Then
xlWB.CustomDocumentPropert
Nothing, xlWB.CustomDocumentPropert
Else
result = xlWB.BuiltinDocumentProper
Nothing, xlWB.BuiltinDocumentProper
result.GetType().InvokeMem
End If
End Sub
As an aside, I was down a bit of a dead end as my little bit of code (above) to get all the parameters of the 'Add' method didn't return all the parameters I needed.
Dim prps as Object
prps = xlWb.BuiltinDocumentProper
Tom.