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;
}