//
//----------------------------------------------------------------------------
// ObjectComponents
// (C) Copyright 1994 by Borland International, All Rights Reserved
//
//   OLE Registration implementation
//----------------------------------------------------------------------------
#include <ocf/ocfpch.h>
#include <ocf/ocreg.h>
#include <ocf/appdesc.h>
#include <shellapi.h>
#include <string.h>
#include <stdlib.h>  // atol()

DIAG_DEFINE_GROUP(OcDll, true, 1);
DIAG_DECLARE_GROUP(OcRefCount);

#if defined(BI_APP_DLL)
  extern TRegistrar* DllRegistrar;
#endif

void TXRegistry::Check(long stat, const char* key)
{
  if (stat != 0 && !InstanceCount) {
    if (stat == 1)      // default bool true to a generic E_FAIL
      stat = E_FAIL;
    char buf[100];
    wsprintf(buf, "Registry failure on key: %s, ErrorCode = %lX\n",
             (const char far*)key, stat); 
    WARN(stat != 0, buf);
    throw TXRegistry(buf, key);
  }
}

//____________________________________________________________________________
//
// Internal registration template table and registration item key table
//____________________________________________________________________________

char OcRegEntryHeader[] = "HKEY_CLASSES_ROOT\\";
char OcRegEntryAssign[] = " = ";

struct TRegParam {
  char*  Param;    // substituted parameter name
  char*  Default;  // default value, 0 if required
  char*  TplList;  // list of template indices to invoke
  static int Find(const char* param); // associative lookup of value by param
};

