COM Shim CLR Loader Bug

The latest version of the COM shim wizards was released back in the summer, here. With the help of Adam Smith of Xobni (and of course, the indispensable Misha), we've identified a minor bug with the project code that the wizard generates for the shim. In fact, this bug was identified prior to the last release (as you can read in Misha's blog comments here), but we simply forgot to include the fix – my apologies for that.

Part of what the shim does is to load the CLR: it does this by calling CorBindToRuntimeEx. We test the return from CorBindToRuntimeEx, and if it's S_FALSE, this indicates that the CLR has already been loaded. In the released version of the shim, if we get S_FALSE, we simply return.

However, there is a scenario where the CLR could already have been loaded but not started. It's an unusual scenario: it could happen if some other managed code was loaded into the process but never executed. The problem is that the shim code assumes that if the CLR is loaded it is not necessary for us to start it. Of course, if the only other things in the process that load the CLR are all shims created with the COM Shim Wizard, then this assumption would be true – because the first one to load would have loaded the CLR and started it. However, this assumption doesn't hold if there are other native components that load the CLR and don't start it.

The change to fix the bug is to EITHER call ICorRuntimeHost::Start if we get S_FALSE, OR simply don't test for S_FALSE at all – this works because we do call Start later on. The fixed code is shown below, with the S_FALSE test removed and highlighted:

HRESULT CCLRLoader::LoadCLR()

{

    HRESULT hr = S_OK;

    // Ensure the CLR is only loaded once.

    if (m_pCorRuntimeHost != NULL)

    {

        return hr;

    }

    // Load the CLR into the process, using the default (latest) version,

    // the default ("wks") flavor, and default (single) domain.

    hr = CorBindToRuntimeEx(

        0, 0, 0,

        CLSID_CorRuntimeHost, IID_ICorRuntimeHost,

      (LPVOID*)&m_pCorRuntimeHost);

    // If CorBindToRuntimeEx returned S_FALSE, the CLR has already

    // been loaded.

    //if (hr == S_FALSE)

    //{

    // return hr;

    //}

    // If CorBindToRuntimeEx returned a failure HRESULT, we failed to load

    // the CLR.

    if (!SUCCEEDED(hr))

    {

        return hr;

    }

    // Start the CLR.

    return m_pCorRuntimeHost->Start();

}

We'll work with the gentle folks at MSDN to get the COM Shim Wizard download updated as soon as possible. In the meantime, you can make this simple change to the wizard-generated code in ClrLoader.cpp. Note that this test for S_FALSE occurs in all variants of the shim code – that is, the shim for add-ins, smart tags and real-time data components. It can be removed from all these variants.

Thanks again to Adam Smith, Shamil Salakhetdinov, and Misha for identifying the problem and the fix.