Home All Groups Group Topic Archive Search About

VB6 Interface implemented in a Delphi COM Object

Author
23 Nov 2007 12:13 PM
Eric
Hi,

We have a VB6 app that, according to the doc's, supports COM add-ons.
These add-ons are required to implement 2 interfaces (from an object in
the VB6 app) and flesh out the interface methods with whatever our
add-on is supposed to do.

VB isn't my first language, so I wrote a Delphi COM object, imported the
VB6 Type library and everything looked good, but when I registered and
installed the DLL into the VB6 app, it promptly crashed.

A few days of digging and I've pinned the problem down to something
falling apart when my object's methods are called. I've writted a VB6
test harness and when I step through the code, when VB6 calls
MyObject.Method1, what gets triggered in MyObject is Method4, the VB6
call to MyObject.Method2 triggers MyObject.Property3 and so on. This
doesn't happen in the Delphi or C# test harness apps - they both work
perfectly. It looks like IDispatch.Invoke is short-circuting somewhere.

I'm wondering if there's anything unusual about a VB COM Interface or if
anyone's come across something like this before. Any ideas or
suggestions would be great.

Thanks

Eric

*** Sent via Developersdex http://www.developersdex.com ***

Author
23 Nov 2007 12:52 PM
Schmidt
"Eric" <nosaint50.at.hotmail.dot.com> schrieb im Newsbeitrag
news:utAw6ocLIHA.4196@TK2MSFTNGP04.phx.gbl...

> VB isn't my first language, so I wrote a Delphi COM object,
> imported the VB6 Type library ...
VB-generated TypeLibs  always derive from IDispatch,
(not from pure IUnknown).
With pure IUnknow your VTable-Offset is 4 (16 Bytes)
With IUnknown and IDispatch your VTable-Offsets
have to be 7 (28 Bytes).
So you will hae to make sure, that your implementation
in Delphi or whatever derive from IDispatch and not
from pure IUnknown.

Best way (without any trouble) is of course, to define
your Interfaces with *.odl or *.idl and use MkTypLib
or Midl to compile from those TypeLib-sources.

This way you could omit the IDispatch-Interface
VB creates under the hood of every COM-Class
(in its own VB-generated TypeLibs) - and the
Implementation should work in VB, C++, Delphi
without problems.

Olaf
Author
23 Nov 2007 2:19 PM
Eric
Hi,

Thanks for the quick response. My Delphi object is derived from
IDispatch, but something still isn't working.
Let me list in short form the steps I took just in case I missed
something.

1. Created a tlb for the VB6 app and found the Addon Interface my object
is supposed to implement. This looks like:

  _AddOn = interface(IDispatch)
    ['{E7979BD7-723E-11D6-AFAC-0050DA682487}']
    procedure Refresh; safecall;
    procedure disconnect; safecall;
    procedure connect; safecall;
    procedure loadConfiguration; safecall;
  end;

  _AddOnDisp = dispinterface
    procedure Refresh; dispid 1610809350;
    procedure disconnect; dispid 1610809351;
    procedure connect; dispid 1610809352;
    procedure loadConfiguration; dispid 1610809353;
  end;

  CoAddOn = class
    class function Create: _AddOn;
    class function CreateRemote(const MachineName: string): _AddOn;
  end;


2) Created a COM DLL Project in Delphi, referencing the VB6 App TLB,
with an object similar to:

type
  TMyObj_AddOn = class(TAutoObject, _AddOn)
  protected
    procedure Refresh; safecall;
    procedure disconnect; safecall;
    procedure connect; safecall;
    procedure loadConfiguration; safecall;
  end;

3) Built, registered this Addon DLL. Then tried a test app in Delphi
with a function like this, which worked as expected:

procedure TForm1.Button1Click(Sender: TObject);
var
  myO : _AddOn ;
begin
  myO := CoAddOn.Create;
  myO.connect;
  myO.Refresh;
  myO.disconnect;
  myO.loadConfiguration;
end;

4) Then tried to replicate this in VB6:

Private Sub Command1_Click()
  Dim MyObject As Object
  Set MyObject = CreateObject("MyDLL.MyObj_AddOn")