//
// Registration template table, ordering determines sequence of registration
//  note: the parameter table is dependent on template indices in this table
//
char* OcRegTemplates[] = {
#if defined(BI_PLAT_WIN32)
/* 001 */ "CLSID\\<clsid>\\<serverctx>Server32 = <debugger> <path> <cmdline><extraopt>",
#else
/* 001 */ "CLSID\\<clsid>\\<serverctx>Server = <debugger> <path> <cmdline><extraopt>",
#endif
/* 002 */ "CLSID\\<clsid>\\ProgID = <progid>",
/* 003 */ "CLSID\\<clsid> = <description>",
/* 004 */ "CLSID\\<clsid>\\DefaultIcon = <path>,<iconindex>",
#if defined(BI_PLAT_WIN32)
/* 005 */ "CLSID\\<clsid>\\InprocHandler32 = <handler>",
#else
/* 005 */ "CLSID\\<clsid>\\InprocHandler = <handler>",
#endif
/* 006 */ "<progid>\\CLSID = <clsid>",
/* 007 */ "<progid> = <description>",

/* 010 */ "<progid>\\protocol\\StdFileEditing\\verb\\0 = <verb0>",
/* 011 */ "<progid>\\protocol\\StdFileEditing\\verb\\1 = <verb1>",
/* 012 */ "<progid>\\protocol\\StdFileEditing\\verb\\2 = <verb2>",
/* 013 */ "<progid>\\protocol\\StdFileEditing\\verb\\3 = <verb3>",
/* 014 */ "<progid>\\protocol\\StdFileEditing\\verb\\4 = <verb4>",
/* 015 */ "<progid>\\protocol\\StdFileEditing\\verb\\5 = <verb5>",
/* 016 */ "<progid>\\protocol\\StdFileEditing\\verb\\6 = <verb6>",
/* 017 */ "<progid>\\protocol\\StdFileEditing\\verb\\7 = <verb7>",

/* 020 */ "CLSID\\<clsid>\\Verb\\0 = <verb0>,<verb0opt>",
/* 021 */ "CLSID\\<clsid>\\Verb\\1 = <verb1>,<verb1opt>",
/* 022 */ "CLSID\\<clsid>\\Verb\\2 = <verb2>,<verb2opt>",
/* 023 */ "CLSID\\<clsid>\\Verb\\3 = <verb3>,<verb3opt>",
/* 024 */ "CLSID\\<clsid>\\Verb\\4 = <verb4>,<verb4opt>",
/* 025 */ "CLSID\\<clsid>\\Verb\\5 = <verb5>,<verb5opt>",
/* 026 */ "CLSID\\<clsid>\\Verb\\6 = <verb6>,<verb6opt>",
/* 027 */ "CLSID\\<clsid>\\Verb\\7 = <verb7>,<verb7opt>",

/* 030 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\0 = <format0>",
/* 031 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\1 = <format1>",
/* 032 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\2 = <format2>",
/* 033 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\3 = <format3>",
/* 034 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\4 = <format4>",
/* 035 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\5 = <format5>",
/* 036 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\6 = <format6>",
/* 037 */ "CLSID\\<clsid>\\DataFormats\\GetSet\\7 = <format7>",

/* 040 */ "CLSID\\<clsid>\\MiscStatus = <aspectall>",
/* 041 */ "CLSID\\<clsid>\\MiscStatus\\1 = <aspectcontent>",
/* 042 */ "CLSID\\<clsid>\\MiscStatus\\2 = <aspectthumbnail>",
/* 043 */ "CLSID\\<clsid>\\MiscStatus\\4 = <aspecticon>",
/* 044 */ "CLSID\\<clsid>\\MiscStatus\\8 = <aspectdocprint>",
/* 045 */ "CLSID\\<clsid>\\AuxUserType\\2 = <menuname>",
/* 046 */ "CLSID\\<clsid>\\AuxUserType\\3 = <appname>",
/* 047 */ "CLSID\\<clsid>\\Insertable",

/* 050 */ "<permid> = <permname>",
/* 051 */ "<permid>\\CLSID = <clsid>",
/* 052 */ "<permid>\\CurVer = <progid>",
/* 053 */ "CLSID\\<clsid>\\VersionIndependentProgID = <permid>",

/* 054 */ "CLSID\\<clsid>\\Version = <version>",
/* 055 */ "CLSID\\<clsid>\\DataFormats\\DefaultFile = <filefmt>",
/* 056 */ "CLSID\\<clsid>\\Ole1Class = <progid>",  // is this really needed?
/* 057 */ ".<extension> = <progid>",

/* 060 */ "<progid>\\Shell\\Print\\Command = <path> %1",
/* 061 */ "<progid>\\Shell\\Open\\Command = <path> %1",
/* 062 */ "<progid>\\protocol\\StdFileEditing\\server = <debugger> <path><extraopt>",
/* 063 */ "<progid>\\Insertable",
           };

const int OcRegTemplateCount   = sizeof(OcRegTemplates)/sizeof(char*);

