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

📄 freserve.txt

📁 服务器方面的
💻 TXT
📖 第 1 页 / 共 2 页
字号:
      TEXT("DllBall1.0"));
    SetRegKeyValue(
      szCLSID,
      TEXT("VersionIndependentProgID"),
      TEXT("DllBall"));
    SetRegKeyValue(
      szCLSID,
      TEXT("NotInsertable"),
      NULL);
    SetRegKeyValue(
      szCLSID,
      TEXT("InprocServer32"),
      szModulePath);
    AddRegNamedValue(
      szCLSID,
      TEXT("InprocServer32"),
      TEXT("ThreadingModel"),
      TEXT("Free"));

    return hr;
  }

The important new code here is the call to AddRegNamedValue. A new named
value must be added to the InprocServer32 key. This value is not a subkey
of InprocServer32. Registry keys can have a series of named values
associated with them. The definition of the AddRegNamedValue is
straightforward and is defined in FRESERVE.CPP. The named value added for
the DllBall component is "ThreadingModel=Free". Since our component and
server are fully thread-safe and are coded to support the free-threaded
model, we specify "Free". This means that clients of this server must be
initialized with COM as free-threaded client processes. We will see how
this is done the next lesson, FRECLIEN.

Other string values for ThreadingModel are "Both" and "Apartment".
"Apartment" means that the client process must be initialized with COM as
apartment-threaded before COM will employ the server on the client's
behalf. "Both" means that client processes can be initialized with COM as
either free-threaded or apartment-threaded. COM will employ the server on
their behalf in either case. If the ThreadingModel value is missing from
the InprocServer32 key, the apartment model is the default.

The COBall COM objects manufactured by this server implement the IBall
custom interface. Here is the IBall interface declaration from IBALL.H,
located in the INC sibling directory.

  DECLARE_INTERFACE_(IBall, IUnknown)
  {
    // IUnknown methods.
    STDMETHOD(QueryInterface) (THIS_ REFIID, PPVOID) PURE;
    STDMETHOD_(ULONG,AddRef)  (THIS) PURE;
    STDMETHOD_(ULONG,Release) (THIS) PURE;

    // IBall methods.
    STDMETHOD(Reset)      (THIS_ RECT*, SHORT) PURE;
    STDMETHOD(GetBall)    (THIS_ POINT*, POINT*, COLORREF*) PURE;
    STDMETHOD_(BOOL,Move) (THIS_ BOOL) PURE;
  };

Here are the matching implementation declarations within the COBall object
class. See the CImpIBall nested class declaration in BALL.H.

     ...
     ...
     STDMETHODIMP   Reset(RECT* pNewRect, short nBallSize);
     STDMETHODIMP   GetBall(POINT* pOrg, POINT* pExt, COLORREF* pcrColor);
     STDMETHODIMP_(BOOL) Move(BOOL bAlive);
     ...
     ...

The Reset method accepts a RECT structure, which specifies the initial
boundaries of the rectangle within which the ball entity is permitted to
move. This rectangle typically matches the client area of a client
application's window. Reset also accepts an initial ball diameter,
specified in pixels.

The GetBall method is the way the client obtains current properties of the
COBall object: its diameter, its color, and the location of its origin.

The Move method simply directs the COBall to move itself. The semantics of
this method are left up to the COBall object. In the current sample,
internal calculation logic is employed to give some continuity to the
ball's motion. The ball moves in the current direction until it hits a
boundary, at which point it bounces. This bounce is a reflection of its
angle of incidence with the boundary. However, the angle of reflection
does not always equal the angle of incidence. A small random skew factor
is used both for the reflection angle and for the speed of the motion.
This skew factor makes the ball's movement appear more natural.

When the bAlive parameter is set to FALSE, the Move method destroys the
ball. In this sample, multiple threads give life to the ball. Because one
thread could issue this termination command before another could know
about it, Move also returns a Boolean value to notify any other threads
that call Move if the ball is dead or alive. A return value of FALSE
indicates that the ball is dead. A dead ball no longer moves. Any thread
can move the ball, and any thread can kill the ball.

