CSharp VB Excel COM Differences

A

Alan Roberts

Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some work
on the running instance of Excel via that reference. As an example, a VB
DLL to return the path to the current active workbook contains the following
(in addition to the COM GUIDS automatically added to a new COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this as
follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a definition
for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 
N

Nicholas Paldino [.NET/C# MVP]

Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will have to
use reflection to get the member names and then invoke them like that.

OR, you could set a reference to the interop assemblies for Excel, and
cast the object passed into your method into one of those classes in the
interop assembly. This would prevent you from having to do any late
binding.

Also, you should be careful of the operations you are performing. When
you call methods on any object in Excel, you are creating a runtime callable
wrapper which should be disposed of if you aren't using it anymore. In the
case of your Path property, you should assign the result of ThisWorkbook to
a variable, then pass that to the static ReleaseComObject method on the
Marshal class.

Hope this helps.
 
A

Alan Roberts

Hi Nicholas, thanks for the thorough reply! Does this mean that the
following simple VB code to calculate the sum of the values specified in an
Excel Range object

Public Function Sum(ByVal range As String) As Double
Dim i, j As Integer
Dim total As Double
For i = 1 To Excel.Range(range).Rows.Count
For j = 1 To Excel.Range(range).Columns.Count
total = total + Convert.ToDouble(Excel.Range(range).cells(i,
j).value)
Next j
Next i
Return total
End Function


must become something like this in C#???


public double Sum(string range)
{
object oRange, oRows, oCols, oRowCount, oColCount, oCell, oValue;
object[] Parameters;
double total= 0;
Parameters = new Object[1];
Parameters[0] = range;
oRange = Excel.GetType().InvokeMember("Range", BindingFlags.GetProperty,
null, Excel, Parameters);
oRows = oRange.GetType().InvokeMember("Rows", BindingFlags.GetProperty,
null, oRange, null);
oRowCount = oRows.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oRows, null);
oCols = oRange.GetType().InvokeMember("Columns",
BindingFlags.GetProperty, null, oRange, null);
oColCount = oCols.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oCols, null);
for (int i = 1; i <= (int)oColCount; i++)
{
for (int j = 1; j <= (int)oRowCount; j++)
{
Parameters = new Object[2];
Parameters[0] = j;
Parameters[1] = i;
oValue = new Object();
oCell = new Object();
oCell = oRange.GetType().InvokeMember("Cells",
BindingFlags.GetProperty, null, oRange, Parameters);
oValue = oCell.GetType().InvokeMember("Value",
BindingFlags.GetProperty, null, oCell, null);
total += (double)oValue;
}
}
return total;
}


Or is there a better way?

Thanks

Alan



Nicholas Paldino said:
Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will have to
use reflection to get the member names and then invoke them like that.

OR, you could set a reference to the interop assemblies for Excel, and
cast the object passed into your method into one of those classes in the
interop assembly. This would prevent you from having to do any late
binding.

