Figure 1   Remote Component


Dim cat ' As MTSAdmin.Catalog
Set cat = CreateObject("MTSAdmin.Catalog")
Dim remComps ' As MTSAdmin.CatalogCollection
Set remComps = cat.GetCollection("RemoteComponents")
remComps.Populate
Dim remUtil ' As MTSAdmin.RemoteComponentUtil
Set remUtil = remComps.GetUtilInterface
remUtil.InstallRemoteComponentByName "changeme", "Sample Bank", "Bank.Account.VC"
remComps.SaveChanges


Figure 3   Remote Activation with a Middleman


STDMETHODIMP Bob::DoWork(void) {
    IObjectContext *poc = 0;
    HRESULT hr = GetObjectContext(&poc);
    if (SUCCEEDED(hr)) {
// get middleman activator
        IActivatorEx *pa = 0;
        hr = poc->CreateInstance(HOSTID_HOSTX, 
                                 IID_IActivatorEx, (void**)&pa);
        if (SUCCEEDED(hr)) {
// use middleman to create sub-object
            ISteve *ps = 0;
            hr = pa->CreateInstance(CLSID_Steve,
                                    IID_ISteve, (void**)&ps);
// get rid of middleman, we're done with him!
            pa->Release();
            if (SUCCEEDED(hr)) {
                ps->DoSomeWork();
                ps->DoALittleMore();
                ps->Release();
            }
        }
        poc->Release();
    }
    return hr;
}


Figure 5   MTSMultiHost

MTSMultiHost.Idl


// MTSMultiHost.idl : Activity-aware creation using logical host name

import "oaidl.idl";
#include <mtxattr.h>

cpp_quote("DEFINE_GUID(HOSTID_MIN, 0x725C1000, 0x3882, 0x11d2, 0xA4, 
          0xB9, 0x00, 0x60, 0x08, 0xD1, 0xA5, 0x34);")
cpp_quote("DEFINE_GUID(HOSTID_MAX, 0x725C1099, 0x3882, 0x11d2, 0xA4, 
          0xB9, 0x00, 0x60, 0x08, 0xD1, 0xA5, 0x34);")
enum { ACTIVATOR_MIN = 0, ACTIVATOR_MAX = 99 };

[
    uuid(725C0F50-3882-11d2-A4B9-006008D1A534), 
    object,
    hidden
]
interface IActivator : IUnknown
{
    HRESULT CreateInstance([in] BSTR bstrProgID, [out, retval] VARIANT *ppUnk);
}

[
    uuid(725C0F51-3882-11d2-A4B9-006008D1A534), 
    object,
    hidden
]
interface IActivatorEx : IUnknown
{
    HRESULT CreateInstance([in] REFCLSID rclsid, [in] REFIID riid, [out,
                           iid_is(riid), retval] void **ppv);
}

[
    uuid(725C0F52-3882-11d2-A4B9-006008D1A534), 
    object
]
interface IRemoteActivator : IUnknown
{
    HRESULT CreateInstance([in] long iHost, [in] BSTR bstrProgID, [out, retval]
                           VARIANT *ppUnk);
}

[
    uuid(725C0F53-3882-11d2-A4B9-006008D1A534), 
    object, hidden
]
interface IRemoteActivatorEx : IUnknown
{
    HRESULT CreateInstance([in] long iHost, [in] REFCLSID rclsid, [in] REFIID
                           riid, [out, iid_is(riid), retval] void **ppv);
}


#define ACTIVATOR(n) [ TRANSACTION_SUPPORTED, 
uuid(725C1##n##-3882-11d2-A4B9-006008D1A534), 
hidden ] coclass Host##n { interface IActivator; interface IActivatorEx; }

