Creating Components - How am I doing?

Everything else that doesn't fall into one of the other PB categories.
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Creating Components - How am I doing?

Post by GedB »

Hello PB Gurus,

I'm trying to get to grips with Interfaces.

Heres my work so far. I'd be grateful if you could critique the work so far.

Once I've got the hang of this I'm going to write up a tutorial, so your help will be appreciated.

A few points to note:
  • * I'm avoiding the word 'Object' in favour of 'Components'. I think calling these things Objects is one of the things thats causing so much confusion. It gives people false expectations.

    * At the moment I'm dropping all my instances into a List. Better memory management is needed. I'm thinking that perhaps the DLL events could be useful here. Any tips/musings on this?

    * Initially I though that I could create a single virtual table as a global variable, and then share it between all instances. However, this kept causing crashes so instead I had to use a new VT for each component. In theory a shared VT should be no problem, any ideas why this approach doesn't work?

Code: Select all

;IPBFriendly is the interface.  Programs will use this interface to 
;access the components methods 
Interface IPBFriendly
  SayHello()
  SayGoodbye()
  TellMyName(Name.s)
EndInterface

;VTPBFriendlyFunctions is IPBFriendly's Virtual Table. 
;The virtual table is used to look up a method's address.
;When Component\Method is encountered the address of the
;method's function is looked up in the VirtualTable.
Structure VTPBFriendlyFunctions
  SayHello.l
  SayGoodbye.l
  TellMyName.l
EndStructure

;PBFriendly is the structure for the Components implementation.
;It must have an entry for the VirtualTable, and any private fields
;that the Component will need to maintain its state.
Structure PBFriendly
  *VirtualTable.VTPBFriendlyFunctions
  Name.s
EndStructure

;We now define the Procedures that will be used for the Components
;methods.
Procedure SayHello(*Self.PBFriendly) 
  If *Self\Name > ""
    MessageRequester("Friendly", "Hello " + *Self\Name + ".")
  Else
    MessageRequester("Friendly", "Hello.  I'm afraid we haven't been introduced")
  EndIf
EndProcedure

Procedure SayGoodbye(*Self.PBFriendly) 
  If *Self\Name > ""
    MessageRequester("Friendly", "Goodbye " + *Self\Name + ".")
  Else
    MessageRequester("Friendly", "Goodbye.  It's a shame we didn't get to know each other better")
  EndIf
EndProcedure

Procedure TellMyName(*Self.PBFriendly, Name.s) 
  *Self\Name = Name
  MessageRequester("Friendly", "Pleased to meet you, " + Name + ", I'm Purebasic.")
EndProcedure

; Each instance of our component will need to have a unique VirtualTable
; and Implementation structure.
; For convenience I am putting both into a 'Holder' structure.
Structure PBFriendlyHolder
  VT.VTPBFriendlyFunctions
  Impl.PBFriendly
EndStructure

; Instances is a linked list that will be used to keep all of the 
; Holders for our instances.
NewList Instances.PBFriendlyHolder()

; The Components contructor.  The creates a new Holder within the 
; Instances list and prepares it.  The Holder's Impl field is returned
; For the user to manipulate through the interface, rather than the whole
; holder.
Procedure.l CreateFriendly()
  ; Create a new Holder within the Instances list.
  AddElement(Instances())

  ; Populate the Virtual Table.
  Instances()\VT\SayHello = @SayHello()
  Instances()\VT\SayGoodbye = @SayGoodbye()
  Instances()\VT\TellMyName = @TellMyName()

  ; Prepare the implementation
  ; Assign the Virtual table 
  Instances()\Impl\VirtualTable = Instances()\VT
  
  ; Initialise the fields
  Instances()\Impl\Name = ""

  ; Return a pointer (hence the @) to the Implementation 
  ProcedureReturn @Instances()\Impl
EndProcedure


; Now for a demonstration.  Create and use two separate components.

Simon.IPBFriendly = CreateFriendly()
Debug Simon
Simon\SayHello()

Peter.IPBFriendly = CreateFriendly()
Peter\TellMyName("Peter")
Peter\SayHello()

Simon\SayGoodbye()
Peter\SayGoodbye()
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Heya GedB,