//
// Registration parameter table, ordering is arbitrary, parameters lowercase
//
TRegParam OcRegParams[] = {
  {"clsid",             0, ""                },// GUID standard string form
  {"progid",            0, "\001\002\003\005\006\007"},// a unique string
  {"insertable",        0, "\046\047\062\063" },// defined to publish server
  {"usage",  ocrSingleUse, ""                },// class factory registration
  {"description",       0, ""                },// human readable, 40 chars max
#if defined(BI_PLAT_WIN32)
  {"handler", "ole32.dll", "\005"            },// inproc handler
#else
  {"handler",  "ole2.dll", "\005"            },// inproc handler
#endif
  {"serverctx",         0, "\001"            },// "Local"(EXE) or "Inproc"(DLL)
  {"path",              0, ""                },// OC defaults to load path
  {"cmdline",          "", ""                },// /Automation if app class
  {"debugger",         "", ""                },// debugger to invoke server
  {"permid",            0, "\050\051\053"    },// version independent clsid
  {"permname",          0, "\052"            },// version independent progid
  {"iconindex",       "0", "\004"            },// zero-based resource icon
  {"menuname",         "", "\045"            },// 15 characters maximum
  {"appname",           0, ""                },// AppName, AuxUserType/3
  {"verb0",             0, "\010\020"        },// name of primary verb
  {"verb1",             0, "\011\021"        },
  {"verb2",             0, "\012\022"        },
  {"verb3",             0, "\013\023"        },
  {"verb4",             0, "\014\024"        },
  {"verb5",             0, "\015\025"        },
  {"verb6",             0, "\016\026"        },
  {"verb7",             0, "\017\027"        },
  {"verb8",             0, "\018\028"        },
  {"verb0opt",      "0,2", "\020"            },//generated from REGVERBOBT macro
  {"verb1opt",      "0,2", "\021"            },
  {"verb2opt",      "0,2", "\022"            },
  {"verb3opt",      "0,2", "\023"            },
  {"verb4opt",      "0,2", "\024"            },
  {"verb5opt",      "0,2", "\025"            },
  {"verb6opt",      "0,2", "\026"            },
  {"verb7opt",      "0,2", "\027"            },
  {"verb8opt",      "0,2", "\028"            },
  {"format0",           0, "\030"            },//generated from REGFORMAT macro
  {"format1",           0, "\031"            },
  {"format2",           0, "\032"            },
  {"format3",           0, "\033"            },
  {"format4",           0, "\034"            },
  {"format5",           0, "\035"            },
  {"format6",           0, "\036"            },
  {"format7",           0, "\037"            },
  {"format8",           0, "\038"            },
  {"filefmt",           0, "\055"            },// default file format name
  {"aspectall",       "0", "\040"            },// option flags for all aspects
  {"aspectcontent",     0, "\041"            },// option flags for content view
  {"aspectthumbnail",   0, "\042"            },// option flags thumbnail view
  {"aspecticon",        0, "\043"            },// option flags for icon view
  {"aspectdocprint",    0, "\044"            },// option flags docprint view
  {"extension",         0, "\057\060\061"    },// extension for shell loading
  {"version",           0, "\054"            },// app/typelib version string
  {"helpdir",           0, ""                },// help directory for typehelp
  {"typehelp",          0, ""                },// help file for type library
  {"language",          0, ""                },// set internally from func arg
  {"docflags",        "0", ""                },
  {"directory",         0, ""                },
  {"docfilter",         0, ""                },
  {"debugclsid",        0, ""/*INTERNAL GEN*/},// internal,NOT USER SPECIFIED 
  {"extraopt",         "", ""/*INTERNAL USE*/},// cmdline debug/non-debug flag
  {"debugprogid",       0, ""                },// define to force debug reg
  {"debugdesc",         0, ""                },// different string for debug
};
const int OcRegParamCount = sizeof(OcRegParams)/sizeof(TRegParam);

int TRegParam::Find(const char* param)
{
  PRECONDITION(param);

  int i = ::OcRegParamCount;
  while (--i >= 0) {
    if (strcmp(::OcRegParams[i].Param, param) == 0)
      break;
  }
  return i;
}

//____________________________________________________________________________
//
// Generate registration file image to an output stream
//____________________________________________________________________________

static int OcRegWrite(const char* key, const char* val, ostream& out)
{
  PRECONDITION(key);

  out << OcRegEntryHeader << key;
  if (val)
    out << OcRegEntryAssign << val;
  out << '\n';
  return 0;
}

