Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
8.6 KiB

  1. COM 'Instance's (and explorer band categories)
  2. 980305
  3. andyp
  4. WARNING WARNING WARNING -- v. early draft of doc in progress, likely to
  5. have many errors/omissions.
  6. - abstract
  7. ...
  8. - contents
  9. ...
  10. - overview
  11. COM has a (core!) notion of a CLSID for the code that implements an object
  12. but no similar notion for a particular instance of that object. thus every
  13. client must write custom code for each instance.
  14. we provide a generalization which makes creation and initialization of such
  15. instances easy and consistent.
  16. - motivation
  17. an e.g. of usage might be a browser band implementation. the code for
  18. such a band is identical no matter what URL one happens to initialize
  19. it to. however it is quite useful to be able to install many different
  20. browser bands, each pointing to a different URL, by writing only
  21. registry 'goo' (vs. writing custom code, however simple it may be,
  22. to do the same thing).
  23. - instance = code + data
  24. an 'INSTID' is a GUID that identifies two things:
  25. - the CLSID for the code for an object
  26. - data for an IPersistXxx iface to initialize an instance of the object
  27. - creation
  28. INSTID's are fully hidden from the client. that is, there is no special
  29. API to create an instance. one simply does a CoCreateInstance, and if the
  30. CLSID happens to actually be an INSTID, we quietly do everything that is
  31. necessary to create and initialize the instance.
  32. - code
  33. the code for an 'instance' is exactly a CLSID.
  34. - initialization
  35. initialization is done from an IPersistXxx iface. we try to load 1st from
  36. an IPropertyBag, next from an IPersistStream, and finally from ???
  37. all of these ifaces are created using our various registry-based
  38. implementations. e.g. CRegStrPropertyBag (IPropertyBag on top of
  39. a registry key), OpenRegStream (IPersistStream on top of a registry
  40. key), etc.
  41. NYI: only IPersistPropertyBag is implemented. this makes (some) sense,
  42. since we're trying to provide a registry-goo-only method for 'coding',
  43. and a property bag is the main string-based COM IPersistXxx mechanism.
  44. - installation
  45. for convenience, we also provide code to create the registry goo (why?).
  46. CRegStrPropBag *InstallInstAndBag(LPTSTR pszInst, LPTSTR pszName, LPTSTR pszClass)
  47. ...
  48. - registry goo
  49. the key looks as follows:
  50. subkey value(s)
  51. ------ --------
  52. HKCR/CLSID/
  53. {instid}/ @=...description...
  54. InProcServer
  55. @=...path...
  56. ThreadingModel=...etc....
  57. Instance
  58. CLSID={clsid}
  59. InitPropertyBag
  60. name1=value1
  61. ...
  62. the InProcServer should point to browseui.dll, which is where we've
  63. implemented the generic support code for InstIDs. (if the idea proves
  64. useful enough, perhaps COM will pick it up as a standard part of the
  65. CoCreateInstance API).
  66. - implementation
  67. here's how it works...
  68. the INSTID points to our DLL which implements inst.cpp. DllGetClassObject
  69. goes thru its usual loop. if it fails, it tries to create a
  70. CInstClassFactory for the given CLSID (INSTID). creation looks for and
  71. caches the magic 'Instance' subkey (and fails if it's not found). then
  72. when the ::CI method is called it gets the appropriate keys/values, does
  73. the CCI, creates the IPropertyBag (etc.), and does the ::Load.
  74. - perf
  75. from the implementation details, it should be clear that a 'normal'
  76. CCI goes thru exactly the same code path as before. we intentionally
  77. keep this code path exactly the same cost.
  78. the only time our INSTID code is hit at all is if the vanilla CCI
  79. fails (due to it not being in our sccls.c table). when that happens,
  80. we look for the 'Instance' subkey. if that fails, we fail the entire
  81. CCI (w/ the only added cost for that failure case being the 1 extra
  82. RegOpenKey call). we intentionally keep this code path as close to
  83. 0 extra cost as possible.
  84. if that succeeds, we open/read several other keys, do the 'real' CCI,
  85. and do the initialization. again, the only extra cost (vs. the equivalent
  86. custom code) is the extra registry operations, which we try to keep
  87. cheap.
  88. to keep the extra registry operations cheap we do 'relative' opens
  89. as we work our way down the registry.
  90. BUGBUG what about cost of CInstClassFactory?
  91. - example
  92. to continue w/ our browser band e.g., here's the registry 'goo' for
  93. such a band:
  94. HKCR/CLSID/
  95. {77777777-7777-7777-7777-777777777777}
  96. InProcServer
  97. @="%systemdir%/browseui.dll"
  98. ThreadingModel="Apartment"
  99. Instance
  100. CLSID=Clsid_BrowserBand
  101. InitPropertyBag
  102. Url="http://www.nytimes.com"
  103. ... other properties ...
  104. a CCI(7777, ...) will do:
  105. - create an uninitialized instance of the object by doing
  106. CCI(Clsid_BrowserBand, ...);
  107. - create an IPropertyBag for the 'InitPropertyBag' registry data (using
  108. our CRegStrPropertyBag implementation)
  109. - call punk->IPB::Load to load the IPropertyBag into the punk
  110. - we now have an initialized instance of the object (we're done!)
  111. - gotcha's and subtleties
  112. - gotcha: GetCLSID et. al.
  113. while each INSTID is unique and will create a separate instance initialized
  114. w/ the appropriate data, once the object has been created there is no
  115. (standard) way to distinguish it from any other instance of the same class
  116. (code).
  117. e.g. two browsers, one pointing at www.nytimes.com and the other at
  118. www.wsj.com, actually just look like two generic browsers.
  119. thus (e.g.)
  120. - IPS::GetCLSID gives the same CLSID (not different INSTID!) for both
  121. of them,
  122. - OleSaveToStream saves out the same CLSID and the (different) URL's
  123. - a subsequent OleLoadFromStream will use the same CLSID and the
  124. (different URL's)
  125. while at 1st this might seem odd, it actually makes a lot of sense. INSTID's
  126. are just a convenient standard 'packaging' of existing COM mechanisms.
  127. if one does a 'classic' CCI of two CLSID_BrowserBand's, initializes them
  128. to point to two different URLs, saves each w/ OleSaveToStream (presumably
  129. to two different streams), and later reloads each w/ OleLoadFromStream
  130. (again from the different streams), one will get *exactly* the same
  131. behavior as w/ the above INSTID e.g.
  132. in fact doing anything else (e.g. saving the object out w/ its INSTID)
  133. would be wrong (or at least inefficient). consider: the user may have
  134. changed various properties (e.g. navigated to a new URL). when we save
  135. it, the object saves the properties that exactly represent its current
  136. state. but if we create it by INSTID, we'd init it to a *different* set
  137. of properties (which we'd then blast w/ the IPB::Load call).
  138. moreover doing so would be impossible, since neither the object nor the
  139. system even know what the INSTID is once the CCI is complete.
  140. that said, one does need to be a bit careful.
  141. - gotcha: find
  142. the fact that IPS::GetCLSID returns the code CLSID rather than the
  143. object INSTID also complicates uniquely identifying the object in
  144. one's code. e.g. in our explorer bar implementation, each menu
  145. item has a unique CLSID or INSTID. the 1st time one clicks on a menu
  146. item, we just call CCI, add the band to the bar, show the band, and
  147. hide all other bands. so far so good.
  148. but now consider what happens steady-state when one re-clicks on a
  149. previously created menu item. while the menu item still knows they
  150. have different INSTID's, we have no way to ask the each object (when
  151. enumerating our list of bands) if it came from the INSTID.
  152. to solve this we use IE's Get/PutProperty mechanism to store a
  153. <name,value> pair, in this case <instIdBand,punkBand>.
  154. - gotcha: client dependencies
  155. since we're providing inst.cpp (vs. ole32.dll), the InProcServer for each
  156. INSTID must point to our implementation (ie4:shdocvw, ie5:browseui).
  157. however while the code 'belongs' to us, the INSTIDs in question 'belong'
  158. to the client app. that is, 3rd-party apps will point *their* INSTIDs at
  159. *our* DLL. this means that their selfreg.inx will contain knowledge of
  160. where we implement our support routines.
  161. this in turn means that we can't move our implementation (or rather when
  162. we do, we need to somehow forward it so that old code continues to work).
  163. use a treat-as or somesuch so that old code continues to work).
  164. BUGBUG we need to do this for ie5 since we've moved stuff!
  165. - links
  166. docs/inst.txt this document
  167. browseui/inst.cpp CCI support code
  168. browseui/stream.cpp CRegStrPropertyBag
  169. - appendix: 777a.reg
  170. here's the exact registry 'goo'
  171. #include 777a.reg