Passing objects between FoxPro and
.NET COM Components
By Rick Strahl
www.west-wind.com
rstrahl@west-wind.com
Last Update:
September 14, 2012
Other Links:
Download Examples for this article
Leave a Comment or
Question
Other articles in the series:
1.
Passing objects between FoxPro and
.NET COM Components
2.
Handling .NET Events in FoxPro via COM Interop
3.
Creating multi-threaded components
.NET for COM Interop with Visual FoxPro
This is Article 1 of a 3 part series on COM Interop between .NET and Visual
FoxPro.
COM Interop between Visual FoxPro
and .NET seems trivial at first, but the devil is in the details. Simple COM
calls using methods and properties with simple parameters are easily
accomplished. However, once you start dealing with complex objects - objects
with hierarchies or specific object types required by .NET as parameters or
properties, you start running into problems. This article looks at some of the
issues that you need to look out for when dealing with objects in applications
that call .NET COM objects from Visual FoxPro.
A while back I wrote an article on the basics of calling
.NET COM components from Visual FoxPro. This article introduced the basics
of creating .NET Components, exposing them to COM and then calling the COM
component from Visual FoxPro. In this new series of articles, I’ll expand on
those original concepts by demonstrating a few more complex Interop scenerios.
This time I’ll look at a few more advanced issues of COM
interop, starting with a close look at passing complex objects between Visual
FoxPro and .NET.
A quick review of the basics
Let’s start with a quick review of how COM Interop works in
.NET. In order to provide backwards compatibility the .NET runtime provides the
ability to publish most .NET components as COM objects.
The .NET runtime accomplishes this through the COM Callable
Wrapper (CCW), which is a wrapper around a plain .NET type that exposes it
through COM. The idea is that you simply create a standard .NET component and
then export that component to COM.
The process works like this:
- You create a .NET component
- You optionally mark up the component with attributes
to tell it how to export to COM
- You run the TlbExp utility to create a Type Library
- You run RegAsm to register the component with COM
A very simple class exported to COM is shown in Listing 1.
using System.Runtime.InteropServices;
…
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("DotnetCom.FirstServer")]
public
class FirstServer
{
public
string HelloWorld(string
Name)
{
return
"Hello " + Name +
" " +
DateTime.Now.ToString();
}
}
Once you’ve compiled the code,
you can use the RegAsm utility is a special COM registration tool that is
specific to .NET and is required to register .NET COM components. .NET
components require a special registry key for locating the assembly to
instantiate and RegAsm registers the component properly. If you’re developing in
VS.NET, it handles all the details of registration for you. All you need to do
is check the ‘Register for COM Interop’ flag and VS will do all the work for
you. If you deploy your application you will need to somehow register the
component through RegAsm, either through an Installer that knows about .NET COM
objects, or by manually running RegAsm as a post install step. For more details
see the article
mentioned earlier.
Once registered the component
becomes available as a COM object and you can easily instantiate it using
CreateObject() in Visual FoxPro.
loDN = CREATEOBJECT("DotNetCom.FirstServer")
? loDN.HelloWorld("Rick")
Behind the scenes the .NET Runtime is invoked when you
instantiate this object and the .NET runtime in turn instantiates your .NET
component for you, with the CCW acting as a proxy between the COM interfaces and
the actual .NET Component. Figure 1 shows the workings of this process.
Figure 1 – Hosting a .NET COM component in Visual
FoxPro loads the .NET runtime into VFP and then uses the Com Callable Wrapper to
marshal object access across COM.
Once you have a reference to the object you can simply fire
away on the COM interface, calling methods accessing properties and fields and
generally work with the interface. For the most part this process is very
straight forward.
Things you need to watch out for are
- The .NET runtime locks into VFP
Once you load a .NET COM component you can’t unload this component
completely. This means during .NET COM development you have to shut down
Visual FoxPro to reload a .NET COM component.
- .NET Components may not unload related resources
The .NET Garbage Collector cleans up objects and object state inside of the
.NET instance running. This means when you delete an object the actual
object and it related resources may unload immediately. For example, if you
load a Bitmap object which has associated memory buffers, and simply clear
the reference, the associated memory buffer data does not release
immediately. Using the Dispose() method, when available, can help here to
explicitly unload related resources.
- Not all types work in Visual FoxPro
Some data types – specifically value types and some specialized collection
types – don’t work in Visual FoxPro when accessed over COM. Also some array
types passed from COM to Visual FoxPro cannot be modified and sent back with
new or removed elements.
- Passing types from FoxPro to .NET is usually not
type safe
Unless you have VFP COM components that you pass to .NET you cannot easily
pass strongly typed objects created in Visual FoxPro to .NET. FoxPro objects
pass as generic ‘object’ references and have to be accessed with Reflection
in most cases.
All of the topics above were covered in my previous
article, so if this is new to you take a look at
this article.
Passing Complex types between Visual FoxPro and .NET
When working on business applications the process of
passing data in the form of objects between Visual FoxPro and .NET is crucial.
Passing object data from .NET to Visual FoxPro is fairly easy, and you can
simply consume most objects passed from .NET in Visual FoxPro. To consume a .NET
object you simply pass it back from .NET.
public Customer
ReturnCustomer()
{
Customer Cust = new
Customer();
Cust.Name = "Rick
Strahl";
Cust.Company = "West
Wind";
Cust.oAddress.StreetAddress =
"32 Kaiea
Place\r\nPaia, HI 96779";
Cust.oAddress.Phone =
"808 579-1234";
Cust.CreditLimit = (decimal)
10000.50;
return Cust;
}
You can access this object in Visual FoxPro easily enough
simply by accessing the property values. To ensure that the .NET object has
Intellisense support make sure you define the class definition for any type you
might return with the following attribute.
[ClassInterface(ClassInterfaceType.AutoDual)]
public
class Customer { … }
FoxPro can see most .NET objects without any problems. Some
limitations include value types or structures. Visual FoxPro can’t access these
objects because they are passed by value rather than by reference and this
functionality is not supported through the IDispatch COM interfaces used by VFP.
For your own objects this likely won’t be a problem, but it might be for some
internal .NET types which often use internal value types for representing data.
Passing objects to .NET is more tricky
Probably the biggest issue with COM Interop to .NET from
Visual FoxPro is the lack of type support when passing complex objects to .NET.
When you pass a FoxPro object to .NET that object comes across as a generic
object type. object in .NET is similar to a Variant; it’s a generic
type that is dynamically interpreted at runtime. In order to access this object
in .NET you have to use Reflection, which is a dynamic runtime type discovery
and invocation mechanism.
Let’s look at a simple scenario. Let’s say you have a
dynamically constructed object in FoxPro and you pass it to .NET. Listing 2
shows an example of this scenario.
loNet =
CREATEOBJECT('DotNetCom.DotNetComPublisher')
*** Create a Customer Object Instance (factory method)
loCustomer = GetCustomer()
loCustomer.Name
= "Rick Strahl"
loCustomer.Company = "West
Wind Technologies"
loCustomer.creditLimit =
9999999999.99
loCustomer.oAddress.StreetAddress = "32 Kaiea Place"
loCustomer.oAddress.Phone =
"808 579-8342"
loCustomer.oAddress.Email = "rickstrahl@hotmail.com"
loNet.PassRecordObject(loCustomer)
RETURN
FUNCTION
GetCustomer
LOCAL
loCustomer, loAddress
loCustomer =
CREATEOBJECT("EMPTY")
ADDPROPERTY(loCustomer,"Name","")
ADDPROPERTY(loCustomer,"Company","")
ADDPROPERTY(loCUstomer,"CreditLimit",0.00)
ADDPROPERTY(loCustomer,"Entered",DATETIME())
loAddress =
CREATEOBJECT("Empty")
ADDPROPERTY(loAddress,"StreetAddress","")
ADDPROPERTY(loAddress,"Phone","")
ADDPROPERTY(loAddress,"Email","")
ADDPROPERTY(loCustomer,"oAddress",loAddress)
RETURN
loCustomer
Note that this is a VFP 8 and later EMPTY object with
properties added, but it could also be a CUSTOM class you designed in the class
designer or created in a PRG with fixed property, or even a SCATTER NAME object
– it really doesn’t matter how this object was created as long as it is a FoxPro
class. I like EMPTY classes for ‘message’ style objects because the object
interface doesn’t include default members like Name, Class etc.
In .NET then you have a method that receives this object in
a method that is shown in Listing 3.
public
string PassRecordObject(object
FoxObject)
{
// *** using raw
Reflection
string
Company = (string)
FoxObject.GetType().InvokeMember(
"Company",
BindingFlags.GetProperty,null,
FoxObject,null);
// *** using the easier
ComUtils class
string Name
= (string)
ComUtils.GetProperty(FoxObject,"Name");
// *** using ComUtils
'ex' functions to use . Syntax
string
Address = (string)
ComUtils.GetPropertyEx(FoxObject,
"oAddress.StreetAddress");
return
Name + Environment.NewLine +
Company + Environment.NewLine +
Address + Environment.NewLine;
}
Notice that the method receives a parameter of type
object, which is the only way that you can send a pure FoxPro object to
.NET. Because the object is not strongly typed you have to use Reflection to
access the object’s properties and methods. Reflection contains functionality
that allows to ‘reflect’ over an object and its members and ‘invoke’ them to get
or set property or field values or calling a method with parameters and
receiving a result value.
Reflection is a bit tedious as you can see in the first
extraction line in the method in Listing 2. It’s also much slower than direct
method or property access. To facilitate the syntax some, I introduced a class
ComUtils that simplifies the process of accessing object members more easily via
Reflection wrapper methods. While using Reflection is a hassle it works fine.
The ComUtils class as well as the samples shown here are included in the code
download.
Things get problematic if the .NET method you are trying to
call requires you to pass an object of a specific type. What if the signature of
the above method looks like this:
public
string PassCustomerObject(Customer Cust)
{
return
Cust.Name + Environment.NewLine +
Cust.Company + Environment.NewLine +
Cust.oAddress.StreetAddress;
}
In this example, .NET has a Customer class definition
(shown in Listing 4) that matches the FoxPro class described in Listing 2 and
the method expects an object of this type as a parameter. This makes the .NET
code a lot cleaner for sure; no more Reflection. But unfortunately it now
becomes impossible to call this method directly from your FoxPro code. There’s
no way that you can cast your FoxPro object to this .NET customer object.
The only workaround for this common problem is to not use
FoxPro objects, but rather to use COM objects to pass the data to .NET. Remember
that the .NET project is exposing its objects to COM so you can explicitly
return a DotNetCom.Customer object. A partial listing of the .NET Customer
object is defined in Listing 4.
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("DotNetCom.Customer")]
public
class Customer
{
// Using properties here
to allow databinding
public
string Name
{
get {
return _Name; }
set
{ _Name = value;
}
}
string
_Name = "";
public
string Company
{
get {
return _Company; }
set {
_Company = value; }
}
string
_Company = "";
.. addition properties omitted
public Address oAddress
{
get {
return _oAddress; }
set {
_oAddress = value; }
}
Address _oAddress =
null;
}
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("DotNetCom.Address")]
public
class Address
{
public
string StreetAddress
{
get {
return _StreetAddress; }
set {
_StreetAddress = value; }
}
string _StreetAddress
= "";
… more members
}
With this object definition exposed to COM, you can now
create it in FoxPro and pass it back to .NET, simply by changing the GetCustomer
method in the FoxPro code shown in Listing 2 to:
FUNCTION GetCustomer
LOCAL loCustomer
as DOTNETCOM.Customer
loCustomer = CREATEOBJECT("DotNetCom.Customer")
RETURN loCustomer
In addition, we have to change the call from
PassRecordObject to PassCustomerObject:
#IF !USE_COM_CUSTOMER
? loNet.PassRecordObject(loCustomer)
#ELSE
?
loNet.PassCustomerObject(loCustomer)
#ENDIF
The rest of the code in Listing 2 can be left alone. And
voila, we can now pass a strongly typed object parameter to .NET.
Instead of directly instantiating the object, you can also
do this with factory methods on the .NET object that returns an instance of the
object in question. This may be desirable with business objects or complex
objects that have child objects that must be initialized before being ready for
use.
Passing Visual FoxPro COM objects to .NET – it works, but…
In the previous example, I created a .NET COM object, and
passed that back to the .NET class. This means that .NET has to expose this
object to COM in order for this mechanism to work.
What about the other way around? What if an object is defined in Visual FoxPro
as COM and .NET needs access to it via parameters or return values? Is this
possible? Yes and no. In some simple situations it works, but there are some
serious limitations.
You can compile any Fox objects that you might need to pass
to .NET into a COM object. Once you have built your COM object you can import
that FoxPro COM object into .NET using TlbImport (or in Visual Studio simply
pick the COM object), which creates .NET wrapper classes for each of the COM
objects imported. Once this is done, you can use these wrapper types as input
parameters and result values for functions in .NET. When called, COM and the CCW
perform the proper type conversions for you.
This approach works fine as long as you can expose all of
your objects into COM and you are willing to take the hit for using COM objects
explicitly for all object references that are passed to .NET. Note that you
should use CREATEOBJECTEX() for each COM instantiation to ensure the object is
created as true COM object rather than a FoxPro object.
loCustomer = CREATEOBJECTEX("MyApp.Customer","")
loNet.PassCOMCustomerObject( loCustomer )
Understand the limitations
While this sounds reasonable, it’s important to understand
that Visual FoxPro’s COM implementation has some major limitations when dealing
with complex objects. It’s not possible in Visual FoxPro to create COM object
hierarchies that are represented in the type library. Even simple compound
objects like the Customer/Address object relation I showed above cannot be
presented as COM hierarchy with Visual FoxPro natively (unless you hack the type
library). There’s no way for FoxPro create a member object that is exposed as a
specific COM type. This means you can’t use this approach even in the simple
example shown above. It only works with flat objects.
Another major problem is that Visual FoxPro cannot define
COM method signatures that return COM objects that are defined in the current
project. You can only reference COM objects that are externally available. This
means you can’t create COM objects that do things like this:
DEFINE CLASS Customer
as Custom OLEPUBLIC
FUNCTION Create() as
MyProject.Customer
RETURN CREATEOBJET( "MyProject.Customer"
)
FUNCTION GetAddress(PK
as integer) as
MyProject.Address
RETURN CreateObject( "MyProject.Address"
)
ENDDEFINE
DEFINE CLASS Address as
Custom OLEPUBLIC
...
ENDDEFINE
The code above fails because MyProject.Customer and
MyProject.Address exist in the current project and FoxPro looks in the registry
for these COM objects. Since you are currently compiling these projects they
don’t exist and aren’t registered yet. VFP will compile the type library for the
GetAddress object as returning a generic object variant.
Why am I showing this here? After all we’re talking about
calling .NET COM objects from Fox code, not the other way around. Well, .NET
objects might need to call back into your FoxPro objects to perform business
logic or do other tasks and this is where it’s important that data can be passed
around both ways and that’s where this situation becomes problematic.
A hack of a workaround:
Calvin Hsia, lead developer on the
FoxPro team, posted a WebLog entry that describes a work around solution to the
strong typing problem, by using Help String Values and explicitly assigning the
ProgId references and type library type definitions. It works but it requires a
separate tool you need to run (or hook with a project hook) everytime you
compile. It also requires VS.NET in order to work. You can find
this article on
Calvins Blog.
Another, more work intensive solution – wrapper types
In my own work I have a FoxPro based product called
Help Builder that provides COM interoperability by extending the product
with Add-ins that can be written in .NET (as well as FoxPro). The Add-in
architecture provides access to the Help Builder ‘business object’ and IDE
component. The user can automate these objects via custom Add-ins. Help Builder
is a FoxPro application, and the Add-In Processing essentially passes a
reference to the IDE object to the Add-in both in .NET and the FoxPro Add-in
engines.
Assuming you are a .NET developer and you want to create an
Add-in with this API, you wouldn’t want to use Reflection to access the
functionality. FoxPro’s COM type library export was of little use because the
type library created could not reflect the hierarchy of objects. The root IDE
object contains a reference to the Help business object, which in turn contains
a reference to the current topic which contains each of the actual topic fields.
I needed a solution to make this object available in a
strongly typed manner. It’s a FoxPro object and there’s no way for me to first
instantiate the object in .NET, so I had no choice but to use this FoxPro COM
object in .NET. So how can I expose this object as a strong .NET type?
The solution for me was to create a set of wrapper classes
that work by retrieving data from a generic COM object reference. The idea is
that I create wrapper instances of each of the objects that I want to expose –
wwHelpForm (IDE), wwHelp (Help), wwHelpTopic and so on. Each of these objects
has a special constructor that receives an instance of the generic COM object
when it is instantiated. The instance is stored and then each of the methods and
property definitions calls into this instance using Reflection return data or
call methods on the original COM object.
There is a base class that provides the plumbing and
implementation classes that make the appropriate Reflection calls. Listing 5
shows the wwHelpComWrapper base class.
public
abstract class
wwHelpComWrapper : IDisposable
{
///
<summary>
///
The underlying COM instance for this Wrapper Object
///
</summary>
public
object ComInstance
{
set
{
this.m_Inst
= null; //
clear ref count
this.m_Inst
= value;
}
get
{
return
this.m_Inst;
}
}
protected
object m_Inst=
null;
///
<summary>
///
Releases the underlying COM instance
///
</summary>
public
void Release()
{
Marshal.ReleaseComObject(m_Inst);
m_Inst = null;
}
///
<summary>
///
Releases the COM pointer. Usually not necessary
///
but can be used in Automation scenarios against a
///
Top Level Com reference.
///
</summary>
public
void Dispose()
{
if (this.m_Inst
!= null)
this.Release();
}
}
This simple class only has a member for storing the COM
reference and for releasing the object cleany. The concrete class
implementations each provide a custom constructor that takes a COM object
reference as a parameter and implements an optional factory method to allow
quick creation of the object. Listing 6 shows an excerpt of the wwHelp class
implementation.
public
class wwHelp : wwHelpComWrapper
{
public
wwHelp(object wwHelpInstance)
{
this.m_Inst
= wwHelpInstance;
}
public
static wwHelp CreateInstance()
{
try
{
object
HelpBuilder =
Activator.CreateInstance(
Type.GetTypeFromProgID("wwHelp.wwHelp"));
return
new wwHelp(HelpBuilder);
}
catch
{}
return
null;
}
public
bool OpenForm(string
Pk, TopicTypes TopicType)
{
return
(bool)
wwUtils.CallMethodCom(this.m_Inst,
"OpenForm",Pk,TopicType);
}
public
bool OpenForm(string
Pk)
{
return
this.OpenForm(Pk,TopicTypes.Pk);
}
public
string Filename
{
get {
return (string)
wwUtils.GetPropertyCom(m_Inst,"cFileName");
}
set {
wwUtils.SetPropertyCom(m_Inst,"cFileName",value);
}
}
…
}
The static CreateInstance method is a factory method that
basically uses late binding to instantiate the COM object and automatically
creates the wrapper type and returns an instance of it. This greatly simplifies
construction of the object instance that is ready to go for accessing the COM
functionality.
The constructor receives a generic COM object instance as a
parameter and stores it in the m_Inst property. Any access to a class property
or method then simply calls out via Reflection against this instance and calls
the underlying member in the COM object. So to retrieve a property
wwUtils.GetPropertyCom (which is identical to ComUtils.GetProperty) is called
with m_Inst as the object reference to retrieve the value from. To call a
method, wwUtils.CallMethod() is called.
Repeat for all the properties of the object I want exposed.
While this is a tedious, manual process, it actually turned out that manually
creating the wrapper allowed cleaning up the interface and made it more .NET
compliant. For example, all the Fox properties have type prefixes (cFilename,
oTopic etc.) which is not common practice for .NET objects. I was able to remove
the prefixes for my new property names so now there are Filename and Topic
properties instead. I was able to filter out some members I didn’t want to have
show up in the Add-in interface and I was able to create overloaded methods to
handle parameter settings that are more logical than the FoxPro variable
parameter lists required. Overall I ended up with a more natural and cleaner
class interface than the raw COM interface.
Finally I can also express hierarchical object
relationships. The wwHelp object includes a Topic member which is defined like
this:
public
wwHelpTopic Topic
{
get
{
return
new wwHelpTopic(
wwUtils.GetPropertyCom(m_Inst,"oTopic")
);
}
}
This creates a new instance of wrapper object from the
wwHelpTopic object and passes it back to the caller. Once all this is done, I
can do the following in .NET code to access my object directly with very little,
natural feeling code:
string TopicId =
"_012dda125";
wwHelp Help = wwHelp.CreateInstance();
Help.Load(TopicId);
string Body =
Help.Topic.Body;
This example actually loads the COM object. However, in the
Add-in code the object is passed from Visual FoxPro to .NET. The add-in itself
in FoxPro is launched by calling into a set of helper methods in .NET. The
FoxPro code is minimal and is shown in Listing 7.
FUNCTION
ActivateNetAddin(loAddin)
lcFileName =
LOWER(TRIM(loAddin.FileName))
LOCAL
aiFactory as
wwReflection.AddinFactory
LOCAL
ai as
wwReflection.AddinExecution
aiFactory =
CREATEOBJECT("wwReflection.AddinFactory")
ai = aiFactory.CreateAddin()
Result = ai.Execute(TRIM(loAddin.Filename),TRIM(loAddin.ObjName),;
TRIM(loAddin.ObjMethod),goHelp)
IF
!Result
this.SetError(ai.ErrorMessage)
aiFactory.UnloadAddin()
RETURN .f.
ENDIF
aiFactory.UnloadAddin()
RETURN
.t.
This code calls yet another wrapper class – wwReflection
which I also introduced in the last article – to dynamically invoke a method in
an assembly. The goHelp parameter in the Execute call is an instance of the
Help Builder IDE. It’s the root reference and the parameter the Add-in receives.
The object is passed as a plain Visual FoxPro object.
The key code in the Execute method takes the goHelp COM
instance and creates a wwHelpForm COM wrapper instance from it, which is then
passed to the user created add-in. The following snippet demonstrates the
dynamic instantiation and method call in .NET:
object Instance =
Activator.CreateInstance(TypeRef);
// *** Convert object into
strongly typed object
Westwind.wwHelp.wwHelpForm Form =
new
Westwind.wwHelp.wwHelpForm(Parameter);
bool Result = (bool)
Instance.GetType().InvokeMember(
BindFlags,null,
Instance,new
object[1] { Form } );
return Result;
The final step is to create an actual Add-in. Here’s a very
simple Add-in that demonstrates the wrappered interface:
public
bool Activate(wwHelpForm HelpForm)
{
// *** Demonstrate objects
wwHelp Help = HelpForm.Help;
wwHelpTopic Topic = HelpForm.Help.Topic;
// *** Let's update the
topic text
Topic.Body = DateTime.Now.ToString() +
"\r\n"
+ Topic.Body ;
HelpForm.Help.SaveTopic();
MessageBox.Show( HelpForm.Config.FtpServer );
// *** Refresh the topic
display
HelpForm.GoTopic();
return
true;
}
The HelpForm parameter is the Help Builder IDE – the goHelp
parameter that was passed from FoxPro, stored into the wwHelpForm wrapper. Once
it arrives this single object gives access to the entire Help Builder IDE and
API giving the user full access to automate the interface and do whatever they
need.
So there you have it – a strongly typed representation of a
FoxPro object, that exposes a complex object hierarchy and doesn’t rely on
Visual FoxPro type library support. It’s not an automatic process to build these
wrapper objects, nor is it the most efficient way to access them, but it
provides the most flexibility and functionality that otherwise is simply not
available.
On the downside this approach requires some diligence to
make sure that if the FoxPro objects change that the .NET wrapper object is kept
in sync. It also relies on your ability to create and extend the .NET object.
You need to be able to use these new wrapper classes in the .NET code. This
approach obviously won’t work if you are given a .NET Object that you have no
control over.
No Control over the .NET code
If you have no control over the .NET code and you need to
call into the .NET object, but can’t because you can’t coerce the type, then
what do you do? The last straw is to create a .NET wrapper that calls into the
.NET object on the behalf of your FoxPro object. The idea is that the wrapper
becomes the proxy that translates between the parameters or property values that
are accessible to VFP and the ones that .NET uses and cannot be accessed by VFP.
For example, if .NET returns a value type you might create a mirrored class that
duplicates the value types properties and pass that to and from FoxPro. Again
this can be a lot of manual grunt work, but it can get you past the hurdles if
need be.
Objects are farther than they appear
COM Interop with .NET is easy in concept, but if you go
beyond the basics you’ll find that the issues are more complicated than it first
appears. Most of the issues revolve about Visual FoxPro’s limited ability to
create appropriate type libraries for anything but the simplest types.
These are serious issues that you have to consider and
should understand before you embark on a large scale COM Interop project.
Passing data between the two environments is a crucial aspect of Interop. The
devil’s in the details – know the limitations before you get too deep and make
sure you can work around them.
In this first article in this series I’ve looked closely at
a fairly complex topic. In the next installment we’ll a little more fun when
I’ll look at Event Handling and Multithreading with .NET COM Components.
Comments or Questions
|