long OcRegisterClass(TRegList& regInfo, HINSTANCE module, ostream& out,
                     TLangId lang, char* filter, TRegItem* defaults,
                     TRegItem* extra)
{
  long docFlags = 0;
  const char* pc;
  int i;
  TRegItem* regItem;
  const int bufSize  = 512;
  const int pathSize = 512;
  const int langSize = 10;
  const int guidSize = 64;
  struct _buf {
    char* buf;
    _buf() { buf = new char[ bufSize + pathSize + langSize + guidSize
                           + OcRegTemplateCount * sizeof(char)
                           + OcRegParamCount * sizeof(char*) ]; }
   ~_buf() {delete[] buf;}
    operator char*() {return buf;}
  } buf;
  char*  path = buf + bufSize;
  char*  langBuf = path + pathSize;
  char*  guidBuf = langBuf + langSize;
  signed char* tplEnab = (signed char*)(guidBuf + guidSize);
  const char** paramVal = (const char**)(tplEnab + OcRegTemplateCount);
  ::GetModuleFileName(module, path, pathSize);

  // Check to see if the reg format buffer overflowed during startup
  //
  if (TRegItem::Heap.Used > TRegItem::Heap.Size) {
    const char frmt[] = "REGISTRATION_FORMAT_BUFFER Overflow. Need %d bytes total.";
    char msg[sizeof frmt + 11];
    wsprintf(msg, frmt, TRegItem::Heap.Used);
    throw TXRegistry(msg, 0);
  }

  // Check for language defaulted to system configuration
  //
  if (lang == LangSysDefault)
    lang = TLocaleString::SystemDefaultLangId;
  else if (lang == LangUserDefault)
    lang = TLocaleString::UserDefaultLangId;

  // initialize local arrays of paramater values and template enable flags
  //
  int userKeyCount = 0;
  for (i = OcRegParamCount; --i >= 0; paramVal[i] = OcRegParams[i].Default)
    ;
  if (filter) {
    memset(tplEnab, 0x80, OcRegTemplateCount);  // disable all templates
    for (pc = filter; (i = *pc++) != 0; )
      tplEnab[i-1] = 0;                         // selectively allow enable
  }
  else {
    memset(tplEnab, 0, OcRegTemplateCount);     // initialize to unselected
  }

  // assign path parameter initially to current module load path
  //
  i = TRegParam::Find("path");
  paramVal[i] = path;

  // assign server context based on whether it is an EXE or a DLL
  //
  i = TRegParam::Find("serverctx");
#if defined (BI_PTR_0_32)
  if (::GetModuleHandle(0) == module)   // check instance handle for EXE
    paramVal[i] = "Local";
  else
    paramVal[i] = "Inproc";
#else
  if (((unsigned)module | 1) == _SS) // check instance handle for 16-bit EXE
    paramVal[i] = "Local";
  else
    paramVal[i] = "Inproc";
#endif

  // assign language parameter to value passed in function argument
  //
  i = TRegParam::Find("language");
  wsprintf(langBuf, "%X", lang);
  paramVal[i] = langBuf;

  // scan through defaults list(0), user list of parameters and custom keys(1),
  // and extra list(2)
  //
  int step = 0;
  for (regItem = defaults; ; regItem++) {

    while (step <= 2 && (!regItem || (pc = regItem->Key) == 0)) {
      switch (step++) {
        case 0:
          regItem = regInfo.Items;
          break;
        case 1:
          regItem = extra;
          break;
        case 2:
          regItem = 0;
      }
    }
    if (!regItem || !pc)
      break;

    // note presence of user-specified key and value, process afterwards
    //
    if (*pc == ' ') {
      userKeyCount++;
    }
    else {
      // replace default with user-specified parameter value
      //
      TXRegistry::Check((i = TRegParam::Find(pc)) < 0, pc);
      paramVal[i] = regItem->Value.Translate(lang);

      // enable all templates invoked by parameter
      //      
      for (char* pEnab = OcRegParams[i].TplList; (i = *pEnab++) != 0; )
        tplEnab[i-1]++;
    }
  }

  // decode document template flags
  //
  if ((i = TRegParam::Find("docflags")) >= 0)
    docFlags = atol(paramVal[i]);

  // process enabled templates, substituting parameter values
  //
  regItem = regInfo.Items;
  for (int itpl = 0; itpl < OcRegTemplateCount || userKeyCount--; itpl++) {
    char* val = 0;
    char* pb = buf;
    const char* pt;
    char c;
    const char* userval = 0;
    if (itpl < OcRegTemplateCount) {  // processing standard template array
      if (tplEnab[itpl] <= 0)
        continue;
      pt = OcRegTemplates[itpl];
    }
    else {                       // now processing user-defined templates
      while (*(pt = regItem->Key) != ' ')
        regItem++;
      pt++;
      userval = regItem->Value;
    }
    do switch (c = *pt++) {
      case 0:
        *pb++ = 0;
        if (!userval)
          break;
        pt = userval;
        userval = 0;
        val = pb;
        c++;
        continue;
      case '<':
        pc = pb;
        continue;
      case '>':
        *pb = 0;
        pb = (char*)pc;
        TXRegistry::Check((i = TRegParam::Find(pc)) < 0, pc); // internal err
        TXRegistry::Check((pc = paramVal[i]) == 0, OcRegParams[i].Param);
        if (*pc == 0 && *pt == ' ')
          pt++;
        while (*pc != 0)
          *pb++ = *pc++;
        continue;
      case '=':
        while (*(pb-1) == ' ')
          pb--;
        *pb++ = 0;
        val = pb;
        while (*pt == ' ')
          pt++;
        continue;
      default:
        *pb++ = c;
    } while (c);
    OcRegWrite(buf, val, out);
  }
  return docFlags;
}

void OcRegistryUpdate(istream& in)
{
  struct TempKey {
    HKEY key;
    TempKey() {::RegOpenKey(HKEY_CLASSES_ROOT, "CLSID", &key);}
   ~TempKey() {::RegCloseKey(key);}
  } tempKey;
  while (!in.eof()) {
    char buf[512];
    char* pc;
    char* val;
    in.getline(buf, sizeof(OcRegEntryHeader));
    if (strcmp(buf,        OcRegEntryHeader) != 0)
      continue;
    in.getline(buf, sizeof(buf));
    if ((val = pc = strchr(buf, '=')) != 0) {
      while (*(pc-1) == ' ')
        pc--;
      *pc = 0;
      while (*(++val) == ' ')
        ;
    }
    else
      val = "";
    TXRegistry::Check(::RegSetValue(HKEY_CLASSES_ROOT,buf, REG_SZ,val,0),buf);
  }
}

//____________________________________________________________________________
//
// Unregister application or class from registration database
//____________________________________________________________________________

char* OcUnregParams[] = {
  "extension",    // special case: extension key needs '.' prepended
  "debugclsid",
  "debugprogid",
  "clsid",
  "progid",
  "permid",
};
const int OcUnregParamCount = sizeof(OcUnregParams)/sizeof(char*);

int OcUnregisterClass(TRegList& regInfo, TRegItem* extra)
{
  struct TempKey {
    HKEY Key;
    TempKey() {::RegOpenKey(HKEY_CLASSES_ROOT, "CLSID", &Key);}
   ~TempKey() {::RegCloseKey(Key);}
  } tempKey;
  int  errorCount = 0;

  // Loop to remove each root level key, thus cleaning up all nested keys too
  //
  for (int i = 0; i < OcUnregParamCount; i++) {
    const char* regKey = regInfo[OcUnregParams[i]];
    if (!regKey && extra && strcmp(extra->Key, OcUnregParams[i]) == 0)
      regKey = extra->Value;
    if (regKey) {
      char buff[16];
      if (i == 0) {       // special case prepending of '.' to extension
        buff[0] = '.';
        strcpy(buff+1, regKey);
        regKey = buff;
      }
      if (::RegDeleteKey(*regKey=='{' ? tempKey.Key : HKEY_CLASSES_ROOT, regKey)
          != ERROR_SUCCESS)   // }matching brace for brace counting editors
        errorCount++;         // should throw exception if certain errors?
    }
  }
  return errorCount;
}

