Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

addref and release in IUnknown, what do they actually do?

Tags:

c++

c

shell

windows

I been trying to get my brain around shell extension in windows. Some functions that needs to be implemented are addref() and release(). It said, it keep tracks of object references and release them when not in use.

Just in a simple explanation what is it actually tracking? In my thought you create your own object that implements various interfaces according to your purpose, then let classfactory returns the object to com engine to run unless im mistaken.

Am just really slow in understanding the concept. Also the step by step process, that windows com engine loads shell extensions from identifying dll through actual execution to unloading. Something simple explanation please. Regards

like image 749
Chopnut Avatar asked Mar 10 '23 12:03

Chopnut


2 Answers

Shell extensions are just normal COM objects.

A interface (usually prefixed with uppercase i) is basically a contract. There can be one or more implementations of a interface.

Release is called by the "user" of a object/interface when they are done using it:

IWhatever*pW;
if (SUCCEEDED(CoCreateInstance(CLSID_Something, ..., IID_IWhatever, (void**) &pW)))
{
  pW->DoWhateverThisThingDoes();
  NotifyMyClients(pW);
  pW->Release(); // Tell this instance that we are done with it
}

In the preceding example we call Release to indicate that we no longer need this interface but we don't actually know if the interface instance is going to be destroyed right now.

If we imagine that one of the clients/plug-ins/extensions known by NotifyMyClients is implemented like this:

class FooClient {
IWhatever*MyWhatever;
void FooClient::OnNotifyNewWhatever(IWhatever*p) // Somehow called by NotifyMyClients
{
  p->AddRef(); // We want to use this object later even if everyone else are done with it
  if (MyWhatever) MyWhatever->Release(); // Throw away the previous instance if we had one
  MyWhatever = p;
  SetTimer(...); // Activate timer so we can interact with MyWhatever later
}
FooClient::FooClient()
{
  MyWhatever = 0; 
}
FooClient::~FooClient()
{
  if (MyWhatever) MyWhatever->Release(); 
}
};

The code that created the object does not need to know anything about what the other code does with the object, it is only responsible for its own interactions with the object.

The basic rules are: Call Release once for each time you call AddRef on a object. If you created a object instance you also have to release it.

Pseudo code for the implementation of a STA interface might look something like this:

#include <Whatever.h> // The skeleton/contract for IWhatever
class Whatever : public IWhatever {
ULONG refcount;
Whatever() : refcount(1) {} // Instance refcount should start at 1
HRESULT QueryInterface(...) { ... }
ULONG AddRef() { return ++refcount; }
ULONG Release()
{
  if (--refcount == 0) // Anyone still using me?
  {
    delete this; // Nope, I can destroy myself
    return 0;
  } 
  return refcount;
}
void DoWhateverThisThingDoes() { PerformMagic(this); }
};

The CLSID of this particular IWhatever implementation (CLSID_Something) must be registered in the registry so COM can find it. The registration includes a path to the .DLL where the code is. This .DLL must export the DllGetClassObject function.

DllGetClassObject hands out a instance of its IClassFactory implementation and when COM asks for a new IWhatever instance the factory would just call new Whatever();.

I did not cover QueryInterface but it is used to ask a object instance if it supports another interface. ICar probably implements IVehicle but not IBus nor ITrain etc. All COM objects support the IUnknown interface and all other interfaces inherit from IUnknown.

It is impossible to explain COM in a single answer but there are plenty of introduction articles online.

You can use/consume shell objects created by Microsoft and 3rd-parties and you can create your own implementation of documented interfaces.

If we take IContextMenu as an example. There can be many implementations of it on a single system. Explorer creates one instance of each registered and applicable (file extension matches registration etc.) IContextMenu implementation when you right click on something and each instance adds their menu items to the menu. The instance that added the chosen menu item is called again to execute its action and then all the instances are released.

MSDN has a list of extension types most commonly used here. The Complete Idiot's Guide to Writing Shell Extensions is a good starting point if you want to write one.

like image 147
Anders Avatar answered Mar 19 '23 14:03

Anders


Just in a simple explanation what is it actually tracking?

Quite simply, what you said:

it keep tracks of object references and release them when not in use

They are used to track how many references to the object are in use.

Calling addref() increments the reference count and release() decrements it. You call addref() to ensure that it knows you are using the object so that it doesn't destroy it. When you are done you release() to tell it you are done with the object.

Once everyone is done with the object (the reference count drops to zero), the object can be destroyed.

Note that obtaining an interface reference (via e.g. DllGetClassObject) will call addref() for you, so the interface reference you receive already has a reference count of (at least) 1.

like image 38
cdhowie Avatar answered Mar 19 '23 15:03

cdhowie