pass worksheets collection from excel vba to c# com interop dll

B

Bill Swartz

Hi,

I have a c# com interop dll containing code to support excel vba. I
want to pass the worksheets collection from excel vba to the c# dll. I
have tried "ref object wkSheets". On the c# side, GetType() returns
System.__ComObject. I have never heard of it.

Inside the c# code, this conditional fails:

if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) != null)
{
_ExLog.TraceEvent("{0}.ReceiveBks Worksheets",
this.ToString());
}

Any ideas?

Bill
 
B

Bill Swartz

That's a "run-time callable wrapper".http://msdn.microsoft.com/en-us/library/8bwh56xe.aspx

It's a managed object that wraps a COM interface pointer.



Define "fails".  To me, "fails" means that the expression in the "if"
statement evaluates to "false", which would mean that "theSheets" is a
reference to an object that _is_ of the
Microsoft.Office.Interop.Excel.Worksheets type.

But your question seems to be more likely to indicate that the condition
itself is true, and that the object isn't of that type.

I don't know whether the Office interop types provide a way to get a
managed Worksheets instance from a COM object.  But if they do, that
would what you want to do.  You'll have to use the
Marshal.GetComInterfaceForObject() to resolve the RCW to an actual COM
interface pointer, unless the Office interop support can unwrap an RCW
itself.

If Office interop doesn't allow conversion from COM to managed type,
then you'll have to use the COM interface directly.  See the above link
for other links to more details about how to do that.  Basically, you'll
need to declare a COM interop interface type and use that type with the
RCW to access members in the COM interface.  Keeping in mind, of course,
that _all_ of the COM types related to that will also require similar
type declarations in order to use their members.

Pete

First of all, I mean by fails that the the cast isn't non-null, that
is, it is null. Here is a test interface and class:

using System.Runtime.InteropServices;
using System.Reflection;
using System;

namespace MirrorCalTestLib
{
[ComVisibleAttribute(true)]
[GuidAttribute("AC062A45-9918-493d-8E49-677EA3E4B892")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[AutomationProxy(true)]
public interface ITestHelper
{
void ReceiveSheets(
[MarshalAs(UnmanagedType.Interface)]
ref object theSheets
);
}

[ComVisibleAttribute(true)]
[GuidAttribute("3A48FE42-2254-4db5-8ECF-B47133C7D4F1")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class TestHelper
: ITestHelper
{
ExLog _ExLog;

public TestHelper()
{
Assembly executingAssembly =
Assembly.GetExecutingAssembly();
string thisToString = this.ToString();
_ExLog = new ExLog(10, executingAssembly.Location,
thisToString);
_ExLog.TraceEvent("{0} ctor", thisToString);
_ExLog.TraceAssemblies(executingAssembly);
}

#region ITestHelper Members

public void ReceiveSheets(
ref object theSheets
)
{
_ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), theSheets.GetType());

if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) != null)
{
_ExLog.TraceEvent("{0}.ReceiveSheets Worksheets",
this.ToString());
}

IntPtr ip = Marshal.GetIDispatchForObject(theSheets);
_ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), ip.ToInt32());

}

#endregion
}
}


The call to GetIDispatchForObject returns something. "ip.ToInt32()"
evaluates to 42074960. I will add a call to GetComInterfaceForObject.

Bill
 
B

Bill Swartz

It's a managed object that wraps a COM interface pointer.
Define "fails".  To me, "fails" means that the expression in the "if"
statement evaluates to "false", which would mean that "theSheets" is a
reference to an object that _is_ of the
Microsoft.Office.Interop.Excel.Worksheets type.
But your question seems to be more likely to indicate that the condition
itself is true, and that the object isn't of that type.
I don't know whether the Office interop types provide a way to get a
managed Worksheets instance from a COM object.  But if they do, that
would what you want to do.  You'll have to use the
Marshal.GetComInterfaceForObject() to resolve the RCW to an actual COM
interface pointer, unless the Office interop support can unwrap an RCW
itself.
If Office interop doesn't allow conversion from COM to managed type,
then you'll have to use the COM interface directly.  See the above link
for other links to more details about how to do that.  Basically, you'll
need to declare a COM interop interface type and use that type with the
RCW to access members in the COM interface.  Keeping in mind, of course,
that _all_ of the COM types related to that will also require similar
type declarations in order to use their members.

First of all, I mean by fails that the the cast isn't non-null, that
is, it is null. Here is a test interface and class:

using System.Runtime.InteropServices;
using System.Reflection;
using System;

namespace MirrorCalTestLib
{
    [ComVisibleAttribute(true)]
    [GuidAttribute("AC062A45-9918-493d-8E49-677EA3E4B892")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    [AutomationProxy(true)]
    public interface ITestHelper
    {
        void ReceiveSheets(
            [MarshalAs(UnmanagedType.Interface)]
            ref object theSheets
        );
    }

    [ComVisibleAttribute(true)]
    [GuidAttribute("3A48FE42-2254-4db5-8ECF-B47133C7D4F1")]
    [ClassInterfaceAttribute(ClassInterfaceType.None)]
    public class TestHelper
        : ITestHelper
    {
        ExLog _ExLog;

        public TestHelper()
        {
            Assembly executingAssembly =
Assembly.GetExecutingAssembly();
            string thisToString = this.ToString();
            _ExLog = new ExLog(10, executingAssembly.Location,
thisToString);
            _ExLog.TraceEvent("{0} ctor", thisToString);
            _ExLog.TraceAssemblies(executingAssembly);
        }

        #region ITestHelper Members

        public void ReceiveSheets(
            ref object theSheets
        )
        {
            _ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), theSheets.GetType());

            if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) != null)
            {
                _ExLog.TraceEvent("{0}.ReceiveSheets Worksheets",
this.ToString());
            }

            IntPtr ip = Marshal.GetIDispatchForObject(theSheets);
            _ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), ip.ToInt32());

        }

        #endregion
    }

}

The call to GetIDispatchForObject returns something. "ip.ToInt32()"
evaluates to 42074960. I will add a call to GetComInterfaceForObject.

Bill

Further testing shows that I can successfully pass these excel object
model items and, on the c# side, cast back to the item:
Range
Worksheet (individual)
Workbook

For some reason, it doesn't work for the Worksheets collection.

Bill
 
B

Bill Swartz

First of all, I mean by fails that the the cast isn't non-null, that
is, it is null. Here is a test interface and class:
using System.Runtime.InteropServices;
using System.Reflection;
using System;
namespace MirrorCalTestLib
{
    [ComVisibleAttribute(true)]
    [GuidAttribute("AC062A45-9918-493d-8E49-677EA3E4B892")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    [AutomationProxy(true)]
    public interface ITestHelper
    {
        void ReceiveSheets(
            [MarshalAs(UnmanagedType.Interface)]
            ref object theSheets
        );
    }
    [ComVisibleAttribute(true)]
    [GuidAttribute("3A48FE42-2254-4db5-8ECF-B47133C7D4F1")]
    [ClassInterfaceAttribute(ClassInterfaceType.None)]
    public class TestHelper
        : ITestHelper
    {
        ExLog _ExLog;
        public TestHelper()
        {
            Assembly executingAssembly =
Assembly.GetExecutingAssembly();
            string thisToString = this.ToString();
            _ExLog = new ExLog(10, executingAssembly.Location,
thisToString);
            _ExLog.TraceEvent("{0} ctor", thisToString);
            _ExLog.TraceAssemblies(executingAssembly);
        }
        #region ITestHelper Members
        public void ReceiveSheets(
            ref object theSheets
        )
        {
            _ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), theSheets.GetType());
            if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) != null)
            {
                _ExLog.TraceEvent("{0}.ReceiveSheets Worksheets",
this.ToString());
            }
            IntPtr ip = Marshal.GetIDispatchForObject(theSheets);
            _ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), ip.ToInt32());
        }
        #endregion
    }

The call to GetIDispatchForObject returns something. "ip.ToInt32()"
evaluates to 42074960. I will add a call to GetComInterfaceForObject.

Further testing shows that I can successfully pass these excel object
model items and, on the c# side, cast back to the item:
    Range
    Worksheet (individual)
    Workbook

For some reason, it doesn't work for the Worksheets collection.

Bill

Even further testing shows that the Worksheets collection gets passed
as the Sheets collection, and can be cast to a Sheets object.
Bill
 
J

Jim Metcalf

Hi Bill, can you please post the whole code snippet? I'm attempting the same thing (grab worksheet object/collection from Excel VBA and pass by reference to c# for further munging).
Hi,