int OcRegistryValidate(istream& in)
{
  struct TempKey {
    HKEY key;
    TempKey() {::RegOpenKey(HKEY_CLASSES_ROOT, "CLSID", &key);}
   ~TempKey() {::RegCloseKey(key);}
  } tempKey;
  int diffCount = 0;
  while (!in.eof()) {
    char entry[512];
    char buf[300];
    long bufSize = sizeof(buf);
    char* pc;
    char* val;
    in.getline(entry, sizeof(OcRegEntryHeader));
    if (strcmp(entry,        OcRegEntryHeader) != 0)
      continue;
    in.getline(entry, sizeof(entry));
    if ((val = pc = strchr(entry, '=')) != 0) {
      while (*(pc-1) == ' ')
        pc--;
      *pc = 0;
      while (*(++val) == ' ')
        ;
    }
    if (::RegQueryValue(HKEY_CLASSES_ROOT,entry,buf,&bufSize) != ERROR_SUCCESS
        || (val && strcmp(val, buf) != 0))
      diffCount++;
  }
  return diffCount;
}

//____________________________________________________________________________
//
// Separate debugging registration for application and documents
// Overrides non-debug values with values from debug keys
//____________________________________________________________________________

struct DebugRegKey { enum {  progid,        clsid,  description,   extraopt};};
char* OcReplaceKeys[] =  {  "progid",      "clsid","description", "extraopt"};
char* OcDebugKeys[] = {"debugprogid", "debugclsid","debugdesc",   "debugopt"};
const int DebugReplaceCount = sizeof(OcReplaceKeys)/sizeof(char*);

TRegItem OcRegNoDebug[] = { {"debugger", {""}}, {0,{0}} };
TRegItem OcRegNotDll[]  = { {"cmdline", {""}}, {"debugger", {""}}, {0,{0}} };
char AppDebugFilter[] = "\001\002\003\005\006\007";
char DocDebugFilter[] = "\001\002\003\005\006\007"
                        "\020\021\022\023\024\025\026\027"
                        "\030\031\032\033\034\035\036\037"
                        "\040\041\042\043\044\045\046\047"
                        "\054\055\062\063";

int OcSetupDebugReg(TRegList& regInfo, TRegItem* regDebug, TLangId lang,
                    char* clsid)
{
  int stat = -1;  // initialize for user-supplied clsid
  char** oldKey = OcReplaceKeys;
  char** newKey = OcDebugKeys;
  for (int i = 0; i < DebugReplaceCount; i++,oldKey++,newKey++,regDebug++) {
    const char* value = regInfo.Lookup(*newKey, lang);
    if (!value) {
      switch(i) {
        case DebugRegKey::progid:
          return 0;                   // no debug registration
        case DebugRegKey::extraopt:
          value = " /Debug";
          break;
        case DebugRegKey::clsid:
          if (clsid) {
            value = clsid;
            stat = 1;    // flag use of supplied clsid
            break;
          } // else fall through to throw exception
        case DebugRegKey::description:
          TXRegistry::Check(1, *newKey); // incomplete debug registration
      }
    }
    regDebug->Key  = *oldKey;
    regDebug->Value = value;
  }
  regDebug->Key = 0;    // null-terminate reg list
  return stat;
}

//____________________________________________________________________________
//
// TRegistrar - Application registration manager interface class
//____________________________________________________________________________

TRegistrar* TRegistrar::RegistrarList = 0;

TRegistrar::TRegistrar(TRegList& regInfo, TComponentFactory callback,
                       string& cmdLine, HINSTANCE hInst)
:
  AppDesc(*new TAppDescriptor(regInfo, callback, cmdLine, hInst))
{
  Next = RegistrarList;
  RegistrarList = this;
  TRACEX(OcRefCount, 1, "TRegistrar() @" << (void*)this);
}

TRegistrar::TRegistrar(TAppDescriptor& appDesc) : AppDesc(appDesc)
{
  Next = RegistrarList;
  RegistrarList = this;
}

TRegistrar::~TRegistrar()
{
  // since TRegistrars are destroyed when the module is shutdown
  // we do not need to detach each registrar from the linked list
  delete &AppDesc;
}