Also, you should be careful of the operations you are performing. When
you call methods on any object in Excel, you are creating a runtime
callable wrapper which should be disposed of if you aren't using it
anymore. In the case of your Path property, you should assign the result
of ThisWorkbook to a variable, then pass that to the static
ReleaseComObject method on the Marshal class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some
work on the running instance of Excel via that reference. As an example,
a VB DLL to return the path to the current active workbook contains the
following (in addition to the COM GUIDS automatically added to a new
COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this as
follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a
definition for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 
N

Nicholas Paldino [.NET/C# MVP]

Alan,

Yes, it's something like that. You could cut down on some of the
reflection calls, but not by much.

Also, you need to call ReleaseComObject for each object you expose. For
example, in your Sum method, you need to release the result returned from:

Excel.Range - Returns a collection of ranges
Excel.Range(range) - Returns the individual range.
Excel.Range(range).Rows - Returns the collection of ranges.
Excel.Range(range).Columns - Returns the collection of columns
Excel.Range(range).Cells - Returns the collection of cells.
Excel.Range(range).Cells(i, j) - Returns the range representing the
individual cell.

All of those need to have their references released properly.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Hi Nicholas, thanks for the thorough reply! Does this mean that the
following simple VB code to calculate the sum of the values specified in
an Excel Range object

Public Function Sum(ByVal range As String) As Double
Dim i, j As Integer
Dim total As Double
For i = 1 To Excel.Range(range).Rows.Count
For j = 1 To Excel.Range(range).Columns.Count
total = total + Convert.ToDouble(Excel.Range(range).cells(i,
j).value)
Next j
Next i
Return total
End Function


must become something like this in C#???


public double Sum(string range)
{
object oRange, oRows, oCols, oRowCount, oColCount, oCell, oValue;
object[] Parameters;
double total= 0;
Parameters = new Object[1];
Parameters[0] = range;
oRange = Excel.GetType().InvokeMember("Range",
BindingFlags.GetProperty, null, Excel, Parameters);
oRows = oRange.GetType().InvokeMember("Rows", BindingFlags.GetProperty,
null, oRange, null);
oRowCount = oRows.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oRows, null);
oCols = oRange.GetType().InvokeMember("Columns",
BindingFlags.GetProperty, null, oRange, null);
oColCount = oCols.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oCols, null);
for (int i = 1; i <= (int)oColCount; i++)
{
for (int j = 1; j <= (int)oRowCount; j++)
{
Parameters = new Object[2];
Parameters[0] = j;
Parameters[1] = i;
oValue = new Object();
oCell = new Object();
oCell = oRange.GetType().InvokeMember("Cells",
BindingFlags.GetProperty, null, oRange, Parameters);
oValue = oCell.GetType().InvokeMember("Value",
BindingFlags.GetProperty, null, oCell, null);
total += (double)oValue;
}
}
return total;
}


Or is there a better way?

Thanks

Alan



Nicholas Paldino said:
Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will have
to use reflection to get the member names and then invoke them like that.

OR, you could set a reference to the interop assemblies for Excel, and
cast the object passed into your method into one of those classes in the
interop assembly. This would prevent you from having to do any late
binding.

Also, you should be careful of the operations you are performing.
When you call methods on any object in Excel, you are creating a runtime
callable wrapper which should be disposed of if you aren't using it
anymore. In the case of your Path property, you should assign the result
of ThisWorkbook to a variable, then pass that to the static
ReleaseComObject method on the Marshal class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some
work on the running instance of Excel via that reference. As an
example, a VB DLL to return the path to the current active workbook
contains the following (in addition to the COM GUIDS automatically added
to a new COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this as
follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a
definition for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 
A

Alan Roberts

Blimey! Thanks a lot Nicholas. I guess I will stick with VB for now. On
the whole I find C# more elegant but VB seems better suited to this
particular application.

Thanks

Alan

Nicholas Paldino said:
Alan,

Yes, it's something like that. You could cut down on some of the
reflection calls, but not by much.

Also, you need to call ReleaseComObject for each object you expose.
For example, in your Sum method, you need to release the result returned
from:

Excel.Range - Returns a collection of ranges
Excel.Range(range) - Returns the individual range.
Excel.Range(range).Rows - Returns the collection of ranges.
Excel.Range(range).Columns - Returns the collection of columns
Excel.Range(range).Cells - Returns the collection of cells.
Excel.Range(range).Cells(i, j) - Returns the range representing the
individual cell.

All of those need to have their references released properly.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Hi Nicholas, thanks for the thorough reply! Does this mean that the
following simple VB code to calculate the sum of the values specified in
an Excel Range object

Public Function Sum(ByVal range As String) As Double
Dim i, j As Integer
Dim total As Double
For i = 1 To Excel.Range(range).Rows.Count
For j = 1 To Excel.Range(range).Columns.Count
total = total + Convert.ToDouble(Excel.Range(range).cells(i,
j).value)
Next j
Next i
Return total
End Function


must become something like this in C#???


public double Sum(string range)
{
object oRange, oRows, oCols, oRowCount, oColCount, oCell, oValue;
object[] Parameters;
double total= 0;
Parameters = new Object[1];
Parameters[0] = range;
oRange = Excel.GetType().InvokeMember("Range",
BindingFlags.GetProperty, null, Excel, Parameters);
oRows = oRange.GetType().InvokeMember("Rows",
BindingFlags.GetProperty, null, oRange, null);
oRowCount = oRows.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oRows, null);
oCols = oRange.GetType().InvokeMember("Columns",
BindingFlags.GetProperty, null, oRange, null);
oColCount = oCols.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oCols, null);
for (int i = 1; i <= (int)oColCount; i++)
{
for (int j = 1; j <= (int)oRowCount; j++)
{
Parameters = new Object[2];
Parameters[0] = j;
Parameters[1] = i;
oValue = new Object();
oCell = new Object();
oCell = oRange.GetType().InvokeMember("Cells",
BindingFlags.GetProperty, null, oRange, Parameters);
oValue = oCell.GetType().InvokeMember("Value",
BindingFlags.GetProperty, null, oCell, null);
total += (double)oValue;
}
}
return total;
}