#define ACTIVATOR2(n) ACTIVATOR(n##0) ACTIVATOR(n##1) \
                      ACTIVATOR(n##2) ACTIVATOR(n##3) \
                      ACTIVATOR(n##4) ACTIVATOR(n##5) \
                      ACTIVATOR(n##6) ACTIVATOR(n##7) \
                      ACTIVATOR(n##8) ACTIVATOR(n##9) 

#define ACTIVATOR3(n) ACTIVATOR2(n##0) ACTIVATOR2(n##1) \
                      ACTIVATOR2(n##2) ACTIVATOR2(n##3) \
                      ACTIVATOR2(n##4) ACTIVATOR2(n##5) \
                      ACTIVATOR2(n##6) ACTIVATOR2(n##7) \
                      ACTIVATOR2(n##8) ACTIVATOR2(n##9) 

[
    uuid(725C0F54-3882-11d2-A4B9-006008D1A534), version(1.0),
    helpstring("MTS MultiHost Activator")
]
library MTSMultiHost
{
    importlib("stdole32.tlb");
    
    [ 
        TRANSACTION_SUPPORTED,
        uuid(725C0F55-3882-11d2-A4B9-006008D1A534)
    ]
    coclass RemoteActivator
    {
        interface IRemoteActivator;
        interface IRemoteActivatorEx;
    }
    ACTIVATOR3(0)
}
MTSMultiHost.cpp

// MTSMultiHost.cpp : Activity-aware creation using logical host name

#include <windows.h>
#include <initguid.h>
#include <mtx.h>
#include "MTSMultiHost.h"
#include "MTSMultiHost_i.c"

// this is the guy that sits on the remote machine to do the activation call
class Activator : public IActivator, public IActivatorEx {
    LONG m_cRef;
public:
    Activator(void) : m_cRef(0) {}

    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
        if (riid == IID_IUnknown || riid == IID_IActivator)
            *ppv = static_cast<IActivator*>(this);
        else if (riid == IID_IActivatorEx)
            *ppv = static_cast<IActivatorEx*>(this);
        else
            return (*ppv = 0), E_NOINTERFACE;
        reinterpret_cast<IUnknown*>(*ppv)->AddRef();
        return S_OK;
    }

    STDMETHODIMP_(ULONG) AddRef() { return ++m_cRef; }
    STDMETHODIMP_(ULONG) Release() { 
        if (--m_cRef)
            return m_cRef;
        delete this;
        return 0;
    }

// just call ViperCreateInstance a la the Transaction Context
    STDMETHODIMP CreateInstance(REFCLSID rclsid, REFIID riid, void **ppv) {
        *ppv = 0;
        IObjectContext *poc = 0;
        HRESULT hr = GetObjectContext(&poc);
        if (SUCCEEDED(hr))
        {
            hr = poc->CreateInstance(rclsid, riid, ppv);
            poc->SetComplete(); // we're done, so get those 12 bytes back ;-)
            poc->Release();
        }
        return hr;
    }

// just call ViperCreateInstance a la the Transaction Context
    STDMETHODIMP CreateInstance(BSTR bstrProgID, VARIANT *ppUnk) {
        ZeroMemory(ppUnk, sizeof(*ppUnk));
        ppUnk->vt = VT_UNKNOWN;
        if (!bstrProgID)
            return E_INVALIDARG;
        CLSID clsid;
        HRESULT hr = CLSIDFromProgID(bstrProgID, &clsid);
        if (SUCCEEDED(hr))
            hr = this->CreateInstance(clsid, IID_IUnknown, 
                                      (void**)&(ppUnk->punkVal));
        return hr;
    }
};

// this is the library object that lives on the client-side and uses
// the IActivator on the remote host
class RemoteActivator : public IRemoteActivatorEx, public IRemoteActivator {
    LONG m_cRef;
public:
    RemoteActivator(void) : m_cRef(0) {}

    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
        if (riid == IID_IUnknown || riid == IID_IRemoteActivatorEx)
            *ppv = static_cast<IRemoteActivatorEx*>(this);
        else if (riid == IID_IRemoteActivator)
            *ppv = static_cast<IRemoteActivator*>(this);
        else
            return (*ppv = 0), E_NOINTERFACE;
        reinterpret_cast<IUnknown*>(*ppv)->AddRef();
        return S_OK;
    }

    STDMETHODIMP_(ULONG) AddRef() { return ++m_cRef; }
    STDMETHODIMP_(ULONG) Release() { 
        if (--m_cRef)
            return m_cRef;
        delete this;
        return 0;
    }

// helper routine to cruft up the CLSID for a given host ID
    HRESULT CLSIDFromHostNum(long iHost, CLSID *pclsid) {
        if (iHost < ACTIVATOR_MIN || iHost > ACTIVATOR_MAX)
            return E_INVALIDARG;
// we don't use contiguous guids, so we need to fix up the decimal
// to hex offsets             
        *pclsid = CLSID_Host000;
        pclsid->Data1 += (iHost % 10);
        if (iHost /= 10)
        {
            pclsid->Data1 += (iHost % 10) * 16;
            if (iHost /= 10)
                pclsid->Data1 += (iHost % 10) * 16 * 16;
        }
        return S_OK;
    }

// forward activation request to the named IActivator machine
    STDMETHODIMP CreateInstance(long iHost, BSTR bstrProgID, VARIANT *ppUnk) {
        ZeroMemory(ppUnk, sizeof(*ppUnk));
        ppUnk->vt = VT_UNKNOWN;
        if (!bstrProgID)
            return E_INVALIDARG;
// get CLSID for host machine
        CLSID host;
        HRESULT hr = CLSIDFromHostNum(iHost, &host);
        if (SUCCEEDED(hr))
        {
            IObjectContext *poc = 0;
            hr = GetObjectContext(&poc);
            if (SUCCEEDED(hr))
            {
// create activator at host machine
                IActivator *pa = 0;
                hr = poc->CreateInstance(host, IID_IActivator, (void**)&pa);
                if (SUCCEEDED(hr))
                {
// ask activator on host machine to create object for us
                    hr = pa->CreateInstance(bstrProgID, ppUnk);
                    pa->Release();
                }
                poc->SetComplete(); // Get those bytes back so we can scale ;-)
                poc->Release();
            }
        }
        return hr;
        
    }

// forward activation request to the named IActivator machine
    STDMETHODIMP CreateInstance(long iHost, REFCLSID rclsid, 
                                REFIID riid, void **ppv)  {
        *ppv = 0;
// get CLSID for host machine
        CLSID host;
        HRESULT hr = CLSIDFromHostNum(iHost, &host);
        if (SUCCEEDED(hr))
        {
            IObjectContext *poc = 0;
            hr = GetObjectContext(&poc);
            if (SUCCEEDED(hr))
            {
                IActivatorEx *pa = 0;
// create activator at host machine
                hr = poc->CreateInstance(host, IID_IActivatorEx, (void**)&pa);
                if (SUCCEEDED(hr))
                {
// ask activator on host machine to create object for us
                    hr = pa->CreateInstance(rclsid, riid, ppv);
                    pa->Release();
                }
                poc->SetComplete(); // Get those 12 bytes back ;-)
                poc->Release();
            }
        }
        return hr;
    }
};

// generic class factory
struct Factory : public IClassFactory
{
public:
    Factory(bool b) : m_bIsRemAct(b) {}
    bool m_bIsRemAct;
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        if (riid == IID_IUnknown || riid == IID_IClassFactory)
            *ppv = static_cast<IClassFactory*>(this);
        else
            return (*ppv = 0), E_NOINTERFACE;
        reinterpret_cast<IUnknown*>(*ppv)->AddRef();
        return S_OK;
    }

    STDMETHODIMP_(ULONG) AddRef() { return 2; }
    STDMETHODIMP_(ULONG) Release() { return 1; }

    STDMETHODIMP LockServer(BOOL bLock) { return S_OK; }

    STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
    {
        *ppv = 0;
        if (pUnkOuter)
            return CLASS_E_NOAGGREGATION;
        IUnknown *pUnk = 0;
// create the approprate type of object
        if (m_bIsRemAct)
            pUnk = static_cast<IRemoteActivatorEx*>(new RemoteActivator);
        else
            pUnk = static_cast<IActivatorEx*>(new Activator);
        if (!pUnk)
            return E_OUTOFMEMORY;
        pUnk->AddRef();
        HRESULT hr = pUnk->QueryInterface(riid, ppv);
        pUnk->Release();
        return hr;
    }
};

// class factory for CLSID_RemoteActivator
Factory remoteFactory(true);
// class factory for CLSID_HostXXX
Factory factory(false);

inline bool operator <= (const GUID& lhs, const GUID& rhs)
{ return memcmp(&lhs, &rhs, sizeof(GUID)) <= 0; }

inline bool operator >= (const GUID& lhs, const GUID& rhs)
{ return memcmp(&lhs, &rhs, sizeof(GUID)) >= 0; }

// extremely generic (and broad) DllGetClassObject
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
    if (rclsid == CLSID_RemoteActivator)
        return remoteFactory.QueryInterface(riid, ppv);
    else if (rclsid >= HOSTID_MIN && rclsid <= HOSTID_MAX)
        return factory.QueryInterface(riid, ppv); // this branch exec's a lot!
    else
        return (*ppv = 0), CLASS_E_CLASSNOTAVAILABLE;
}

// static data needed for CLSID_RemoteActivator
char g_szFileName[MAX_PATH];
char *g_rgRegKeys[][3] = { 
    { 
        "CLSID\\{725C0F55-3882-11d2-A4B9-006008D1A534}", 
        0, 
        "MTSMultiHost.RemoteActivator" 
    },
    { 
        "CLSID\\{725C0F55-3882-11d2-A4B9-006008D1A534}\\InprocServer32", 
        0, 
        g_szFileName 
    },
    { 
        "CLSID\\{725C0F55-3882-11d2-A4B9-006008D1A534}\\InprocServer32", 
        "ThreadingModel", 
        "Both" 
    },
    { 
        "CLSID\\{725C0F55-3882-11d2-A4B9-006008D1A534}\\ProgID", 
        0, 
        "MTSMultiHost.RemoteActivator" 
    },
    { 
        "MTSMultiHost.RemoteActivator", 
        0, 
        "MTSMultiHost.RemoteActivator" 
    },
    { 
        "MTSMultiHost.RemoteActivator\\CLSID", 
        0, 
        "{725C0F55-3882-11d2-A4B9-006008D1A534}" 
    }
};

// add an arbitrary value to the registry
HRESULT WriteKey(const char *pszKey, 
                 const char *pszValueName, 
                 const char *pszValue)
{
    HRESULT hr = S_OK;
    HKEY hkey;
    LONG err = RegCreateKeyA(HKEY_CLASSES_ROOT, pszKey, &hkey);
    if (err != ERROR_SUCCESS)
        hr = HRESULT_FROM_WIN32(err);
    else
    {
        err = RegSetValueExA(hkey, pszValueName, 0, REG_SZ, 
                             (const BYTE*)pszValue, lstrlenA(pszValue));
        if (err != ERROR_SUCCESS)
            hr = HRESULT_FROM_WIN32(err);
        RegCloseKey(hkey);
    }
    return hr;
}

// add the keys for a given CLSID_HostXXX
HRESULT WriteActivatorKey(int iHost) {
    char szKey[1024];
    char szValue[1024];
    
    wsprintfA(szKey, "CLSID\\{725C1%03d-3882-11d2-A4B9-006008D1A534}", iHost);
    wsprintfA(szValue, "MTSMultiHost.Host%03d", iHost);

    HRESULT hr = WriteKey(szKey, 0, szValue);
    if (FAILED(hr))
        return hr;
    lstrcatA(szKey, "\\InprocServer32");
    hr = WriteKey(szKey, 0, g_szFileName);
    if (FAILED(hr))
        return hr;
    hr = WriteKey(szKey, "ThreadingModel", "Both");
    if (FAILED(hr))
        return hr;
    wsprintfA(szKey, "CLSID\\{725C1%03d-3882-11d2-A4B9-006008D1A534}\\ProgID", 
              iHost);
    wsprintfA(szValue, "MTSMultiHost.Host%03d", iHost);
    hr = WriteKey(szKey, 0, szValue);
    if (FAILED(hr))
        return hr;
    wsprintfA(szKey, "MTSMultiHost.Host%03d", iHost);
    wsprintfA(szValue, "MTSMultiHost.Host%03d", iHost);
    hr = WriteKey(szKey, 0, szValue);
    if (FAILED(hr))
        return hr;
    
    lstrcatA(szKey, "\\CLSID");
    wsprintfA(szValue, "{725C1%03d-3882-11d2-A4B9-006008D1A534}", iHost);
    hr = WriteKey(szKey, 0, szValue);
    if (FAILED(hr))
        return hr;

    return hr;
}

// nuke the keys for a given CLSID_HostXXX
HRESULT DeleteActivatorKey(int iHost) {
    char szKey[1024];
    wsprintfA(szKey, 
              "CLSID\\{725C1%03d-3882-11d2-A4B9-006008D1A534}\\InprocServer32",
              iHost);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);
    wsprintfA(szKey, "CLSID\\{725C1%03d-3882-11d2-A4B9-006008D1A534}\\ProgID", 
              iHost);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);
    wsprintfA(szKey, "CLSID\\{725C1%03d-3882-11d2-A4B9-006008D1A534}", iHost);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);

    wsprintfA(szKey, "MTSMultiHost.Host%03d\\CLSID", iHost);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);
    wsprintfA(szKey, "MTSMultiHost.Host%03d", iHost);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);
    return S_OK;
}

