Introduction
Typically, the available tools that provide interoperation between .NET code and VB6 code are based on proxies, wrappers or other forms of middleware that allow VB6 applications to call .NET Winforms. So far, we haven't seen a solution that allows us to do the opposite. That is, to embed VB6 forms in a .NET application. For that reason, we decided to create that solution on our own. At MasterSoft, we have many applications developed in Visual Basic 6 in which we invested several thousands of development hours. Right now we face the necessity of migrating them to .NET in a gradual way and with the least amount of effort.
To reach that goal, we envisioned a kind of interoperation between VB6 and .NET that would allow us to open VB6 forms in a .NET application. This would give us the opportunity to migrate the applications gradually, incorporating new functionality with "native" .NET code and maintaining intact � or as intact as possible � everything that's written in VB6. The main advantage of this strategy is in leveraging a .NET framework developed by ourselves, which gives us powerful tools for menu designing and security management, among many other things. All these benefits would be lost if we maintained the core of our applications in VB6.
The solution we applied is based on a .NET MDI environment from which we can open VB6 forms contained in a DLL, just as if they were MDI children of the .NET form. Instead of using toolkits or power packs for .NET-VB6 interop, we used instances of a .NET container form in which we embed the VB6 forms. By using API functions, the normal behavior of the embedded forms is simulated.
Implementation details
To use VB6 forms from within a .NET application, we opted to develop a "shell" (container) form in VB.NET with MDI child functionality. This creates an instance of the VB6 form through a specific class in the DLL ActiveX, which is ClsForms. Using API functions and communication with the DLL, the VB6 form is instanced and embedded in the container.
Within the VB6 DLL, there is a public class that does the job of instancing the forms and communicating with the .NET application. This class acts as a controller of the instantiated forms in the DLL. It provides access to them through several methods that help the container to behave as the VB6 form would in a VB6 MDI container. Due to the fact that the VB6 forms reside in a DLL ActiveX, we needed to make sure that their MDIChild property is set to False because we couldn't figure out how to change that property at runtime. In order to manage the events of the VB6 form, the .NET container uses a Timer control that constantly queries the VB6 class for messages to handle.
A big issue we needed to solve was the communication between VB6 forms. In many cases, our application forms call each other, passing parameters and results -- of searches, for instance -- between them through properties. In order to keep this behavior in the .NET application with the VB6 forms, it was necessary to give the VB6 forms the chance to create new instances of the container and embed in them other VB6 forms. This was done as explained below.
Showing the sequence
To make things a bit more encapsulated, we develop a FormController class in the .NET project which maintains the instance of the DLL and creates multiple instances of the .NET container. Thus, we can open multiple forms using the same instance of ClsForms.
Dim objFormController As New FormController(objMDIForm, "FormsVB6")
objFormController.OpenForm("frmChildForm")
The best way to show the interaction between objects is with this UML sequence diagram:

The OpenForm function in the FormController creates an instance of the ClsForms class in the ActiveX DLL -- the one that contains all the VB6 forms -- and then instantiates the frmContainer form. Finally, it calls its OpenForm method. The OpenForm method in the container in turn calls the OpenForm method in ClsForms. It does this in order to create a new instance of the form's name, passed by argument using the hidden VB6 Forms.Add("formname") instruction.
When the form instance is created, the OpenForm method returns the handle of the instance to the container. With that handle and through API functions, as well as through other methods of the ClsForms class, the VB6 form is "drawn" inside the .NET container. The OpenForm container obtains the property values of the VB6 form such as Title, Width, Height, etc. in order to replicate them in the container, i.e. the GetWindowTitle in the diagram above. The container form is also responsible for checking the state of its embedded form. In this way, it can know if the VB6 form has been closed by an Unload Me instruction and thus can close the container.
Form behavior
In order to mimic the event firing from the VB6 form, the container uses a Timer control to constantly check if there are events in the VB6 form that need to be handled. We could have chosen to pass the events directly from the VB6 form to the .NET application, but that would have implied a much deeper modification of those forms' code, which was exactly what we wanted to avoid.
To give the VB6 form the ability to open "child" non-modal forms and keep its condition of MDIChild, it is necessary to use the ShowForm function in VB6 instead of the usual .Show method. The ShowForm function does all the process needed to keep the called child form in the .NET environment within its container. That is basically all the work that needs to be done with the VB6 forms in order to embed them in a .NET container.
ShowForm(frmChildForm, True)
ShowForm(frmModalForm, False)
So we need only to replace the Show calls in the code to ShowForm(), passing the form we want to show and a second boolean argument indicating if you want it to be an MDI child or not. If so, it calls the OpenForm in the container by passing a message "OpenForm" to it. If not, it just opens the form as a regular modal form.
To preserve the normal behavior of the VB6 forms and their respective containers as MDI child forms � for instance, the normal use of Ctrl-Tab, the focus change between forms or the fact that the .NET MDI Form keeps active to open other .NET or VB6 forms � we needed to use various API functions like SendMessage, SetForegroundWindow and LockWindow.
To enhance this technique, we are planning to add code to the intermediate class that sits between the .NET MDI form and the container. This would allow event firing from VB6 that affects the .NET environment. Examples would be modifying the Caption property of the MDI Form, opening .NET "native" forms, etc.
References
We use a function from Stephen Kent to change the form border style at runtime.
Using the code
The code is pretty simple and is commented where I think it's needed. We didn't use advanced programming techniques, only a few API calls to do the "magic."
Conclusion
This way of interoperating between .NET and VB6 allows us to start a gradual migration path to .NET for our applications that won't force us to make immediate modifications to them. It also instantly adds many of the benefits of the .NET framework we developed to the applications. As an additional feature, this gradual migration path allows the end users to learn gradually how to use the new features that the .NET version of the application offers, without being forced to learn all the changes at once.
Special thanks go to Gustavo Du Mortier and Mariano Aranda from MasterSoft; they really help and colaborate with this.
History
- 27 June, 2007 -- Original version posted
- 12 July, 2007 -- Article edited and moved to the main CodeProject.com article base
|
|
 |
 | Set frmContrainer.vb top of all but as modeless abdh | 4:12 27 Oct '09 |
|
 |
help, after testing the sample code it work fine but i discoverd after adding a panel on the MDI form that the frmContrainer is drown under it and this is upnormal from normal .net forms in my application that can easly drown above all the panel and controls when it first initialised and opend, any help might solve this issue ,im trying to go arround the APi func but seems useless 
|
|
|
|
 |
 | active x error tenro | 6:52 14 Aug '08 |
|
 |
when i even try to run your sample open vb form i get this error See the end of this message for details on invoking just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text ************** System.Exception: Cannot create ActiveX component. at Microsoft.VisualBasic.Interaction.CreateObject(String ProgId, String ServerName) at NetForms.FormController.GetInstance() in C:\Documents and Settings\******\Desktop\wow\NETProject\NetForms\Classes\FormController.vb:line 20 at NetForms.FormController.OpenForm(String strFormName) in C:\Documents and Settings\*******\Desktop\wow\NETProject\NetForms\Classes\FormController.vb:line 30 at NetForms.frmMain.mnuVB6Form_Click(Object sender, EventArgs e) in C:\Documents and Settings\******\Desktop\wow\NETProject\NetForms\Forms\frmMain.vb:line 17 at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e) at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e) at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e) at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e) at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met) at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met) at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea) at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea) at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ScrollableControl.WndProc(Message& m) at System.Windows.Forms.ToolStrip.WndProc(Message& m) at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
its coming from this line mobjActiveXInstance = CreateObject(mstrActiveXLib & ".ClsForms") mstractive at the time is "FormsVB6"
|
|
|
|
 |
|
 |
You have to run the VB6 project first so the .NET project can instantiate the VB6 project's ClsForms class.
Hope this helps.
|
|
|
|
 |
|
|
 |
|
 |
I got the same error and running the VB6 project didn't work out. Any idea?
|
|
|
|
 |
|
 |
If you run the VB6 project you should be able to create an instance of the ClsForms class from the .NET project. Place a breakpoint in the line mobjActiveXInstance = CreateObject(mstrActiveXLib & ".ClsForms") and check if values are correct. If you still have prblems to instantiate the VB6 ClsForms class you should check the VB6 project properties and ClsForms properties to make them available for the CreateObject function.
|
|
|
|
 |
 | AccessViolations and Application-Hangs Geert Teugels | 22:52 12 Jun '08 |
|
 |
We are developing a similar application : ~5000 ActiveX-VB6-dll's of which 20% has been converted to VB.Net 2.0. We used a different technique to keep the window-forms modal : WindowHooks, WndProc and SetActiveWindow.
We experience however AccessViolations causing the application to hang or close unexpectedly.
Our question to you is whether you are experiencing the same (or similar) problems with your solution. If not, we might try to implement your solution.
|
|
|
|
 |
 | VB6 interop in CAB and SCSF screens RathiV | 20:28 15 May '08 |
|
 |
Hi, Can you please let me know if i can invoke a VB6 form from my CAB screen (Composite UI Application block). My requirement is not to use Winforms but use forms developed using CAB. Any help is most welcome.
Thanks a lot, Rathi
|
|
|
|
 |
|
 |
Sorry but I didn't try the CAB yet. I think it's possible but you'll need to change al least some of the .net code.
|
|
|
|
 |
|
 |
Hi, Thanks for the input. Can you please let me know, if i can embed the VB form in my windows forms control like Web Browser control and navigate to multiple VB6 forms?
Thanks, Rathi
|
|
|
|
 |
 | Why I can't create a child form which is not midchild in the FormVB? dick.mei | 20:41 19 Sep '07 |
|
 |
I tried to create a child form in frmChildForm as you suggested, but It doesn't work.
Private Sub Command3_Click() ShowForm frmChildForm2, False End Sub When I press button Command3, nothing is happened. And I can't figure out the cause even in debug mode.
Do you know why?
|
|
|
|
 |
|
 |
Ok you found two bugs, please correct the ShowForm function to
Public Sub ShowForm(ByRef objForm As Form, ByVal blnMDIChild As Boolean) If blnMDIChild Then SendMessage "OpenForm", objForm.Name Else objForm.Show vbModal End If
End Sub
the Not in the condition was wrong and then the
strMessage = mobjClsFormsInstance.GetMessage(strParameters)
line in frmContainer form in the .NET project to
strMessage = mobjClsFormsInstance.GetMessage(mintFormHandle, strParameters)
Now you can open a form as a modal form using ShowForm(Form1, False) or as another MDI child using ShowForm(Form1, True) The first bug is a wrong condition and the second is breaking the communication between .NET and VB6
Thank you for the message
|
|
|
|
 |
|
 |
Thank you for your reply! It's very helpful!
I have studied your sample for about 4 hours, and find a little problem. When I open the FromVB again, an exception occurrs. Well I can fix it myself. My concern is that is this solution strong enough to handle all the problems. You know in a product there may be many vb forms and maybe one show another, modal or non-modal. If I invoke the vb forms in .net forms, do they act the same with what they acted in vb environment. Is there any big problems that can't be fixed by this solution?
|
|
|
|
 |
|
 |
I don't really it's a fully stable solution yet. As it uses Win32 APIs, I don't think all the behaviours, like focus or key shortcuts, are beeing well handled. I started with it cause we need to migrate a big project from VB6, but the migration stopped for now so I couldn't continue this work.
|
|
|
|
 |
 | When you press the TAB key, the focus does not change from one control to another control. victorleo | 7:52 1 Sep '07 |
|
 |
Hello.
Apply east example and you have the following problems
When you press the TAB key, the focus does not change from one control to to another control.
When you press ENTER on to command button that there are the focus, the Click event of the command button does not occur.
Looking for in Internet on this encontre this I articulate http://support.microsoft.com/kb/925369/en-us in Microsoft.
According to I articulate To supported hotfix is now available from Microsoft, but it is only intended to correct the problem that this article you describe. Apply it only to systems that plows experiencing this specific problem.
If somebody has the bookstores they podrian to give them to me.
Victor
|
|
|
|
 |
|
 |
Have obtained the HotFix but I follow such with problems
|
|
|
|
 |
|
 |
Is there a work around for this?
Also, is there anyway to communicate between forms of VB 6 and .Net?
|
|
|
|
 |
|
 |
Hi,
We are having this problem, but with VB6 Forms under .NET; I'm not sure I understand you were saying... Did the hotfix worked ?
|
|
|
|
 |
|
 |
Good day guys actually i didn't try the hotfix yet because i couldn't download it. but i have an idea may it will be useful to you 1. add two textboxs to VB.net Container which contains the vb6 form. 2. change BorderStyle property to None, BackColour to Control, and ReadOnly to True. now you have two TextBoxs invisible in the form. you will see that when you press tab index it will not go to the next form but the problem now is you can't go to the next control in vb6 3. if you print this code in vb6
Private Sub Form_KeyPress(KeyAscii As Integer) If KeyAscii = 13 Then SendKeys "{Tab}" end if End Sub it will not work lol because Tab key not sensitive in Vb6 also in VB.net so if anyone know how it will be sensitive we will solve this problem.
question is hotfix work verywell or what?
Mhd Sam Kouka
|
|
|
|
 |
|
 |
What hotfix are you all refering too. I am having the same problem and am wondering if this will even work for my app. Are you referring to http://support.microsoft.com/kb/925369[^] if so, that doesnt apply to windows 7.
|
|
|
|
 |
 | Suggetions VeNoM00 | 23:25 27 Jun '07 |
|
 |
Try
Catch ex As Exception
MsgBox(ex.Message)
End Try What's that?  Anyway, you can't interact with the VB6 form (or at least you didn't explained how), you can just insert it as an MDI child. What can do a form alone? This would be useful just in the case that form is a stand-alone application itself. Moreover this technique seems too complex to me. Wouldn't it be simpler transfer the code from the vb6 form into an activex control and then embed it in .net winform? In this way you shouldn't make all that api calls and manage the form better. Don't you think?
|
|
|
|
 |
|
 |
Ok, sorry about the try...chatch, it's horrible. As I explain in the article, the main goal of this solution was to use MDI child forms written in VB6 touching as less as we can of the VB6 code. The applications we build use a layered architecture, so VB6 forms are nearly stand-alone as you say, they communicate with a business layer to process the loaded data. This way we simply moved the forms from the Exe to an ActiveX Dll, switch their MDIChild properties to False and we can open them from a NET MDI. (Don't forget to replace the Show method to ShowForm, so you can open another MDI child from the VB6 code). Imagine that our VB6 applications got about 100 forms (and we reuse them, there's not repeated code), so moving the content of the forms to an activex control and create a fixed shell of each one in the NET side would take a lot. This technique allow us to add new functionality (new forms) in the NET side and gradually migrate the VB6 forms to NET with much less effort (consider the product realeases).
It's the first version, I have a large list for performance and comunication improvements to make. I'd like to receive suggestions too.
|
|
|
|
 |
|
|