I have a c# com interop dll containing code to support excel vba. I
want to pass the worksheets collection from excel vba to the c# dll. I
have tried "ref object wkSheets". On the c# side, GetType() returns
System.__ComObject. I have never heard of it.

Inside the c# code, this conditional fails:

if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) != null)
{
_ExLog.TraceEvent("{0}.ReceiveBks Worksheets",
this.ToString());
}

Any ideas?

Bill
That's a "run-time callable wrapper".
http://msdn.microsoft.com/en-us/library/8bwh56xe.aspx

it is a managed object that wraps a COM interface pointer.


Define "fails". To me, "fails" means that the expression in the "if"
statement evaluates to "false", which would mean that "theSheets" is a
reference to an object that _is_ of the
Microsoft.Office.Interop.Excel.Worksheets type.

But your question seems to be more likely to indicate that the condition
itself is true, and that the object is not of that type.

I do not know whether the Office interop types provide a way to get a
managed Worksheets instance from a COM object. But if they do, that
would what you want to do. You'll have to use the
Marshal.GetComInterfaceForObject() to resolve the RCW to an actual COM
interface pointer, unless the Office interop support can unwrap an RCW
itself.

If Office interop does not allow conversion from COM to managed type,
then you will have to use the COM interface directly. See the above link
for other links to more details about how to do that. Basically, you will
need to declare a COM interop interface type and use that type with the
RCW to access members in the COM interface. Keeping in mind, of course,
that _all_ of the COM types related to that will also require similar
type declarations in order to use their members.

Pete
On Thursday, September 02, 2010 6:29 PM Bill Swartz wrote:
ary/8bwh56xe.aspx
heets",
l
,

First of all, I mean by fails that the the cast is not non-null, that
is, it is null. Here is a test interface and class:

using System.Runtime.InteropServices;
using System.Reflection;
using System;

namespace MirrorCalTestLib
{
[ComVisibleAttribute(true)]
[GuidAttribute("AC062A45-9918-493d-8E49-677EA3E4B892")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[AutomationProxy(true)]
public interface ITestHelper
{
void ReceiveSheets(
[MarshalAs(UnmanagedType.Interface)]
ref object theSheets
);
}

[ComVisibleAttribute(true)]
[GuidAttribute("3A48FE42-2254-4db5-8ECF-B47133C7D4F1")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class TestHelper
{
ExLog _ExLog;

public TestHelper()
{
Assembly executingAssembly =3D
Assembly.GetExecutingAssembly();
string thisToString =3D this.ToString();
_ExLog =3D new ExLog(10, executingAssembly.Location,
thisToString);
_ExLog.TraceEvent("{0} ctor", thisToString);
_ExLog.TraceAssemblies(executingAssembly);
}


public void ReceiveSheets(
ref object theSheets
)
{
_ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), theSheets.GetType());

if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) !=3D null)
{
_ExLog.TraceEvent("{0}.ReceiveSheets Worksheets",
this.ToString());
}

IntPtr ip =3D Marshal.GetIDispatchForObject(theSheets);
_ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), ip.ToInt32());

}

}
}


The call to GetIDispatchForObject returns something. "ip.ToInt32()"
evaluates to 42074960. I will add a call to GetComInterfaceForObject.

Bill
 
B

Bill Swartz

Hi Bill, can you please post the whole code snippet? I'm attempting the same thing (grab worksheet object/collectionfromExcelVBAandpassby reference toc#for further munging).
On Thursday, September 02, 2010 4:02 PM Bill Swartz wrote:
Hi,
I have a c# cominteropdllcontaining code to supportexcelvba. I
want topasstheworksheetscollectionfromexcelvbato thec#dll. I
have tried "ref object wkSheets". On thec#side, GetType() returns
System.__ComObject. I have never heard of it.
Inside thec#code, this conditional fails:
if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) != null)
{
_ExLog.TraceEvent("{0}.ReceiveBksWorksheets",
this.ToString());
}
Any ideas?
Bill
That's a "run-time callable wrapper".
http://msdn.microsoft.com/en-us/library/8bwh56xe.aspx
it is a managed object that wraps a COM interface pointer.
Define "fails".  To me, "fails" means that the expression in the "if"
statement evaluates to "false", which would mean that "theSheets" is a
reference to an object that _is_ of the
Microsoft.Office.Interop.Excel.Worksheetstype.
But your question seems to be more likely to indicate that the condition
itself is true, and that the object is not of that type.
I do not know whether the Officeinteroptypes provide a way to get a
managedWorksheetsinstance from a COM object.  But if they do, that
would what you want to do.  You'll have to use the
Marshal.GetComInterfaceForObject() to resolve the RCW to an actual COM
interface pointer, unless the Officeinteropsupport can unwrap an RCW
itself.
If Officeinteropdoes not allow conversion from COM to managed type,
then you will have to use the COM interface directly.  See the abovelink
for other links to more details about how to do that.  Basically, you will
need to declare a COMinteropinterface type and use that type with the
RCW to access members in the COM interface.  Keeping in mind, of course,
that _all_ of the COM types related to that will also require similar
type declarations in order to use their members.
Pete
On Thursday, September 02, 2010 6:29 PM Bill Swartz wrote:
ary/8bwh56xe.aspx
heets",
l
,
First of all, I mean by fails that the the cast is not non-null, that
is, it is null. Here is a test interface and class:
using System.Runtime.InteropServices;
using System.Reflection;
using System;
namespace MirrorCalTestLib
{
[ComVisibleAttribute(true)]
[GuidAttribute("AC062A45-9918-493d-8E49-677EA3E4B892")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[AutomationProxy(true)]
public interface ITestHelper
{
void ReceiveSheets(
[MarshalAs(UnmanagedType.Interface)]
ref object theSheets
);
}
[ComVisibleAttribute(true)]
[GuidAttribute("3A48FE42-2254-4db5-8ECF-B47133C7D4F1")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class TestHelper
{
ExLog _ExLog;
public TestHelper()
{
Assembly executingAssembly =3D
Assembly.GetExecutingAssembly();
string thisToString =3D this.ToString();
_ExLog =3D new ExLog(10, executingAssembly.Location,
thisToString);
_ExLog.TraceEvent("{0} ctor", thisToString);
_ExLog.TraceAssemblies(executingAssembly);
}
public void ReceiveSheets(
ref object theSheets
)
{
_ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), theSheets.GetType());
if ((theSheets as
Microsoft.Office.Interop.Excel.Worksheets) !=3D null)
{
_ExLog.TraceEvent("{0}.ReceiveSheetsWorksheets",
this.ToString());
}
IntPtr ip =3D Marshal.GetIDispatchForObject(theSheets);
_ExLog.TraceEvent("{0}.ReceiveSheets {1}",
this.ToString(), ip.ToInt32());
}
}
}
The call to GetIDispatchForObject returns something. "ip.ToInt32()"
evaluates to 42074960. I will add a call to GetComInterfaceForObject.
Bill
On Friday, September 03, 2010 11:50 AM Bill Swartz wrote:
I
brary/8bwh56xe.aspx
ksheets",
n
nk
'll
se,
on,
sheets",
eets);
Further testing shows that I can successfullypasstheseexcelobject
model items and, on thec#side, cast back to the item:
Range
Worksheet (individual)
Workbook
For some reason, it does not work for theWorksheetscollection.
Bill
On Sunday, September 05, 2010 6:04 PM Bill Swartz wrote:
. I
library/8bwh56xe.aspx
orksheets",
f"
a
ion
t
M
W
link
ou'll
he
urse,
tion,
rksheets",
Sheets);
Even further testing shows that theWorksheetscollectiongets passed
as the Sheetscollection, and can be cast to a Sheets object.
Bill
Submitted via EggHeadCafe - Software Developer Portal of Choice
ExcelTips: Net Present Value (NPV) and Internal Rate of Return (IRR)
http://www.eggheadcafe.com/tutorials/aspnet/e426181a-90f1-4bdb-a58a-e...

I have zipped a test c# dll and an Excel workbook. I see no way to
attach them!
Bill
 
B

Bill Swartz

I can't find where to attach a zip file with a VS2008 library project and a workbook. Here is some workbook code:

Dim tObj As MirrorCalTestLib.TestHelper

Set tObj = New MirrorCalTestLib.TestHelper

