Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#3.5

Using Unmanaged (VB6) Code in .NET

4.86/5 (7 votes)
5 Feb 2011CPOL9 min read 71.4K   1.5K  
Using Unmanaged Code in .NET

Introduction

In this article, I will try to explain:

  1. Managed code
  2. Unmanaged code
  3. Runtime Callable Wrapper (RCW)

This article will explain how we can use Unmanaged Code in Managed Environment using Runtime Callable Wrapper.

Managed Code

Managed code is code that is written to target the services of the managed runtime execution environment (like Common Language Runtime in .NET Framework). The managed code is always executed by a managed runtime execution environment rather than the operating system directly. Managed refers to a method of exchanging information between the program and the runtime environment. Because the execution of code is governed by the runtime environment, the environment can guarantee what the code is going to do and provide the necessary security checks before executing any piece of code. Because of the same reason, the managed code also gets different services from the runtime environment like Garbage Collection, type checking, exception handling, bounds checking, etc. This way managed code does not have to worry about memory allocations, type safety, etc. Applications written in Java, C#, VB.NET, etc. target a runtime environment which manages the execution and the code written using these types of languages is known as Managed Code. Managed code is always compiled into an Intermediate Language (MSIL in case of .NET Framework). The compiler used by .NET Framework to compile managed code compiles it into Intermediate Language and generates the necessary metadata, symbolic information that describes all of the entry points and the constructs exposed in the Intermediate Language (e.g., methods, properties) and their characteristics. The Common Language Infrastructure (CLI) Standard describes how the information is to be encoded, and programming languages that target the runtime emit the correct encoding.

In .NET Framework, Managed Code runs within the .NET Framework’s CLR and benefits from the services provided by the CLR. When we compile the managed code, the code gets compiled to an intermediate language (MSIL) and an executable is created. When a user runs the executable, the Just In Time Compiler of CLR compiles the intermediate language into native code specific to the underlying architecture. Since this translation happens by the managed execution environment (CLR), the managed execution environment can make guarantees about what the code is going to do, because it can actually reason about it. It can insert traps and sort of protection around, if it's running in a sandboxed environment, it can insert all the appropriate garbage collection hooks, exception handling, type safety, array bounce, index checking and so forth.

Managed code also provides platform independence. As the managed code is first compiled to intermediate language, the CLR’s JIT Compiler takes care of compiling this intermediate language into the architecture specific instructions.

Unmanaged Code

Code that is directly executed by the Operating System is known as un-managed code. Typically, applications written in VB 6.0, C++, C, etc. are all examples of unmanaged code. Unmanaged code typically targets the processor architecture and is always dependent on the computer architecture. Unmanaged code is always compiled to target a specific architecture and will only run on the intended platform. This means that if you want to run the same code on different architecture, then you will have to recompile the code using that particular architecture. Unmanaged code is always compiled to the native code which is architecture specific. When we compile unmanaged code, it gets compiled into a binary X86 image. And this image always depends on the platform on which the code was compiled and cannot be executed on the other platforms that are different than the one on which the code was compiled. Unmanaged code does not get any services from the managed execution environment.

In unmanaged code, the memory allocation, type safety, security, etc. needs to be taken care of by the developer. This makes unmanaged code prone to memory leaks like buffer overruns and pointer overrides and so forth.

Unmanaged executable files are basically a binary image, x86 code, loaded into memory. The program counter gets put there and that’s the last the Operating System knows. There are protections in place around memory management and port I/O and so forth, but the system doesn’t actually know what the application is doing.

Runtime Callable Wrapper (RCW)

The common language runtime exposes COM objects through a proxy called the runtime callable wrapper (RCW). Although the RCW appears to be an ordinary object to .NET clients, its primary function is to marshal calls between a .NET client and a COM object.

The runtime creates exactly one RCW for each COM object, regardless of the number of references that exist on that object. The runtime maintains a single RCW per process for each object. If you create an RCW in one application domain or apartment, and then pass a reference to another application domain or apartment, a proxy to the first object will be used. As the following illustration shows, any number of managed clients can hold a reference to the COM objects that expose INew and INewer interfaces.

RCW.gif

Using metadata derived from a type library, the runtime creates both the COM object being called and a wrapper for that object. Each RCW maintains a cache of interface pointers on the COM object it wraps and releases its reference on the COM object when the RCW is no longer needed. The runtime performs garbage collection on the RCW.

Among other activities, the RCW marshals data between managed and unmanaged code, on behalf of the wrapped object. Specifically, the RCW provides marshaling for method arguments and method return values whenever the client and server have different representations of the data passed between them.

The standard wrapper enforces built-in marshaling rules. For example, when a .NET client passes a String type as part of an argument to an unmanaged object, the wrapper converts the string to a BSTR type. Should the COM object return a BSTR to its managed caller, the caller receives a String. Both the client and the server send and receive data that is familiar to them. Other types require no conversion. For instance, a standard wrapper will always pass a 4-byte integer between managed and unmanaged code without converting the type.

