Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / Visual Basic

Using .NET DLL in VB6

Rate me:
Please Sign up or sign in to vote.
4.65/5 (23 votes)
8 Nov 2017CPOL3 min read 74.1K   3.1K   24   17
A generic DLL that call any .NET DLL in VB6.

Introduction

This article has the intention to help teams interested in migrating VB6 applications to .NET (C# or VB.NET) or improve VB6 applications with .NET modern resources. The full migration of a VB6 project to .NET can be costly and complex. The approach described here allows you to convert small pieces of code at time. Another possibility is not to convert any code, but just add the new requirements directly in .NET avoiding increase the legacy application.

Background

There are several articles helping to create .NET user controls to use in VB6 applications. This approach simplifies the process with a generic COM Assembly developed in C# that uses reflection to call any another DLL that we want. With this generic DLL, we just need one registration in the OS and encapsulate a lot of complex code. Not having the need to register several DLL in the OS is useful when you have an environment with several machines.

Using the Code

Creating a COM Assembly

In Visual Studio, create a new Class Library project. In the project Properties, click on the Application tab, click on the Assembly Information button, check the option Make assembly COM-Visible. In the Build tab, check the option Register for COM interop.

Add a new class CSharpInteropService.cs to the project.

The interface below exposes an event used to invoke an action in the VB6 application. It allows to open a VB6 form that hasn't been converted yet.

C#
[ComVisible(true), Guid(LibraryInvoke.EventsId), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ILibraryInvokeEvent
{
    [DispId(1)]
    void MessageEvent(string message);
}

The interface below exposes the method used to call a DLL.

C#
[ComVisible(true), Guid(LibraryInvoke.InterfaceId)]
public interface ILibraryInvoke
{
    [DispId(1)]
    object[] GenericInvoke(string dllFile, string className, string methodName, object[] parameters);
}

The class below is the implementation of the interfaces. GenericInvoke receives the DLL full patch, class name and method name that will be invoked using reflection. You can send an array of parameters. At the end, it is going to return an array. GenericInvoke expects a method called MessageEvent in the DLL target to create a delegate with OnMessageEvent. You need to declare MessageEvent in the target only if you want to communicate back with VB6.

C#
[ComVisible(true), Guid(LibraryInvoke.ClassId)]
[ComSourceInterfaces("CSharpInteropService.ILibraryInvokeEvent")]
[ComClass(LibraryInvoke.ClassId, LibraryInvoke.InterfaceId, LibraryInvoke.EventsId)]
public class LibraryInvoke : ILibraryInvoke
{
    public const string ClassId = "3D853E7B-01DA-4944-8E65-5E36B501E889";
    public const string InterfaceId = "CB344AD3-88B2-47D8-86F1-20EEFAF6BAE8";
    public const string EventsId = "5E16F11C-2E1D-4B35-B190-E752B283260A";

    public delegate void MessageHandler(string message);
    public event MessageHandler MessageEvent;

    public object[] GenericInvoke(string dllFile, string className, 
                                  string methodName, object[] parameters)
    {
        Assembly dll = Assembly.LoadFrom(dllFile);

        Type classType = dll.GetType(className);
        object classInstance = Activator.CreateInstance(classType);
        MethodInfo classMethod = classType.GetMethod(methodName);

        EventInfo eventMessageEvent = classType.GetEvent
                 ("MessageEvent", BindingFlags.NonPublic | BindingFlags.Static);

        if (eventMessageEvent != null)
        {
            Type typeMessageEvent = eventMessageEvent.EventHandlerType;

            MethodInfo handler = typeof(LibraryInvoke).GetMethod
                    ("OnMessageEvent", BindingFlags.NonPublic | BindingFlags.Instance);
            Delegate del = Delegate.CreateDelegate(typeMessageEvent, this, handler);

            MethodInfo addHandler = eventMessageEvent.GetAddMethod(true);
            Object[] addHandlerArgs = { del };
            addHandler.Invoke(classInstance, addHandlerArgs);
        }

        return (object[])classMethod.Invoke(classInstance, parameters);
    }

    private void OnMessageEvent(string message)
    {
        MessageEvent?.Invoke(message);
    }
 }

Registering the COM Assembly

After building the project, you will get a DLL and TLB file. You need to register this assembly using the RegAsm.exe tool (32 bits). This tool is located in your .NET Framework version in C:\Windows\Microsoft.NET\Framework.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase 
"C:\Temp\CSharpInterop\CSharpInteropService\CSharpInteropService\bin\Debug\CSharpInteropService.dll" 
/tlb:"C:\Temp\CSharpInterop\CSharpInteropService\CSharpInteropService\bin\Debug\CSharpInteropService.tlb"

To unregister use /u instead of /codebase.

If you try registering CSharpInteropService.dll from a network location and receive an error, it may be necessary to include the following lines in the regasm.exe.config inside the configuration key:

XML
<runtime>
    <loadFromRemoteSources enabled="true"/>
</runtime>

Invoking a DLL

With the assembly registered, you just need to select it at VB6 project reference. The code below shows how to make a basic call.

VB.NET
Dim param(0) As Variant
param(0) = Me.hWnd 'To made the c# form a child of vb6 mdi
   
Dim load As New LibraryInvoke
Set CSharpInteropServiceEvents = load
  
load.GenericInvoke "C:\Temp\CSharpInterop\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.dll", 
"ClassLibrary1.Class1", "ShowFormParent", param

The C# DLL

C#
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

public void ShowFormParent(long parent)
{
    Form1 form = new Form1();
    form.Show();

    IntPtr p = new IntPtr(parent);
    SetParent(form.Handle, p);
}

private delegate void MessageHandler(string message);
private static event MessageHandler MessageEvent = delegate { };

public static void OnMessageEvent(string message)
{
    MessageEvent(message);
}

Image 1

Network Environment

If you try to load an assembly from a network location and receive an exception (System.IO.FileLoadException), try to create a config file for the VB6 executable. In the case of my sample project, I create a Project1.exe.config for the Project1.exe with the following content:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <runtime>
      <loadFromRemoteSources enabled="true"/>
   </runtime>
</configuration>

Limitations

In the current stage, there is a limitation when a C# form is opened with ShowDialog(). In this case, the event in VB6 is not invoked when the form is open, but before opening the form and after closing it, the communication works.

License

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


Written By
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to send List<T> Pin
mummyildiz26-Mar-23 20:13
mummyildiz26-Mar-23 20:13 
QuestionSatisfactory C- Pin
vadimmarkoff17-Oct-20 9:07
vadimmarkoff17-Oct-20 9:07 
QuestionChange the arguments of GenericInvoke in the COM Assembly Pin
Member 1487712515-Jul-20 0:19
Member 1487712515-Jul-20 0:19 
QuestionGreat tutorial Pin
pm823-Jan-20 22:01
pm823-Jan-20 22:01 
QuestionNot work in other dll Pin
Member 120995617-Sep-19 16:26
Member 120995617-Sep-19 16:26 
AnswerRe: Not work in other dll Pin
Ismael M.24-Oct-19 7:55
Ismael M.24-Oct-19 7:55 
Questionany plan to update? Pin
loquat9-Aug-18 4:03
professionalloquat9-Aug-18 4:03 
PraiseGreat Job! Pin
Massimo Conti7-Jul-18 4:55
Massimo Conti7-Jul-18 4:55 
Questionnull error -2147467261 Pin
TT(n)30-Jun-18 17:57
TT(n)30-Jun-18 17:57 
AnswerRe: null error -2147467261 Pin
Bruce Schiller20-Apr-20 10:31
Bruce Schiller20-Apr-20 10:31 
QuestionDownload Link Pin
yrenders20-Oct-17 0:26
yrenders20-Oct-17 0:26 
AnswerRe: Download Link Pin
Ismael M.8-Nov-17 2:22
Ismael M.8-Nov-17 2:22 
Question.net form open vb6 form or the other way round? Pin
martinrj3017-Jul-17 18:58
martinrj3017-Jul-17 18:58 
AnswerRe: .net form open vb6 form or the other way round? Pin
Ismael M.18-Jul-17 8:57
Ismael M.18-Jul-17 8:57 
Answerfollow this steps Pin
Adams thomas13-Jul-17 0:21
Adams thomas13-Jul-17 0:21 
GeneralRe: follow this steps Pin
CHill6016-Jul-17 22:42
mveCHill6016-Jul-17 22:42 
QuestionThink it will be useful... Pin
glennPattonWork310-Jul-17 2:43
professionalglennPattonWork310-Jul-17 2:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.