Call tObj.ReceiveObj(ActiveWorkbook.Worksheets(1).Range("B1"))
Call tObj.ReceiveObj(Application)
Call tObj.ReceiveObj(ActiveWorkbook.Worksheets(1).Range("A1"))
Call tObj.ReceiveObj(ActiveWorkbook.Worksheets(1))
Call tObj.ReceiveObj(ActiveWorkbook.Worksheets(2))
Call tObj.ReceiveObj(ActiveWorkbook.Worksheets(3))
Call tObj.ReceiveObj(ActiveWorkbook.Worksheets)
Call tObj.ReceiveObj(ActiveWorkbook.Sheets)
Call tObj.ReceiveObj(ActiveWorkbook)

Here is some c# code:

public void ReceiveObj(
ref object theObj
)
{
_ExLog.TraceEvent("{0}.ReceiveObj, theObj.GetType()={1}", this.ToString(), theObj.GetType());

Excel.Application app = theObj as Excel.Application;
if (app != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Application.ActiveWorkbook.Name={1}", this.ToString(), app.ActiveWorkbook.Name);
}

Excel._Workbook _wb = theObj as Excel._Workbook;
if (_wb != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, _Workbook.Name={1}", this.ToString(), _wb.Name);
}

Excel.Workbook wb = theObj as Excel.Workbook;
if (wb != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Workbook.FullName={1}", this.ToString(), wb.FullName);
}

Excel.WorkbookClass wbc = theObj as Excel.WorkbookClass;
if (wbc != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, WorkbookClass.Application.Name={1}", this.ToString(), wbc.Application.Name);
}

Excel.Worksheet ws = theObj as Excel.Worksheet;
if (ws != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Worksheet.Name={1}", this.ToString(), ws.Name);
object obj = ws.Cells[1, 1];
if (obj == null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Worksheet.Cells[1, 1] is empty", this.ToString());
}
else
{
object o = (obj as Excel.Range).Value2;
if (o == null)
{
_ExLog.TraceEvent(
"{0}.ReceiveObj, Worksheet.Cells[1, 1].Value2 is empty",
this.ToString()
);
}
else
{
_ExLog.TraceEvent(
"{0}.ReceiveObj, Worksheet.Cells[1, 1].Value2.GetType()={1}",
this.ToString(), o.GetType()
);
_ExLog.TraceEvent(
"{0}.ReceiveObj, Worksheet.Cells[1, 1].Value2={1}",
this.ToString(), o
);
}
}
}

if ((theObj as Excel.Sheets) != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Sheets", this.ToString());
}

if ((theObj as Excel.Worksheets) != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Worksheets", this.ToString());
}

Excel.Range rng = theObj as Excel.Range;
if (rng != null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Range", this.ToString());
if (rng.Value2 == null)
{
_ExLog.TraceEvent("{0}.ReceiveObj, Range is empty", this.ToString());
}
else
{
_ExLog.TraceEvent("{0}.ReceiveObj, Range.Value2={1}", this.ToString(), rng.Value2);
_ExLog.TraceEvent("{0}.ReceiveObj, Range.Value2.GetType()={1}", this.ToString(), rng.Value2.GetType());
_ExLog.TraceEvent(
"{0}.ReceiveObj, Range.get_Value(XlRangeValueDataType.xlRangeValueDefault)={1}",
this.ToString(), rng.get_Value(Excel.XlRangeValueDataType.xlRangeValueDefault)
);
}
}

//IntPtr ip1 = Marshal.GetIDispatchForObject(theObj);
//_ExLog.TraceEvent("{0}.ReceiveObj, GetIDispatchForObject={1}", this.ToString(), ip1.ToInt32());

//Type t = typeof(Worksheets);
//_ExLog.TraceEvent("{0}.ReceiveObj, typeof(Worksheets)={1}", this.ToString(), t);

////IntPtr ip2 = Marshal.GetComInterfaceForObject(theObj, typeof(Worksheets));
////_ExLog.TraceEvent("{0}.ReceiveObj, GetComInterfaceForObject={1}", this.ToString(), ip2.ToInt32());
}

Hope this helps.
Bill

Submitted via EggHeadCafe - Software Developer Portal of Choice
Visual Studio Async CTP Overview
http://www.eggheadcafe.com/tutorial...3bbad6d/visual-studio-async-ctp-overview.aspx
 

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