How to determine why an add-in didn't start

D

David Thielen

Hi all;

Ok, here's what I have found. If you use the shim for a C# add-in, the shim
fails silently. Look for the use of the macro IfFail (BTW - someone please
tell the person who write the shim that they should not make such heavy use
of goto) you will see that any failure leads to a silent failure of the
add-in.

I have edited CLRLoader.cpp (below) to pop-up a MessageBox when it hits an
error. This is not as good as it could be - there are some errors that I
didn't add this to and my messages are very terse.

Also, I used StringCchPrintf and MessageBox as I don't know if the shim dll
normally pulls in MFC or anything else so I just went with the basic windows
API and CRT.

Comments/suggestions welcomed.

--
thanks - dave

CLRLoader.cpp:
#include "StdAfx.h"
#include "clrloader.h"
#include "strsafe.h"

// some interfaces that will be used are from this namespace.
using namespace mscorlib;

// static class variables

// auto_ptr is used to enforce clean-up on DLL unload.
std::auto_ptr<CCLRLoader> CCLRLoader::m_pInstance;


// forward declarations
static void DisplayError(HRESULT hr, LPCWSTR msg);
static HRESULT GetDllDirectory(TCHAR *szPath, DWORD nPathBufferSize);

// class functions

CCLRLoader *CCLRLoader::TheInstance()
{
if (m_pInstance.get() == NULL)
{
m_pInstance = std::auto_ptr<CCLRLoader>(new CCLRLoader());
}

return m_pInstance.get();
}

CCLRLoader::CCLRLoader(void)
: m_pHost(NULL)
, m_pLocalDomain(NULL)
{
}

CCLRLoader::~CCLRLoader(void)
{
if (m_pLocalDomain)
{
m_pLocalDomain->Release();
}

if (m_pHost)
{
m_pHost->Release();
}
}

// LoadCLR: This method starts up the Common Language Runtime
HRESULT CCLRLoader::LoadCLR()
{
HRESULT hr = S_OK;

// ensure the CLR is only loaded once.
if (m_pHost != NULL)
return hr;

// Load runtime into the process ...
hr = CorBindToRuntimeEx(
// version, use default
0,
// flavor, use default
0,
// domain-neutral"ness" and gc settings
STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN | STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(PVOID*) &m_pHost);

// couldn't load....
if (!SUCCEEDED(hr))
{
return hr;
}

// start CLR
return m_pHost->Start();
}

// CreateLocalAppDomain: the function creates AppDomain with BaseDirectory
// set to location of unmanaged DLL containing this code. Assuming that the
// target assembly is located in the same directory, the classes from this
// assemblies can be instantiated by calling _AppDomain::Load() method.
HRESULT CCLRLoader::CreateLocalAppDomain()
{
USES_CONVERSION;

HRESULT hr = S_OK;

// ensure the domain is created only once
if (m_pLocalDomain != NULL)
{
return hr;
}

CComPtr<IUnknown> pDomainSetupPunk;
CComPtr<IAppDomainSetup> pDomainSetup;
CComPtr<IUnknown> pLocalDomainPunk;
TCHAR szDirectory[MAX_PATH + 1];

// Create an AppDomainSetup with the base directory pointing to the
// location of the managed DLL. The assumption is made that the
// target assembly is located in the same directory
hr = m_pHost->CreateDomainSetup(&pDomainSetupPunk);
if (FAILED(hr)) {
::DisplayError (hr, L"Could not create setup domain");
goto Error;
}
hr = pDomainSetupPunk->QueryInterface(__uuidof(pDomainSetup),
(LPVOID*)&pDomainSetup);
if (FAILED(hr)) {
::DisplayError (hr, L"Could not query setup domain interface");
goto Error;
}

// Get the location of the hosting DLL
hr = ::GetDllDirectory(szDirectory,
sizeof(szDirectory)/sizeof(szDirectory[0]));
if (FAILED(hr)) {
::DisplayError (hr, L"Could not get host DLL directory");
goto Error;
}

// Configure the AppDomain to search for assemblies in the above directory
pDomainSetup->put_ApplicationBase(CComBSTR(szDirectory));

// Create an AppDomain that will run the managed assembly
hr = m_pHost->CreateDomainEx(T2W(szDirectory), pDomainSetupPunk, 0,
&pLocalDomainPunk);
if (FAILED(hr)) {
::DisplayError (hr, L"Could not create parent domain");
goto Error;
}

// Cast IUnknown pointer to _AppDomain pointer
hr = pLocalDomainPunk->QueryInterface(__uuidof(m_pLocalDomain),
(LPVOID*)&m_pLocalDomain);
if (FAILED(hr)) {
::DisplayError (hr, L"Could not query parent domain interface");
goto Error;
}

Error:
return hr;
}

// CreateInstance:
HRESULT CCLRLoader::CreateInstance(LPCWSTR szAssemblyName, LPCWSTR
szClassName, const IID &riid, void ** ppvObject)
{
HRESULT hr = S_OK;
_ObjectHandle *pObjHandle = NULL;
VARIANT v;

// Ensure the common language runtime is running ...
hr = LoadCLR();
if (FAILED(hr)) {
::DisplayError (hr, L"Could not load .NET common runtime");
goto Error;
}

// In order to securely load an assembly, its fully qualified strong name
// and not the filename must be used. To do that, the target AppDomain's
// base directory needs to point to the directory where the assembly is
// residing. CreateLocalAppDomain() ensures that such AppDomain exists.
hr = CreateLocalAppDomain();
if (FAILED(hr)) {
::DisplayError (hr, L"Could not create app domain");
goto Error;
}

// Create an instance of the managed class
hr = m_pLocalDomain->CreateInstance(CComBSTR(szAssemblyName),
CComBSTR(szClassName), &pObjHandle);
if (FAILED(hr)) {
::DisplayError (hr, L"Could not create loader instance");
goto Error;
}

// extract interface pointer from the object handle
VariantInit(&v);
hr = pObjHandle->Unwrap(&v);
hr = v.pdispVal->QueryInterface(riid, ppvObject);
if (FAILED(hr)) {
::DisplayError (hr, L"Could not query interface");
goto Error;
}

Error:

return hr;
}

const int MSG_SIZE = 256;

static void DisplayError(HRESULT hr, LPCWSTR msg) {

if(FACILITY_WINDOWS == HRESULT_FACILITY(hr))
hr = HRESULT_CODE(hr);
TCHAR buf [MSG_SIZE];
::StringCchPrintf(buf, MSG_SIZE, L"%s\nError: %x", msg, hr);
msg = buf;

LPCSTR szErrMsg;
if(FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&szErrMsg, 0, NULL) != 0) {
TCHAR buf2 [MSG_SIZE];
::StringCchPrintf(buf2, MSG_SIZE, L"%s\n%s", buf, szErrMsg);
msg = buf2;
LocalFree((HLOCAL)szErrMsg);
}

::MessageBox(NULL, msg, L"AutoTagShim Load Error", MB_OK | MB_ICONERROR);
}

// GetDllDirectory() gets loaction directory of DLL containing this code
static HRESULT GetDllDirectory(TCHAR *szPath, DWORD nPathBufferSize)
{
HMODULE hInstance = _AtlBaseModule.GetModuleInstance();

if (hInstance == 0)
{
return E_FAIL;
}

TCHAR szModule[MAX_PATH + 1];
DWORD dwFLen = ::GetModuleFileName(hInstance, szModule, MAX_PATH);

if (dwFLen == 0)
{
return E_FAIL;
}

TCHAR *pszFileName;
dwFLen = ::GetFullPathName(szModule, nPathBufferSize, szPath, &pszFileName);
if (dwFLen == 0 || dwFLen >= nPathBufferSize)
{
return E_FAIL;
}

*pszFileName = 0;
return S_OK;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top