Macro framework for creating COM objects

Developed or developing a new product in PureBasic? Tell the world about it.
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Macro framework for creating COM objects

Post by freak »

With the introduction of interfaces in PureBasic, accessing external COM (Component Object Model) objects
has become fairly simple (in terms of needed amounts code to do so). However, creating COM objects in PB
to be used externally, or with API commands is still something that requires lots of knowledge about PB and
COM and quite some work to do. This framework tries to change that. It provides a set of macros to easily define
an object and also to automate most of the tasks common to all object implementations (like implementing the
IUnknown interface). Additionally, it provides extensive debugging functionality to track down bugs in your

implementation. There are also some macros that are useful for general COM development.

Feature list

Code: Select all

General

 - No preprocessing needed. Evetything is pure PB code.
 - Everything contained in residents and includefiles, minimizing possible problems with future PB versions
 - Threadsafe and unicode ready
 - Works with the EnableExplicit compiler option

Object implementation

 - Simple macros to define the class structure
 - Multiple interfaces supported in one class (up to 9)
 - Implementation of IUnknown is completly done by the framework
 - Constructor/Destructor support
 - For not-implemented methods, a default method is automatically inserted which returns #E_NOTIMPL
 - A VTable is created with a single macro call
 - Macros for easy definition and handling of GUID values (IID, CLSID, ...)

Debugging

 - Complete tracking of all calls to the objects methods, including displaying return values
 - Tracking of calls to dead (allready freed) objects
 - Catching of method calls outside the VTable (to find calls to wrong interfaces) 
 - The amout of debug output for the tracking can be customized with 'DebugLevel'
 - Conversion of GUID and HRESULT values to text for easier debugging
 - With Debugger off, the debug output is printed with OutputDebugString_() for easy dll debugging
This framework is intended for those that have allready some understanding
of how COM works, but want to make their life easier when it comes
to creating objects with PB.

NOTE: The purpose of this framework is to implement interfaces that are defined in the Microsoft Component
Object Model. The intend is not to provide general OOP functionality for PureBasic. To automate most
of the work, the framework needs information on the given interfaces, this is why only predefined interfaces
can be implemented, not any custom one. (currently 855 interfaces are known to the framework)

I started this project to ease my own work with COM in PureBasic, and
also als a challange to see how far i could go with the PB4 macro capabilities.
As this shows, there is quite a lot that can be done with it. It also prooves
that you can produce totally ugly code with them.
(have a look at the main includefiles and you will see. Better don't try to understand
how all of it works ;))

As an Example, implementing a basic IBindStatusCallback interface to be used
with UrlDownloadToFile_() can be as simple as this:

Code: Select all

COMClass(StatusObject)
  COMInterface(StatusObject, IBindStatusCallback) 
  
  COMConstructor(StatusObject)
    COMConstructorReturn IBindStatusCallback
  EndCOMConstructor  

EndCOMClass(StatusObject, IBindStatusCallback)

Procedure StatusObject_IBindStatusCallback_OnProgress(*THIS.StatusObject, Progress.l, ProgressMax.l, StatusCode.l, szStatusText.l) COMMethodOf(StatusObject)
  Protected percent.f

  If ProgressMax = 0
    percent.f = 0
  Else
    percent.f = (Progress*100)/ProgressMax
  EndIf
    
  Debug Str(Progress)+"/"+Str(ProgressMax)+"  ("+StrF(percent, 1)+"%)"

  ProcedureReturn #S_OK
EndProcedure

BuildCOMVTable_IBindStatusCallback(StatusObject)

RemoteName$ = InputRequester("File Download:", "Enter remote FileName: ", "http://www.purebasic.com/")
If RemoteName$ = "": End: EndIf

LocalName$ = SaveFileRequester("Select Local Filename:", "C:\temp.html", "All Files|*.*", 1)
If LocalName$ = "": End: EndIf


BindStatus.IBindStatusCallback = New_StatusObject()

Debug "Starting download..."
If URLDownloadToFile_(0, @RemoteName$, @LocalName$, 0, BindStatus) = #S_OK
  Debug "Download successful."
Else
  Debug "Download failed."
EndIf

BindStatus\Release()

End
Here is the download link. Make sure you have a look in the Readme file on how it all works.
There are also some example codes included.
To install it, just extract the archive directly into the purebasic folder.
(it adds a 'ComFramework' subfolder and 2 resident files, nothing more)

http://freak.purearea.net/tools/ComFramework.zip

I'll be glad to hear any comments or bugreports.
quidquid Latine dictum sit altum videtur
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Re: Macro framework for creating COM objects

Post by traumatic »

OMG this must be the ugliest yet at the same time the nicest PB-code I've ever seen. Great, great work!

sorry for being the first to reply ;)
Last edited by traumatic on Sun Jun 04, 2006 9:07 pm, edited 1 time in total.
Good programmers don't comment their code. It was hard to write, should be hard to read.
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

Damn ... didnt test it yet (time) but the feature list sounds tremendous :!:

