⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch05.htm

📁 本书详述了VC++环境下网络化多媒体对象技术编程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<P><FONT COLOR="#0066FF"><TT><BR>
OBJECTINFO g_ObjectInfo[] = { <BR>
AUTOMATIONOBJECT(Tracker), <BR>
EMPTYOBJECT </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
}; </TT></FONT></P>
<P>To prevent an application from being exposed as an automation server, you only
need to remove or comment out the entry in the object map. On the opposite side,
when adding additional creatable servers to an application, it is important to add
an entry for each new server to the <TT>ObjectInfo</TT> structure.</P>
<P>All BaseCtl servers contain a static function <TT>Create</TT> that is used to
create instances of themselves. The <TT>Create</TT> function is the only way that
you should instantiate a server since it is implemented by the class factory of the
server and will manage the server creation correctly. This is critical in the cases
where the server is shared among two or more applications, as you will see in the
next sections.</P>
<P>Listing 5.13 shows an example of creating a server using C++ syntax. <TT>Create</TT>
returns an <TT>IUnknown *</TT>, which is used to retrieve the proper custom interface
that can then be cast to the C++ class of the server. Remember to check all of the
return values and pointers to ensure that all of the method calls returned successfully.
Don't forget to increment and decrement the reference counts of the server to ensure
that its lifetime is what you expect. You must also navigate to the correct interface
using the function <TT>QueryInterface</TT>, as is the case in Listing 5.13, which
is looking for the custom interface pointer of the <TT>IID_ITracker</TT> object.
A simple cast from the <TT>Create</TT> function <TT>IUnknown *</TT> to the C++ class
is not enough to get the correct function offsets.
<H3><A NAME="Heading23"></A>Listing 5.13 Example--Creating an OLE Server Using C++</H3>
<P><FONT COLOR="#0066FF"><TT>// create the server <BR>
IUnknown * pUnk = CTracker::Create(NULL); <BR>
<BR>
// if we have an IUnknown <BR>
if(pUnk) <BR>
{ <BR>
// custom interface reference <BR>
ITracker * pTrack = NULL; <BR>
<BR>
// QI for the custom interface and if successful <BR>
if(pUnk-&gt;QueryInterface(IID_ITracker, (LPVOID *) &amp;pTrack) == S_OK) <BR>
{ <BR>
// too many ref counts - release one <BR>
pTrack-&gt;Release(); <BR>
<BR>
// object reference <BR>
CTracker * opTracker = NULL; <BR>
<BR>
// cast to the object <BR>
opTracker = (CTracker *) pTrack; <BR>
<BR>
// use the object <BR>
opTracker-&gt;put_Indent(1); <BR>
<BR>
// release the object - this is the last one so the <BR>
// object should be destroyed <BR>
opTracker-&gt;Release(); <BR>
} <BR>
else <BR>
// release the IUnknown - we couldn't find the custom interface <BR>
pUnk-&gt;Release(); </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
} </TT></FONT></P>
<P>After a server has been created this way, it can be used like any other C++ class
or OLE server. Since the server is a running OLE Object, all of the standard OLE
functions work, and the server can be used from other applications; for example,
<TT>QueryInterface</TT> can be used to retrieve <TT>IDispatch</TT> or custom interface
pointers that can be passed to other applications. See <A HREF="ch03.htm">Chapter
3</A> for more information regarding the creation and use of OLE servers with C++.</P>
<P>So far, you've looked only at how to create individual instances of objects. Next
you will find out how to share objects.
<H2><A NAME="Heading24"></A>Shared Servers</H2>
<P>OLE defines a facility, called the Running Object Table, for sharing objects.
An object that is shareable will publish its <TT>CLSID</TT> and an <TT>IUnknown</TT>
reference to itself in the Running Object Table. Any application that so desires
can ask for the running instance of the object rather than create a new instance.
This capability is useful for applications that may need to work with a single instance
of an application rather than create multiple copies. The <TT>Tracker</TT> object
is a perfect candidate for this kind of functionality. Multiple applications could
use the same <TT>Tracker</TT> object to log information thus saving on memory.</P>
<P>The first step is to register the server as running (see Listing 5.14). The BaseCtl
Framework allows access to the basic <TT>ClassFactory</TT> operation of creating
the server through the static <TT>Create</TT> function. Since this function is where
all instances of the server are created, the <TT>Create</TT> function is the most
logical location to register the server as running.</P>
<P>After the new object is created, you should <TT>QueryInterface</TT> for an <TT>IUnknown</TT>
reference to the server. If successful, you call the <TT>RegisterActiveObject</TT>
passing the <TT>IUnknown</TT> and the <TT>CLSID</TT> of the server. This sample implementation
passes the constant <TT>ACTIVEOBJECT_STRONG</TT>, which will result in the reference
count of the server being incremented by one. See the VC++ documentation for more
information regarding this parameter. You must also pass the address of a member
variable to store the ID of the running object. The ID is used later when revoking
the object from the Running Object Table. Last you must decrement the reference count
of the server to offset the <TT>QueryInterface</TT> call.
<H3><A NAME="Heading25"></A>Listing 5.14 TRACKER.CPP--Create Function Implementation
of Shared Object Support</H3>
<P><FONT COLOR="#0066FF"><TT>IUnknown *CTracker::Create <BR>
( <BR>
IUnknown *pUnkOuter <BR>
) <BR>
{ <BR>
// make sure we return the private unknown so that we support aggegation <BR>
// correctly! <BR>
// <BR>
CTracker *pNew = new CTracker(pUnkOuter); <BR>
<BR>
LPUNKNOWN pIUnknown = NULL; <BR>
<BR>
// QI for the IUnknown and if successful <BR>
if(pNew-&gt;QueryInterface(IID_IUnknown, (LPVOID *) &amp;pIUnknown) == S_OK) <BR>
{ <BR>
// register the clsid as an active object <BR>
// so other applications will get the same object <BR>
// and if it didn't succeed - this function will <BR>
// increment the reference count because of the ACTIVEOBJECT_STRONG <BR>
if(::RegisterActiveObject(pIUnknown, CLSID_Tracker, <BR>
ACTIVEOBJECT_STRONG, &amp;pNew-&gt;m_dwRegister) != S_OK) <BR>
// make sure that the reference ID is clear <BR>
pNew-&gt;m_dwRegister = NULL; <BR>
<BR>
// remove the extra refcount from the QI <BR>
pIUnknown-&gt;Release(); <BR>
} <BR>
<BR>
return pNew-&gt;PrivateUnknown(); </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
} </TT></FONT></P>
<P>You've added the code to register the server in the Running Object Table, but
now you need to add code to remove the server from the table. The only practical
way to remove the server from the Running Object Table is to monitor the reference
counts of the server. When the reference count reaches one--indicating that the only
reference left is the Running Object Table's reference--revoke the server's reference
from the Running Object Table. The only location that you can use to accurately monitor
the reference count of the server is in the <TT>IUnknown::Release</TT> function implementation.</P>
<P>To aid in the implementation of the <TT>IUnknown::Release</TT> function, we have
supplied a new macro to handle the revoking the server from the Running Object Table
(see Listing 5.15). The implementation checks to see whether an ID is present for
the server and whether the reference count is one and revokes the server from the
Running Object Table. The implementation also increments the reference count and
clears the ID member variable to prevent the object from being destroyed before this
function has completed and also to prevent the code from becoming recursive. Incrementing
the reference count and clearing the member variable is necessary since the <TT>RevokeActiveObject</TT>
function call will result in another call to <TT>Release</TT> to decrement the reference
of the server. After <TT>RevokeActiveObject</TT> returns, the server removes its
last reference count, which results in the destruction of the server.
<H3><A NAME="Heading26"></A>Listing 5.15<SPACER TYPE="HORIZONTAL" SIZE="10"> RACKER.H--DECLARE_STANDARD_UNKNOWN_SHARED
Macro Implementation</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
#define DECLARE_STANDARD_UNKNOWN_SHARED() \ <BR>
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObjOut) { \ <BR>
return ExternalQueryInterface(riid, ppvObjOut); \ <BR>
} \ <BR>
STDMETHOD_(ULONG, AddRef)(void) { \ <BR>
return ExternalAddRef(); \ <BR>
} \ <BR>
STDMETHOD_(ULONG, Release)(void) \ <BR>
{ \ <BR>
long lRefCount = this-&gt;ExternalRelease(); \ <BR>
if(this-&gt;m_dwRegister &amp;&amp; lRefCount == 1) \ <BR>
{ \ <BR>
this-&gt;ExternalAddRef(); \ <BR>
DWORD tdwRegister = this-&gt;m_dwRegister; \ <BR>
this-&gt;m_dwRegister = 0; \ <BR>
::RevokeActiveObject(tdwRegister, NULL); \ <BR>
return this-&gt;ExternalRelease(); \ <BR>
} \ <BR>
else \ <BR>
return lRefCount; \ <BR>
} \ <BR>
DWORD m_dwRegister; </TT></FONT></P>

<P>The last step is to update the class declaration of your server to use the new
macro (see Listing 5.16). Replace the <TT>DECLARE_STANDARD_UNKNOWN()</TT> with <TT>DECLARE_STANDARD_UNKNOWN_SHARED()</TT>.
<H3><A NAME="Heading27"></A>Listing 5.16 TRACKER.H--New Macro</H3>
<P><FONT COLOR="#0066FF"><TT>class CTracker : public ITracker, public CAutomationObject,
ISupportErrorInfo { <BR>
public: <BR>
// IUnknown methods <BR>
// <BR>
DECLARE_STANDARD_UNKNOWN_SHARED(); <BR>
// IDispatch methods <BR>
// <BR>
DECLARE_STANDARD_DISPATCH(); <BR>
. . . </TT></FONT></P>

<P>During the lifetime of the server, an application is capable of getting the instance
of the running server with a method call to OLE. In VB, you use the <TT>GetObject</TT>
method call, and in VC++, you use the <TT>GetActiveObject</TT> function. After the
pointer to the server is retrieved, the server can be used as though it were created
through normal OLE mechanisms.</P>

<P>This method of sharing objects is fine but requires that the developer of the
application using the server take an active role in deciding to use the shared object
versus creating a new instance of the object. Another approach can be taken, and
that is to supply the instance of a running server to an application that calls <TT>CreateObject</TT>
rather than <TT>GetObject</TT>. A server implemented to always return the same instance
to any application is referred to as a single instance server.
<H2><A NAME="Heading28"></A>Single Instance Servers</H2>
<P>A single instance server is used in the cases where you do not want the users
of the component to have a choice in using a shared instance of the server or a copy
that they create themselves. No matter what, you want to create only a single running
instance of the server that is shared by all applications.</P>
<P>To enable a BaseCtl server for single instance requires that you modify the code
that was added to the <TT>Create</TT> function in the section &quot;Shared Servers.&quot;
Listing 5.17 shows the changes that you made to the sample implementation to enable
single instance support. First, you must look to see whether the server is already
registered as running. If not, a server is created and registered as running.</P>
<P>If the server is already running, you must <TT>QueryInterface</TT> for the custom
interface pointer so that you can cast to the correct C++ class type. The last step
is to call <TT>Release</TT> since there will be an extra reference count from the
<TT>GetActiveObject</TT> and <TT>QueryInterface</TT> calls.
<H3><A NAME="Heading29"></A>Listing 5.17 TRACKER.CPP--Create Function Implementation
Updated to Support Single Instance Servers</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
IUnknown *CTracker::Create <BR>
( <BR>
IUnknown *pUnkOuter <BR>
) <BR>
{ <BR>
CTracker *pNew = NULL; <BR>
<BR>
// Initialize an IUnknown reference <BR>
LPUNKNOWN pIUnknown = NULL; <BR>
<BR>
// see if the object is already running <BR>
::GetActiveObject(CLSID_Tracker, NULL, &amp;pIUnknown); <BR>
<BR>
// if we didn't get a reference to a running object <BR>
if(!pIUnknown) <BR>
{ <BR>
// make sure we return the private unknown so that we support // aggegation correctly!
<BR>
// <BR>
pNew = new CTracker(pUnkOuter); <BR>
<BR>
// QI for the IUnknown and if successful <BR>
if(pNew-&gt;QueryInterface(IID_IUnknown, (LPVOID *) &amp;pIUnknown) == S_OK) <BR>
{ <BR>
// register the clsid as an active object <BR>
// so other applications will get the same object <BR>
// and if it didn't succeed - this function will <BR>
// increment the reference count because of the ACTIVEOBJECT_STRONG <BR>
if(::RegisterActiveObject(pIUnknown, CLSID_Tracker, <BR>
ACTIVEOBJECT_STRONG, &amp;pNew-&gt;m_dwRegister) != S_OK) <BR>
// make sure that the reference ID is clear <BR>
pNew-&gt;m_dwRegister = NULL; <BR>
<BR>
// remove the extra refcount from the QI <BR>
pIUnknown-&gt;Release(); <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
// custom interface reference <BR>
ITracker * pTrack = NULL; <BR>
<BR>
// QI for the custom interface and if successful <BR>
if(pIUnknown-&gt;QueryInterface(IID_ITracker, (LPVOID *) &amp;pTrack) == S_OK) <BR>
{ <BR>
// too many ref counts - release one <BR>
pTrack-&gt;Release(); <BR>
<BR>
// cast to the object <BR>
pNew = (CTracker *) pTrack; <BR>
} <BR>
else <BR>
{ <BR>
// release the IUnknown - we couldn't find the custom interface <BR>
pIUnknown-&gt;Release(); <BR>
<BR>
// go ahead and create an object <BR>
pNew = new CTracker(pUnkOuter); <BR>
<BR>
// QI for the IUnknown and if successful <BR>
if(pNew-&gt;QueryInterface(IID_IUnknown, (LPVOID *) &amp;pIUnknown) == S_OK) <BR>
{ <BR>
// register the clsid as an active <BR>
// object so other applications will get the same object <BR>
// and if it didn't succeed - this function will <BR>
// increment the reference count because of the ACTIVEOBJECT_STRONG <BR>
if(::RegisterActiveObject(pIUnknown, CLSID_Tracker, <BR>
ACTIVEOBJECT_STRONG, &amp;pNew-&gt;m_dwRegister) != S_OK) <BR>
// make sure that the reference ID is clear <BR>
pNew-&gt;m_dwRegister = NULL; <BR>
<BR>
// remove the extra refcount from the QI <BR>
pIUnknown-&gt;Release(); <BR>
} <BR>
} <BR>
} <BR>
return pNew-&gt;PrivateUnknown(); <BR>
} </TT></FONT></P>

<P>Shared server support is very straightforward to implement and use and adds a
level of functionality not normally available to standard server implementations.
<H2><A NAME="Heading30"></A>User Interface and Events</H2>
<P>Like MFC and ATL, BaseCtl servers can also benefit from the addition of User Interface
(UI) and Events. Again, since BaseCtl does not offer integration with MFC, you will
be forced to implement all of the UI with the standard Win32 functions, which is
not a very appealing prospect. At the time this book was written, only Visual Basic
5 was capable of using event interfaces in automation servers.
<H2><A NAME="Heading31"></A>From Here...</H2>
<P>In this chapter, you created a simple implementation of a BaseCtl automation server.
You also learned how to expand upon the basic framework provided by BaseCtl to create
new and interesting features within your implementation. BaseCtl provides a simple
and straightforward framework for creating Automation Servers. Unfortunately, the
lack of MFC integration and support from Microsoft as a development platform will
put it last on your list of choices when selecting a development direction.</P>
<P>For those of you who are willing to or interested in learning more about Automation
Servers and ActiveX architecture, the BaseCtl is a fine way to learn since it is
so lean. The BaseCtl is probably better in this area than ATL because you won't have
to understand the concept of templates while also learning ActiveX.</P>

<P>The next six chapters take a look at implementing ActiveX controls using the same
three tool sets: MFC, ATL, and BaseCtl.


</BODY>

</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -