The Running Object Table
So how does an application notify other applications that the file is in use and what capabilities are available? This is done by inserting the instantiated IFileIsInUse object into the Running Object Table. The Running Object Table (ROT) is a globally accessible lookup table on each workstation. The ROT keeps track of those objects that can be identified by a moniker and that are currently running on the workstation. When a client tries to bind a moniker to an object, the moniker checks the ROT to see if the object is already running. This allows the moniker to bind to the current instance instead of loading a new one.
For example, to place an object in the ROT:
DWORD dwCookie;
IRunningObjectTable *prot;
HRESULT hr = GetRunningObjectTable(NULL, &prot);
if (SUCCEEDED(hr))
{
IMoniker *pmk;
hr = CreateFileMoniker(pszFileName, &pmk);
if (SUCCEEDED(hr))
{
// Add ROTFLAGS_ALLOWANYCLIENT to make this work accross security boundaries
hr = prot->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE | ROTFLAGS_ALLOWANYCLIENT,
static_cast<IFileIsInUse *>(this), pmk, &dwCookie);
if (hr == CO_E_WRONG_SERVER_IDENTITY)
{
// this failure is due to ROTFLAGS_ALLOWANYCLIENT and the
// fact that we don't have the AppID registered for our CLSID.
// Try again without ROTFLAGS_ALLOWANYCLIENT knowing that this
// means this can only work in the scope of apps running with
// the same MIC level.
hr = prot->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE,
static_cast<IFileIsInUse *>(this), pmk,
&dwCookie);
}
pmk->Release();
}
prot->Release();
}
In the above code, we call GetRunningObjectTable to retrieve an instance of IRunningObjectTable. We then create an IMoniker for the file that is currently in use. This moniker is then inserted into the ROT in the call to IRunningObjectTable::Register. We specify the ROTFLAGS_ALLOWANYCLIENT in our call to Register. This is required to allow our entry in the ROT to work across security boundaries. This requires AppID registration so COM can inspect our security settings. Without this the Register()call will fail. In that case, we call Register() again, but without the ROTFLAGS_ALLOWANYCLIENT – which will allow our application to work correctly but only in the same security level. The dwCookie parameter is used to identify the entry in later calls to retrieve or remove it from the ROT.
In the below code, the cookie is used to remove the moniker in the ROT.
IRunningObjectTable *prot;
HRESULT hr = GetRunningObjectTable(NULL, &prot);
if (SUCCEEDED(hr))
{
hr = prot->Revoke(dwCookie);
prot->Release();
}
So from the above code we now know how to insert our object into the ROT so other applications can find it. But how does an application check if a particular file has been added as an instance of an IFileIsInUse object to the ROT? The application needs to enumerate the contents of the ROT to find a matching moniker.
IFileIsInUse *pfiu;
PCWSTR pszFile = L"c:\\some\\full\\path\\to\\file";
IRunningObjectTable *prot;
HRESULT hr = GetRunningObjectTable(0, &prot);
if (SUCCEEDED(hr))
{
IMoniker *pmkFile;
hr = CreateFileMoniker(pszFile, &pmkFile);
if (SUCCEEDED(hr))
{
IEnumMoniker *penumMk;
hr = prot->EnumRunning(&penumMk);
if (SUCCEEDED(hr))
{
hr = E_FAIL;
ULONG celt;
IMoniker *pmk;
while (FAILED(hr) && (penumMk->Next(1, &pmk, &celt) == S_OK))
{
DWORD dwType;
if (SUCCEEDED(pmk->IsSystemMoniker(&dwType)) &&
(dwType == MKSYS_FILEMONIKER))
{
// Is this a moniker prefix?
IMoniker *pmkPrefix;
if (SUCCEEDED(pmkFile->CommonPrefixWith(pmk, &pmkPrefix)))
{
if (S_OK == pmkFile->IsEqual(pmkPrefix))
{
// Get the IFileIsInUse instance
IUnknown *punk;
if (prot->GetObject(pmk, &punk) == S_OK)
{
hr = punk->QueryInterface(IID_PPV_ARGS(&pfiu));
punk->Release();
}
}
pmkPrefix->Release();
}
}
pmk->Release();
}
penumMk->Release();
}
pmkFile->Release();
}
prot->Release();
}
If the moniker was found in the ROT, the application now has an instance of an IFileIsInUse that was created in the application that is using the file. This object can then be used to query for the capabilities that are available or any other method exposed by IFileIsInUse. This is essentially what Windows Explorer does when it encounters a file that is in use during file operations.
The goal of creating this interface is to allow applications to play nice with one another when contending for access to common files. As more developers implement this interface, the better it is for the entire community.