That example code is neat and understandable (to the limits of my ability at least) and works on my system.

I am assuming / hoping that you are a guru in this area. :)

How would you use a component like this in real life?

Would you include the code in all programs requiring the specific component?

Or would you create a dll, or something, for example, and access it that way? - And if so, how?

Also, do you have any other examples of using components, activeX, etc anywhere?

Thanks for your time. :)
LarsG
Enthusiast
Enthusiast
Posts: 713
Joined: Mon Jun 02, 2003 1:06 pm
Location: Norway
Contact:

Post by LarsG »

That's some nice code there GedB.
I'm still a little flakey on the COM/OOP/Interface stuff in PB, but I think it would be a nice contribution ot the community if you made a tutorial on the subject.. :)

AMD Athlon XP2400, 512 MB RAM, Hercules 3D Prophet 9600 256MB RAM, WinXP
PIII 800MHz, 320 MB RAM, Nvidia Riva Tnt 2 Mach 64 (32MB), WinXP + Linux
17" iMac, 1.8 GHz G5, 512 MB DDR-RAM, 80 GB HD, 64 MB Geforce FX 5200, SuperDrive, OSX
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

Dare2,

Sorry I missed you post. I was away over Xmas.

It is my hope to become an expert in the area :-) There certainly seems to be a void to be filled.

LarsG,

I'm rejecting COM as being too heavyweight. Besides, its just to complicated for me to get to grips with :-(

My grand scheme is PROBE : Pure Relational-Object Basic Entities. However, this is a rather grand scheme. What I'm trying achieve is a neat method for memory management that is 100% reliable but doesn't require a garbage collector.

In the meantime I'll try to write up what I have so far and post it up on my website.

I have an assignment due in March, so don't expect anything until April.
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

I've cracked the shared virtual table problem.

The reason I was having problems was because I was creating my virtual table as a local variable within the CreatePBFriendly function.

Once the function returned the VirtualTable was going out of scope.

The solution was simple, I just had to create the VirtualTable as a GlobalVariable. :DOH:

I've posted a streamlined example in the Tips and Tricks forum.

viewtopic.php?p=47528#47528
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Cut, pasted, saved and ran it. :)

Thanks.

I am still fuzzy - totally fogged in, actually :D - on how to convert this to an ocx. Or how to deploy it in real life. Any clues, or am I jumping the gun a bit?
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

Dare2,

So far this is just a vTable, the equivalent of a C++ class.

OCX files are components, and require a lot more work.

Here is a good article explaining how COM works:
http://msdn.microsoft.com/library/defau ... 020298.asp

Here is an article explaining how to do COM in ASM
http://ourworld.compuserve.com/homepage ... orld/a.htm
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Hi GedB,

Thanks for the response and the links. And for your patience. :)
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Heya GedB,

Do you know if it is possible to interrogate a component? Is there some way to ask the component what a particular function wants in the way of parameters?

For example, if the function associated with vtable element #5 is unknown, is it possible to somehow get the object to give you some clues about the function and required arguments?

Ack. I am grasping for words. If that was as clear as mud, I apologise.
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

Dare2,

The Purebasic interface is just a specific type of structure.

Just like a structure, you need to have its specification in order to use it.

For example, when calling the windows API you need to use structures. To use these you need the structure declarations as provided by the Structure Veiwer tool.

It's exactly the same with the Interface. Again, the interface is a special type of Structure that is treated special by the compiler.

The ability to query an interface to see what services it provides is a facility of COM. Specifically through the IUnknown interface.

I'm still new to this, so I may be getting it wrong. Here is my understanding so far.

A COM object is a vTable structure that has at least the IUNKnown interface. The IUnknown interface looks like this:

Code: Select all

Interface IUnknown
  QueryInterface(GUID, Interface)
  AddRef()
  Release()
EndInterface
If you compare this to the C declarations you will see that each item also inclused an additional paramater: pObject this.

This is the pointer to the Interface's underlying structure. In my example I call it *Self. Purebasic provides this paramater and hides it in the interface. You do need to include it in the actual functions.

QueryInterface is used to query an object to see if it supports an interface. You call it with that Interface, and it if does support it the second paramater is updated with a pointer to that interface.

Now I've reached the end of my knowledge. I'm thinking aloud from here on.

