The Wayback Machine - https://web.archive.org/web/20080930104456/http://www.codeguru.com:80/cpp/w-p/files/browserfunctionsdialogs/article.php/c4443/

CodeGuru
Earthweb Search
Forums Wireless Jars Gamelan Developer.com
CodeGuru Navigation
Member Sign In
User ID:
Password:
Remember Me:
Forgot Password?
Not a member?
Click here for more information and to register.

jobs.internet.com

internet.commerce
Partners & Affiliates
















RSS Feeds

RSSAll

RSSVC++/C++

RSS.NET/C#

RSSVB

See more EarthWeb Network feeds

Home >> Visual C++ / C++ >> Windows Programming >> File & Folder >> Browser Functions & Dialogs


YABFFW'—Yet Another BrowseForFolder Wrapper
Rating: none

Michael Herstine (view profile)
August 18, 2003


(continued)



Environment: VC6, VC7, Win2K

Quick Overview

YABFFW (Yet Another BrowseForFolder Wrapper) is a CWnd subclass wrapping the Shell API SHBrowseForFolder() in an MFC-friendly way. There are certainly others out there, but the approach taken here (in my opinion) integrates more cleanly into MFC apps, and makes it easier to customize the appearance and behavior of the dialog box.

Why Another BFF Wrapper?

Sooner or later, we all need to display a dialog allowing the user to select a directory. A search in MSDN turns up the function ::SHBrowseForFolder() (<shlobj.h>). The next step is usually to either write or steal a C++ class that wraps up the C-style function call in a more MFC-friendly way.

At least, that's what happened to me. In my case, I needed to customize the dialog a bit, too. Specifically, I wanted to add a little checkbox labelled "Recurse" to it, and retrieve that checkbox's value after the user dismissed the dialog. So, I started surfing the Web looking for examples. I found several (C++ wrappers as well as examples of customizing the "Browse for Folder" dialog), and happily started copying them. However, as I implemented the code, I began to see a cleaner and more extensible way to accomplish my task. CYABFFW is the product of that process.

So How Does It Work?

What makes CYABFFW different is that it is a CWnd that subclasses the "Browse for Folder" dialog right after creation. This gives the class access to things such as Message Maps, CObject diagnostics, DDX/DDV, and so on. It also gets us out of calling ::SetWindowLong, writing a wndproc, and so on. Of course, it commits you to MFC; if you're not using MFC, CYABFFW won't be much use to you. The source code appears below (though of course you can download it along with a sample project).

How Do I Use It?

In the simplest case, CYABFFW looks and acts just like most any MFC dialog class. You instantiate it on the stack, call DoModal(), and check the return value to see what the user did. Once the user clicks "OK," you can retrieve the item they selected by calling either GetPath() or GetItemIdList(), depending on whether you want the selection as a path or an ITEMIDLIST.

For example:

CYABFFW dlg();
if (IDOK == dlg.DoModal())
{
    CString s = dlg.GetPath();
    // Do something with 's' ...

SHBrowseForFolder() allows quite a bit of customization of the dialog's appearance and behavior. You do this by filling out a BROWSEINFO struct that is passed to the function. I've tried to expose all the functionality provided by SHBrowseForFolder(), just in a way more familiar to C++ programmers.

The first point of extension is the CYABFFW constructor. There are several constructors, each taking an array of parameters that control the resulting dialog. For a full list, refer to the source code, or the documentation below. Here's an example, however:

CYABFFW dlg(_T("Please select a directory"),  // Hint to user
            BIF_USE_NEWUI,                    // Flags for the dlg
            this,                             // Parent window
            CSIDL_DRIVES);                    // Root of search
if (IDOK == dlg.DoModal())
{
    CString s = dlg.GetPath();
    // Do something with 's' ...

If you want to customize the dialog beyond what you can do with arguments to the constructor, you'll need to subclass CYABFFW. CYABFFW defines three virtual functions that can be overridden: OnInitBFFDialog(), OnBFFSelChanged(), or OnBFFValidateFailed(). These correspond to the custom messages SHBrowseForFolder() sends to its optional callback functions BFFM_INITIALIZED, BFFM_SELCHANGED, and BFFM_VALIDATEFAILED. Of course, you can also Mesage Map entries to your subclass and process any Windows Messages you're interested in. The demo project (see below) includes an example of doing this to add my "Recurse" checkbox.

The Code

YABFFW.h:

/**
 * \class CYABFFW
 *
 * \brief MFC Dialog wrapping \c ::SHBrowseForFolder
 *
 *
 * CYABFFW implements a different approach to wrapping \c
 * SHBrowseForFolder in an MFC-friendly way.  It actually
 * subclasses \c CWnd, allowing you to hook into the MFC Message
 * Mapping mechanism for customization.  Naturally, it also takes
 * care of some unpleasant chores associated with with \c
 * SHBrowseForFolder, such as translating from an \c LPITEMIDLIST
 * to a CString, and so on.
 *
 * Use it just like you would any other MFC-based Dialog:
 *
 \code
   void CMyClass::CMyHandler()
   {
     YABFFW dlg(_T("Choose a folder"), BIF_USENEWUI, this,
                CSIDL_DESKTOP);
     if (IDOK == dlg.DoModal())
     {
       CString s = dlg.GetPath();
       // Do something with 's' ...
     }
 \endcode
 *
 *
 */

class CYABFFW : public CWnd
{
  // Construction
public:
  /// Construct a CYABFFW dialog
  CYABFFW(const CString &strHint = CString(), UINT nFlags = 0U,
          CWnd *pParentWnd = NULL,
          int nRootFolder = CSIDL_DESKTOP);
  /// Load the "hint" from Resource
  CYABFFW(int nHint, UINT nFlags = 0U, CWnd *pParentWnd = NULL,
          int nRootFolder = CSIDL_DESKTOP);
  /// Construct a CYABFFW dialog rooted at an arbitrary folder
  CYABFFW(const CString &strHint, const CString &strRoot,
          UINT nFlags = 0U, CWnd *pParentWnd = NULL);
  /// Load the "hint" from Resource, but root the browse
  /// operation at an arbitrary folder
  CYABFFW(int nHint, const CString &strRoot, UINT nFlags = 0U,
          CWnd *pParentWnd = NULL);

  // Attributes
public:
  /// Retrieve the display name of the selected item
  CString GetDisplayName() const;
  /// Retrieve the ITEMIDLIST of the selected item
  LPCITEMIDLIST GetItemIdList() const;
  /// Retrieve the path to the selected item
  CString GetPath() const;

  // Operations
public:
  /// Display the Dialog - returns IDOK or IDCANCEL
  int DoModal();
  /// En/Dis-able the "OK" button
  void EnableOk(BOOL bEnable);
  /// Set the current selection in the tree control
  void SetSelection(LPCITEMIDLIST pItemIdList);
  /// Set the current selection in the tree control
  void SetSelection(const CString &strPath);
  /// Set the "hint" text
  void SetText(const CString &strNewText);

  // Overrides
public:
  /// Called when the BFF Dialog has initialized
  virtual void OnInitBFFDialog();
  /// Called when the selection has changed in the tree control
  virtual void OnBFFSelChanged(LPITEMIDLIST pNewSel);
  /// Called when the user types an invalid name
  virtual BOOL OnBFFValidateFailed(const CString &strBadName);

  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CYABFFW)
  //}}AFX_VIRTUAL

  // Implementation
public:
  virtual ~CYABFFW();

  // Generated message map functions
protected:
  //{{AFX_MSG(CYABFFW) NOTE - the ClassWizard will add and remove
  // member functions here.
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()

private:
  /// Static method to be used for the SHBFF callback
  static int CALLBACK BrowseCallbackProc(HWND hWnd, UINT nMsg,
                                         LPARAM lParam,
                                         LPARAM lpData);

private:
  /// Free the memory referenced by m_pItemIdList
  void FreeItemIdList(IMalloc *pMalloc = NULL);
  /// CSIDL => ITEMIDLIST
  LPITEMIDLIST ResolveCsidl(int nCsidl) const;
  /// Textual filesystem path => ITEMIDLIST
  LPITEMIDLIST ResolveFsPath(const CString &strPath) const;

private:
  /// Display name of the selected item
  CString m_strDisplayName;
  /// Flags to be passed to the browse dialog
  UINT m_nFlags;
  /// "Hint" to be displayed above the tree control
  CString m_strHint;
  /// ITEMIDLIST identifying the selected Shell item
  LPITEMIDLIST m_pItemIdList;
  /// Parent CWnd (NULL => App main window)
  CWnd *m_pParentWnd;
  /// Selected path
  CString m_strPath;
  /// ITEMIDLIST identifying the root
  LPITEMIDLIST m_pRoot;


};


/////////////////////////////////////////////////////////////////


#ifndef _DEBUG
inline CString CYABFFW::GetDisplayName() const
{ return m_strDisplayName; }

inline LPCITEMIDLIST CYABFFW::GetItemIdList() const
{ return m_pItemIdList; }

inline CString CYABFFW::GetPath() const
{ return m_strPath; }
#endif    // not _DEBUG

YABFFW.cpp:

/**
 * \brief Construct a CYABFFW dialog
 *
 * \sa SetText
 *
 *
 * \param strHint Optional string to display in the dialog; can
 * be changed later by SetText
 *
 * \param nFlags Optional Bit vector made up of \c BIF_* values
 * controlling various aspects of the dialog's appearance &
 * behavior
 *
 * \param pParentWnd Points to the parent or owner window object
 * (of type CWnd) to which the dialog object belongs. If it is
 * NULL, the dialog objects parent window is set to the main
 * application window.
 *
 * \param nRootFolder Optional \c CSIDL value naming the root of
 * the tree that the user may browse
 *
 *
 */

CYABFFW::CYABFFW(const CString &strHint  /*= CString()*/,
                 UINT nFlags             /*= 0U*/,
                 CWnd *pParentWnd        /*= NULL*/,
                 int nRootFolder         /*= CSIDL_DESKTOP*/) :
  m_nFlags(nFlags),
  m_strHint(strHint),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd);      // Paranoia

  m_pRoot = ResolveCsidl(nRootFolder);
}