Thanks, fr34k 8)
regards,
benny!
-
pe0ple ar3 str4nge!!!
User avatar
Droopy
Enthusiast
Enthusiast
Posts: 658
Joined: Thu Sep 16, 2004 9:50 pm
Location: France
Contact:

Post by Droopy »

Thanks :D
remi_meier
Enthusiast
Enthusiast
Posts: 468
Joined: Sat Dec 20, 2003 6:19 pm
Location: Switzerland

Post by remi_meier »

Krass :shock:
Athlon64 3700+, 1024MB Ram, Radeon X1600
LuckyLuke
Enthusiast
Enthusiast
Posts: 181
Joined: Fri Jun 06, 2003 2:41 pm
Location: Belgium

Post by LuckyLuke »

VERY COOL !!! 8)

Thanks for sharing :D
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Post by Inf0Byt3 »

Thanks! BTW, how much did it take to code all the includes? :shock: . Very nice work :D .
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Re: Macro framework for creating COM objects

Post by Dare »

freak wrote:It also prooves that you can produce totally ugly code with them.
:D

Thanks Freak. :)


Edit: This is huge! :shock: Big work, thank you for sharing!
Dare2 cut down to size
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Post by ts-soft »

thx freak, nice work :!:
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

thanks everyone.

> BTW, how much did it take to code all the includes?

Only the two main ones (BaseInclude.pb / Debugging.pb) are hand coded. The rest is
generated from PB's interface list and my own guid list that i generated a while ago.

btw, even for those who only want to access external COM objects, the 'GuidList.res'
file in the framework might be intresting.
It is independent from the rest, and contains the 'DefineKnownGUID/IID/CLSID()' macros only.
They can be used to easily define those needed IID/CLSID values without the need to look them up.
Just like this:

Code: Select all

DefineKnownIID(IID_IDispatch)

; now it is accessible like this:
;
a = ?IID_IDispatch
quidquid Latine dictum sit altum videtur
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Are any of you people making use of this framework ? I am just curious :)

Well, to anyone who uses this it, i have a new procedure which i find very helpfull for debugging:

The procedure displays all the interfaces that a given (foreign/unknown) object supports
trough its QueryInterface() method. (It checks all the interfaces known by the framework.)

It may surprise you how much stuff is actually supported by various objects.
Try this with a IWebBrowser2 or IHTMLDocument2 pointer from a webgadget
for example... there is much stuff available there. ;)

You can use the code as it is, or copy the procedure to the end of "Debugging.pb" in the framework,
then it is available always when you use "EnableCOMDebugging".

Note: as with the other debugging functions of the framework, the included guid list
is very big, so this is not meant for the final executable, but only for testing/debugging.

Here is the procedure + a short example:

Code: Select all

; this is important so the GuidList is included
EnableCOMDebugging

Procedure DisplaySupportedInterfaces(This.IUnknown)
  Protected index, StoredName$, New.IUnknown
  Restore __COM_DEBUG_GUIDNames
  
  For index = 1 To #__COM_DEBUG_GUIDCount
    Read StoredName$
    If Left(StoredName$, 4) = "IID_"
      If This\QueryInterface(?__COM_DEBUG_GUIDValues + (index-1)*SizeOf(GUID), @New.IUnknown) = #S_OK
        New\Release() ; very important!
        New = 0        
        __COM_DEBUG("Object supports: " + Right(StoredName$, Len(StoredName$)-4))
      EndIf
    EndIf    
  Next index
EndProcedure

; ---------------- Example code: Lets check the AutoComplete object --------------------

DefineKnownCLSID(CLSID_AutoComplete)
DefineKnownIID(IID_IAutoComplete)

; This is only needed for the CoCreateInstance_(), its not needed for the above procedure
CoInitialize_(0)

; create autocomplete object
If CoCreateInstance_(?CLSID_AutoComplete, 0, 1, ?IID_IAutoComplete, @AutoComplete.IAutoComplete) = #S_OK

  ; check what is supported
  DisplaySupportedInterfaces(AutoComplete)
  
  ; release again
  AutoComplete\Release()
EndIf

CoUninitialize_()
quidquid Latine dictum sit altum videtur
LuckyLuke
Enthusiast
Enthusiast
Posts: 181
Joined: Fri Jun 06, 2003 2:41 pm
Location: Belgium

Post by LuckyLuke »

@Freak
Could you give some more examples ?
Is it possible to use this framework to create an addon for msword?
Or to call msword macros ?

Thanks.

LuckyLuke
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

LuckyLuke wrote:@Freak
Could you give some more examples ?
Is it possible to use this framework to create an addon for msword?
Or to call msword macros ?

Thanks.
Well, its hard to give simple examples for that. Did you look at the included ones ?
You probably can access msword stuff, but i imagine it will be some work to
get that done.
quidquid Latine dictum sit altum videtur
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

freak,

I downloaded this and browsed around a bit. I noticed the AsyncXXXXXXX.pb files in the ComFramework/Interfaces directory. Are those for activesync?
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

A couple seconds after posting that I knew the answer was 'no'. So... do you have any tips on how I could possibly get started with working with ActiveSync? To sync contacts between outlook contacts on the Windows Mobile device and the desktop PC.
Post Reply