|
Introduction
In this article I cover the area Interoperability issues. There
is no doubt that with the help of .NET one can create powerful
components and Distributed applications than any other language.
But we have to think over about the past reusable components,
which were created by many languages such as VB etc.
Is it the usage of those past components is end after evolving
of .NET?
No we can use those components in the .NET and the .NET types
in the Classic COM clients. Are there any possibilities of communication
between managed and unmanaged types? Yes it is possible to make
it possible to use existing COM objects (Unmanaged) from within
managed applications and expose-managed objects to existing COM
(Unmanaged) applications. Now let us see those things in detail.
In the first part of this article Part1, I focus on how .NET types
calling c DLLs (Win32 API). In the Part2, I illustrate how you
can Build a .NET Server Callable from COM clients and then in
the latter Part-3, I will explain you that how you can Build a
.NET Client That Uses a COM Server. Confidently, at the end of
the article, you'd have achieved enough information to understand
how Classic COM and the .NET framework can peacefully co-exist
together. Hence if you're geared up, let's take an expedition
through travel around how Classic COM can be used in the .NET
world.
The .NET and COM Mediator
.NET runtime affords COM Interoperability wrapper for overcoming
the differences between .NET and COM environments. For example,
runtime make an instance of COM Callable Wrapper (CCW) when a
COM client accesses a .NET component. In the same way, an instance
of Runtime Callable Wrapper (RCW) is formed when a .NET client
accesses a COM component. These wrappers abstract the dissimilarity
and provide the faultless incorporation between the two environments.
Part1 - .NET TYPES CALLING C DLLs (Win32 API)
Platform Invoke:
The platform invoke services offers a method to call functions
that are exported from an unmanaged DLL. The most distinctive
use of PInvoke is to allow .NET components to interact with the
Win32 API. PInvoke is also used to access functions exports defined
in custom DLLs.
To exemplify the use of PInvoke I create a C# class that makes
a call to theWin32 Message Box () function. Before we move into
the C# class let us see an example program in VB 6.
The C prototype:
int MessageBox(Hwnd hwnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType);
Parameters
hwnd ---> Handle to the dialog's owner.
lpText ---> The text you wish to display
lpCaption ---> The caption of the message box.
wType ---> The dialog definition. Ex: vbYesNo
Returns ---> Returns the value of the button that was clicked.
VB Declaration:
Private Declare Function MessageBox
Lib "user32" Alias "MessageBoxA"_
(ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As
String,_
ByVal wType As Long) As Long
Private Sub Command1_Click()
Dim ar As Long
ar = MessageBox(hwnd, "Msgbox using API",
"Arungg", vbInformation)
End Sub
In the above example program, I show you how you can use the Windows
API in VB 6.The Visual Basic MsgBox() function actually wrap the
MessageBox API. You don't need to use this API; in fact, it would
just be unnecessary work. However to understand the usage of API,
I show you the above example.
Let us progress to how to call Win32 MessageBox() function from
a C# class using PInvoke.
namespace APIExample
{
using System;
// Must refernce this library to use
PI nvoke types
using System.Runtime.InteropServices;
public class PinvokeClient
{
[DllImport("user32")]
public static
extern int MessageBox(int hWnd,
String pText
,
String pCaption
,
int uType);
public static
int Main(string[] args)
{
String pText = "HELLO INDIA!!";
String pCaption = "Example by Arungg";
MessageBox(0,pText,pCaption,0);
return 0;
}
}
}
Explanation:
Before calling a C-style Dll we have to declare the function to
call using the static and extern C# keywords. After this you have
to specify the name of the raw DLL that contain the function you
are attempting to call, as shown here.
[DllImport("user32")]
public static extern int MessageBox(.......);
After declare the DLL Pass the arguments such as pText,pCaption.
It should be clear that it does not matter in which order you
specify the values.
In the above way one can use .NET types calling any type of raw
C DLLs (Win32 API). This comes to an end of Part1 and I think
the users now know how to call a raw C DLLs (Win32 API) using
PInvoke in .NET. This is an end of Part1.
Part 2 - Build a .NET Server Callable from COM Clients
This article elucidates how to build and install-managed code
that will be used from COM applications. A classic COM server
is activated using the Service Control Manager (SCM). It looks
up numerous information such as CLSIDs, IIDs, ProgIDs etc.
So what is the solution to use the .NET assemblies in the classic
COM clients,
These are the steps concerned in the build process are as follows:
- Write and compile the managed code.
- Generate a COM type library (*.tlb) for the assembly using
the tlbexp.exe utility so that allow the COM client to interact
with the exposed types.
- Install and register the assembly so that COM SCM to locate
it.
- Write and compile the COM code that references types in the
assembly.
For demonstration purposes, I have created a .NET component in
C# named Calculator. For client side, I have created a Visual
Basic (VB) 6.0-based client.
Writing and Compiling the Managed Code:
To illustrate COM type communication with managed code, Let us
see an example in which I create a C# class library which has
a class named Calculator which supports three methods named Add(),Subtract()and
Hello(). Notice that we define another interface named Imuldiv.
namespace Simpleclasslib
{
using System;
using System.Runtime.InteropServices;
public interface Imuldiv
{
int Multiply(int
x,int y);
int Division(int
x,int y);
}
public class Calculator:Imuldiv
{
public Calculator(){}
public int Add(int
x,int y)
{
return x+y;
}
public int Subtract(int
x,int y)
{
return x-y;
}
int Imuldiv.Multiply(int
x,int y)
{
return x*y;
}
int Imuldiv.Division(int x,int y)
{
return x/y;
}
public string Hello(string strName)
{
string str ;
str = "Hello " + strName ;
return str ;
}
}
}
Once the managed code is written, the compilation process is the
same as it would be for any other piece of managed code.
csc /out:Server.dll /target:library
Calculator.cs
Now let us see the Server.dll in the ILDasm.exe.In that you can
see the calculator class members and Imuldiv Interface members.
Generating a Type Library and Register the Assembly:
After compiling the project we have to create a type library file.
So that only most unmanaged application development tools require
a type library before you can make references to external types.
COM states that all server application that is programmed to share
their features with other applications is to be register at a
common location. The client needs to determine the exposed methods,
properties and events. This is done via the libraries. The client
reads the registry; determine the properties, methods, and event
s of the object. COM requires up information such as CLSIDs, IIDs,
and ProgIDs etc. But we know the assemblies are not registered.
.NET framework does not depend on the registry and uses metadata
for this information. Hence, we have to produce the COM-compatible
registry entries for our managed server so that the COM runtime
could instantiate our server.
The .NET framework provides a couple of tools for this. You can
use the Type Library Exporter utility (TLBEXP.exe) or the Assembly
Registration Utility (Regasm.exe), both of which you'll find in
the Bin directory of your .NET SDK installation.
REGASM is a superset of the TLBEXP utility in that it also does
much more than generating a type library. It's also used to register
the assembly, so that the appropriate registry entries are made
to smooth the progress of the COM runtime and the .NET runtime
to fastener up the COM aware client to the .NET component.
Usually a type library can be generated from an assembly using
the regasm.exe utility. The regasm.exe utility not only registers
an assembly and it also creates the required type library file,
as shown here.
regasm Server.dll /tlb:Netserver.tlb
After creating the type library file you have to add a reference
this to your project. For example, with Visual Basic 6.0, you
can reference the .tlb file or dll file from the Project/References
dialog. In Visual C++ 6.0, you can use the #import statement to
import the type definitions from the type library directly into
C++. Once the reference to the type library is added to the project,
the types defined within that library can be referenced from unmanaged
code.
Installing The Assembly:
In order to actually create managed types from unmanaged code,
the assembly needs to be installed in the global assembly cache
(GAC) and registered for use from COM.
You can install an assembly in the global assembly cache using
gacutil.exe utility. Assemblies can be uninstalled using the /u
option.
gacutil /i simpleserver.dll
Writing and Compiling the Unmanaged Code:
Once the assembly is registered and properly installed, the types
defined within the assembly can be used from COM as though they
were normal COM types.
Now you can create a simple VB 6 Standard EXE project type and
as I already said confirm whether you set a reference to the new
generated type Library. Place this code in the code section and
now you see the usage of our .NET type in Classic COM.
Private Sub Command1_Click()
Dim a As New Calculator
MsgBox a.Add(100, 100), vbInformation,
"ADDITION (100,100)"
MsgBox a.GetType, vbInformation, "INFORMATION-TYPE"
MsgBox a.Hello("India"), vbInformation,
"STRING"
MsgBox a.Subtract(100, 1), vbInformation,
"SUBTRACTION(100-1)"
Dim i As Imuldiv'INTERFACE
Set i = a
MsgBox i.Multiply(100, 100), vbInformation,
"MULTIPLICATION(100*100)"
MsgBox i.Division(100, 100), vbInformation,
"DIVISION(100/100)"
End Sub
Summary:
A lot of companies have spent a great amount of money and time
in COM components. With introduction of .NET people are worried
about the future of COM. Microsoft has recognized this and provide
means to use classic COM components in .NET code and vice versa.
Part 3 - Build a .NET Client that calls a COM Server
Introduction:
In this article I cover the area how to use a COM server in a
.NET client. Existing COM components are precious resources to
your managed applications. So now let us observe how you can build
a .NET Client that uses a COM Server.
The steps involved in the build process are as follows:
- Write and compile the unmanaged code.
- Generate an assembly containing definitions of the COM types
using the tlbimp.exe utility so that allow the .NET client to
interact with the exposed types.
- Install the assembly in the global assembly cache. (Optional)
- Write and compile the .NET client code that reference the
assembly containing the COM type definitions.
1. Write and compile the unmanaged code.
Open an ActiveX DLL project workspace in VB 6.0 and put the below
code in code window and name the project as VBServer and Class
to Add
Public Function Add (ByVal x
As Integer, ByVal y As Integer) As Integer
Add = x + y
End Function
Finally save your workspace and compile the VBServer to VBServer.dll
In the OLE/COM Object Viewer we can see the name of default custom
interface(Add) as well as a number of other COM interfaces implemented
by VB.
2. Importing the Type Library:
The .NET Framework needs metadata for individual COM types at
both compile time and run time. Your choice for generating metadata
is the Type Library Importer (TlbImp.exe) utility generates an
assembly containing metadata. When you carry out this tool on
a type library it generates a standard runtime callable wrapper,
based on the contents of the type library.
tlbimp VBServer.dll /out:NetClient.dll
Now we create the NetClient.dll using the Type Library Importer
utility.
If you open the NetClient.dll with the ILDasm.exe.
In that You notice the _Add interface and Add coclass are mapped
as .NET equivalents.
3. Install the assembly in the global assembly cache. (Optional)
To make assembly to be shared among several applications, it must
be installed in the global assembly cache (GAC). Use gacutil.exe
to install an assembly in the GAC.
gacutil /i NetClient.dll
4. Write and compile the .NET client code with reference to
the assembly
During compilation we have to reference the assembly using the
compiler /r switch or we can add reference to the project directly
from Visual Studio.NET development tool. Even by using Reflection
one can use the COM server (Late Binding). I planned to cover
this topic in another article.
csc TestClient.cs /r: NetClient.dll
The .NET client: (Early Binding)
namespace Addvbserver
{
using System;
using NetClient;
public class TestClient
{
public static int Main(string[]
args)
{
Add c = new Add();
Console.WriteLine(c.Add(2,2));
return 0;
}
}
}
In the above code You notice that all members of a Add interface
are accessed directly from an object instance. If you want to
explicitly reference the underlying _Add interface you have to
write the code as below.
namespace Addvbserver
{
using System;
using NetClient;
public class Client
{
public static
int Main(string[] args)
{
Add c = new Add();
_Add gg =c;
Console.WriteLine(gg.Add(2,2));
return 0;
}
}
}
Conclusion:
I hope after reading the three parts regarding Interoperability
issues one can gain some good knowledge over communication between
managed and unmanaged world and vice versa.
About the Author:
G. Gnana Arun Ganesh is the Administrator and the Founder of ARUN
MICRO SYSTEMS (www.arunmicrosystems.netfirms.com).
He has been programming in C++, Visual Basic, COM, Java and Microsoft
Technologies for 4 years. He has written over 50 articles on .NET
published in the top C# websites such as CsharpCorner, CsharpHelp,
MSDNAA, Code Project, Developersdex, 411asp.net, ProgrammersHeaven,
VB-World, and Devguru etc. He has performed numerous technical
reviews for Prentice Hall, Prentice Hall PTR and Sams. He has
also created real time projects in Web Services. Currently reviewing
a book on .NET Security and put forward a couple of proposals
on .NET and in C#. You can contact him for technological support
and queries through ggarung@rediffmail.com |