'MyObject is created and does point to the DLL I created

  MyObject.Connect             <-- Calls MyObject.Refresh
  MyObject.loadConfiguration   <-- Calls MyObject.Connect
  MyObject.Refresh             <-- Calls MyObject.disconnect
  MyObject.disconnect          <-- VB6 Crashes and Quits

End Sub

The actual interface is slightly larger and includes some properties,
but the really strange thing is if I add a watch in VB6, the information
displayed is correct. EG, a Property called IsActive displays as True in
the Watch window, but if I write a line of VB code bBoolean =
MyObject.IsActive, the value returned is from a completely different
property.

In the meantime, I'll look into MIDL.

Cheers

Eric

*** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 2:35 PM
Eric
Hi,

Quick update on this issue. I've just tried to write a C# Addon for this
VB6 app, but when I add a COM Reference and view it in the Object
Browser, it's different. For some reson, the following has been added to
what I think is the interface:

Sub _VtblGap7_3()
     Member of: VB6App.AddOn._AddOn

Any ideas what this is???

Cheers

Eric

*** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 7:47 PM
Schmidt
"Eric" <nosaint50.at.hotmail.dot.com> schrieb im Newsbeitrag
news:OI3ZQvdLIHA.5140@TK2MSFTNGP05.phx.gbl...

First thought is, you shouldn't work with MyObject LateBound,
as shown in your code here:
> Private Sub Command1_Click()
>  Dim MyObject As Object
>  Set MyObject = CreateObject("MyDLL.MyObj_AddOn")

In my post I assume, that the above was just a typo in
your reply and you've instead worked early bound with
MyObject inside VB (Dim MyObject As Addon).

But if the behaviour below was indeed caused whilst working
LateBound (As Object), then you should look at
your Delphi-IDispatch-Implementation (DispIDs) and
skip the rest of my post.
>   MyObject.Connect             <-- Calls MyObject.Refresh
>   MyObject.loadConfiguration   <-- Calls MyObject.Connect
>   MyObject.Refresh             <-- Calls MyObject.disconnect
>   MyObject.disconnect          <-- VB6 Crashes and Quits




----Only read this, if the "wild jumping" occured with
      an early bound Object---------------
You will have to take the exact Method (or Property-)order
into respect (as it is defined in the original VB-Class, wich contains
the interface-definition).
From your calling-scheme above it doesn't seem to be a fixed
VTable-Offset-Error (wich was my first thought) - instead the
calls "jump around".
The only cause I can imagine is, that the VB-Interface-Class
has a different method/property-order than your Delphi-Interface.
Your order, defined in delphi is:
Refresh
disconnect
connect
loadConfiguration

If in VB, calling a method of the VB-defined-Interface as eg.:
MyObject.Connect - jumps to 'Refresh' inside Delphi (wich
is your first VTable-Entry after IUnknown(3), IDispatch(4)),
then the method 'Connect' is probably the very first entry inside
the VB-defined Class/Interface (just look at the Code-Module).
Your second defined Delphi-Interface-Method is 'disconnect'.
This is jumped on from VB-defined 'Refresh', probably meaning,
that 'Refresh' is the second Method inside the VB-Interface-Def.
etc...
MyObject.disconnect resulting in a crash probably means,
that inside the VB-InterfaceDef 'disconnect' is NOT the second
Entry (as in your Delphi-Interface), instead it is placed somewhere
behind the fourth VB-Method or Property-Definition.

Just look at the code of your VB-Interface - or simply define
a new one, wich has exactly the same order as your Delphi-
Test-Class.
'.e.g. Inside an Empty VB-Class ITest:
Public Sub Refresh(): End Sub
Public Sub disconnect(): End Sub
Public Sub connect(): End Sub
Public Sub loadConfiguration(): End Sub

Olaf
Author
23 Nov 2007 9:35 PM
Eric
Hi Olaf,

Thanks for the help.

This is definitely late binding, so I'm homing in on something in the
registry being wrong.

I asked the VB developers for a sample Addon and they've sent a VB .cls
file. It looks pretty clear and I see there's a line that says
"Implements Addon" and a bunch of methods Sub Addon_Connect, Sub
Addon_loadConfiguration and so on.
On the face of it, I can't see much of a difference between this and the
way I've done it.

Back to the battle on Monday.

Cheers

Eric

*** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 4:47 PM
Ralph
Show quote
"Eric" <nosaint50.at.hotmail.dot.com> wrote in message
news:utAw6ocLIHA.4196@TK2MSFTNGP04.phx.gbl...
>
> Hi,
>
> We have a VB6 app that, according to the doc's, supports COM add-ons.
> These add-ons are required to implement 2 interfaces (from an object in
> the VB6 app) and flesh out the interface methods with whatever our
> add-on is supposed to do.
>
> VB isn't my first language, so I wrote a Delphi COM object, imported the
> VB6 Type library and everything looked good, but when I registered and
> installed the DLL into the VB6 app, it promptly crashed.
>
> A few days of digging and I've pinned the problem down to something
> falling apart when my object's methods are called. I've writted a VB6
> test harness and when I step through the code, when VB6 calls
> MyObject.Method1, what gets triggered in MyObject is Method4, the VB6
> call to MyObject.Method2 triggers MyObject.Property3 and so on. This
> doesn't happen in the Delphi or C# test harness apps - they both work
> perfectly. It looks like IDispatch.Invoke is short-circuting somewhere.
>
> I'm wondering if there's anything unusual about a VB COM Interface or if
> anyone's come across something like this before. Any ideas or
> suggestions would be great.
>

I'm probably missing something, but it looks like you're making it harder
than it should be.

First off, "VB-generated TypeLibs" are NOT "always derive from IDispatch".
The typelibs for VB created COM components are always DUAL Interfaces. (Dual
supports both IUnknown and IDispatch but with subtle differences at this
scale.)

Also I'm a bit confused by why you are importing a typelib from a VB App
into a COM component? If the COM component is a service for the App you
should be Referencing IT within the VB app. BUT - I'm also confused by why
you would be importing anything if your intention is to use Automation or
Late Binding from the VB App.

"Triggers"?

No reflection on your expertise, but the whole thing sounds squirrely. You
are likely over-thinking it. What is it you are really trying to do - and
skip the "typelib" stuff? <g>

But then I'm probably missing something.

-ralph
Author
23 Nov 2007 5:30 PM
Eric
Hi Ralph,

You could be right ... by now, I'm suffering brain cramp like never
before...

The requirement is to add a new function to an existing VB6 app. The VB6
app polls a web service and returns messages. By adding objects, you can
extend this app and register an interest in certain message types. It's
a glorified COM based Observer pattern as I understand it.

According to the developers of the VB6 app, any extension has to
implement a standard interface they provide - that way they can loop and
call the same methods for every registered object, passing a list of
messages to them. We tell the VB app that we are a new object by adding
an entry to an INI file specifying the "DLL.Object" and the message type
we want to get.

It sounds simple enough, but the wheels are falling off somewhere and
I'm not ruling myself out.

>>I'm a bit confused by why you are importing a typelib >>from a VB App
into a COM component?

Well, in theory, this provides me with the Interface definition I have
to implement. That should let the VB app call the functions I've written
via it's interface.

"Triggers"?

Hey-ho Silver ... Ok, it's Friday and late .... Invokes/Calls.

>>I'm also confused by why you would be importing anything >>if your
intention is to use Automation or Late Binding >>from the VB App.

According to the devlopers of the app, they call something like this:

  Dim AddOnObject As Object
  Dim VB6Object As AddOn <-- This is the interface they insist everyone
implements

  Set AddOnObject = CreateObject("DLL.Object") <-- The entry in the ini
file for My Delphi object.

  Set VB6Object = AddOnObject <-- I'm guessing this is a VB6
QueryInterface variation to get at their Addon Interface inside my COM
object

  VB6Object .Connect
  VB6Object .loadConfiguration
  VB6Object .Refresh
  VB6Object .disconnect

I hope that's a bit clearer

Cheers

Eric

*** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 8:58 PM
Ralph
Show quote
"Eric" <nosaint50.at.hotmail.dot.com> wrote in message
news:%237K4bafLIHA.1208@TK2MSFTNGP03.phx.gbl...
> Hi Ralph,
>
> You could be right ... by now, I'm suffering brain cramp like never
> before...
>
> The requirement is to add a new function to an existing VB6 app. The VB6
> app polls a web service and returns messages. By adding objects, you can
> extend this app and register an interest in certain message types. It's
> a glorified COM based Observer pattern as I understand it.
>
> According to the developers of the VB6 app, any extension has to
> implement a standard interface they provide - that way they can loop and
> call the same methods for every registered object, passing a list of
> messages to them. We tell the VB app that we are a new object by adding
> an entry to an INI file specifying the "DLL.Object" and the message type
> we want to get.
>
> It sounds simple enough, but the wheels are falling off somewhere and
> I'm not ruling myself out.
>
> >>I'm a bit confused by why you are importing a typelib >>from a VB App
> into a COM component?
>
> Well, in theory, this provides me with the Interface definition I have
> to implement. That should let the VB app call the functions I've written
> via it's interface.
>
> "Triggers"?
>
> Hey-ho Silver ... Ok, it's Friday and late .... Invokes/Calls.
>
> >>I'm also confused by why you would be importing anything >>if your
> intention is to use Automation or Late Binding >>from the VB App.
>
> According to the devlopers of the app, they call something like this:
>
>   Dim AddOnObject As Object
>   Dim VB6Object As AddOn <-- This is the interface they insist everyone
> implements
>
>   Set AddOnObject = CreateObject("DLL.Object") <-- The entry in the ini
> file for My Delphi object.
>
>   Set VB6Object = AddOnObject <-- I'm guessing this is a VB6
> QueryInterface variation to get at their Addon Interface inside my COM
> object
>
>   VB6Object .Connect
>   VB6Object .loadConfiguration
>   VB6Object .Refresh
>   VB6Object .disconnect
>
> I hope that's a bit clearer
>
> Cheers
>

So you aren't actually adding any code to the VB App?

Then all you have do create a COM component that implements those four
methods:
   Connect
   loadConfiguration
   Refresh
   disconnect
(If there are also parameters then they need to match any signature given.)

The only possible problem I see is perhaps the argument delivered to the VB
App thru the INI file. This argument is referred to as the ProgID or AppID.
There is no 'official' protocol when it comes to naming these creatures. It
depends on the tool that creates the TypeLib. The variable of that type may
be ...
     DllFilename.Class
     TypeLibName.Class
     LibraryName.Class
     ProgramName.Class
     ...
[You can create your own name if you want, and it will work as long as you
register it to point to the correct component.]

When VB calls CreateObject and is making an assignment to an Object
Reference the COMlib is loaded which then uses the Registry to seek out that
ID.
MyComputer\HKEY_CLASSES_ROOT\<LibraryID>.<ClassID>
Under that Key will be a Key called CLSID which gives the ClassID value to
lookup.

For that CLSID there will be an entry for the COM Registration.
You should see at least the following for a component that supports
IDispatch.
     ImprocSeerver32
     ProgID   <- the animal that got you there
     VersionIndependentProgID
     TypeLib <- sometimes
     ...  there may be additional ...

Some tools expose additional "ProgIDs". But they may not actually be a
variable for the correct Interface Type you need.
That's coming out a bit wrong. Basically all I am suggesting is that you
merely trace the argument given manually thru the Registry to insure it
dereferences the expected interface. Also make sure your component is
Registered. (And get rid of any old news. A good registry cleaner helps
here.)

So if you simply create a COM component in Delphi and configure it to
support an IDispatch interface (or Dual). Register it and then set the INI
file to point to the correct ProgID - all should work as planned.

-ralph
Author
23 Nov 2007 9:54 PM
Eric
Hi Ralph,

No, I'm not changing the VB app at all.

My first try at this was creating my own COM object with those methods,
but that crashed and burned as well with a VB error 13 at run-time.
The VB guys said that was because it didn't implement the _Addon
interface and sent me a .cls file as a sample of what to do.

On the face of it, I can't see too much difference between the cls file
and the way I've coded it.

I'm going to trawl the registry - all roads seem to be pointing in that
direction.

Cheers

Eric



*** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 10:26 PM
Ralph
Show quote
"Eric" <nosaint50.at.hotmail.dot.com> wrote in message
news:eAiX8thLIHA.5140@TK2MSFTNGP05.phx.gbl...
>
> Hi Ralph,
>
> No, I'm not changing the VB app at all.
>
> My first try at this was creating my own COM object with those methods,
> but that crashed and burned as well with a VB error 13 at run-time.
> The VB guys said that was because it didn't implement the _Addon
> interface and sent me a .cls file as a sample of what to do.
>
> On the face of it, I can't see too much difference between the cls file
> and the way I've coded it.
>
> I'm going to trawl the registry - all roads seem to be pointing in that
> direction.
>
> Cheers
>

