Sieve.h: Declaration of the CSieve
#ifndef __SIEVE_H_
#define __SIEVE_H_
#include "resource.h" // main symbols
typedef struct tagTHREADPARMS {
IStream** ppStream;
int nMax;
} THREADPARMS;
/////////////////////////////////////////////////////////////////////////////
// CSieve
class ATL_NO_VTABLE CSieve :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSieve, &CLSID_Sieve>,
public ISieve
{
public:
CSieve()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_SIEVE)
DECLARE_NOT_AGGREGATABLE(CSieve)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CSieve)
COM_INTERFACE_ENTRY(ISieve)
END_COM_MAP()
// ISieve
public:
STDMETHOD(CountPrimes)(/*[in]*/ int nMax, /*[in]*/ ISieveCallback*
pCallback);
};
#endif //__SIEVE_H_
Sieve.cpp: Implementation of CSieve
#include "stdafx.h"
#include "AsyncServer.h"
#include "Sieve.h"
DWORD WINAPI ThreadFunc (LPVOID pThreadParms);
/////////////////////////////////////////////////////////////////////////////
// CSieve
STDMETHODIMP CSieve::CountPrimes(int nMax, ISieveCallback *pCallback)
{
//
// Marshal the interface pointer into a stream object.
//
IStream** ppStream = new IStream*;
HRESULT hr = CoMarshalInterThreadInterfaceInStream (IID_ISieveCallback,
pCallback, ppStream);
if (FAILED (hr))
return hr;
//
// Start a new thread and pass it nMax and ppStream.
//
THREADPARMS* ptp = new THREADPARMS;
ptp->ppStream = ppStream;
ptp->nMax= nMax;
DWORD dwThreadID;
HANDLE hThread = CreateThread (NULL, 0, ThreadFunc, ptp, 0, &dwThreadID);
if (hThread == NULL) {
(*ppStream)->Release ();
return E_OUTOFMEMORY;
}
CloseHandle (hThread); // Don't need this anymore
//
// Return now so the caller won't have to wait.
//
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// Thread function (global scope)
DWORD WINAPI ThreadFunc (LPVOID pThreadParms)
{
CoInitialize (NULL);
//
// Make a local copy of the thread parameters passed in THREADPARMS.
//
THREADPARMS* ptp = (THREADPARMS*) pThreadParms;
IStream** ppStream = ptp->ppStream;
int nMax = ptp->nMax;
delete ptp;
//
// Unmarshal the ISieveCallback interface pointer.
//
ISieveCallback* pCallback;
CoGetInterfaceAndReleaseStream (*ppStream, IID_ISieveCallback,
(void**) &pCallback);
delete ppStream;
//
// Count prime numbers between 2 and nMax.
//
int nCount = 0;
PBYTE pBuffer = new BYTE[nMax + 1];
if (pBuffer != NULL) {
::FillMemory (pBuffer, nMax + 1, 1);
int nLimit = 2;
while (nLimit * nLimit < nMax)
nLimit++;
for (int i=2; i<=nLimit; i++) {
if (pBuffer[i]) {
for (int k=i + i; k<=nMax; k+=i)
pBuffer[k] = 0;
}
}
for (i=2; i<=nMax; i++)
if (pBuffer[i])
nCount++;
delete[] pBuffer;
}
//
// Pass the result back to the caller and end this thread.
//
pCallback->CountPrimes_Result (nCount);
pCallback->Release ();
CoUninitialize ();
return 0;
}
Figure 3 AsyncClientDlg
AsyncClientDlg.h: header file
#if !defined(AFX_ASYNCCLIENTDLG_H__749DBF78_016B_11D3_8E53_006008A82731__INCLUDED_)
#define AFX_ASYNCCLIENTDLG_H__749DBF78_016B_11D3_8E53_006008A82731__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CAsyncClientDlg dialog
class CAsyncClientDlg : public CDialog
{
// Construction
public:
void DisplayResult (int nCount);
CAsyncClientDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CAsyncClientDlg)
enum { IDD = IDD_ASYNCCLIENT_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAsyncClientDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
ISieve* m_pSieve;
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CAsyncClientDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnGo();
afx_msg void OnDestroy();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
BEGIN_INTERFACE_PART (Callback, ISieveCallback)
virtual HRESULT __stdcall CountPrimes_Result (int nCount);
END_INTERFACE_PART (Callback)
DECLARE_INTERFACE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before
// the previous line.
#endif
//!defined(AFX_ASYNCCLIENTDLG_H__749DBF78_016B_11D3_8E53_006008A82731__INCLUDED_)
AsyncClientDlg.cpp: implementation file
#include "stdafx.h"
#include "AsyncServer.h"
#include "AsyncServer_i.c"
#include "AsyncClient.h"
#include "AsyncClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAsyncClientDlg dialog
CAsyncClientDlg::CAsyncClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CAsyncClientDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CAsyncClientDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pSieve = NULL;
}
void CAsyncClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAsyncClientDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAsyncClientDlg, CDialog)
//{{AFX_MSG_MAP(CAsyncClientDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_GO, OnGo)
ON_WM_DESTROY()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_INTERFACE_MAP(CAsyncClientDlg, CDialog)
INTERFACE_PART(CAsyncClientDlg, IID_ISieveCallback, Callback)
END_INTERFACE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CAsyncClientDlg message handlers
BOOL CAsyncClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
//
// Create a Sieve object instance.
//
HRESULT hr = ::CoCreateInstance (CLSID_Sieve, NULL, CLSCTX_SERVER,
IID_ISieve, (void**) &m_pSieve);
if (FAILED (hr)) {
MessageBox (_T ("CoCreateInstance failed"), _T ("Error"),
MB_ICONSTOP);
GetDlgItem (IDC_GO)->EnableWindow (FALSE);
}
return TRUE; // return TRUE unless you set the focus to a control
}
void CAsyncClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
HCURSOR CAsyncClientDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CAsyncClientDlg::OnDestroy()
{
//
// Release the ISieve interface pointer.
//
CDialog::OnDestroy();
if (m_pSieve != NULL) {
m_pSieve->Release ();
m_pSieve = NULL;
}
//
// Forcibly break extant ISieveCallback connections (if any).
//
ISieveCallback* pSieveCallback;
HRESULT hr = m_xCallback.QueryInterface (IID_ISieveCallback,
(void**) &pSieveCallback);
ASSERT (SUCCEEDED (hr));
m_xCallback.Release (); // Undo the AddRef performed by QueryInterface
CoDisconnectObject (pSieveCallback, NULL);
}
void CAsyncClientDlg::OnGo()
{
SetDlgItemText (IDC_RESULT, _T (""));
GetDlgItem (IDC_GO)->EnableWindow (FALSE);
//
// Get the address of our callback object's ISieveCallback interface.
//
ISieveCallback* pSieveCallback;
HRESULT hr = m_xCallback.QueryInterface (IID_ISieveCallback,
(void**) &pSieveCallback);
ASSERT (SUCCEEDED (hr));
m_xCallback.Release (); // Undo the AddRef performed by QueryInterface
//
// Call the Sieve object and provide an ISieveCallback interface pointer.
//
m_pSieve->CountPrimes (10000000, pSieveCallback);
}
void CAsyncClientDlg::DisplayResult(int nCount)
{
SetDlgItemInt (IDC_RESULT, nCount);
GetDlgItem (IDC_GO)->EnableWindow (TRUE);
}
///////////////////////////////////////////////////////////////////////
// XCallback methods
ULONG __stdcall CAsyncClientDlg::XCallback::AddRef ()
{
METHOD_PROLOGUE (CAsyncClientDlg, Callback)
return pThis->ExternalAddRef ();
}
ULONG __stdcall CAsyncClientDlg::XCallback::Release ()
{
METHOD_PROLOGUE (CAsyncClientDlg, Callback)
return pThis->ExternalRelease ();
}
HRESULT __stdcall CAsyncClientDlg::XCallback::QueryInterface (REFIID riid,
void** ppv)
{
METHOD_PROLOGUE (CAsyncClientDlg, Callback)
return pThis->ExternalQueryInterface (&riid, ppv);
}
HRESULT __stdcall CAsyncClientDlg::XCallback::CountPrimes_Result (int nCount)
{
METHOD_PROLOGUE (CAsyncClientDlg, Callback)
pThis->DisplayResult (nCount);
return S_OK;
}
Figure 5 StringClient Excerpts
StringClientDlg.h: header file
#if !defined(AFX_STRINGCLIENTDLG_H__59D16A78_0134_11D3_8E53_006008A82731__INCLUDED_)
#define AFX_STRINGCLIENTDLG_H__59D16A78_0134_11D3_8E53_006008A82731__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CStringClientDlg dialog
class CStringClientDlg : public CDialog
{
// Construction
public:
static HWND m_hTargetWnd;
static BOOL CALLBACK EnumWindowsProc (HWND hWnd, LPARAM lParam);
CStringClientDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CStringClientDlg)
enum { IDD = IDD_STRINGCLIENT_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CStringClientDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HWND PidToHwnd (DWORD dwProcessID);
IStringCache* m_pStringCache;
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CStringClientDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnOptionsGetString();
afx_msg void OnOptionsSetString();
afx_msg void OnOptionsNewProcess();
afx_msg void OnOptionsExit();
afx_msg void OnDestroy();
afx_msg BOOL OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before
// the previous line.
#endif // !defined(AFX_STRINGCLIENTDLG_H__59D16A78_0134_11D3_8E53_006008A82731__INCLUDED_)
StringClientDlg.cpp: implementation file
#include "stdafx.h"
#include "StringServer.h"
#include "StringServer_i.c"
#include "StringClient.h"
#include "StringClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
HWND CStringClientDlg::m_hTargetWnd = NULL;
/////////////////////////////////////////////////////////////////////////////
// CStringClientDlg dialog
CStringClientDlg::CStringClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CStringClientDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CStringClientDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pStringCache = NULL;
}
void CStringClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CStringClientDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CStringClientDlg, CDialog)
//{{AFX_MSG_MAP(CStringClientDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_COMMAND(ID_OPTIONS_GET_STRING, OnOptionsGetString)
ON_COMMAND(ID_OPTIONS_SET_STRING, OnOptionsSetString)
ON_COMMAND(ID_OPTIONS_NEW_PROCESS, OnOptionsNewProcess)
ON_COMMAND(ID_OPTIONS_EXIT, OnOptionsExit)
ON_WM_DESTROY()
ON_WM_COPYDATA()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CStringClientDlg message handlers
BOOL CStringClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
//
// Disable the menu items used to pass and test interface pointers.
//
GetMenu ()->EnableMenuItem (ID_OPTIONS_GET_STRING, MF_GRAYED);
GetMenu ()->EnableMenuItem (ID_OPTIONS_SET_STRING, MF_GRAYED);
GetMenu ()->EnableMenuItem (ID_OPTIONS_NEW_PROCESS, MF_GRAYED);
//
// Instantiate a string cache object if this instance of StringClient
// wasn't launched by another instance of StringClient.
//
CStringClientApp* pMyApp = (CStringClientApp*) AfxGetApp ();
if (pMyApp->CreateStringCache ()) {
HRESULT hr = ::CoCreateInstance (CLSID_StringCache, NULL,
CLSCTX_SERVER, IID_IStringCache, (void**) &m_pStringCache);
if (FAILED (hr))
MessageBox (_T ("CoCreateInstance failed"), _T ("Error"),
MB_ICONSTOP);
else { // Enable the menu items
GetMenu ()->EnableMenuItem (ID_OPTIONS_GET_STRING, MF_ENABLED);
GetMenu ()->EnableMenuItem (ID_OPTIONS_SET_STRING, MF_ENABLED);
GetMenu ()->EnableMenuItem (ID_OPTIONS_NEW_PROCESS, MF_ENABLED);
}
}
return TRUE; // return TRUE unless you set the focus to a control
}
void CStringClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
HCURSOR CStringClientDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CStringClientDlg::OnOptionsGetString()
{
ASSERT (m_pStringCache != NULL);
USES_CONVERSION;
wchar_t* pBuffer = NULL;
HRESULT hr = m_pStringCache->GetString (&pBuffer);
if (SUCCEEDED (hr)) {
LPTSTR pString = W2T (pBuffer);
SetDlgItemText (IDC_STRING, pString);
::CoTaskMemFree (pBuffer);
}
}
void CStringClientDlg::OnOptionsSetString()
{
ASSERT (m_pStringCache != NULL);
USES_CONVERSION;
CString string;
GetDlgItemText (IDC_STRING, string);
m_pStringCache->SetString (T2W (string));
}
void CStringClientDlg::OnOptionsNewProcess()
{
ASSERT (m_pStringCache != NULL);
//
// Allocate memory to hold a marshaled IStringCache interface pointer.
//
ULONG ulSize;
HRESULT hr = ::CoGetMarshalSizeMax (&ulSize, IID_IStringCache,
m_pStringCache, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
if (FAILED (hr)) {
MessageBox (_T ("CoGetMarshalSizeMax failed"), _T ("Error"),
MB_ICONEXCLAMATION);
return;
}
HGLOBAL hGlobal = ::GlobalAlloc (GMEM_MOVEABLE, (DWORD) ulSize);
if (hGlobal == NULL) {
MessageBox (_T ("Insufficient memory to marshal interface"),
_T ("Error"), MB_ICONEXCLAMATION);
return;
}
//
// Marshal the interface pointer into the memory block.
//
IStream* pStream;
hr = ::CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
if (FAILED (hr)) {
MessageBox (_T ("CreateStreamOnHGlobal failed"), _T ("Error"),
MB_ICONEXCLAMATION);
::GlobalFree (hGlobal);
return;
}
hr = ::CoMarshalInterface (pStream, IID_IStringCache, m_pStringCache,
MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
if (FAILED (hr)) {
MessageBox (_T ("CoMarshalInterface failed"), _T ("Error"),
MB_ICONEXCLAMATION);
pStream->Release ();
return;
}
//
// Launch another instance of StringClient and wait for it to create a
// window.
//
TCHAR szCmd[MAX_PATH + 16];
::GetModuleFileName (AfxGetInstanceHandle (), szCmd,
sizeof (szCmd) / sizeof (TCHAR));
::lstrcat (szCmd, _T (" /NoCreate"));
STARTUPINFO si;
::ZeroMemory (&si, sizeof (STARTUPINFO));
si.cb = sizeof (STARTUPINFO);
PROCESS_INFORMATION pi;
if (!::CreateProcess (NULL, szCmd, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) {
MessageBox (_T ("CreateProcess failed"), _T ("Error"),
MB_ICONEXCLAMATION);
pStream->Release ();
return;
}
::WaitForInputIdle (pi.hProcess, INFINITE);
::CloseHandle (pi.hThread);
::CloseHandle (pi.hProcess);
//
// Convert the process ID returned by CreateProcess into a window handle
// and pass the marshaled interface pointer to the new process.
//
HWND hWnd = PidToHwnd (pi.dwProcessId);
ASSERT (hWnd != NULL);
COPYDATASTRUCT cds = { 0, (DWORD) ulSize, ::GlobalLock (hGlobal) };
::SendMessage (hWnd, WM_COPYDATA, (WPARAM) m_hWnd, (LPARAM) &cds);
::GlobalUnlock (hGlobal);
//
// Clean up by releasing the stream and freeing the memory block.
//
pStream->Release ();
}
void CStringClientDlg::OnOptionsExit()
{
PostMessage (WM_CLOSE, 0, 0);
}
void CStringClientDlg::OnDestroy()
{
CDialog::OnDestroy();
if (m_pStringCache != NULL) {
m_pStringCache->Release ();
m_pStringCache = NULL;
}
}
HWND CStringClientDlg::PidToHwnd(DWORD dwProcessID)
{
m_hTargetWnd = NULL;
::EnumWindows (EnumWindowsProc, (LPARAM) dwProcessID);
return m_hTargetWnd;
}
BOOL CALLBACK CStringClientDlg::EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
DWORD dwProcessID;
::GetWindowThreadProcessId (hWnd, &dwProcessID);
if (dwProcessID == (DWORD) lParam) {
m_hTargetWnd = hWnd;
return FALSE;
}
return TRUE;
}
BOOL CStringClientDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
BOOL bResult = CDialog::OnCopyData(pWnd, pCopyDataStruct);
//
// Make a local copy of the data encapsulated in the message.
//
HGLOBAL hGlobal = ::GlobalAlloc (GMEM_MOVEABLE, pCopyDataStruct->cbData);
if (hGlobal == NULL) {
MessageBox (_T ("Insufficient memory to unmarshal interface"),
_T ("Error"), MB_ICONEXCLAMATION);
return bResult;
}
PBYTE pData = (PBYTE) ::GlobalLock (hGlobal);
::CopyMemory (pData, pCopyDataStruct->lpData, pCopyDataStruct->cbData);
::GlobalUnlock (hGlobal);
//
// Unmarshal the interface pointer.
//
IStream* pStream;
HRESULT hr = CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
if (FAILED (hr)) {
MessageBox (_T ("CreateStreamOnHGlobal failed"), _T ("Error"),
MB_ICONEXCLAMATION);
::GlobalFree (hGlobal);
return bResult;
}
IStringCache* pStringCache;
hr = CoUnmarshalInterface (pStream, IID_IStringCache,
(void**) &pStringCache);
if (FAILED (hr)) {
MessageBox (_T ("CoUnmarshalInterface failed"), _T ("Error"),
MB_ICONEXCLAMATION);
pStream->Release ();
return bResult;
}
//
// Cache the marshaled interface pointer and enable menu items.
//
m_pStringCache = pStringCache;
GetMenu ()->EnableMenuItem (ID_OPTIONS_GET_STRING, MF_ENABLED);
GetMenu ()->EnableMenuItem (ID_OPTIONS_SET_STRING, MF_ENABLED);
GetMenu ()->EnableMenuItem (ID_OPTIONS_NEW_PROCESS, MF_ENABLED);
//
// Clean up and return.
//
pStream->Release ();
return bResult;
}