Step 1: Creating ActiveX DLL using Visual Basic 6.0

In this project, I created an ActiveX DLL using Visual Basic 6.0 (Unmanaged code) which will receive Request from Client (Managed Code) and after processing, unmanaged code will response back to managed code. For the beginners who do not know how to create a DLL using Visual Basic 6.0, I will try my best to explain step-by-step.

  1. Create an ActiveX project using Visual Basic 6.0.

    Active_X_Project.PNG

  2. By Default, It will create Class1.cls and Project1.vbp. Rename Class1.cls to ClsName.cls and Project1.vbp to ProjClsName.vbp from properties window.

    Properties.PNG

  3. If you want to create a COM+ application in this project, then go to Project-->References and Select "COM+ Services Template Library".

    COM_STL.PNG

  4. Now write a function, which receives Name from Managed code (VS.NET application) and this function returns Name with some extra concatenated string.
    VB.NET
    Function Name(aName As String)
       MsgBox ("Your Name is " & aName)
    End Function
  5. Compile your project.
  6. After successful compilation of your code, the next step you need is to create a DLL of your project. For this, go to File--> Make ProjClsName.dll as shown below in figure. This will create DLL in your application folder. You can also specify Version, Comments, etc. to your DLL before Create.

Step 2: Creating Runtime Callable Wrapper of DLL

After creating a DLL, our next step is to make Runtime Callable Wrapper of DLL so that we can use this DLL with our .NET application. The most straightforward way to produce primary interop assemblies is to use the Tlbimp.exe (Type Library Importer). To generate a primary interop assembly using TLBIMP.EXE.

  • At the Visual Studio 2008 command prompt, type:

    tlbimp tlbfile /primary /keyfile:filename /out:assemblyname

    In this command, tlbfile is the file containing the COM type library, filename is the name of the container or file that contains the key pair, and assemblyname is the name of the assembly to sign with a strong name.

Primary interop assemblies can reference only other primary interop assemblies. If your assembly references types from a third-party COM type library, you must obtain a primary interop assembly from the publisher before you can generate your primary interop assembly. If you are the publisher, you must generate a primary interop assembly for the dependent type library before generating the referencing primary interop assembly.

tlbimp.PNG

The simple syntax for creating Runtime Callable Wrapper is:

tlbimp C:\Source.dll /out:C:\Destination.dll

Step 3: Communication Between Managed & Unmanaged Code

After creating a DLL in Visual Basic 6.0 & creating a Runtime Callable Wrapper, our next step is to create an application using Visual Studio .NET (Client Application) that will load this runtime callable wrapper (Imported DLL, which is ComClsName.dll) and will use. For this:

  1. Open Visual Studio.NET.
  2. Select Language of your choice. I selected C#.NET. By this, I just want to explain how C#.NET interacts with Visual Basic 6.0 code.
  3. I am just creating a simple Windows application here with a simple Text box which will receive name from user and a simple button which will send user entered
    name to our unmanaged code (ComClsName.dll).

    Create_Form_Design.PNG

  4. After creating Form, now we need to add comClsName.dll in Reference as shown below:

    Add_Refrence.PNG

  5. Go to the Application folder path of your Visual Basic 6.0 Project where you placed ComClsName.dll after creating Runtime Callable Wrapper using TLBIMP.EXE.

    Add_TLBIMP_Imported_File_ComClsName.PNG

  6. Select comClsName.dll and click ok to add selected DLL to your reference folder of your Windows Form project.
  7. After adding ComClsName.dll to your project, add namespace for comClsName.dll in
    code file by using "using" keyword.
    C#
    using ComClsName;  
  8. Now we need to add some code on Click event of button. When user will input the name in text box and will click the button, our .NET application will call the Name function of comClsName.dll and will return Name with concatenation of:
    C#
    private void button1_Click(object sender, EventArgs e)
     {
         ClsName UnManagedCode = new ClsName();
         string aName = this.textBox1.Text;
         UnManagedCode.Name(ref aName);
     }  
  9. Here, I initialized the instance of class ClsName. String aName will receive name supplied by user in text box and call Name function of comClsName.dll with parameter aName as ref. The ref keyword causes argument to be passed as reference.
  10. Build and run the project now.

    Run_The_Project.PNG

  11. When you will click the Button "Call unmanaged Function", it will initiate the unmanaged class ClsName declared in comClsName.dll, will pass name provided in text box as parameters to Name function of class ClsName, and will show a message box with text "YOUR NAME IS" defined in Name function of Visual Basic Project as shown below:

    Return.PNG

Summary

In this article, I tried to explain managed code, unmanaged code, Runtime Callable Wrapper, creating RCW of DLL using TLBIMP.EXE and how managed code (.NET code) interacts with unmanaged code (Visual Basic 6.0, Visual C++ 6.0), etc.

History

  • 5th February, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)