Or is there a better way?

Thanks

Alan



Nicholas Paldino said:
Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will have
to use reflection to get the member names and then invoke them like
that.

OR, you could set a reference to the interop assemblies for Excel,
and cast the object passed into your method into one of those classes in
the interop assembly. This would prevent you from having to do any late
binding.

Also, you should be careful of the operations you are performing.
When you call methods on any object in Excel, you are creating a runtime
callable wrapper which should be disposed of if you aren't using it
anymore. In the case of your Path property, you should assign the
result of ThisWorkbook to a variable, then pass that to the static
ReleaseComObject method on the Marshal class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some
work on the running instance of Excel via that reference. As an
example, a VB DLL to return the path to the current active workbook
contains the following (in addition to the COM GUIDS automatically
added to a new COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this
as follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a
definition for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 
N

Nicholas Paldino [.NET/C# MVP]

Alan,

The problem still exists in VB. In VB, the RCW for all of the objects I
list out still are out there, and you don't release them.

The only thing that VB makes easier is having to actually make the
calls, it will make it easy to perfrom the reflection for you, but not the
actuall calls to ReleaseComObject.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Blimey! Thanks a lot Nicholas. I guess I will stick with VB for now. On
the whole I find C# more elegant but VB seems better suited to this
particular application.

Thanks

Alan

Nicholas Paldino said:
Alan,

Yes, it's something like that. You could cut down on some of the
reflection calls, but not by much.

Also, you need to call ReleaseComObject for each object you expose.
For example, in your Sum method, you need to release the result returned
from:

Excel.Range - Returns a collection of ranges
Excel.Range(range) - Returns the individual range.
Excel.Range(range).Rows - Returns the collection of ranges.
Excel.Range(range).Columns - Returns the collection of columns
Excel.Range(range).Cells - Returns the collection of cells.
Excel.Range(range).Cells(i, j) - Returns the range representing the
individual cell.

All of those need to have their references released properly.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Hi Nicholas, thanks for the thorough reply! Does this mean that the
following simple VB code to calculate the sum of the values specified in
an Excel Range object

Public Function Sum(ByVal range As String) As Double
Dim i, j As Integer
Dim total As Double
For i = 1 To Excel.Range(range).Rows.Count
For j = 1 To Excel.Range(range).Columns.Count
total = total + Convert.ToDouble(Excel.Range(range).cells(i,
j).value)
Next j
Next i
Return total
End Function


must become something like this in C#???


public double Sum(string range)
{
object oRange, oRows, oCols, oRowCount, oColCount, oCell, oValue;
object[] Parameters;
double total= 0;
Parameters = new Object[1];
Parameters[0] = range;
oRange = Excel.GetType().InvokeMember("Range",
BindingFlags.GetProperty, null, Excel, Parameters);
oRows = oRange.GetType().InvokeMember("Rows",
BindingFlags.GetProperty, null, oRange, null);
oRowCount = oRows.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oRows, null);
oCols = oRange.GetType().InvokeMember("Columns",
BindingFlags.GetProperty, null, oRange, null);
oColCount = oCols.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oCols, null);
for (int i = 1; i <= (int)oColCount; i++)
{
for (int j = 1; j <= (int)oRowCount; j++)
{
Parameters = new Object[2];
Parameters[0] = j;
Parameters[1] = i;
oValue = new Object();
oCell = new Object();
oCell = oRange.GetType().InvokeMember("Cells",
BindingFlags.GetProperty, null, oRange, Parameters);
oValue = oCell.GetType().InvokeMember("Value",
BindingFlags.GetProperty, null, oCell, null);
total += (double)oValue;
}
}
return total;
}


Or is there a better way?

Thanks

Alan



in message Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will have
to use reflection to get the member names and then invoke them like
that.

