Figure 3   MTS Features

Feature
Description
Connection Pooling
Some of the most precious resources in an enterprise client/server application are its database connections. In the traditional high-performance application model, database connections are created at application startup and maintained throughout the life of the program. This reduces the amount of time required to make database accesses, but it puts an inefficient resource strain on the database server. Microsoft Transaction Server (along with ODBC 3.0) supports connection pooling, which allows applications to create and destroy database connections on an as-needed basis without significant performance penalty. The net result is more efficient use of resources, which greatly improves scalability.
Just-in-Time Activation
In traditional COM applications, objects are often created at startup and maintained (via reference counting) throughout the lifetime of the application. This practice tends to improve performance because the application has a ready reference to all objects whenever it needs to use them, but it makes inefficient use of server resources. The object (along with all of its associated state, including database handles and file handles) must be maintained in memory even when it is not in use. To overcome this inefficiency, MTS has the ability to destroy and recreate stateless or state-aware objects—that is, objects that know how to manage their own internal state—in between method invocations, while the controlling client holds what appears to be a contiguous reference to a single object. This means more work for the developer (since state must be manually preserved from method call to method call), but it can increase scalability because resources are not tied up unnecessarily when the object is not in use.
Role-based Security
MTS provides a flexible security mechanism based on logical groupings of users, called roles, which allows application administrators to easily restrict component usage. Rather than having to create multiple component-specific Windows NT user accounts, administrators can simply associate existing accounts with different component privilege levels. MTS-aware components can dynamically determine (on a per-method basis) if the calling client is assuming a particular role.
Transaction Processing
In conjunction with transaction-compliant data sources, MTS can treat one or more method calls and database operations as a single logical transaction, even if they span multiple objects and data sources. If any aspect of the transaction fails, MTS can roll back every change made by the partially completed transaction.
Deployment Packages
MTS provides a rich, effective model for application deployment based on the grouping of logically related components into packages. By using the Microsoft Management Console (MMC) user interface, MTS makes the installation and configuration of component-based applications a much easier process than in the past.


Figure 6   TipServer.idl


 // TipServer.idl : IDL source for TipServer.dll
 //
 import "oaidl.idl";
 import "ocidl.idl";
 
 [ object, uuid(F2323B5F-59C0-11D2-A608-006008DF6641), dual ]
   interface ITipOfTheDay : IDispatch
 {
     [id(1)] HRESULT GetNextTip(
         [in, out] VARIANT* pvCookie,
         [out, retval] BSTR* pbstrText);
 };
 
 [ uuid(F2323B52-59C0-11D2-A608-006008DF6641), version(1.0),
   helpstring("TipServer 1.0 Type Library") ]
 library TIPSERVERLib
 {
     importlib("stdole32.tlb");
     importlib("stdole2.tlb");
 
     [ uuid(F8FC12E0-47ED-11D2-B387-006008A667FD),
       helpstring("TipOfTheDay Class") ]
     coclass TipOfTheDay
     {
         [default] interface ITipOfTheDay;
     };
 };

Figure 7   TipDlg.cpp


 // TipDlg.cpp : implementation of the CTipDlg class
 //
 
 #include "stdafx.h"
 #include "resource.h"
 #include "TipDlg.h"
 
 #include "TipServer.h"
 #include "TipServer_i.c"
 
 /////////////////////////////////////////////////////////////////////////////
 // CTipDlg dialog
 
 static const TCHAR szSection[] = _T("Tip");
 static const TCHAR szIntStartup[] = _T("StartUp");
 static const TCHAR szCookie[] = _T("Cookie");
 
 CTipDlg::CTipDlg(CWnd* pParent /*=NULL*/)
 : CDialog(IDD_TIP, pParent)
 {
     //{{AFX_DATA_INIT(CTipDlg)
     m_bStartup = !AfxGetApp()->GetProfileInt(szSection, szIntStartup, 0);
     //}}AFX_DATA_INIT
 
     m_vtCookie.vt = VT_I4;
     m_vtCookie.lVal = AfxGetApp()->GetProfileInt(szSection, szCookie, 1);
 
     m_pTipOfDay = NULL;
     HRESULT hr = CoCreateInstance(CLSID_TipOfTheDay,
         NULL, CLSCTX_SERVER, IID_ITipOfTheDay,
         (void**) &m_pTipOfDay);
 }
 
 CTipDlg::~CTipDlg()
 {
     // Store the updated cookie
     
     ::VariantChangeType(&m_vtCookie, &m_vtCookie, 0, VT_I4);
     AfxGetApp()->WriteProfileInt(szSection, szCookie, m_vtCookie.lVal);
     
     // Release TipOfTheDay component
 
     if (m_pTipOfDay)
         m_pTipOfDay->Release();
 }
         
 void CTipDlg::DoDataExchange(CDataExchange* pDX)
 {
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CTipDlg)
     DDX_Check(pDX, IDC_STARTUP, m_bStartup);
     //}}AFX_DATA_MAP
 }
 
 BEGIN_MESSAGE_MAP(CTipDlg, CDialog)
     //{{AFX_MSG_MAP(CTipDlg)
     ON_BN_CLICKED(IDC_NEXTTIP, OnNextTip)
     ON_WM_CTLCOLOR()
     ON_WM_PAINT()
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CTipDlg message handlers
 
 BOOL CTipDlg::OnInitDialog()
 {
     CDialog::OnInitDialog();
 
     if (m_pTipOfDay)
         OnNextTip();
     else
         GetDlgItem(IDC_NEXTTIP)->EnableWindow(FALSE);
 
     return TRUE;
 }
 
 void CTipDlg::OnOK()
 {
     AfxGetApp()->WriteProfileInt(szSection, szIntStartup, !m_bStartup);
     CDialog::OnOK();
 }
 
 void CTipDlg::OnNextTip()
 {
     BSTR bstrTip;
     HRESULT hr = m_pTipOfDay->GetNextTip(&m_vtCookie, &bstrTip);
     SetDlgItemText(IDC_TIPSTRING, CString(bstrTip));
     ::SysFreeString(bstrTip);
 }
 
 HBRUSH CTipDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
 {
     if (pWnd->GetDlgCtrlID() == IDC_TIPSTRING)
         return (HBRUSH)GetStockObject(WHITE_BRUSH);
 
     return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
 }
 
 void CTipDlg::OnPaint()
 {
     // Paint code removed in the interest of brevity
 }

Figure 10   TipOfTheDay.cpp


 // TipOfTheDay.cpp : Implementation of CTipOfTheDay
 
 #include "stdafx.h"
 #include "TipServer.h"
 #include "TipOfTheDay.h"
 #include "TipsTable.h"
 
/////////////////////////////////////////////////////////////////////////////
 // CTipOfTheDay
 
STDMETHODIMP CTipOfTheDay::GetNextTip(VARIANT *pvCookie, BSTR *pbstrText)
 {
     // Treat the cookie as a long integer
 
    ::VariantChangeType(pvCookie, pvCookie, 0, VT_I4);
 
    // Open the database
 
    CDataSource connection;
     CSession session;
     HRESULT hr = connection.Open(_T("MSDASQL"), "TipOfTheDay");
     hr = session.Open(connection);
 
    // If the cookie is zero, we'll use a pseudo-random
     //   number from one to the number of tips in the table
     //   instead. This code is required in case the client
     //   is unable to keep track of the cookie (such as from
     //   a cookie-disabled Web browser).
 
    if (pvCookie->lVal == 0)
     {
         CCommand<CAccessor<CTipCountAccessor> > tipCount; 
         LPCTSTR strSQL = _T("select count(*) from tips");
         HRESULT hr = tipCount.Open(session, strSQL);
         if (FAILED(hr))
             return hr;
         hr = tipCount.MoveNext();   // Retrieve count
         tipCount.Close();
 
         SYSTEMTIME sysTime;
         GetSystemTime(&sysTime);
         pvCookie->lVal = (sysTime.wMilliseconds % tipCount.lCount) + 1;
     }
 
    // Use the cookie as the index in our tip query
 
    CCommand<CAccessor<CTipsAccessor> > tips; 
     tips.m_lIndex = pvCookie->lVal;
     LPCTSTR strSQL = _T("select TipText from tips where Index = ?");
     hr = tips.Open(session, strSQL);
     if (FAILED(hr))
         return hr;
 
    // If we're off the end of the tips, set the index
     // back to the beginning (index = 1)
 
    if (tips.MoveNext() == DB_S_ENDOFROWSET)
     {
         tips.Close();
         tips.m_lIndex = 1;
         hr = tips.Open(session, strSQL);
         tips.MoveNext();
     }
     tips.Close();
 
    // Pass back the tip text and the newly incremented cookie value
 
    *pbstrText = CComBSTR(tips.m_sTipText).Detach();
     pvCookie->lVal = tips.m_lIndex + 1;
     return S_OK;
 }