Introduction#

DismHost.exe is the out-of-process COM host used by DISM image sessions.

In this quick writeup, we explore how DismHost.exe parses its command line, registers its COM class object, and loads provider DLLs to create image-session objects.

Command-Line Parsing#

CDismHostModule::ParseCommandLine expects a command line containing dismhost.exe and a braced CLSID. The parser searches for the last { and } characters, checks that it matches a CLSID string, and passes the substring to CLSIDFromString.

Simplified, the command-line contract looks like:

DismHost.exe {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

After parsing the CLSID, DismHost registers the class object with COM via CoRegisterClassObject.

CoRegisterClassObject(
    parsedClsid,
    classFactory,
    CLSCTX_LOCAL_SERVER, // 0x4 - https://learn.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx
    REGCLS_MULTIPLEUSE, // 0x1 - https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/ne-combaseapi-regcls
    &g_dwRegisterClassFactory);

If the command line is malformed, the function returns 0x80070057 (HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)).

Loading Objects From DLLs#

The other side of the story is CDismHostManager::CreateObjectFromDLL. This is the function that DismCore ultimately calls to ask DismHost to load a DLL and create an object from it. A good way to look at it is with a concrete command:

dism /online /get-packages /ScratchDir:C:\Temp\DismScratch /sandbox

For this kind of run, DismCore first decides where the image-session binaries live. With /sandbox and /ScratchDir, that usually becomes a temporary GUID directory:

C:\Temp\DismScratch\{GUID}

Then CDISMManager::LoadRemoteImageSession builds the DLL path for the remote image session:

AppendPath(
    imageSessionLocation,
    L"\\dismprov.dll",
    &dismProvPath);

So the DLL path becomes:

C:\Temp\DismScratch\{GUID}\dismprov.dll

For the normal system directory case, the same code points at:

%SystemRoot%\System32\Dism\dismprov.dll

The call chain looks like this:

CDISMManager::CreateImageSession (dismcore.dll)
  -> CDISMManager::LoadRemoteImageSession (dismcore.dll)
      -> DismCreateObjectInHostFromCLSID (dismcore.dll)
          -> IDismHostManager::CreateObjectFromDLL (COM call to DismHost.exe)

DismCreateObjectInHostFromCLSID receives both paths:

imageSessionLocation = C:\Temp\DismScratch\{GUID}
dllPath              = C:\Temp\DismScratch\{GUID}\dismprov.dll

It uses the first one to start the host:

C:\Temp\DismScratch\{GUID}\DismHost.exe {runtime-clsid}

Then, after CoCreateInstance, it uses the second one to ask the host manager to create the image-session object:

CreateObjectFromDLL(
    L"C:\\Temp\\DismScratch\\{GUID}\\dismprov.dll",
    CLSID_DismImageSession,
    IID_IDismImageSession,
    &imageSession);

Inside CDismHostManager::CreateObjectFromDLL, the logic is roughly:

if (dllPath == L"DumpState")
{
    // Diagnostic path. No DLL is loaded.
    DumpRefCounts();
    return S_OK;
}
 
module = LoadLibraryExW(dllPath, NULL, 0);
object = CreateObjectFromModule(
    module,
    requestedClsid,
    requestedIid);
 
privateSession = object->QueryInterface(IDismImageSessionPrivate);
privateSession->SetHostManager(this);

So in the sandbox example, the interesting object is not loaded from %SystemRoot%\System32\Dism\dismprov.dll. It is loaded from the copied DISM stack under the GUID directory. Later, once the image session is alive, dismprov.dll can load the individual providers such as CbsProvider.dll, WimProvider.dll, or OSProvider.dll.

Related Articles

Other threads in the archive worth reading next.