/**
 * \brief Load the "hint" from Resource
 *
 * \sa SetText
 *
 *
 * \param nHint Resource Id of a string to be loaded and used in
 * the dialog; can be changed later by SetText
 *
 * \param nFlags Optional Bit vector made up of \c BIF_* values
 * controlling various aspects of the dialog's appearance &
 * behavior
 *
 * \param pParentWnd Points to the parent or owner window object
 * (of type CWnd) to which the dialog object belongs. If it is
 * NULL, the dialog objects parent window is set to the main
 * application window.
 *
 * \param nRootFolder Optional \c CSIDL value naming the root of
 * the tree that the user may browse
 *
 *
 */

CYABFFW::CYABFFW(int nHint,
                 UINT nFlags         /*= 0U*/,
                 CWnd *pParentWnd    /*= NULL*/,
                 int nRootFolder     /*= CSIDL_DESKTOP*/) :
  m_nFlags(nFlags),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd);  // Paranoia

  if (!m_strHint.LoadString(nHint))
    AfxThrowResourceException();

  m_pRoot = ResolveCsidl(nRootFolder);
}

/**
 * \brief Construct a CYABFFW dialog rooted at an arbitrary folder
 *
 * \sa SetText
 *
 *
 * \param strHint String to display in the dialog; can be changed
 * later by SetText.
 *
 * \param strRoot Path to a directory in the filesystem to root
 * the browse operation at
 *
 * \param nFlags Optional Bit vector made up of \c BIF_* values
 * controlling various aspects of the dialog's appearance &
 * behavior
 *
 * \param pParentWnd Points to the parent or owner window object
 * (of type CWnd) to which the dialog object belongs. If it is
 * NULL, the dialog objects parent window is set to the main
 * application window.
 *
 *
 */

 CYABFFW::CYABFFW(const CString &strHint,
                  const CString &strRoot,
                  UINT nFlags         /*= 0U*/,
                  CWnd *pParentWnd    /*= NULL*/) :
  m_nFlags(nFlags),
  m_strHint(strHint),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd);   // Paranoia

  m_pRoot = ResolveFsPath(strRoot);
}


/**
 * \brief Load the "hint" from Resource, but root the browse
 * operation at an arbitrary folder
 *
 * \sa SetText
 *
 *
 * \param nHint Resource Id of a string to be loaded and used in
 * the dialog; can be changed later by SetText
 *
 * \param strRoot Path to a directory in the filesystem to root
 * the browse operation at
 *
 * \param nFlags Optional Bit vector made up of \c BIF_* values
 * controlling various aspects of the dialog's appearance &
 * behavior
 *
 * \param pParentWnd Points to the parent or owner window object
 * (of type CWnd) to which the dialog object belongs. If it is
 * NULL, the dialog objects parent window is set to the main
 * application window.
 *
 *
 */

CYABFFW::CYABFFW(int nHint,
                 const CString &strRoot,
                 UINT nFlags         /*= 0U*/,
                 CWnd *pParentWnd    /*= NULL*/) :
  m_nFlags(nFlags),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd);  // Paranoia

  if (!m_strHint.LoadString(nHint))
    AfxThrowResourceException();

  m_pRoot = ResolveFsPath(strRoot);
}