HINSTANCE g_hInstance = 0;

STDAPI DllRegisterServer() {
// register our Type Library
    HRESULT hr = S_OK;
    GetModuleFileName(g_hInstance, g_szFileName, MAX_PATH);
    OLECHAR wszFileName[MAX_PATH];
    MultiByteToWideChar(CP_ACP, 0, g_szFileName, lstrlenA(g_szFileName) + 1, 
                        wszFileName, MAX_PATH);
    ITypeLib *ptl = 0;
    hr = LoadTypeLib(wszFileName, &ptl);
    if (FAILED(hr))
        return hr;
    hr = RegisterTypeLib(ptl, wszFileName, 0);
    ptl->Release();
    if (FAILED(hr))
        return hr;
// add the static keys for CLSID_RemoteActivator
    for (int i = 0; 
         SUCCEEDED(hr) && i < sizeof(g_rgRegKeys)/sizeof(*g_rgRegKeys); 
         i++)
        hr = WriteKey(g_rgRegKeys[i][0], g_rgRegKeys[i][1], g_rgRegKeys[i][2]);

// add the dynamic keys for CLSID_HostXXX
    for (int iHost = ACTIVATOR_MIN; 
         SUCCEEDED(hr) && iHost <= ACTIVATOR_MAX; 
         iHost++)
        hr = WriteActivatorKey(iHost);

    return hr;
}

STDAPI DllUnregisterServer() {
// remove static keys for CLSID_RemoteActivator
    for (int i = sizeof(g_rgRegKeys)/sizeof(*g_rgRegKeys) - 1; i >=0; i--)
        RegDeleteKey(HKEY_CLASSES_ROOT, g_rgRegKeys[i][0]);
// remove dynamic keys for CLSID_HostXXX
    for (i = ACTIVATOR_MIN; i <= ACTIVATOR_MAX; i++)
        DeleteActivatorKey(i);
    return S_OK;
}

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, void*) {
    if (dwReason == DLL_PROCESS_ATTACH) {
        g_hInstance = hInstance;
        DisableThreadLibraryCalls(hInstance);
    }
    return TRUE;
}