PDA

View Full Version : PowerBasic: 4th post from Bob about objects



ErosOlmi
05-08-2008, 23:44
http://www.powerbasic.com/support/pbforums/showthread.php?t=38134
_________________________________________________

What are the parts of an object?
We are going technical here.

John Spikowski
05-08-2008, 23:53
This might be a helpful link.

http://en.wikipedia.org/wiki/OLE_Automation


John

Petr Schreiber
06-08-2008, 00:00
Nice explained again,

I am just curious if it would be needed to specify somehow CLSID and PROGID in case of non-COM class, or maybe it will be automatic?
We will see, tommorow will be nice day ... to refresh the website again :)


Petr

ErosOlmi
06-08-2008, 00:10
Yes, I hope syntax will be simple like VBS or VB6 and not too much technical like current COM interfaces and variant handling.
We will see.

José Roca
06-08-2008, 00:11
I am just curious if it would be needed to specify somehow CLSID and PROGID in case of non-COM class, or maybe it will be automatic?


PROGIDs and CLSIDs are used just to find information, mainly the path, about a COM server in the registry. In the case of an internal class, the compiler knows where to find it: in your application!

In fact, neither PROGIDs nor CLSIDs are needed to create an instance of an in-process server. Just knowing the path is enough.

John Spikowski
06-08-2008, 00:13
Bob has indicated that PowerBasic will be able to access COM OLE Automation objects without having to look up the CLSID in the registry. Hopefully Jose Roca's contributions has blazed the trail for PowerBasic to access these objects directly rather then using the iDispatch interface that it currently uses.

I think PowerBasic is about to bring COM programming to Basic so it is as common as creating a standard DLL.

The approach seems to be inline with how PowerBasic introduces technology to the language and makes it easy for all to use.

These advancements are good news for thinBasic users as this opens a whole new wave of extension modules based on COM OLE Automation.


John

ErosOlmi
06-08-2008, 00:18
These advancements are good news for thinBasic users as this opens a whole new wave of extension modules based on COM OLE Automation.

Well, very hard to say. I need to put my hands and brain over it before been able to tell something.

José Roca
06-08-2008, 00:19
To create an instance of an in-process server bypassing the registry is simple:



' // See if the library is already loaded in the address space
hLib = GetModuleHandle(BYCOPY strLibName)
' // If it is not loaded, load it
IF hLib = %NULL THEN hLib = LoadLibrary(BYCOPY strLibName)
' // If it fails, abort
IF hLib = %NULL THEN EXIT FUNCTION

' // Retrieve the address of the exported function DllGetClassObject
pProc = GetProcAddress(hLib, "DllGetClassObject")
IF pProc = %NULL THEN EXIT FUNCTION

' // Request a reference to the IClassFactory interface
CALL DWORD pProc USING DllGetClassObject(rclsid, $IID_IClassFactory, pIClassFactory) TO hr

'*** Call the CreateInstance method of the IClassFactory interface ***


Using this technique, you don't need to register your COM servers, just place them in your application folder (or in another folder) as with standard DLLs.

José Roca
06-08-2008, 00:31
I think PowerBasic is about to bring COM programming to Basic so it is as common as creating a standard DLL.


In fact, an in-process server is a standard DLL. You just need to add a few functions and a class factory.

John Spikowski
06-08-2008, 01:04
Hi José,

Would you be so kind as to expand on what a "class factory" is and it's purpose?

Thanks !


John

José Roca
06-08-2008, 01:23
A class factory is a class whose purpose is to create instances of other classes upon request.

Every COM DLL server should implement and export a function called DllGetClassObject. DllGetClassObject creates an instance of the class factory (IClassFactory interface), and returns a pointer to it. Then you call the CreateInstance method of IClassFactory to create instances of other interfaces implemented in the server.

MikeStefanik
06-08-2008, 01:51
The simplest definition of a class factory is that its a class designed to dynamically create an instance of another class, when it's unknown what that specific class will be at compile time. For example, let's say that you have two classes, CImageFile and CAudioFile, and both are derived from the same base class CDataFile. Now, let's say that you want to be able to dynamically create an instance of the appropriate class, based on the contents of a file; you obviously can't do this at compile time, so to make your program extensible (let's say in the future, you're going to want to add a CVideoFile class as well), you use a class factory. In C++, it could look something like:



class CDataFileFactory
{
public:
enum FileType { fileTypeUnknown, fileTypeAudio, fileTypeImage };

CDataFile* CreateInstance(LPCTSTR lpszFileName)
{
switch (GetFileType(lpszFileName))
{
case fileTypeAudio:
return new CAudioFile(lpszFileName);

case fileTypeImage:
return new CImageFile(lpszFileName);

default:
return NULL;
}
}

private:
FileType GetFileType(LPCSTR lpszFileName)
{
// Determine the type of file and return the correct value
}
}


That's a really simple, contrived example, but it's the general idea. With COM objects, it's a bit more complex. The idea is that you have the COM object, which is derived from CComCoClass which implements a class factory interface named IClassFactory (a COM object is actually derived from multiple classes, but I'm trying to keep things simple). The implementation of the COM object then overrides that IClassFactory interface, providing it's own implementation, so that it "knows" how to create an instance of itself.

A program that wants to use the COM object first calls CoGetClassObject, passing it the class ID of the object, as well as the ID of the IClassFactory interface (along with some other parameters), and it gets back a pointer to the IClassFactory interface for that specific COM object. It then calls the class factory's CreateInstance method, and gets back a pointer to the IUnknown interface for the COM object (an interface that all COM objects must implement). With that, it can call the QueryInterface method, passing in the interface ID for the specific interface in the COM object that it wants, and gets back a pointer to that interface.

All of this allows the COM API to abstract the creation of objects, so that with just the class ID of the object you want to create, and the interface ID of the specific interface in that class, it can be used to create an instance of any COM object.

I apologize for the complexity there, I understand that can be a tough read. I made it as simple as I could.

MikeStefanik
06-08-2008, 02:12
In fact, an in-process server is a standard DLL. You just need to add a few functions and a class factory.


That's true, but just to point out, it's not quite as simple as it sounds. The practical reality is that a full-blown ATL-based COM object multiply derives from about 15 other classes, more if it implements connection points (events). Real-world objects that implement persistance, property pages, the ErrorInfo interface, object saftey, etc. are a lot more complex. So they are DLLs, but they really aren't just DLLs with a few exported functions, there's a lot of infrastructure there under the hood.

I point this out just so folks don't get the idea that a COM object is really just a fancy name for a DLL with a couple of functions; it's more complicated than that.

José Roca
06-08-2008, 02:43
Nothing of this is needed unless you want to write a COM server or ActiveX control to be used by Automation languages. If you want to write a low-level COM server (just interfaces that inherit from IUnknown, not dual or dispatch interfaces), a few functions and a class factory is all you need.

MikeStefanik
06-08-2008, 04:02
In fairness, that's an extremely small subset of the COM objects that are out there. A very simple COM object with just a vtable interface and no support for automation tends to be less than useful in a practical sense. For example, a commerical ActiveX control wouldn't typically be implemented that way, otherwise it couldn't be used with scripting languages such as VBScript. Some APIs may do this (I believe that DirectX has interfaces that are vtable only), but those are the exceptions rather than the rule.

Edit: I think the point you're trying to make is that COM objects don't have to be so complicated. I'm just pointing out that in real-world applications, however, they usually are.

José Roca
06-08-2008, 04:46
For performance reasons, not only all the DirectX interfaces are vtable only, but all of the hundreds of interfaces of the COM subsystem. In some cases, such OLEDB, M$ writes the server as vtable only and later writes ActiveX wrappers to allow to use them using Automation languages. For example, ADO is just a wrapper on top of OLEDB; CDO is a wrapper on top of Extended MAPI...

If commercial servers and ActiveX are written using dual interfaces, and some just using dispatch interfaces, is because they want to reach a broader audience, but if you intend to write servers for your own use, or to be used with languages like C that allow to use direct calls, then low-level servers offer many advantages. You can use any kind of variable, structures, arrays and even asciiz strings (do you know that DirectX uses asciiz strings instead of unicode?), just like with standard functions, without the need to use variants and safearrays, and instead of events, you can call callback functions and send Wndows messages.

These ActiveX controls with its property pages, type libraries and events are who have given to COM the undeserved reputation of being slow and bloated.

ErosOlmi
06-08-2008, 05:41
So, new PB compilers will have a big improvement in COM area.

Now my question is: are all new features only for COM or new PB will also have OOP per se?
I mean, will something like the following pseudo-code be possible?



class MyClass
private:
name as string
age as long

public:
constructor (optional byval NewName as string, byval NewAge as long)
me.name = NewName
me.age = NewAge
end constructor

destructor
reset me.name
end destructor

method SetName(byval NewName as string)
me.name = NewName
end method

method GetName() as string
method = me.name
end method

method SetAge(byval NewAge as long)
me.Age = NewAge
end method

method GetAge() as long
method = me.Age
end method

'...

end class

...
DIM MyNewObject("Eros", 41) AS MyClass

? MyNewObject.GetName & " is" & str$(MyNewObject.GetAge) & " old."
...

José Roca
06-08-2008, 05:55
What I have posted applies to any language able to call functions and deference pointers, not necessarily to PB. You will have to wait for further posts by Bob.

Anyway, COM classes can be used for OOP, although aren't as well suited as C-like classes because they have single inheritance and other limitations.

ErosOlmi
06-08-2008, 05:57
>:(

:'(
:P
;D

Thanks. Even single inheritance is something! If there are properties, methods, constructors and destructors it is a BIG BIG addition.

José Roca
06-08-2008, 06:18
Methods and Properties quite sure. What would be an object without them?

One constructor and one destructor for each class are possible, but without parameters, because there is no way to pass them to the class factory, but if you need them, you always can implement an Init method.

Private methods are also possible.

ErosOlmi
06-08-2008, 06:24
Thanks José. It is more than expected.
Well in reality all this was really un-expected (for me) so it will be a fantastic summer (hoping "Order here" button is coming out soon).