OR, you could set a reference to the interop assemblies for Excel,
and cast the object passed into your method into one of those classes
in the interop assembly. This would prevent you from having to do any
late binding.

Also, you should be careful of the operations you are performing.
When you call methods on any object in Excel, you are creating a
runtime callable wrapper which should be disposed of if you aren't
using it anymore. In the case of your Path property, you should assign
the result of ThisWorkbook to a variable, then pass that to the static
ReleaseComObject method on the Marshal class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some
work on the running instance of Excel via that reference. As an
example, a VB DLL to return the path to the current active workbook
contains the following (in addition to the COM GUIDS automatically
added to a new COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this
as follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a
definition for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 
A

Alan Roberts

Thanks Nicholas. I guess I am still having dificulties with the Release
idea....
I have come up with the following routine in C#


private static object GetProperty(object obj, string sProperty)
{
string[] Properties = sProperty.Split(new char[] {'.'});
object oProp;
oProp = obj;
for (int i = 0; i <= Properties.GetUpperBound(0); i++)
{
if (Properties.Contains("("))
oProp = oProp.GetType().InvokeMember(Properties.Substring(0,
Properties.IndexOf('(')),
BindingFlags.GetProperty, null, oProp,
Properties.Substring(Properties.IndexOf('(')
+ 1, Properties.IndexOf(')') - (Properties.IndexOf('(') +
1)).Split(new char[] { ','
}));
else
oProp = oProp.GetType().InvokeMember(Properties,
BindingFlags.GetProperty, null,
oProp, null);
}

return oProp;
}


basically it simplifies my code as I once I have a link to the Excel object
I can access properties using code along the lines of
GetProperty(oExcel, "ActiveSheet.Cells(1,1).Value");
rather than having all of the step by step code I presented previousle.

If I use this function in a routine to calculate the total of a range of
cells eg


double total = 0;
for (int i = 1; i <= 5; i++)
{
total += (double)GetProperty(oRange, "Cells(" + i.ToString() +",
1").Value");
}


What objects do I have to release both in the for loop code above and in the
GetProperty function that it calls?

Thanks for your help with this

Alan



Nicholas Paldino said:
Alan,

The problem still exists in VB. In VB, the RCW for all of the objects
I list out still are out there, and you don't release them.

The only thing that VB makes easier is having to actually make the
calls, it will make it easy to perfrom the reflection for you, but not the
actuall calls to ReleaseComObject.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Blimey! Thanks a lot Nicholas. I guess I will stick with VB for now.
On the whole I find C# more elegant but VB seems better suited to this
particular application.

Thanks

Alan

Nicholas Paldino said:
Alan,

Yes, it's something like that. You could cut down on some of the
reflection calls, but not by much.

Also, you need to call ReleaseComObject for each object you expose.
For example, in your Sum method, you need to release the result returned
from:

Excel.Range - Returns a collection of ranges
Excel.Range(range) - Returns the individual range.
Excel.Range(range).Rows - Returns the collection of ranges.
Excel.Range(range).Columns - Returns the collection of columns
Excel.Range(range).Cells - Returns the collection of cells.
Excel.Range(range).Cells(i, j) - Returns the range representing the
individual cell.

All of those need to have their references released properly.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Hi Nicholas, thanks for the thorough reply! Does this mean that the
following simple VB code to calculate the sum of the values specified
in an Excel Range object

Public Function Sum(ByVal range As String) As Double
Dim i, j As Integer
Dim total As Double
For i = 1 To Excel.Range(range).Rows.Count
For j = 1 To Excel.Range(range).Columns.Count
total = total + Convert.ToDouble(Excel.Range(range).cells(i,
j).value)
Next j
Next i
Return total
End Function


must become something like this in C#???


public double Sum(string range)
{
object oRange, oRows, oCols, oRowCount, oColCount, oCell, oValue;
object[] Parameters;
double total= 0;
Parameters = new Object[1];
Parameters[0] = range;
oRange = Excel.GetType().InvokeMember("Range",
BindingFlags.GetProperty, null, Excel, Parameters);
oRows = oRange.GetType().InvokeMember("Rows",
BindingFlags.GetProperty, null, oRange, null);
oRowCount = oRows.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oRows, null);
oCols = oRange.GetType().InvokeMember("Columns",
BindingFlags.GetProperty, null, oRange, null);
oColCount = oCols.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oCols, null);
for (int i = 1; i <= (int)oColCount; i++)
{
for (int j = 1; j <= (int)oRowCount; j++)
{
Parameters = new Object[2];
Parameters[0] = j;
Parameters[1] = i;
oValue = new Object();
oCell = new Object();
oCell = oRange.GetType().InvokeMember("Cells",
BindingFlags.GetProperty, null, oRange, Parameters);
oValue = oCell.GetType().InvokeMember("Value",
BindingFlags.GetProperty, null, oCell, null);
total += (double)oValue;
}
}
return total;
}


