// //---------------------------------------------------------------------------- // ObjectComponents // (C) Copyright 1994 by Borland International, All Rights Reserved // // OLE Registration implementation //---------------------------------------------------------------------------- #include #include #include #include #include #include // 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\\\\Server32 = ", #else /* 001 */ "CLSID\\\\Server = ", #endif /* 002 */ "CLSID\\\\ProgID = ", /* 003 */ "CLSID\\ = ", /* 004 */ "CLSID\\\\DefaultIcon = ,", #if defined(BI_PLAT_WIN32) /* 005 */ "CLSID\\\\InprocHandler32 = ", #else /* 005 */ "CLSID\\\\InprocHandler = ", #endif /* 006 */ "\\CLSID = ", /* 007 */ " = ", /* 010 */ "\\protocol\\StdFileEditing\\verb\\0 = ", /* 011 */ "\\protocol\\StdFileEditing\\verb\\1 = ", /* 012 */ "\\protocol\\StdFileEditing\\verb\\2 = ", /* 013 */ "\\protocol\\StdFileEditing\\verb\\3 = ", /* 014 */ "\\protocol\\StdFileEditing\\verb\\4 = ", /* 015 */ "\\protocol\\StdFileEditing\\verb\\5 = ", /* 016 */ "\\protocol\\StdFileEditing\\verb\\6 = ", /* 017 */ "\\protocol\\StdFileEditing\\verb\\7 = ", /* 020 */ "CLSID\\\\Verb\\0 = ,", /* 021 */ "CLSID\\\\Verb\\1 = ,", /* 022 */ "CLSID\\\\Verb\\2 = ,", /* 023 */ "CLSID\\\\Verb\\3 = ,", /* 024 */ "CLSID\\\\Verb\\4 = ,", /* 025 */ "CLSID\\\\Verb\\5 = ,", /* 026 */ "CLSID\\\\Verb\\6 = ,", /* 027 */ "CLSID\\\\Verb\\7 = ,", /* 030 */ "CLSID\\\\DataFormats\\GetSet\\0 = ", /* 031 */ "CLSID\\\\DataFormats\\GetSet\\1 = ", /* 032 */ "CLSID\\\\DataFormats\\GetSet\\2 = ", /* 033 */ "CLSID\\\\DataFormats\\GetSet\\3 = ", /* 034 */ "CLSID\\\\DataFormats\\GetSet\\4 = ", /* 035 */ "CLSID\\\\DataFormats\\GetSet\\5 = ", /* 036 */ "CLSID\\\\DataFormats\\GetSet\\6 = ", /* 037 */ "CLSID\\\\DataFormats\\GetSet\\7 = ", /* 040 */ "CLSID\\\\MiscStatus = ", /* 041 */ "CLSID\\\\MiscStatus\\1 = ", /* 042 */ "CLSID\\\\MiscStatus\\2 = ", /* 043 */ "CLSID\\\\MiscStatus\\4 = ", /* 044 */ "CLSID\\\\MiscStatus\\8 = ", /* 045 */ "CLSID\\\\AuxUserType\\2 = ", /* 046 */ "CLSID\\\\AuxUserType\\3 = ", /* 047 */ "CLSID\\\\Insertable", /* 050 */ " = ", /* 051 */ "\\CLSID = ", /* 052 */ "\\CurVer = ", /* 053 */ "CLSID\\\\VersionIndependentProgID = ", /* 054 */ "CLSID\\\\Version = ", /* 055 */ "CLSID\\\\DataFormats\\DefaultFile = ", /* 056 */ "CLSID\\\\Ole1Class = ", // is this really needed? /* 057 */ ". = ", /* 060 */ "\\Shell\\Print\\Command = %1", /* 061 */ "\\Shell\\Open\\Command = %1", /* 062 */ "\\protocol\\StdFileEditing\\server = ", /* 063 */ "\\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(!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"); }