How do we know what Interfaces are implemented, so that we know what GUID to use when calling QueryInterface?

I don't know yet. My guess is the GUIDs are published somehow in the dll and ocx files. When you call RegSvr32 it reads this information and puts it into the registery. I also think that %PurebasicPath%\Library SDK\Interface Importer\Interface Importer.exe extracts this information. I only guessing, though.

COM is massive, and there is so much more to learn.

Purebasic does not have COM support. 3.80 introduced the tools necessary to build COM support, not COM support itself.

If you need COM today, then your better off with a language that already has full COM support, like VB or Delphi.

If, like me, you would like to learn how COM works then Purebasic is a good way in.
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

GedB, I am wasting your time, I apologise. I do appreciate your efforts to assist.

For your info only - I'm not asking for more effort on your behalf, I feel guilty already at the effort you've made - a brief background.

I have online services used by a number of business and professional groups around the world. The interface is handled by flash/swfs, the grunt work by a server. Many of the people using the services are in countries with unreliable connections and/or with costly (timed/bandwidth) connections.

I can save them time and money, and earn some myself (nice win/win) by moving some of the functionality onto their desktop and reducing the connection time. By encapsulating the swf in an exe I save myself a heap of time.

But I can't work out how to interact with the flash.ocx. :( So I can't trap FScommands, pass data to and from vars, etc.

There are other solutions: swf to exe proggies, VB, Delphi, rewrite from scratch.

I am commited to PB and will use it to provide the solutions, one way or the other.

So with the Qs, I am just digging around for clues. I can't find docs on this anywhere, or if I have found them I haven't recognised them for what they are.

You're into some heavy stuff with com. What you post is of real interest and I think many will benefit from your efforts and willingness to share.

Thanks again for the time you put into your response.
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

Dare2,

Answering your queries is a great motivator, so I apprectiate them.

My real concern was that, if you do have a pressing need, I might have mislead you into thinking an answer was close.

I just wanted to stress that getting to the point where COM access is generally available in PB is some way off. If you need something now, look elsewhere.

However, I'm willing to bet that a year from now COM will be as accessible and simple in PB as it is in VB. At least it will be if I have my way :)

Keep the questions coming and good luck with the SWF project.
User avatar
Derlidio
User
User
Posts: 77
Joined: Fri Feb 27, 2004 9:19 pm
Location: SP - Brazil

Post by Derlidio »

@GedB...

Hey, where did you find documentation about PB Interface usage? I mean, there is no "usable" information within PB Help file regarding to this issue, at least nothing that shows the creation/usage of interfaces as you did in the code you have posted (by the way, a really good sample).

Best wishes...
Derlidio Siqueira
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

Derlidio,

It took a bit of trial and error, but it isn't difficult to understand once you know that the interface is basically a C++ vTable.

Here is a good explanations of C++ vtables from an ASM point of view:

http://ourworld.compuserve.com/homepage ... orld/a.htm

Scroll down to 'Accessing COM Objects from Assembly'

And here is a stack object, which has refined the technique and might actually be useful:

viewtopic.php?t=10319&start=15

I also have a tutorial that I really must get round to completing.
aXend
Enthusiast
Enthusiast
Posts: 103
Joined: Tue Oct 07, 2003 1:21 pm
Location: Netherlands

Post by aXend »

@Derlido:
I agree that there isn't much documentation on Interfaces yet. I have posted a couple of examples and an Interface Generator to use COM components from PB, like ADO, Word, Excel, XML. See for yourself.
viewtopic.php?t=11031
viewtopic.php?t=11018
viewtopic.php?t=10836

@GedB:
I don't understand why you use a Structure for the vTable, since an Inteface IS a vTable. For general use you should also implement the IUnknown Interface and the IDispath Interface or define your Interface as extensions to them.

[EDIT]
@GedB: I'm sorry, I didn't read the whole post before I commented. :oops: I think it is very interesting and I will try to do something like this myself.
FYI (if you hadn't found out already): GUID's (CLSID, IID) are declared in a typelibrary. This lib also contains all the info about functions and variables (enumerations). I use this to build the Interface description with the Interface Generator.
Im very interested in what you have achieved until now. Could you post that too please? :?:
Post Reply