Click here to Skip to main content
13,247,336 members (90,254 online)
Click here to Skip to main content
Add your own
alternative version

Stats

10.5K views
93 downloads
18 bookmarked
Posted 8 Jul 2017

Using .NET DLL in VB6

, 8 Nov 2017
Rate this:
Please Sign up or sign in to vote.
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.

[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.

[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.

[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:

<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.

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

[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);
}

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 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)

Share

About the Author

Ismael M.
Brazil Brazil
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionDownload Link Pin
yrenders20-Oct-17 1:26
memberyrenders20-Oct-17 1:26 
AnswerRe: Download Link Pin
Ismael M.8-Nov-17 3:22
memberIsmael M.8-Nov-17 3:22 
Question.net form open vb6 form or the other way round? Pin
martinrj3017-Jul-17 19:58
membermartinrj3017-Jul-17 19:58 
AnswerRe: .net form open vb6 form or the other way round? Pin
Ismael M.18-Jul-17 9:57
memberIsmael M.18-Jul-17 9:57 
Answerfollow this steps Pin
Adams thomas13-Jul-17 1:21
memberAdams thomas13-Jul-17 1:21 
GeneralRe: follow this steps Pin
CHill6016-Jul-17 23:42
protectorCHill6016-Jul-17 23:42 
QuestionThink it will be useful... Pin
glennPattonInThePubAGAIN10-Jul-17 3:43
professionalglennPattonInThePubAGAIN10-Jul-17 3: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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171114.1 | Last Updated 8 Nov 2017
Article Copyright 2017 by Ismael M.
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid