using LLBLGen from VB6

Posts   
 
    
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 16-Jan-2006 19:47:28   

I thought I would be the last person in the world to ask for this.

I am consulting with a company that have a big investment software build using VB6/SQL. Currently they are using inline-SQL for all their data-access frowning

I know that it is possible to build .NET DLLs that can be called from VB6, but the question here is: Can I build a DAL/BL DLLs and then reference those from a VB6 project. The issue to consider here is: 1- Licensing; is there any special licensing considerations I have to consider? 2- Structural; are there any structural reasons why I can't apply the standard .NET techniques (used to reference a .NET dll in VB6) on LLBL's runtime libraries?

Of course, advice from anyone tried this would be greatly appreciated...

jtgooding
User
Posts: 126
Joined: 26-Apr-2004
# Posted on: 16-Jan-2006 21:35:47   

We have successfully, encapsulated our business layer to be callable from our VB6 legacy application.

We built our business layer in our normal .Net fashion then created an interop DLL to expose any business functions, data transfers etc. that we wanted to expose.

We have taken it as far as replacing forms/dialogs with .Net equivilants, that can be interoped from VB6.

From the users perspective they don't even realize (other than the controls are nicer looking) that say the Salesman Edit form is now a C# .Net form.

John

omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 17-Jan-2006 05:01:47   

jtgooding wrote:

We have successfully, encapsulated our business layer to be callable from our VB6 legacy application.

We built our business layer in our normal .Net fashion then created an interop DLL to expose any business functions, data transfers etc. that we wanted to expose.

We have taken it as far as replacing forms/dialogs with .Net equivilants, that can be interoped from VB6.

From the users perspective they don't even realize (other than the controls are nicer looking) that say the Salesman Edit form is now a C# .Net form.

John

My usual setup for a typical .NET/LLBL application is: 1- generate DAL (Adapter model) 2- generate BL (JCL templates based on extended entity templates) that references both DAL dlls 3- create a UI project that refernces BL and DAL.DBGeneric dll

What I want to do is treat the VB6 project like a UI project. I want to build the DAL dlls and the BL dll and reference all these dlls from the VB6 project.

Is that similar to your experience? and how smoot was it to create the interop DLL for your .NET dlls?

jtgooding
User
Posts: 126
Joined: 26-Apr-2004
# Posted on: 17-Jan-2006 16:26:17   

I do the following:

DB layer - modified LLBLGen templates BL Layer - Encapsulates calls to the DB, and provides validation for all business objects, this is where I expose typed lists, business processes, and validation. GUI Layer - I created a framework of user controls that can have parent/child relation ships, this calls the BL and never the DB.

COM Layer - this exposes specific calls to the BL, or instatiates the GUI layer and presents it to the user. This layer handles any data translations needed for VB6 etc. I keep this interface the simplest possible.

In this way my normal 3 layers are all .Net designed and can be used from any of our .Net applications, and the COM exposes just the functionality I need to expose to VB6. This in my opinion is the key to it working, you can't expose a complex object through COM without taking some pretty big performance penalties.

I expose things in my COM layer like a fetch routine, that will instatiate the BL layer, call a retrieval method, massage any data if required and return it to VB6 for display in a VB6 grid.

Other examples include, long business processes as a simple call, or a method that causes a .Net form to appear just as if I had called it from .Net which encapsulates all the business logic and load/save functionalities where possible.

The COM stuff is really basic here is a coding example, I removed most of the logic and put in a comment placeholder but it should be enough to give you the idea, this piece gets put in a file called SHIP.COM.Leads:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

using SHIP.Framework;
using SHIP.Framework.Exceptions;
using SHIP.Framework.GUI;
using SHIP.Framework.Security;

using SHIP.BL.Leads.SalesmanMileage;
using SHIP.GUI.Leads.SalesmanMileage;

namespace SHIP.COM.Leads.Interop
{
    /// <summary>
    /// Provide an interface so we get intellisense in VB6
    /// </summary>
    [Guid("REPLACE WITH UNIQUE GUID")]
    public interface IFormAddMileage
    {
        int LoadForm(int salesRepID, int leadID, DateTime apptDate);
        int ShowForm(bool modal);
        int UnloadForm();
        int AddMileage(int salesRepID, int leadID, DateTime apptDate, double mileage);
    }

    [Guid("REPLACE WITH A SECOND UNIQUE GUID")]
    [ClassInterface(ClassInterfaceType.None)]
    public class AddMileageHandler : IFormAddMileage
    {
                // private vars here

        public AddMileageHandler()
        {
        }   

        #region IFormAddMileage Members

        public int LoadForm(int salesRepID, int leadID, DateTime apptDate)
        {
            int RetVal = 0;
            try
            {
                               // instantiate our GUI and BL here
                RetVal = 1;
            }
            catch (Exception ex)
            {
                ExceptionManager.Publish(ex);
                RetVal = -1;
            }
            return RetVal;
        }

        public int ShowForm(bool modal)
        {
            int RetVal = 0;
            try
            {
                if (_AddMileageDialog != null)
                {
                    if (modal)
                    {
                        _AddMileageDialog.ShowDialog();
                    }
                    else
                    {
                        _AddMileageDialog.Show();
                    }
                    RetVal = 1;
                }
                else
                {
                    RetVal = -2;
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Publish(ex);
                RetVal = -1;
            }
            return RetVal;
        }

        public int UnloadForm()
        {
            int RetVal = 0;

            try
            {
                if (_USRAddMileage != null)
                {
                    _USRAddMileage.Dispose();
                }
                if (_AddMileageDialog != null)
                {
                    _AddMileageDialog.Dispose();
                }
                RetVal = 1;
            }
            catch (Exception ex)
            {
                ExceptionManager.Publish(ex);
                RetVal = -1;
            }
            finally
            {
                _USRAddMileage = null;
                _AddMileageDialog = null;
                _Manager = null;
            }
            return RetVal;
        }

        public int AddMileage(int salesRepID, int leadID, DateTime apptDate, double mileage)
        {
            int RetVal = 0;
            try
            {
                               // instantiate the BL and do a process that would normally be in .net gui

                if (_Manager.SaveSalesmanMileageExpenseBill())
                    RetVal = 1;
                else
                    RetVal = -2;
            }
            catch (Exception ex)
            {
                ExceptionManager.Publish(ex);
                RetVal = -1;
            }
            finally
            {
                _Manager = null;                
            }
            return RetVal;
        }
        #endregion

        }
}

The VB6 side of this looks like this:

        Dim MileageForm As SHIP_COM_Leads_Interop.IFormAddMileage

        Set MileageForm = New SHIP_COM_Leads_Interop.AddMileageHandler

        Call MileageForm.LoadForm(CInt(TDBGrid1.Columns(eGrid.SalesRepID).Value), mlLeadID, mdApptDate)
        Call MileageForm.ShowForm(True)
        Call MileageForm.UnloadForm

and

    Dim SalesmanMileage As IFormAddMileage
    Set SalesmanMileage = New AddMileageHandler
    SalesmanMileage.AddMileage SalesRepID, LeadID, ApptDate, Mileage
    Set SalesmanMileage = Nothing
    AddSalesmanMileage = True


omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 17-Jan-2006 18:07:42   

thaks John... this really helps... I might come back with some questions if you don't mind... smile

yin
User
Posts: 13
Joined: 23-Feb-2006
# Posted on: 03-Mar-2006 16:26:49   

Ok that's pretty good. But what if i want to expose the rich objects and performance is not a concern? Wish for the people scripting VBA to have the benefit of the object model and don't really want to recode the whole bang shoot.

Ideas?

jtgooding
User
Posts: 126
Joined: 26-Apr-2004
# Posted on: 03-Mar-2006 17:12:33   

You can expose rich objects, but you are setting yourself up for a world of hurt, COM is order specific, so say you add a new property to a table for an Entity you exposed, you must not break compatibility, otherwise VB will attempt to modify the wrong property.

Things like overloads do not come through to VB6 as well, and you must write special handlers for events.

it IS possible, but the support and debugging issues can far out weigh writting a wrapper module, to gain intellisense, maintanability, and clarity of usage.

GetEntity GetEntity2 GetEntity3 etc. aren't very clear, especially when you can't see the parameter list on the VB6 side to know which is which.

John

yin
User
Posts: 13
Joined: 23-Feb-2006
# Posted on: 03-Mar-2006 19:12:13   

Surprisingly not particularly bothered by the usual com problems. there are few clients and i can rebuild those apps quickly.

Trouble is the Decimal type on the COM is not supported by VBA. There is no default interop handling for that type.

That is the part of the problem i need to fix.

The most obvious way to fix this is to change the mappings of the relevant fields from Decimal to double. VBA does ok with doubles.

Know what is involved in changing a type mapping of a number column in sqlserver from Decimal so it appears in code as double?

cheers

yin
User
Posts: 13
Joined: 23-Feb-2006
# Posted on: 14-Mar-2006 11:28:24   
Rushmore
User
Posts: 125
Joined: 27-Jan-2005
# Posted on: 29-Sep-2006 10:26:20   

jtgooding wrote:

I do the following:

DB layer - modified LLBLGen templates

What have you modified in the LLBLGen templates and in which templates. Does ist work in both directions? I have read some articels, that it is a little be tricky, to pass rich object structures to .NET rage , whereas retrieving objects is pretty simple simple_smile .

Those articels I have read are relating to VFP flushed .

Regards, Carlo