Don't know that much about the newer Delphi.

This might help. What they gave you was a way of creating a specific
interface. It is interesting that they didn't just send a cls file that
exposed those members as the public interface. So in VB you would have is
two Interfaces available (The Public default and I_Addon) Not sure how you
would do that in VB.

The errors you are getting would be identical to what would happen if you
were creating a VB component and merely created a Class and set your
reference to it.

-ralph
Author
23 Nov 2007 11:52 PM
Ralph
Show quote
"Ralph" <nt_consultin***@yahoo.com> wrote in message
news:%23e6N5$hLIHA.4476@TK2MSFTNGP06.phx.gbl...
>
> "Eric" <nosaint50.at.hotmail.dot.com> wrote in message
> news:eAiX8thLIHA.5140@TK2MSFTNGP05.phx.gbl...
> >
> > Hi Ralph,
> >
> > No, I'm not changing the VB app at all.
> >
> > My first try at this was creating my own COM object with those methods,
> > but that crashed and burned as well with a VB error 13 at run-time.
> > The VB guys said that was because it didn't implement the _Addon
> > interface and sent me a .cls file as a sample of what to do.
> >
> > On the face of it, I can't see too much difference between the cls file
> > and the way I've coded it.
> >
> > I'm going to trawl the registry - all roads seem to be pointing in that
> > direction.
> >
> > Cheers
> >
>
> Don't know that much about the newer Delphi.
>
> This might help. What they gave you was a way of creating a specific
> interface. It is interesting that they didn't just send a cls file that
> exposed those members as the public interface. So in VB you would have is
> two Interfaces available (The Public default and I_Addon) Not sure how you
> would do that in VB.
>
> The errors you are getting would be identical to what would happen if you
> were creating a VB component and merely created a Class and set your
> reference to it.
>
> -ralph
>

Oops!
s/"Not sure how you would do that in VB"/"Not sure how you would do that in
DELPHI."/g

<g>
Author
27 Nov 2007 2:34 PM
Eric
Hi,

Just a note to say I've finally got this working ... Adding a few
"Ghost" methods to the imported TLB seemed to be just what was needed.
I'm not quite sure why that is, but having it work is good enough for me
for the moment - when I recover a bit, I'll delve back into it.

Thanks to all who offered help.

Eric


*** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 6:12 PM
Robert Morley
You may want to try asking this in Borland's newsgroups, if you haven't already.

    newsgroups.borland.com

There are several promising-looking groups there.


Rob

Show quote
"Eric" <nosaint50.at.hotmail.dot.com> wrote in message news:utAw6ocLIHA.4196@TK2MSFTNGP04.phx.gbl...
>
> Hi,
>
> We have a VB6 app that, according to the doc's, supports COM add-ons.
> These add-ons are required to implement 2 interfaces (from an object in
> the VB6 app) and flesh out the interface methods with whatever our
> add-on is supposed to do.
>
> VB isn't my first language, so I wrote a Delphi COM object, imported the
> VB6 Type library and everything looked good, but when I registered and
> installed the DLL into the VB6 app, it promptly crashed.
>
> A few days of digging and I've pinned the problem down to something
> falling apart when my object's methods are called. I've writted a VB6
> test harness and when I step through the code, when VB6 calls
> MyObject.Method1, what gets triggered in MyObject is Method4, the VB6
> call to MyObject.Method2 triggers MyObject.Property3 and so on. This
> doesn't happen in the Delphi or C# test harness apps - they both work
> perfectly. It looks like IDispatch.Invoke is short-circuting somewhere.
>
> I'm wondering if there's anything unusual about a VB COM Interface or if
> anyone's come across something like this before. Any ideas or
> suggestions would be great.
>
> Thanks
>
> Eric
>
> *** Sent via Developersdex http://www.developersdex.com ***
Author
23 Nov 2007 10:04 PM
Eric
Hi Rob,

The Borland Groups are an option, but I just wanted to confirm or
eliminate the VB end of it.

It's the one part of this project I know very little about, so if there
were any pitfalls or known "gotcha's", you guys would point it out
quicker than a weasel on a rabbit.

Cheers

Eric

*** Sent via Developersdex http://www.developersdex.com ***

AddThis Social Bookmark Button