// //---------------------------------------------------------------------------- // ObjectComponents // (C) Copyright 1994 by Borland International, All Rights Reserved // // Application class factory and type library implementation //---------------------------------------------------------------------------- #include #include #include #include DIAG_DECLARE_GROUP(OcDll); static TLangId ParseLangId(const char* text); //____________________________________________________________________________ // // TAppDescriptor implementation //____________________________________________________________________________ TAppDescriptor* TAppDescriptor::This = 0;// for components, other DLL use table TAppDescriptor::TAppDescriptor(TRegList& regInfo, TComponentFactory callback, string& cmdLine, HINSTANCE hInst, const TRegLink* linkHead) : AppClassId(regInfo), RegInfo(regInfo), AppInstance (hInst), LinkHead(linkHead), FactoryCallback(callback) { Init(0); ProcessCmdLine(cmdLine); } // // // #if defined(BI_PLAT_WIN32) void TAppDescriptor::Init(IMalloc*) #else void TAppDescriptor::Init(IMalloc* alloc) #endif { AppProgId= &RegInfo.LookupRef("progid"); AppName = &RegInfo.LookupRef("appname"); AppDoc = &RegInfo.LookupRef("description"); HelpFile = &RegInfo.LookupRef("typehelp"); This = this; // direct access for destructors from component main module RegClassHdl = RegObjectHdl = ClassCount = LinkGuidCount = 0; DebugGuidOffset = LinkGuidOffset = LibGuidOffset = 0; ServedList = ActiveObject = 0; RefCnt = 0; LockCount = 0; TypeLib = 0; Options = 0; ClassList = 0; // Determine if we are running an EXE or a DLL, initialize OLE if an EXE // #if defined(BI_PTR_0_32) if (::GetModuleHandle(0) == AppInstance) { // check instance handle for EXE Options = amExeModule | amExeMode; OLECALL(OleInitialize(0), "OleInitialize"); } #else if ((unsigned)AppInstance + 1 == _SS) { // check instance handle for EXE Options = amExeModule | amExeMode; OLECALL(OleInitialize(alloc), "OleInitialize"); } #endif // Set the initial usage mode based on the reglist entry and module type // if (Options & amExeModule) { // inproc servers are multiple-use const char* usage = RegInfo.Lookup("usage"); char su[] = ocrSingleUse; if (usage && *usage == *su) Options |= amSingleUse; } // Set the app language to the one in the reglist if provided // const char* regLang = RegInfo.Lookup("language"); AppLang = regLang ? ParseLangId(regLang) : TLocaleString::UserDefaultLangId; // Lookup the version, providing a default of 1.0 // Version = RegInfo.Lookup("version", AppLang); if (!Version) Version = "1.0"; // Assign GUID for debug registration // if ((Options & amExeModule) && RegInfo.Lookup("debugprogid")) // check if alternate debug registration DebugGuidOffset = AppClassId.AllocId(); // allocate GUID for debugging // Generate array of all automated classes and assign GUIDs // MergeAutoClasses(); } TAppDescriptor::~TAppDescriptor() { UnregisterClass(); delete TypeLib; delete[] ClassList; This = 0; if (RefCnt > 0) ::CoDisconnectObject(this,0); // should not normally happen if (Options & amExeModule) ::OleUninitialize(); // must happen after everything is unregistered } TUnknown* TAppDescriptor::CreateAutoApp(TObjectDescriptor objDesc, uint32 options, IUnknown* outer) { if (objDesc.Destruct == TObjectDescriptor::Delete) { if (!(options & (amServedApp | amAutomation))) objDesc.Destruct = TObjectDescriptor::Quiet; else if (options & amExeMode) objDesc.Destruct = TObjectDescriptor::PostQuit; } TServedObjectCreator* creator = new TServedObjectCreator(*this); TUnknown* obj = creator->CreateObject(objDesc, outer); // RefCnt=0 RegisterObject(objDesc); // OLE will do an AddRef() return obj; // RefCnt++ when Aggregated or converted to IUnknown* } void TAppDescriptor::ReleaseAutoApp(TObjectDescriptor app) { TServedObject* obj; if (ActiveObject && (obj = FindServed(app)) != 0 && obj == ActiveObject) UnregisterObject(); } TUnknown* TAppDescriptor::CreateAutoObject(TObjectDescriptor objDesc, TServedObject& app, IUnknown* outer) { return app.Creator.CreateObject(objDesc, outer); // RefCnt initially 0 } TUnknown* TAppDescriptor::CreateAutoObject(const void* obj, const typeinfo& objInfo, const void* app, const typeinfo& appInfo, IUnknown* outer) { TServedObject* autoApp = FindServed(MostDerived(app, appInfo)); TServedObjectCreator& creator = autoApp ? autoApp->Creator : *new TServedObjectCreator(*this); TAutoClass::TAutoClassRef* ref = ClassList; for (int i = 0; i < ClassCount; i++, ref++) if (objInfo == ref->Class->TypeInfo) return creator.CreateObject(TObjectDescriptor(obj, *ref->Class), outer); return 0; } HRESULT _IFUNC TAppDescriptor::QueryInterface(const IID far& riid, void far* far* retIface) { if (riid == IID_IUnknown || riid == IID_IClassFactory) { AddRef(); *retIface = (IUnknown*)this; return HR_NOERROR; } *retIface = 0; return HR_NOINTERFACE; } unsigned long _IFUNC TAppDescriptor::AddRef() { return ++RefCnt; } unsigned long _IFUNC TAppDescriptor::Release() { return --RefCnt; // note: this object will not delete itself } HRESULT _IFUNC TAppDescriptor::CreateInstance(IUnknown* outer, const IID far& riid, void far* far* ppv) { *ppv = 0; IUnknown* obj; if (!FactoryCallback) return HR_FAIL; try { // Test for special condition to force run DLL as an EXE // if (outer && outer->QueryInterface(IID_NULL, ppv) == HR_NOERROR) obj = FactoryCallback(0, Options | amServedApp | amExeMode | amRun); else obj = FactoryCallback(outer, Options | amServedApp); if (!obj) return HR_FAIL; if (Options & amSingleUse) UnregisterClass(); if (riid == IID_IUnknown) { // can't return outer if aggregated *ppv = obj; return HR_OK; } HRESULT stat = obj->QueryInterface(riid, ppv); obj->Release(); return stat; } catch (...) { // we can't throw any exception through OLE return HR_OUTOFMEMORY; // probably a resource problem, better error code? } } HRESULT _IFUNC TAppDescriptor::LockServer(BOOL fLock) { if (fLock) LockCount++; else LockCount--; // should notify app when count reaches 0? TRACEX(OcDll, 1, "LockServer(" << fLock << ") Count:" << LockCount); return HR_NOERROR; } uint16 TAppDescriptor::GetVersionField(unsigned field) { const char* cp = Version; char c; while (field--) { while ((c = *cp++) != '.') { if (c < '0' || c > '9') return 0; // terminate on null or invalid digit } } uint16 ver = 0; while ((c = *cp++) >= '0' && c <= '9') ver = uint16(ver * 10 + c - '0'); return ver; } void TAppDescriptor::RegisterClass() { int guidOffset = IsOptionSet(amDebug) ? DebugGuidOffset : 0; if (!RegClassHdl) { OLECALL(::CoRegisterClassObject(AppClassId[guidOffset], this, CLSCTX_LOCAL_SERVER, (Options & amSingleUse) ? REGCLS_SINGLEUSE : REGCLS_MULTIPLEUSE, &RegClassHdl), "Register App class"); } } void TAppDescriptor::UnregisterClass() { if (RegClassHdl) { OLECALL(::CoRevokeClassObject(RegClassHdl), "Unregister App class"); RegClassHdl = 0; } } bool TAppDescriptor::RegisterObject(TObjectDescriptor app) { if (ActiveObject) return false; TServedObject* obj = FindServed(app); if (!obj) return false; int guidOffset = IsOptionSet(amDebug) ? DebugGuidOffset : 0; // Refcnt may be zero, protect against AddRef/Release // obj->AdjustRefCount(+1); // Register object with Ole. OLE 2.02 suggests ACTIVEOBJECT_WEAK, but that // does not appear to provide adequate locking. // OLECALL(::RegisterActiveObject(&(IUnknown&)*obj, AppClassId[guidOffset], ACTIVEOBJECT_STRONG, &RegObjectHdl), "Register App as active"); obj->AdjustRefCount(-1); ActiveObject = obj; return true; } void TAppDescriptor::UnregisterObject() { if (RegObjectHdl) { ActiveObject = 0; // must zero before OLE call in case obj destructor run OLECALL(::RevokeActiveObject(RegObjectHdl, 0), "Unregister App"); RegObjectHdl = 0; } } int TAppDescriptor::GetClassIndex(TAutoClass* cls) { TAutoClass::TAutoClassRef* ref = ClassList; for (int index = 0; index < ClassCount; ref++, index++) { if (ref->Class == cls) return index; } return -1; } bool TAppDescriptor::GetClassId(TAutoClass* cls, GUID& retId) { int offset; if (cls) { int index = GetClassIndex(cls); if (index == -1) return false; offset = ClassList[index].GuidOffset; } else { // null class arg means type library itself offset = LibGuidOffset; } retId = AppClassId[offset]; return true; } TClassId TAppDescriptor::GetLinkGuid(int index) { while (index >= LinkGuidCount) { int id = AppClassId.AllocId(); if (!LinkGuidOffset) LinkGuidOffset = id; LinkGuidCount++; } return AppClassId[LinkGuidOffset + index]; } TAutoClass* TAppDescriptor::GetAutoClass(unsigned index) { if (index >= ClassCount) return 0; return ClassList[index].Class; } TAutoClass* TAppDescriptor::GetAutoClass(const GUID far& clsid) { int offset = AppClassId.GetOffset(clsid); if (offset != -1) { TAutoClass::TAutoClassRef* ref = ClassList; for (int count = ClassCount; count--; ref++) { if (ref->GuidOffset == offset) return ref->Class; } } return 0; } // // Get the document template with the given GUID // TRegLink* TAppDescriptor::GetRegLink(const GUID far& clsid) { int linkGuidIndex = 0; for (const TRegLink* link = LinkHead; link; link=link->GetNext()) { TRegList& regList = link->GetRegList(); if (regList["progid"]) { const char* id = regList["clsid"]; if (!id) { if (clsid == (GUID&)GetLinkGuid(linkGuidIndex++)) return const_cast(link); } else { TClassId classId(id); if (clsid == (GUID&) classId) return const_cast(link); } } } return 0; } ITypeLib* TAppDescriptor::GetTypeLibrary() { if (!ClassCount) return 0; if (!TypeLib) { TypeLib = new TTypeLibrary(*this, AppLang); } ((IUnknown&)*TypeLib).AddRef(); return TypeLib; } // TServedObject list management TServedObject* TAppDescriptor::FindServed(const void far* mostDerivedObj) { for (TServedObject* p = ServedList; p; p = p->Next) if (p->RootObject == mostDerivedObj) break; return p; } TServedObject* TAppDescriptor::FindServed(TObjectDescriptor& objDesc) { if (objDesc.Object) return FindServed(objDesc.MostDerived()); for (TServedObject* p = ServedList; p; p = p->Next) if (p->Object == 0 && p->Class == objDesc.Class) return p; return 0; } void TAppDescriptor::AddServed(TServedObject& obj) { if (ServedList) ServedList->Prev = &obj.Next; obj.Next = ServedList; obj.Prev = &ServedList; ServedList = &obj; } void TAppDescriptor::RemoveServed(TServedObject& obj) { *obj.Prev = obj.Next; if (obj.Next) obj.Next->Prev = obj.Prev; } void TAppDescriptor::FlushServed() { while (ServedList) delete ServedList; } void TAppDescriptor::InvalidateObject(const void far* obj) { TServedObject* p = FindServed(obj); if (p) { p->RootObject = 0; p->Object = 0; } } void TAppDescriptor::ReleaseObject(const void far* obj) { TServedObject* p = FindServed(obj); if (p) { p->RootObject = 0; p->Object = 0; ((IUnknown&)*p).Release(); // destructs if no external connections } } ITypeInfo* TAppDescriptor::CreateITypeInfo(TAutoClass& cls) { TServedObjectCreator& creator = ServedList ? ServedList->Creator : *new TServedObjectCreator(*this); TUnknown* obj = creator.CreateObject(TObjectDescriptor(0, cls)); IUnknown& unk = (IUnknown&)*obj; ITypeInfo* typeInfo = 0; unk.QueryInterface(IID_ITypeInfo, (void far* far*) &typeInfo); return typeInfo; } //____________________________________________________________________________ // // Parse command line for Ole flags. // Check for [-/] and remove options that are found. Gets option arguments // and performs any immediate option actions //____________________________________________________________________________ void TAppDescriptor::RegisterServer(TLangId lang, const char* regFilename) { try { TRegItem regDebug[DebugRegCount]; // Check if registration output to Registry or to user-specified reg file // char guidStr[40]; strstream regStrm; ostream* strm = ®Strm; bool alwaysReg = IsOptionSet(amRegServer); ofstream fileStrm; if (regFilename) { fileStrm.open(regFilename); if (!fileStrm.good()) throw TXRegistry(regFilename, "file"); strm = &fileStrm; fileStrm << "REGEDIT\n"; // write registration file header alwaysReg = true; } SetOption(amUnregServer, false);// cancel unregister on reregister // Make sure that the app reginfo is in the registry // bool forceReg = alwaysReg; if (AppProgId) { // don't attempt register app if no progid reg key if (!forceReg) { strstream vstrm; ::OcRegisterClass(RegInfo, AppInstance, vstrm, lang, "\001\002\006"); if (::OcRegistryValidate(vstrm) != 0) forceReg = true; } if (forceReg) { char* debugGuid = 0; if (DebugGuidOffset) { // setup reg item for debug reg TClassId debugClsid(AppClassId[DebugGuidOffset]); lstrcpy(guidStr, debugClsid); debugGuid = guidStr; if (::OcSetupDebugReg(RegInfo, regDebug, lang, debugGuid)) { ::OcRegisterClass(RegInfo, AppInstance, *strm, lang, 0, 0, OcRegNoDebug); ::OcRegisterClass(RegInfo, AppInstance, *strm, lang, AppDebugFilter, 0, regDebug); } else { debugGuid = 0; // if debug registration fails, use normal } } if (!debugGuid) ::OcRegisterClass(RegInfo, AppInstance, *strm, lang, 0, 0, (Options & amExeModule) ? 0 : OcRegNotDll); } } // Write templates to registration file as needed // int linkGuidIndex = 0; TRegItem regAppName[2] = {{"appname",{RegInfo["appname"]}}, {0,{0}}}; for (const TRegLink* link = LinkHead; link; link=link->GetNext()) { TRegList& regList = link->GetRegList(); if (regList["progid"]) { char guidStr[40]; int debugStat = (Options & amExeModule) ? ::OcSetupDebugReg(regList, regDebug, lang, guidStr) : 0; TRegItem regClsid[3] = {{"debugger",{""}}, {"clsid",{guidStr}}, {0,{0}}}; TRegItem* clsInfo = regClsid; if (!debugStat && Options & amExeModule) clsInfo++; // no cancel debugger if no debugprogid if (!regList["clsid"]) { // check if GUID needs to be assigned TClassId linkClsid(GetLinkGuid(linkGuidIndex++)); lstrcpy(guidStr, linkClsid); } else { regClsid[1].Key = 0; // shorten list to exclude auto-assigned clsid } if (!alwaysReg) { strstream checkStrm; ::OcRegisterClass(regList, AppInstance, checkStrm, lang, "\001\002\006", 0, clsInfo); if (::OcRegistryValidate(checkStrm) == 0) continue; } if (debugStat) { ::OcRegisterClass(regList, AppInstance, *strm, lang, 0, regAppName, regClsid); if (debugStat == 1) { // needs generated guid string TClassId linkClsid(GetLinkGuid(linkGuidIndex++)); lstrcpy(guidStr, linkClsid); } ::OcRegisterClass(regList, AppInstance, *strm, lang, DocDebugFilter, regAppName, regDebug); } else { ::OcRegisterClass(regList, AppInstance, *strm, lang, 0, regAppName, clsInfo); } forceReg = true; } } if (forceReg && !regFilename) ::OcRegistryUpdate(regStrm); if (forceReg) ::RegDeleteKey(HKEY_CLASSES_ROOT, "OcErr_RegServer"); } catch (TXBase& xcpt) { ::RegSetValue(HKEY_CLASSES_ROOT, "OcErr_RegServer", REG_SZ, AppClassId, 0); if (!(Options & amQuietReg)) throw; } return; } void TAppDescriptor::UnregisterServer(TLangId, const char*) { // remove application and all type library info from registry // HKEY Key; char guidStr[40]; TRegItem debugItem = {"debugclsid", {guidStr}}; TRegItem* debugInfo = 0; // init to no debug registration if (AppProgId) { // don't attempt unregister app if no progid reg key if (DebugGuidOffset) { // setup reg item for debug reg TClassId debugClsid(AppClassId[DebugGuidOffset]); lstrcpy(guidStr, debugClsid); debugInfo = &debugItem; } ::OcUnregisterClass(RegInfo, debugInfo); // unregister app if (LibGuidOffset) { ::RegOpenKey(HKEY_CLASSES_ROOT, "TypeLib", &Key); ::RegDeleteKey(Key, AppClassId[LibGuidOffset]); ::RegCloseKey(Key); } if (ClassCount) { ::RegOpenKey(HKEY_CLASSES_ROOT, "Interface", &Key); for (int i= 0; i < ClassCount; i++) ::RegDeleteKey(Key, AppClassId[ClassList[i].GuidOffset]); ::RegCloseKey(Key); } } // remove templates from registration file as needed // int linkGuidIndex = 0; for (const TRegLink* link = LinkHead; link; link=link->GetNext()) { TRegList& regList = link->GetRegList(); if (regList["progid"]) { if (!regList["clsid"]) { // check if GUID needs to be computed TClassId linkClsid(GetLinkGuid(linkGuidIndex++)); lstrcpy(guidStr, linkClsid); TRegItem clsItem = {"clsid", {guidStr}}; ::OcUnregisterClass(regList, &clsItem); } else ::OcUnregisterClass(regList); } } } void TAppDescriptor::SetLangId(TLangId /*prevLang*/, const char* langIdStr) { AppLang = ParseLangId(langIdStr); } void TAppDescriptor::MakeTypeLib(TLangId lang, const char* typeLibName) { if (!ClassCount) return; // write the typelib file to // char fullPath[_MAX_PATH]; char exeDrive[_MAX_DRIVE]; char exeDir [_MAX_DIR]; char exeFName[_MAX_FNAME]; char exeExt [_MAX_EXT]; char libDrive[_MAX_DRIVE]; char libDir [_MAX_DIR]; char libFName[_MAX_FNAME]; char libExt [_MAX_EXT]; ::GetModuleFileName(AppInstance, fullPath, sizeof(fullPath)); _splitpath(typeLibName? typeLibName:"", libDrive, libDir, libFName, libExt); _splitpath(fullPath, exeDrive, exeDir, exeFName, exeExt); _makepath (fullPath, libDrive[0] ? libDrive : exeDrive, libDir[0] ? libDir : exeDir, libFName[0] ? libFName : exeFName, libExt[0] ? libExt : "OLB"); try { WriteTypeLibrary(lang, fullPath); ::RegDeleteKey(HKEY_CLASSES_ROOT, "OcErr_Typelib"); } catch (TXOle&) { ::RegSetValue(HKEY_CLASSES_ROOT, "OcErr_Typelib", REG_SZ, AppClassId, 0); if (!(Options & amQuietReg)) throw; } } // // // void TAppDescriptor::ProcessCmdLine(string& cmdLine) { struct { uint32 Flag; char* String; void (TAppDescriptor::*Action)(TLangId, const char*); } optionTbl[] = { { amRegServer, "RegServer", &TAppDescriptor::RegisterServer }, { amUnregServer, "UnregServer", &TAppDescriptor::UnregisterServer }, { amAutomation, "Automation", 0 }, { amEmbedding, "Embedding", 0 }, { amLangId, "Language", &TAppDescriptor::SetLangId }, { amTypeLib, "TypeLib", &TAppDescriptor::MakeTypeLib }, { amNoRegValidate,"NoRegValidate", 0 }, { amQuietReg , "QuietReg", 0 }, { amDebug, "Debug", 0 }, }; const int optionTblCount = sizeof(optionTbl)/sizeof(optionTbl[0]); TCmdLine cmd(cmdLine.c_str()); while (cmd.Kind != TCmdLine::Done) { switch (cmd.Kind) { default: cmd.NextToken(); // ignore token, not one of ours break; case TCmdLine::Option: for (int i = 0; i < optionTblCount; i++) { if (strnicmp(cmd.Token, optionTbl[i].String, cmd.TokenLen) == 0) { Options |= optionTbl[i].Flag; cmd.NextToken(true); // eat token & get next if (cmd.Kind == TCmdLine::Value) { string optionArg(cmd.Token, 0, cmd.TokenLen); while (cmd.NextToken(true) == TCmdLine::Value) ; // eat token & get next (keep going if more Values are there) if (optionTbl[i].Action) (this->*optionTbl[i].Action)(AppLang, optionArg.c_str()); } else { if (optionTbl[i].Action) (this->*optionTbl[i].Action)(AppLang, 0); } break; // out of for loop } } if (i >= optionTblCount) cmd.NextToken(); // ignore token, wasn't one of ours break; } } cmdLine = cmd.GetLine(); // Set single use if this is an automated exe server // if ((Options & (amAutomation | amExeModule)) == (amAutomation | amExeModule)) Options |= amSingleUse; // Perform normal registry update if no other registry option was specified // if (!(Options & (amRegServer | amUnregServer | amNoRegValidate))) RegisterServer(0); } static char HexChar[] = "0123456789ABCDEF0123456789abcdef"; static TLangId ParseLangId(const char* text) { TLangId lang = 0; unsigned char c; char* pc; while (text && (c = *text++) != 0) { if ((pc = strchr(HexChar, c)) == 0) return 0; // illegal character lang = TLangId((lang << 4) + (short(pc-HexChar) & 15)); } return lang ? lang : TLocaleString::UserDefaultLangId; } void TAppDescriptor::MergeAutoClasses() { int oldCount = ClassCount; ClassCount = TAutoClass::ClassList.CountAutoClasses(); if (ClassCount) { if (!LibGuidOffset) LibGuidOffset = AppClassId.AllocId(); // allocate GUID for type library TAutoClass::TAutoClassRef* oldList = ClassList; ClassList = new TAutoClass::TAutoClassRef[ClassCount]; TAutoClass::ClassList.MergeAutoClasses(ClassList); TAutoClass::TAutoClassRef* ref = ClassList; for (int count = ClassCount; count--; ref++) { if (oldCount) { ref->GuidOffset = oldList->GuidOffset; oldCount--; oldList++; } else { ref->GuidOffset = AppClassId.AllocId(); } } } } int TAutoClass::TClassList::CountAutoClasses() { int count = Count; for (TExtLink* link = Link; link; link = link->Next) count += link->Classes->CountAutoClasses(); return count; } TAutoClass::TAutoClassRef* TAutoClass::TClassList::MergeAutoClasses(TAutoClass::TAutoClassRef* array) { for (TAutoClass* cls = List; cls; cls = cls->NextClass, array++) array->Class = cls; for (TExtLink* link = Link; link; link = link->Next) array = link->Classes->MergeAutoClasses(array); return array; }