The construction of the COBall COM object is based on techniques seen in
earlier samples of this series. The familiar technique of nested class
declarations are used for the multiple interface implementations. Here is
the COBall class declaration in BALL.H.

  class COBall : public IUnknown, public CThreaded
  {
    public:
      // Main Object Constructor & Destructor.
      COBall(IUnknown* pUnkOuter, CServer* pServer);
      ~COBall(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpIBall : public IBall, public CThreaded
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIBall(COBall* pBackObj, IUnknown* pUnkOuter);
          ~CImpIBall(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IBall methods.
          STDMETHODIMP   Reset(RECT* pNewRect, short nBallSize);
          STDMETHODIMP   GetBall(POINT* pOrg, POINT* pExt, COLORREF* pcrColor);
          STDMETHODIMP_(BOOL) Move(BOOL bAlive);

        private:
          // Data private to this COBall interface implementation of IBall.
          COBall*      m_pBackObj;     // Parent Object back pointer.
          IUnknown*    m_pUnkOuter;    // Outer unknown for Delegation.

          // The following private data and methods constitute the working
          // heart of COBall as an actual application object.
          BOOL         m_bAlive;
          RECT         m_WinRect;
          int          m_nWidth;
          int          m_nHeight;
          int          m_xDirection;
          int          m_yDirection;
          BOOL         m_bNewPosition;
          int          m_xPosition;
          int          m_yPosition;
          short        m_xSkew;
          short        m_ySkew;
          COLORREF     m_crColor;
          CXForm       m_XForm;
          CBallThread  m_BallThreads[MAX_BALLTHREADS];

          // Private methods for internal use.
          void GetDimensions(POINT*);
          void SetDimensions(int,int);
          void GetDirection(POINT*);
          void SetDirection(int,int);
          void GetPosition(POINT*);
          void SetPosition(int,int);
          void CheckBounce(void);
          void FindThread(void);
      };

      // Make the otherwise private and nested IBall interface
      // implementation a friend to COM object instantiations of this
      // COBall COM object class.
      friend CImpIBall;

      // Private data of COBall COM objects.

      // Nested IBall implementation instantiation.  This IBall interface
      // is instantiated inside this COBall object as a native interface.
      CImpIBall        m_ImpIBall;

      // Main Object reference count.
      ULONG            m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*        m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*         m_pServer;
  };

The heart of the COBall object is coded inside the CImpIBall nested
interface implementation. The advantage of this design is that it doesn't
burden the main COBall object class with the details of the ball's motion.
It encapsulates the coding logic of the IBall interface entirely within
the interface implementation. Another advantage of this is that, if you
were evolving something like a CBall C++ class from legacy code into a COM
COBall implementation, the transition from CBall to CImpIBall is somewhat
less complicated than it would be from CBall to the outer COBall class,
where the nested interface implementations tend to dominate attention. In
this sample the issue is not as pronounced as a case where the outer
COBall class might have many nested interface class declarations.

Notice that the outer COBall class and the nested CImpIBall class are both
derived from CThreaded to inherit the OwnThis thread safety mechanism. The
methods of both these classes need this mechanism to protect data
encapsulated in their C++ objects.

The CImpIBall class implements many internal private methods, such as
SetPostion and FindThread. Of all these CImpIBall methods, only the
IUnknown and IBall interface methods are exposed as public in the C++
sense. The IBall interface exposes only the public Reset, GetBall, and
Move methods. Other of the private methods, such as SetPosition, could be
promoted to the status of public members of some new IBall2 evolution of
the IBall interface. Should such an evolution occur, the COM contract
requires that the new interface adopt a name and a new interface
identifier (IID). However, it must be derived from IBall to inherit and
retain its prior semantics.

The data that defines the ball is declared in the private area of
CImpIBall. The m_BallThreads array is maintained by the object to map
color attributes to the threads that call the object's Move method. In
conjunction with the FindThread method, program logic assigns colors to
passing threads and reuses those colors when previous threads revisit the
object. As new threads are added to the array, each is assigned a new
color. In the current sample, a random selection of 64 such thread colors
is accommodated using the compile-time macro MAX_BALL_THREADS.

The CXForm class is also declared in BALL.H. It is part of the inner
algorithms that govern ball behavior and is not relevant to the threading
model presented in this lesson.

The class factory for the DllBall component, CFBall, is declared in
FACTORY.H and implemented in FACTORY.CPP. This code is borrowed from many
previous samples in this series. Like COBall, CFBall is derived from
IUnknown and CThreaded using multiple inheritance. CThreaded gives the
class factory its thread safety using the OwnThis mechanism seen earlier.
There is one special issue worth mentioning.

The CFBall::Release has an odd arrangement of the OwnThis, UnOwnThis pair.
Here is the code from FACTORY.CPP.

  STDMETHODIMP_(ULONG) CFBall::Release(void)
  {
    ULONG cRefs;

    if (OwnThis())
    {
      cRefs = --m_cRefs;

      if (0 == cRefs)
      {
        // We've reached a zero reference count for this COM object.
        // So we tell the server housing to decrement its global object
        // count so that the server will be unloaded if appropriate.
        if (NULL != m_pServer)
          m_pServer->ObjectsDown();

        // We artificially bump the main reference count to prevent
        // reentrancy via the main object destructor.  Not really needed
        // in this CFBall but a good practice because we are aggregatable
        // and may at some point in the future add something entertaining
        // like some Releases to the CFBall destructor.
        m_cRefs++;
        UnOwnThis();
        delete this;
      }
      else
        UnOwnThis();
    }

    return cRefs;
  }

The extra call to UnOwnThis is needed because of the "delete this"
statement. The UnOwnThis call must precede the delete statement so that
the currently owning thread will relinquish ownership of this object
before an attempt is made to destroy the entire object. The object must
remain in existence as long as OwnThis is using the governing mutex, but
no longer.

⌨️ 快捷键说明

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