Yesterday, I
explained? how easy it is to inadvertedly
load the same executable twice into the same process address space - you simply
run it using its short DOS-ish filename (like
Sample~1.exe
)
instead of its original long filename (such as
SampleApplication.exe
).
For details, please consult the original blog entry.
I mentioned that one fine day I might report how exactly this happened
to us, i.e. why in the world our app was started using its short filename.
Seems like today is such a fine day
Said application registered itself as a COM server, and it does so using
the services of the
ATL Registrar.
Upon calling
RegisterServer
, the registrar will kindly create all the required
registry entries for a COM server, including the
LocalServer
entry which
contains the path and filename of the server. Internally, this will call the
following code in
atlbase.h
:
inline HRESULT WINAPI CComModule::UpdateRegistryFromResourceS(UINT nResID,
BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries)
{
USES_CONVERSION;
ATL::CRegObject ro;
TCHAR szModule[_MAX_PATH];
GetModuleFileName(_pModule->GetModuleInstance(), szModule, _MAX_PATH);
// Convert to short path to work around bug in NT4's CreateProcess
TCHAR szModuleShort[_MAX_PATH];
GetShortPathName(szModule, szModuleShort, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModuleShort);
...
Aha! So ATL deliberately converts the module name (something like
SampleApplication.exe
)
into its short-name equivalent (
Sample~1.exe
) to work around an issue in the
CreateProcess
implementation of Windows NT.
MSKB:179690
describes this problem:
CreateProcess
could not always handle blanks in pathnames
correctly, and so the ATL designers had to convert the path into its short-path
version which converts everything into an 8+3 filename and hence guarantees that
the filename contains no blanks.
Adding insult to injury,
MSKB:201318
shows that this NT-specific bug fix in ATL has a bug itself... and, of course, our problem is,
in fact, caused by yet another bug in the bug fix (see
earlier blog entry?).
For my application, the first workaround was to use a modified version of
atlbase.h
which checks the
OS version; if it is Windows 2000 or later, no short-path conversion
takes place. Under Windows NT, however, we're caught in a pickle: Either we
use the original ATL version of the registration code and thus map the executable
twice into the address space, or we apply the same fix as for Windows 2000,
and will suffer from the bug in
CreateProcess
if the application is installed
in a path which has blanks in the pathname.
In my case, this was not a showstopper issue because the application is targeting Windows 2000 and XP
only, so I simply left it at that.
Another approach is to use the
AddReplacement
and
ClearReplacements
APIs of the ATL registrar to set our own conversion rules
for the module name and thereby override ATL's own rules for the module name:
#include <atlbase.h>
#include <statreg.h>
void RegisterServer(wchar_t *widePath, bool reg)
{
ATL::CRegObject ro;
ro.AddReplacement(L"Module", widePath);
reg ? ro.ResourceRegister(widePath, IDR_REGISTRY, L"REGISTRY") :
ro.ResourceUnregister(widePath, IDR_REGISTRY, L"REGISTRY");
}
to top