TRegistrar* TRegistrar::GetNext(TRegistrar* reg)
{
  return reg ? reg->Next : RegistrarList;
}

void far* TRegistrar::GetFactory(const GUID& clsid, const GUID far& iid)
{
  void far* retObj = 0;
  if (clsid != AppDesc.AppClassId)
    return 0;
  ((IUnknown&)AppDesc).QueryInterface(iid, &retObj);
  return retObj;   // QueryInterface sets to null if fails
}

bool TRegistrar::CanUnload()
{
  return static_cast<bool>(!AppDesc.IsBound());
}

int TRegistrar::Run()
{
  if (!IsOptionSet(amExeModule))
    return 0;    // inproc server waits until class factory created and called
  TComponentFactory factory = AppDesc.GetFactory();
  if (!factory)
    return 1;
  if (AppDesc.IsAutomated() && IsOptionSet(amAutomation))
    RegisterAppClass();
  IUnknown* ifc = factory(0, GetOptions() | amRun);  // create app and run it

  // app is now running its message loop, factory may get called again by OLE
  //
  ifc = factory(ifc, GetOptions() | amShutdown);  // EXE finished, destroy it
  if (ifc && !(GetOptions() & amServedApp))
    ifc->Release();  // we own the reference count, else container released it
  return 0;
}

void TRegistrar::Shutdown(IUnknown* releasedObj, uint32 options)
{
  if (!AppDesc.GetFactory())
    return;
  releasedObj = AppDesc.GetFactory()(releasedObj, options|amShutdown);
  if (releasedObj)
    releasedObj->Release();
}

//____________________________________________________________________________
//
// DLL server entry points
//____________________________________________________________________________

//
// Entry point for complete registration management via command line
//
STDAPI __export DllRegisterCommand(const char far* cmdLine)
{
  try {
    bool isDebug = false;
    TRegistrar* registrar = 0;

    // Need to set up typelibrary state information in case multiple components
    //
    while ((registrar = TRegistrar::GetNext(registrar)) != 0) {
      string cmd(cmdLine);
      registrar->ProcessCmdLine(cmd);
      if (registrar->IsOptionSet(amDebug))
        isDebug = true;
    }
    if (isDebug)
      __emit__(0xCC); // force debugg trap if REGISTER.EXE loaded with debugger
    return HR_OK;
  }
  catch (...) {
    return HR_FAIL;
  }
}

//
// OLE 2.0 entry point for obtaining a class factory for a particular CLSID
//
STDAPI __export DllGetClassObject(const GUID far& clsid, const GUID far& iid,
                                  void far* far* retObj)
{
  try {
    TRegistrar* registrar = 0;
    while ((registrar = TRegistrar::GetNext(registrar)) != 0) {
      *retObj = registrar->GetFactory(clsid, iid);
      if (*retObj)
        return HR_OK;
    }
    return HR_FAIL;
  }
  catch (...) {
    return HR_FAIL;
  }
}

//
// OLE 2.0 entry point for checking if DLL has no clients and can be unloaded
//
STDAPI __export DllCanUnloadNow(void)
{  
  TRegistrar* registrar = 0;
  while ((registrar = TRegistrar::GetNext(registrar)) != 0) {
    if (!registrar->CanUnload()) {
      TRACEX(OcDll, 1, "DllCanUnloadNow returning " << hex << uint32(HR_FALSE));
      return HR_FALSE;
    }
  }
  TRACEX(OcDll, 1, "DllCanUnloadNow returning " << hex << uint32(HR_OK));
  return HR_OK;
}     

//
// OLE 2.0 entry point for registering DLL, no locale info passed
//
STDAPI __export DllRegisterServer()
{
  return DllRegisterCommand("/RegServer");
}

//
// OLE 2.0 entry point for unregistering DLL
//
STDAPI __export DllUnregisterServer()
{
  return DllRegisterCommand("/UnregServer");
}