#ifdef _DEBUG
CString CYABFFW::GetDisplayName() const
{
  return m_strDisplayName;
}

/**
 * \brief Retrieve the ITEMIDLIST of the selected item
 *
 *
 * \return A constant pointer to an \c ITEMIDLIST naming the
 * selected object.
 *
 *
 * Note that the caller is \em not responsible for cleaning up
 * this memory.  The flip side is that this pointer's validity is
 * bounded by the lifetime of its owning CYABFFW instance.
 *
 *
 */

LPCITEMIDLIST CYABFFW::GetItemIdList() const
{
  return m_pItemIdList;
}

CString CYABFFW::GetPath() const
{
  return m_strPath;
}
#endif    // _DEBUG

int CYABFFW::DoModal()
{
  // We'll need this eventually ...
  HRESULT hr;
  IMalloc *pMalloc;
  if (FAILED(hr = ::SHGetMalloc(&pMalloc)))
    AfxThrowOleException(hr);

  // Fill out a 'BROWSEINFO' structure to hand to 'SHBrowseFor-
  // Folder':
  BROWSEINFO browseInfo;
  ::ZeroMemory(&browseInfo, sizeof(BROWSEINFO));
  browseInfo.hwndOwner = (NULL == m_pParentWnd ? NULL :
                          m_pParentWnd->m_hWnd);

  browseInfo.pidlRoot = m_pRoot;

  // Use a CString for memory management
  browseInfo.pszDisplayName =
    m_strDisplayName.GetBufferSetLength(MAX_PATH);

  browseInfo.lpszTitle = m_strHint;
  browseInfo.ulFlags   = m_nFlags;
  browseInfo.lpfn      = BrowseCallbackProc;
  browseInfo.lParam    = (long)this;

  if (NULL != m_pItemIdList)
    FreeItemIdList();           // Probably never happen, but ...

  if (NULL == (m_pItemIdList = ::SHBrowseForFolder(&browseInfo)))
  {
    // User Cancelled out - clean up & bail.
    m_strDisplayName.ReleaseBuffer();
    pMalloc->Release();
    return IDCANCEL;
  }

  // Right - if we're here, the user actually selected an item.
  // Try to get a full path.  This will fail if the selected item
  // is not part of the FileSystem.
  ::SHGetPathFromIDList(m_pItemIdList,
                        m_strPath.GetBufferSetLength(MAX_PATH));

  // Cleanup time ...
  m_strPath.ReleaseBuffer();
  m_strDisplayName.ReleaseBuffer();
  pMalloc->Release();

  // Note: m_pItemIdList has *not* been freed!  We keep around in
  // case the caller wants to retrieve it later.  It will
  // ultimately be freed in the destructor.

  return IDOK;
}

void CYABFFW::EnableOk(BOOL bEnable)
{
  ASSERT(NULL != m_hWnd);
  SendMessage(BFFM_ENABLEOK, 0U, bEnable ? 1L : 0L);
}

void CYABFFW::SetSelection(LPCITEMIDLIST pItemIdList)
{
  ASSERT(NULL != m_hWnd);
  ASSERT_POINTER(pItemIdList, ITEMIDLIST);

  SendMessage(BFFM_SETSELECTION, (WPARAM)FALSE,
              (LPARAM)pItemIdList);
}

void CYABFFW::SetSelection(const CString &strPath)
{
  ASSERT(NULL != m_hWnd);

  SendMessage(BFFM_SETSELECTION, (WPARAM)TRUE,
              (LPARAM)(LPCTSTR)strPath);
}

void CYABFFW::SetText(const CString &strNewText)
{
  ASSERT(NULL != m_hWnd);

  SendMessage(BFFM_SETSTATUSTEXT, 0U, reinterpret_cast<LPARAM>
              (static_cast<LPCTSTR>(strNewText)));
}

void CYABFFW::OnInitBFFDialog()
{ /* No handling by default */ }

void CYABFFW::OnBFFSelChanged(LPITEMIDLIST /*pNewSel*/)
{ /* No handling by default */ }

/**
 * \brief Called when the user types an invalid name
 *
 *
 * \param strBadName Invalid item name provided by the user
 *
 * \return Return \c TRUE to indicate that the dialog may be
 * dismissed, \c FALSE to force the dialog to remain displayed
 *
 *
 */

BOOL CYABFFW::OnBFFValidateFailed(const CString & /*strBadName*/)
{ return TRUE; /* No handling by default */ }