Or is there a better way?

Thanks

Alan



"Nicholas Paldino [.NET/C# MVP]" <[email protected]>
wrote in message Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will
have to use reflection to get the member names and then invoke them
like that.

OR, you could set a reference to the interop assemblies for Excel,
and cast the object passed into your method into one of those classes
in the interop assembly. This would prevent you from having to do any
late binding.

Also, you should be careful of the operations you are performing.
When you call methods on any object in Excel, you are creating a
runtime callable wrapper which should be disposed of if you aren't
using it anymore. In the case of your Path property, you should
assign the result of ThisWorkbook to a variable, then pass that to the
static ReleaseComObject method on the Marshal class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some
work on the running instance of Excel via that reference. As an
example, a VB DLL to return the path to the current active workbook
contains the following (in addition to the COM GUIDS automatically
added to a new COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this
as follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a
definition for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 
A

Alvin Bruney - ASP.NET MVP

On a side note, you can optimize your loop heavy code to take advantage of
the excel internally supported methods.
double total = 0;
for (int i = 1; i <= 5; i++)
{
total += (double)GetProperty(oRange, "Cells(" + i.ToString() +",
1").Value");
}

can be replaced with
Range r = rangeInQuestion.Formula = "=Sum(1,5)"

FYI

--
Regards,
Alvin Bruney [MVP ASP.NET]

[Shameless Author plug]
The Microsoft Office Web Components Black Book with .NET
Now Available @ www.lulu.com/owc
Forth-coming VSTO.NET - Wrox/Wiley 2006
-------------------------------------------------------



Alan Roberts said:
Thanks Nicholas. I guess I am still having dificulties with the Release
idea....
I have come up with the following routine in C#


private static object GetProperty(object obj, string sProperty)
{
string[] Properties = sProperty.Split(new char[] {'.'});
object oProp;
oProp = obj;
for (int i = 0; i <= Properties.GetUpperBound(0); i++)
{
if (Properties.Contains("("))
oProp = oProp.GetType().InvokeMember(Properties.Substring(0,
Properties.IndexOf('(')),
BindingFlags.GetProperty, null, oProp,
Properties.Substring(Properties.IndexOf('(')
+ 1, Properties.IndexOf(')') - (Properties.IndexOf('(') +
1)).Split(new char[] { ','
}));
else
oProp = oProp.GetType().InvokeMember(Properties,
BindingFlags.GetProperty, null,
oProp, null);
}

return oProp;
}


basically it simplifies my code as I once I have a link to the Excel object
I can access properties using code along the lines of
GetProperty(oExcel, "ActiveSheet.Cells(1,1).Value");
rather than having all of the step by step code I presented previousle.

If I use this function in a routine to calculate the total of a range of
cells eg


double total = 0;
for (int i = 1; i <= 5; i++)
{
total += (double)GetProperty(oRange, "Cells(" + i.ToString() +",
1").Value");
}


What objects do I have to release both in the for loop code above and in the
GetProperty function that it calls?

Thanks for your help with this

Alan



message news:[email protected]...
Alan,

The problem still exists in VB. In VB, the RCW for all of the objects
I list out still are out there, and you don't release them.

The only thing that VB makes easier is having to actually make the
calls, it will make it easy to perfrom the reflection for you, but not the
actuall calls to ReleaseComObject.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alan Roberts said:
Blimey! Thanks a lot Nicholas. I guess I will stick with VB for now.
On the whole I find C# more elegant but VB seems better suited to this
particular application.

Thanks

Alan

in message Alan,

Yes, it's something like that. You could cut down on some of the
reflection calls, but not by much.

Also, you need to call ReleaseComObject for each object you expose.
For example, in your Sum method, you need to release the result returned
from:

Excel.Range - Returns a collection of ranges
Excel.Range(range) - Returns the individual range.
Excel.Range(range).Rows - Returns the collection of ranges.
Excel.Range(range).Columns - Returns the collection of columns
Excel.Range(range).Cells - Returns the collection of cells.
Excel.Range(range).Cells(i, j) - Returns the range representing the
individual cell.

All of those need to have their references released properly.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Hi Nicholas, thanks for the thorough reply! Does this mean that the
following simple VB code to calculate the sum of the values specified
in an Excel Range object

Public Function Sum(ByVal range As String) As Double
Dim i, j As Integer
Dim total As Double
For i = 1 To Excel.Range(range).Rows.Count
For j = 1 To Excel.Range(range).Columns.Count
total = total + Convert.ToDouble(Excel.Range(range).cells(i,
j).value)
Next j
Next i
Return total
End Function


must become something like this in C#???


public double Sum(string range)
{
object oRange, oRows, oCols, oRowCount, oColCount, oCell, oValue;
object[] Parameters;
double total= 0;
Parameters = new Object[1];
Parameters[0] = range;
oRange = Excel.GetType().InvokeMember("Range",
BindingFlags.GetProperty, null, Excel, Parameters);
oRows = oRange.GetType().InvokeMember("Rows",
BindingFlags.GetProperty, null, oRange, null);
oRowCount = oRows.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oRows, null);
oCols = oRange.GetType().InvokeMember("Columns",
BindingFlags.GetProperty, null, oRange, null);
oColCount = oCols.GetType().InvokeMember("Count",
BindingFlags.GetProperty, null, oCols, null);
for (int i = 1; i <= (int)oColCount; i++)
{
for (int j = 1; j <= (int)oRowCount; j++)
{
Parameters = new Object[2];
Parameters[0] = j;
Parameters[1] = i;
oValue = new Object();
oCell = new Object();
oCell = oRange.GetType().InvokeMember("Cells",
BindingFlags.GetProperty, null, oRange, Parameters);
oValue = oCell.GetType().InvokeMember("Value",
BindingFlags.GetProperty, null, oCell, null);
total += (double)oValue;
}
}
return total;
}


Or is there a better way?

Thanks

Alan



"Nicholas Paldino [.NET/C# MVP]" <[email protected]>
wrote in message Alan,

The reason this works in VB is that the VB compiler will translate
property and method calls on variables of type object to be reflection
calls, which cause your code to work.

Unfortunately, C# doesn't support this functionality. You will
have to use reflection to get the member names and then invoke them
like that.

OR, you could set a reference to the interop assemblies for Excel,
and cast the object passed into your method into one of those classes
in the interop assembly. This would prevent you from having to do any
late binding.

Also, you should be careful of the operations you are performing.
When you call methods on any object in Excel, you are creating a
runtime callable wrapper which should be disposed of if you aren't
using it anymore. In the case of your Path property, you should
assign the result of ThisWorkbook to a variable, then pass that to the
static ReleaseComObject method on the Marshal class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Can someone please explain the following for me...

I am trying to link to a .NET DLL from Excel. Excel needs to pass a
reference to itself to the DLL and then the DLL needs to perform some
work on the running instance of Excel via that reference. As an
example, a VB DLL to return the path to the current active workbook
contains the following (in addition to the COM GUIDS automatically
added to a new COMClass)...

Public Class Class1
Public Sub New()
MyBase.New()
End Sub
Private gExcel As Object
Public Property Excel() As Object
Set(ByVal value As Object)
gExcel = value
End Set
Get
Excel = gExcel
End Get
End Property
Public Function Path() As String
Return Excel.ThisWorkbook.Path
End Function
End Class

After adding a refernece to the DLL, a macro in Excel could call this
as follows

Sub VBTest()
Dim VBTest As New VBExcelTest.Class1
Set VBTest.Excel = Application
Debug.Print VBTest.Path
End Sub

This all work fine

How can I do the same thing using C#? If I try to create a DLL with
equivalent(?) code eg

public class Class1
{
public Class1(){
}
private object gExcel;
public object Excel{
get{
return gExcel;
}
set{
gExcel = value;
}
}
public string Path(){
return Excel.ThisWorkbook.Path;
}
}

I get a compile error saying that - 'object' does not contain a
definition for 'ThisWorkbook'

How can I get this to work?

Thanks

Alan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top