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 objectsthat is, objects that know how to manage their own internal statein 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;
}