CYABFFW::~CYABFFW()
{
  HRESULT hr;
  IMalloc *pMalloc;
  if (FAILED(hr = ::SHGetMalloc(&pMalloc)))
    AfxThrowOleException(hr);

  pMalloc->Free(m_pRoot);

  FreeItemIdList(pMalloc);
}


BEGIN_MESSAGE_MAP(CYABFFW, CWnd)
  //{{AFX_MSG_MAP(CYABFFW)
  // NOTE - the ClassWizard will add and remove mapping macros
  // here.
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()


int CYABFFW::BrowseCallbackProc(HWND hWnd,
                                UINT nMsg,
                                LPARAM lParam,
                                LPARAM lpData)
{
  CYABFFW *pWnd = reinterpret_cast<CYABFFW*>(lpData);
  ASSERT_VALID(pWnd);
  ASSERT(NULL == pWnd->m_hWnd || hWnd == pWnd->m_hWnd);

  if (NULL == pWnd->m_hWnd && !pWnd->SubclassWindow(hWnd))
    AfxThrowOleException(HRESULT_FROM_WIN32(::GetLastError()));

  switch (nMsg)
  {
  case BFFM_INITIALIZED:
    // Indicates the browse dialog box has finished
    // initializing. The lParam value is zero.
    pWnd->OnInitBFFDialog();
    return 0;
  case BFFM_SELCHANGED:
    // Indicates the selection has changed. The lParam parameter
    // points to the item identifier list for the newly selected
    // item.
    {
      LPITEMIDLIST p = reinterpret_cast<LPITEMIDLIST>(lParam);
      ASSERT_POINTER(p, ITEMIDLIST);
      pWnd->OnBFFSelChanged(p);
      return 0;
    }
  case BFFM_VALIDATEFAILED:
    // Indicates the user typed an invalid name into the edit box
    // of the browse dialog box. The lParam parameter is the
    // address of a character buffer that contains the invalid
    // name. An application can use this message to inform the
    // user that the name entered was not valid. Return zero to
    // allow the dialog to be dismissed or nonzero to keep the
    // dialog displayed.
    {
      LPTSTR p = reinterpret_cast<LPTSTR>(lParam);
      ASSERT(!::IsBadStringPtr(p, UINT_MAX));
      BOOL bDismissOk = pWnd->OnBFFValidateFailed(CString(p));
      return bDismissOk ? 0 : 1;
    }
  default:
    TRACE(_T("WARNING: Unknown message 0x%08x (0x%08x) ")
          _T("passed to CYABFFW::BrowseCallbackProc!"),
          nMsg, lParam);
    return 0;
  }    // End switch on nMsg.
}

void CYABFFW::FreeItemIdList(IMalloc *pMalloc /*= NULL*/)
{
  if (NULL == m_pItemIdList)
    return;

  bool bWeRelease = false;
  if (NULL == pMalloc)
  {
    bWeRelease = true;
    HRESULT hr;
    IMalloc *pMalloc;
    if (FAILED(hr = ::SHGetMalloc(&pMalloc)))
      AfxThrowOleException(hr);
  }

  pMalloc->Free(m_pItemIdList);

  if (bWeRelease)
    pMalloc->Release();

  m_pItemIdList = NULL;
}

LPITEMIDLIST CYABFFW::ResolveCsidl(int nCsidl) const
{
  // Short-circuit special case ...
  if (CSIDL_DESKTOP == nCsidl)
    return NULL;

  LPITEMIDLIST pidlRoot;
  HRESULT hr = ::SHGetFolderLocation(NULL, nCsidl, NULL, 0U,
                                     >pidlRoot);
  if (FAILED(hr))
  {
    ASSERT(NULL == pidlRoot);
    AfxThrowOleException(hr);
  }

  return pidlRoot;              // Caller assumes responsibility
}

LPITEMIDLIST CYABFFW::ResolveFsPath(const CString &strPath) const
{
  USES_CONVERSION;

# ifdef _DEBUG
  DWORD dwFileAttrs = ::GetFileAttributes(strPath);
  ASSERT(0xffffffff != dwFileAttrs &&
         FILE_ATTRIBUTE_DIRECTORY & dwFileAttrs);
# endif    // _DEBUG

  HRESULT hr;
  IShellFolder *pDesktop;
  if ( FAILED(hr = ::SHGetDesktopFolder(&pDesktop)) )
    AfxThrowOleException(hr);

  // Unfortunately, T2OLE expects a non-const string, so ...
  LPOLESTR p2 = T2OLE(const_cast<LPTSTR>(
                      static_cast<LPCTSTR>(strPath)));
  LPITEMIDLIST pItemIdList;
  if ( FAILED(hr = pDesktop->ParseDisplayName(NULL, NULL,
                                              p2,
                                              NULL,
                                              &pItemIdList,
                                              NULL)) )
  {
    pDesktop->Release();
    AfxThrowOleException(hr);
  }

  pDesktop->Release();
  return pItemIdList;           // Caller assumes responsibility
}

