How is COM/OLE/ActiveX different from DLL?

The original question was: where do I learn about the differences between DLL and COM/OLE and so on, so I don't confuse one with the other and know the limitations/features of each.

Jeez I dunno... where to start? Please help me out by editing this topic whenever you want to change something!

an introduction

A DLL (Dynamic Link Library) is much like a persistent procedure in Progress. In Progress, the persistent procedure contains one or more internal procedures and one or more functions, private or not. The IP's and UDF's that are not private can be called "from outside", provided that you know the name and the parameters and that you have any clue what kind of action the IP/UDF will perform.
Likewise, a DLL contains one or more functions, published or not. (it is uncommon to have procedures instead of functions). You can load the DLL persistent with LoadLibrary, call the published functions and finally use FreeLibrary to get rid of the persistent DLL. As with a Progress persistent procedure, you need to have some sort of written documentation to find the function names, their parameters and expected behaviour.
DLL's work well although problems may rise - especially in setup, maintenance and documentation.
Setup: where should you install these things - before an application can load a DLL it must first try to locate it. You could put it in a subdirectory of your application, but that is not practical if you have many applications (like Microsoft has) and want to reuse common components. You could put them in a shared directory, like windows/system32, but then you enter the maintenance nightmare.
Maintaining a DLL can become a problem both for the programmer and for the end-user. How can you add a new function to an existing DLL, or even a new parameter to an existing fuction, and still make sure that you don't get in trouble with the user base? Since a single DLL can be used by several different applications, there is no way of forcing all those application vendors to simultaniously create an update that matches with the new DLL version.
Documentation: a DLL is a black box, it does not contain its own documentation. It becomes messy when you have a lot of programmers writing DLL's, especially if the company is so big that there are different cultures between departments and different parameter styles you can't get used to.
So even though DLL work well technically, there was a need to invent a way to have DLL's document themselves, publish their file locations to the apps that needed to use them, and to be somewhat version independent.
An ActiveX control is basically just a DLL with some extra functions in it. One of those functions is RegisterServer, which writes the location of the DLL in the registry database under some unique identifier. A calling application does not need to know beforehand where the DLL is located, it only needs to know the unique identifier and can then locate the DLL by looking in the registry, before it loads that DLL into memory. (I say DLL but most of these have the extension OCX, although they are really just DLL with extras). So you don't call LoadLibrary directly, you call it through a wrapper function (not sure but I believe it is CoCreateInstance).
An application is not supposed to call any of the built-in functions directly, because of the risk that you call the wrong version of a function. Instead, the app needs to query the supported interfaces first. To make a long story short, the OCX sends the application a table of built-in functions that can be used. The OCX might support several versions but not all versions can be mixed. Suppose for example an OCX that contains functions AddCustomer and DeleteCustomer, and both have a version 1 and a version 2. One application might want to call AddCustomer version 1 and DeleteCustomer version 1, an other app might want to use version 2 of the interface. A mix is not allowed, e.g. AddCustomer version 2 and DeleteCustomer version 1 will have undesired results. So the OCX sends a table of alllowed functions to the app, with functionpointers to the functions that match the version of the calling application. (in reality there is probably only one function that has different versions. In that case, version 2 of the interface just points to the one and only version of the other functions)
An OCX is typically linked to a TLB resource (Type Library) that contains documentation for the interfaces. Not just for human eyes, but also for the calling application that can now automatically validate the number and type of parameters. This also means that parameter types needed to be standarized, which was not the case with bare DLL's.
Responding to events:

For a normal DLL it is pretty difficult to raise an event that can be handled in the calling application. For this to work, the calling application should have defined an exported function and must have told the DLL what the funcionpointer is for this callback function. (Progress 4GL does not support the definition of exported functions, so this does not work for us). ActiveX has several solutions for this problem, the **eventsink** is most commonly used. The application queries not only the interface for functions that can be called in the OCX, but also queries for a different interface to receive a list of expected callback-functions. The TLB plays an important role here. The received table contains a list of functions that the OCX expects to find in the calling application. The calling app can now dynamically create an eventsink (in its runtime module): a block of dynamic functions that do nothing but forward the call (from the OCX) to the event-handler procedure that you have written (in 4GL). Well actually they do a little bit more than that: they also have to take care of parameter translation.
About parameter tranlation and mashalling:

Back to the TLB for a moment. The TLB describes parameters in standarized datatypes. The host application (Progress) may have to transform the bits and bytes of the actual parameter (like, a character string) to whatever structure the OCX expects (like, a BSTRING). For simple types like integer this may be trivial, but for more complex types like datetime, fonts, colorref, arrays or even a logical it may involve some more work. In Progress this is all handled by the runtime module, although not all types are fully supported yet (like the time part in a datettime structure, or a variant array).
Marshalling guards you against hostile pointers. Remember the oldfashioned DLL API: you define a memory pointer in your application and store a bunch of data behind it. You then pass the pointervalue to the DLL, which can now read and even write the data directly. Works like a charm, until mistakes happen - the data segment may become corrupted and the application may crash, or worse. This is undesirable for a client, but even worse for a server process or a background process. OCX protects you by not allowing to pass pointers - this includes also character strings and output parameters. Instead of passing the pointer itself, the data behind the pointer is packed in a protected envelope and transferred to the OCX and back. Of course this is only possible if the structure of whatever the pointer is pointing to, is well-known and standarized.

so how does this affect your Progress source?

Well, for one thing, you cannot pass pointers to an OCX.
what else? I suppose we need to explain what's going on with instance counters and RELEASE OBJECT...
Ok, the story is unfinished, I give up for now. I might return later to write some more... in the meantime you're welcome to take over