Downloads

Download demo project - 45 Kb
Download doxygen documentation - 106 Kb

Tools:
Add www.codeguru.com to your favorites
Add www.codeguru.com to your browser search box
IE 7 | Firefox 2.0 | Firefox 1.5.x
Receive news via our XML/RSS feed







RATE THIS ARTICLE:   Excellent  Very Good  Average  Below Average  Poor  

(You must be signed in to rank an article. Not a member? Click here to register)

Latest Comments:
I cannot compile ! - lguzman (05/12/2006)
how do i remove the control panel item? - Legacy CodeGuru (12/11/2003)

View All Comments
Add a Comment:
Title:
Comment:
Pre-Formatted: Check this if you want the text to display with the formatting as typed (good for source code)



(You must be signed in to comment on an article. Not a member? Click here to register)


JupiterOnlineMedia

internet.comearthweb.comDevx.commediabistro.comGraphics.com

Search:

Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

Jupitermedia Corporate Info

Solutions
Whitepapers and eBooks
IBM Whitepaper: Service Component Architecture Enabling XML Web Services for Java Programmers
IBM Whitepaper: Innovative Collaboration to Advance Your Business
Intel Article: Using Power & Display Context in the Intel Mobile Platform SDK
Internet.com eBook: Real Life Rails
IBM SCA Center Article: Simplifying Composite Applications with Service Component Architecture
Intel PDF: Quad-Core Impacts More Than the Data Center
Internet.com eBook: The Pros and Cons of Outsourcing
Go Parallel Article: Scalable Parallelism with Intel(R) Threading Building Blocks
Intel PDF: Analysis of Early Testing of Intel vPro in Large IT Departments
Internet.com eBook: Best Practices for Developing a Web Site
Intel PDF: IT Agility through Automated, Policy-based Virtual Infrastructure
IBM CIO Whitepaper: The New Information Agenda. Do You Have One?
Microsoft Article: BitLocker Brings Encryption to Windows Server 2008
IBM Whitepaper: Service Component Architecture & Java EE Integration
Microsoft Article: RODCs Transform Branch Office Security
Go Parallel Article: James Reinders on the Intel Parallel Studio Beta Program
Avaya Article: Advancing the State of the Art in Customer Service
IBM Whitepaper: How are other CIOs driving growth?
Adobe Acrobat Connect Pro: Web Conferencing and eLearning Whitepapers
Avaya Article: Avaya AE Services Provide Rapid Telephony Integration with Facebook
Go Parallel Article: Getting Started with TBB on Windows
HP eBook: Storage Networking , Part 1
MORE WHITEPAPERS, EBOOKS, AND ARTICLES
Webcasts
Go Parallel Video: Intel(R) Threading Building Blocks: A New Method for Threading in C++
HP Video: Is Your Data Center Ready for a Real World Disaster?
HP On Demand Webcast: Virtualization in Action
Go Parallel Video: Performance and Threading Tools for Game Developers
Rackspace Hosting Center: Customer Videos
Intel vPro Developer Virtual Bootcamp
HP Disaster-Proof Solutions eSeminar
HP On Demand Webcast: Discover the Benefits of Virtualization
MORE WEBCASTS, PODCASTS, AND VIDEOS
Downloads and eKits
Download: IBM WebSphere Application Server V7.0 Feature Pack for Service Component Architecture
Actuate Download: Free Visual Report Development Tool
Microsoft Download: Silverlight 2 Software Development Kit Beta 2
30-Day Trial: SPAMfighter Exchange Module
Red Gate Download: SQL Toolbelt
IBM SCA Download: Start Building SCA Applications Today
Iron Speed Designer Application Generator
Microsoft Download: Silverlight 2 Beta 2 Runtime
MORE DOWNLOADS, EKITS, AND FREE TRIALS
Tutorials and Demos
IBM IT Innovation Article: Green Servers Provide a Competitive Advantage
Microsoft Article: Expression Web 2 for PHP Developers--Simplify Your PHP Applications
Featured Algorithm: Intel Threading Building Blocks - parallel_reduce
MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES