Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1803 lines
55 KiB

// ChainWiz.cpp : Implementation of CChainWiz
#include "stdafx.h"
#include "WizChain.h"
#include "ChainWiz.h"
#include "propsht.h"
#include <commctrl.h>
#include <shellapi.h>
#include <htmlhelp.h>
#include "txttohtm.h"
#include "createtempfile.h"
// From sdnt\shell\comctl32\v6\rcids.h
#define IDD_NEXT 0x3024
// helper(s)
static LPDLGTEMPLATE GetDialogTemplate( HINSTANCE hinst, LPCWSTR wszResource )
{
HRSRC hrsrc = FindResourceW( hinst, wszResource, (LPCWSTR)RT_DIALOG );
if (hrsrc)
{
HGLOBAL hg = LoadResource( hinst, hrsrc );
if( hg )
{
return (LPDLGTEMPLATE)LockResource( hg );
}
}
return NULL;
}
static LPCWSTR DupStringResource( HINSTANCE hinst, LPCWSTR wszResource )
{
if ( NULL == wszResource )
return NULL;
LONG_PTR lid = (LONG_PTR)wszResource;
// first, try a static 256 wchar buffer....
WCHAR wszBuffer[256];
int iLen = LoadStringW( hinst, (UINT)lid, wszBuffer, 256 );
if( iLen <= 0 )
{
// resource not found: should be a string....
if ( IsBadStringPtrW( wszResource, 16384 ))
{
return NULL; // resource not found, and can't read memory
}
return _wcsdup( wszResource );
}
if( iLen < 256 )
{
return _wcsdup( wszBuffer );
}
// else alloc a bigger and bigger buffer until it all fits, then dup
for( int i = 512; i < 16384; i += 256)
{
WCHAR* pw = (WCHAR*)malloc( i * sizeof(WCHAR) );
if( !pw )
{
break; // yikes!
}
else
{
iLen = LoadStringW( hinst, (UINT)lid, pw, i );
if( iLen < i )
{
WCHAR* pwResult = _wcsdup( pw );
free( pw );
return pwResult;
}
free( pw );
}
}
return NULL;
}
void FreeStringResources( PROPSHEETPAGEW* psp )
{
if( psp->pszTitle )
{
free( (void*)psp->pszTitle );
}
if( psp->pszHeaderTitle )
{
free( (void*)psp->pszHeaderTitle );
}
if( psp->pszHeaderSubTitle )
{
free( (void*)psp->pszHeaderSubTitle );
}
}
// thunking stuff
struct CThunkData
{
public:
void* m_sig; // signature (pointer back to self)
CChainWiz* m_pCW;
PROPSHEETPAGEW* m_theirPSP;
WNDPROC m_theirWndProc;
IAddPropertySheets* m_pAPSs; // not AddRef'd
CThunkData( CChainWiz* pCW, PROPSHEETPAGEW* theirPSP, WNDPROC WndProc, IAddPropertySheets* pAPSs )
{
m_sig = (void*)this;
m_theirWndProc = WndProc;
m_pCW = pCW; // need one of these inside my thunking layers
m_pAPSs = pAPSs; // not AddRef'd
// their stuff (variable size!!!)
BYTE* temp = new BYTE[theirPSP->dwSize];
m_theirPSP = (PROPSHEETPAGEW*)temp;
if( temp )
{
MoveMemory( temp, theirPSP, theirPSP->dwSize );
}
}
~CThunkData( )
{
delete [] (BYTE*)m_theirPSP;
}
};
UINT CALLBACK ChainCallback( HWND hWnd, UINT uMsg, LPPROPSHEETPAGE ppsp )
{
if( !ppsp ) return 0;
// get my data
CThunkData* pTD = (CThunkData*)ppsp->lParam;
if( !pTD ) return 0;
if( pTD->m_theirPSP &&
(pTD->m_theirPSP->dwFlags & PSP_USECALLBACK) &&
(pTD->m_theirPSP->pfnCallback != NULL) )
{
// swap their data and my data
CThunkData td( NULL, ppsp, NULL, NULL );
MoveMemory( ppsp, pTD->m_theirPSP, ppsp->dwSize );
// call their callback
ppsp->pfnCallback( hWnd, uMsg, ppsp );
// change everything back
MoveMemory( ppsp, td.m_theirPSP, ppsp->dwSize );
}
if( uMsg == PSPCB_RELEASE )
{
delete pTD; // delete my thunk data
ppsp->lParam = NULL;
}
return 1;
}
INT_PTR CALLBACK ChainSubclassProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
CThunkData* pTD = (CThunkData*)GetPropW( hwndDlg, L"IWizChain" );
if( !pTD || (pTD->m_sig != (void*)pTD) )
{
return FALSE; // this is bad....
}
// special thunking for PSN_WIZBACK, PSN_WIZNEXT
if( uMsg == WM_NOTIFY )
{
NMHDR* pNMHDR = (NMHDR*)lParam;
if( pNMHDR->code == PSN_SETACTIVE )
{
CChainWiz* pCW = pTD->m_pCW;
assert( pCW != NULL );
CPropertyPagePropertyBag* pCPPPBag = pCW->GetPPPBag( );
assert( pCPPPBag != NULL );
IAddPropertySheets* pThisAPSs = pTD->m_pAPSs;
if( pThisAPSs != pCW->GetCurrentComponent( ) )
{
// crossed component boundary:
pCW->SetPreviousComponent( pCW->GetCurrentComponent( ) );
pCW->SetCurrentComponent( pThisAPSs );
IAddPropertySheets* pLastAPSs = pCW->GetPreviousComponent( );
// let previous guy write
if( pLastAPSs )
{
pCPPPBag->SetReadOnly( FALSE );
pCPPPBag->SetOwner( (DWORD)(LONG_PTR)pLastAPSs );
IPropertyPagePropertyBag* pOPPPBag = COwnerPPPBag::Create( pCPPPBag, (DWORD)(LONG_PTR)pLastAPSs );
if( pOPPPBag )
{
pLastAPSs->WriteProperties( pOPPPBag );
pOPPPBag->Release( );
}
}
// let current guy read
if( pThisAPSs )
{
pCPPPBag->SetReadOnly( TRUE );
pCPPPBag->SetOwner( PPPBAG_SYSTEM_OWNER );
IPropertyPagePropertyBag* pOPPPBag = COwnerPPPBag::Create( pCPPPBag, (DWORD)(LONG_PTR)pThisAPSs );
if( pOPPPBag )
{
pThisAPSs->ReadProperties( pOPPPBag );
pOPPPBag->Release( );
}
}
}
}
else if( (pNMHDR->code == PSN_WIZBACK) || (pNMHDR->code == PSN_WIZNEXT) )
{
// MFC hack:
// they don't set the DWL_MSGRESULT like they're supposed to!
// instead, they just return the IDD
// so, I'm gonna put a bogus value up there and check for it
const LONG BOGUS_IDD = -10L;
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, (LONG_PTR)BOGUS_IDD );
// fixup IDDs from PSN_WIZBACK, PSN_WIZNEXT
LPARAM lparamTemp = CallWindowProc( pTD->m_theirWndProc, hwndDlg, uMsg, wParam, lParam );
// get IDDs (maybe)
LONG_PTR idd = GetWindowLongPtr( hwndDlg, DWLP_MSGRESULT );
if( idd == BOGUS_IDD )
{
idd = lparamTemp; // MFC hack: see above.
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, idd );
}
// translate as necessary.
switch (idd)
{
case 0:
case -1:
{
break;
}
default:
{
// try to map idd to LPCDLGTEMPLATE
// if it fails, it must have been a LPCDLGTEMPLATE already
if( pTD->m_theirPSP )
{
LPDLGTEMPLATE lpdt = GetDialogTemplate( pTD->m_theirPSP->hInstance, (LPCWSTR)idd );
if( lpdt )
{
LPDLGTEMPLATE lpdt2 = pTD->m_pCW->GetAtlTemplate( lpdt );
if( lpdt2 )
{
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, (LONG_PTR)lpdt2 );
return (LPARAM)lpdt2;
}
else
{
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, (LONG_PTR)lpdt );
return (LPARAM)lpdt;
}
}
}
break;
}
}
return lparamTemp;
}
else if( pNMHDR->code == PSN_QUERYCANCEL )
{
CChainWiz* pCW = pTD->m_pCW;
WCHAR wsz[2048];
::LoadStringW( (HINSTANCE)_Module.GetModuleInstance(), IDS_QUERYCANCEL, wsz, 2048 );
return (IDYES != ::MessageBoxW( hwndDlg, wsz, (LPOLESTR)pCW->GetTitle (), MB_YESNO | MB_ICONWARNING ));
}
}
return CallWindowProc( pTD->m_theirWndProc, hwndDlg, uMsg, wParam, lParam );
}
static LOGFONT g_lf;
static LPARAM g_lp;
INT_PTR CALLBACK ChainDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
/*
the first time we ever get into this function and have my CThunkData
(this will be after the WM_SETFONT message, during WM_INITDIALOG),
1. subclass dlg hwnd
2. hang my stuff off window prop
3. setup for atl hook discarding test
4. call their dlgproc
Do thunking and PPPBag IO inside subclass function
*/
if( uMsg == WM_SETFONT )
{
// hang onto this so I can send it before WM_INITDIALOG
HFONT hf = (HFONT)wParam;
GetObject( (HGDIOBJ)hf, sizeof(g_lf), (void*)&g_lf );
g_lp = lParam;
return FALSE;
}
if( uMsg == WM_INITDIALOG )
{
// get my thunking data
PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)lParam;
CThunkData* pTD = (CThunkData*)psp->lParam;
if( pTD && pTD->m_theirPSP )
{
// 1. subclass dlg hwnd
pTD->m_theirWndProc = (WNDPROC)SetWindowLongPtr( hwndDlg, GWLP_WNDPROC, (LONG_PTR)ChainSubclassProc );
// 2. hang my stuff off window prop
SetPropW( hwndDlg, L"IWizChain", (HANDLE)pTD );
// 3. see 5. below
DLGPROC dlgproc = (DLGPROC)GetWindowLongPtr( hwndDlg, DWLP_DLGPROC );
// 4. call their dlgproc
// first send 'em WM_SETFONT (see above)
HFONT hf = CreateFontIndirect( &g_lf );
if( hf )
{
pTD->m_theirPSP->pfnDlgProc( hwndDlg, WM_SETFONT, (WPARAM)hf, g_lp );
DeleteObject( (HGDIOBJ)hf ); // should I delete this later? or now?
}
// 5.
// atl has this great feature where they blindly subclass the hwndDlg
// and throw the rest of 'em away.
// I can detect this...
if( dlgproc != (DLGPROC)GetWindowLongPtr( hwndDlg, DWLP_DLGPROC ) )
{
// re-subclass
pTD->m_theirPSP->pfnDlgProc = (DLGPROC)SetWindowLongPtr( hwndDlg, DWLP_DLGPROC, (LONG_PTR)ChainDlgProc );
}
// then send 'em the WM_INITDIALOG
return pTD->m_theirPSP->pfnDlgProc( hwndDlg, uMsg, wParam, (LPARAM)pTD->m_theirPSP );
}
else
{
return FALSE;
}
}
CThunkData* pTD = (CThunkData*)GetPropW( hwndDlg, L"IWizChain" );
if( !pTD || (pTD->m_sig != (void*)pTD) || !pTD->m_theirPSP )
{
return FALSE;
}
return pTD->m_theirPSP->pfnDlgProc( hwndDlg, uMsg, wParam, lParam );
}
static void ModifyStyleEx( HWND hwnd, DWORD dwRemove, DWORD dwAdd, UINT nFlags )
{
// cloned from atl
DWORD dwStyle = ::GetWindowLongPtr( hwnd, GWL_EXSTYLE );
DWORD dwNewStyle = (dwStyle & ~dwRemove) | dwAdd;
if( dwStyle == dwNewStyle )
{
return;
}
::SetWindowLongPtr( hwnd, GWL_EXSTYLE, (LONG_PTR)dwNewStyle );
::SetWindowPos ( hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | nFlags );
}
static void ShowBorder( HWND hwnd, BOOL bBorder )
{
if( bBorder )
{
ModifyStyleEx( hwnd, 0, WS_EX_CLIENTEDGE, SWP_FRAMECHANGED );
}
else
{
ModifyStyleEx( hwnd, WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED );
}
}
BOOL IsComctrlVersion6orGreater( )
{
BOOL bVersion6 = FALSE;
INITCOMMONCONTROLSEX init;
ZeroMemory( &init, sizeof(init) );
init.dwSize = sizeof(init);
init.dwICC = ICC_LINK_CLASS; // This is the SysLink control in v6
if( InitCommonControlsEx( &init ) )
{
bVersion6 = TRUE;
}
else
{
bVersion6 = FALSE;
}
return bVersion6;
}
INT_PTR CALLBACK FinishDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// Note that this function is not calling DefWindowProc at the end.
// Respect that otherwise things can be screwed up.
CChainWiz* pCW = (CChainWiz*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
switch( uMsg )
{
case WM_CLOSE:
{
// This will cover us when the focus is in the edit control and the user hits esc
if( ::GetFocus() == GetDlgItem( hwndDlg, IDC_EDIT1 ) )
{
HWND hWndParent = ::GetParent( hwndDlg );
if( hWndParent )
{
::SendMessage( hWndParent, PSM_PRESSBUTTON, PSBTN_CANCEL, 0 );
}
}
break;
}
case DM_GETDEFID:
{
// This will cover us when the focus is in the edit control and the user hits Enter
if( ::GetFocus() == GetDlgItem( hwndDlg, IDC_EDIT1 ) )
{
HWND hWndParent = ::GetParent( hwndDlg );
if( hWndParent )
{
::SendMessage( hWndParent, PSM_PRESSBUTTON, PSBTN_FINISH, 0 );
}
}
break;
}
case WM_INITDIALOG:
{
PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)lParam;
// get whatever's hanging on psp->lParam
// and hang it on hwndDlg via SetWindowLong
SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)psp->lParam );
pCW = reinterpret_cast<CChainWiz*>(psp->lParam);
if( pCW )
{
CString csLink;
BOOL bCommctrlV6 = IsComctrlVersion6orGreater();
RECT rect;
// Get the size of the static control
GetClientRect( GetDlgItem( hwndDlg, IDC_STATIC_3 ), &rect );
// Map rect coordinates to the dialog
MapWindowPoints( GetDlgItem( hwndDlg, IDC_STATIC_3 ), hwndDlg, reinterpret_cast<LPPOINT>(&rect), 2 );
if( bCommctrlV6 )
{
//
// if Comctrl version >= 6, we will stick to the Syslink control
//
pCW->m_hWndLink = CreateWindow( WC_LINK, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP,
rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top),
hwndDlg, NULL, NULL, NULL );
}
else
{
// Will use rich edit control for the hyperlink
if( NULL == pCW->m_hModuleRichEdit)
{
TCHAR szSystemDirectory[MAX_PATH + 1];
szSystemDirectory[0] = NULL;
if (GetSystemDirectory(szSystemDirectory, MAX_PATH + 1))
{
if (PathAppend(szSystemDirectory, _T("RichEd20.dll")))
{
pCW->m_hModuleRichEdit = LoadLibrary(szSystemDirectory);
}
}
}
if (NULL != pCW->m_hModuleRichEdit)
{
pCW->m_hWndLink = CreateWindowEx( ES_MULTILINE, RICHEDIT_CLASS, NULL,
WS_VISIBLE | WS_TABSTOP | WS_CHILD | ES_READONLY|ES_LEFT | ES_MULTILINE,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
hwndDlg, 0, _Module.GetModuleInstance(), NULL );
if( pCW->m_hWndLink )
{
// We want to recive ENM_LINK notifications
::SendMessage( pCW->m_hWndLink, EM_SETEVENTMASK, 0, ENM_LINK );
pCW->m_Hotlink.SubclassWindow( pCW->m_hWndLink );
}
}
}
}
break;
}
case WM_COMMAND:
{
if( pCW )
{
switch( HIWORD( wParam ) )
{
default:
{
break;
}
case EN_SETFOCUS:
{
if (LOWORD(wParam) == IDC_EDIT1)
{
HWND hwnd = (HWND)lParam;
_ASSERT( IsWindow( hwnd ) );
::SendMessage( hwnd, EM_SETSEL, 0, 0 );
}
break;
}
}
}
break;
}
case WM_NOTIFY:
{
if (pCW)
{
// do something with notifies...
NMHDR* pNMHDR = (NMHDR*)lParam;
if( !pNMHDR )
{
return FALSE;
}
switch( pNMHDR->code )
{
case NM_RETURN:
case NM_CLICK:
{
if( pCW->m_hWndLink && pNMHDR->idFrom == GetDlgCtrlID(pCW->m_hWndLink) )
{
pCW->LaunchMoreInfo();
}
break;
}
case EN_LINK:
{
if( pCW->m_hWndLink )
{
if( (WM_LBUTTONDOWN == ((ENLINK*)lParam)->msg) || (WM_CHAR == ((ENLINK*)lParam)->msg) )
{
pCW->LaunchMoreInfo();
}
}
break;
}
case PSN_SETACTIVE:
{
// set font
::SendMessage ( GetDlgItem( hwndDlg, IDC_STATIC_1 ), WM_SETFONT, (WPARAM)pCW->GetBoldFont(), MAKELPARAM(TRUE, 0) );
// set text
::SetWindowText( GetDlgItem( hwndDlg, IDC_STATIC_1 ), (LPWSTR)pCW->GetFinishHeader() );
::SetWindowText( GetDlgItem( hwndDlg, IDC_STATIC_2 ), (LPWSTR)pCW->GetFinishSubHeader() );
// get finish text from each component...
LPOLESTR szFinish = NULL;
LPOLESTR szMoreInfo = NULL;
HRESULT hr;
CString csLink;
hr = pCW->GetAllFinishText( &szFinish, &szMoreInfo );
if( SUCCEEDED(hr) )
{
if( szFinish )
{
// ... and add to edit field
HWND hwnd = GetDlgItem( hwndDlg, IDC_EDIT1 );
if( hwnd )
{
ShowScrollBar ( hwnd, SB_VERT, TRUE );
SetWindowTextW( hwnd, szFinish );
// hide vertical scroll if we don't need it
SCROLLINFO si = {0};
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
GetScrollInfo( hwnd, SB_VERT, &si );
if( si.nMax < si.nPage )
{
ShowBorder ( hwnd, FALSE );
ShowScrollBar( hwnd, SB_VERT, FALSE );
}
else
{
ShowBorder( hwnd, TRUE );
}
}
}
if( szMoreInfo )
{
if( csLink.LoadString(IDS_LINK_TEXT_WITHINFO) )
{
if( pCW->m_hWndLink )
{
::SendMessage( pCW->m_hWndLink, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(static_cast<LPCTSTR>(csLink)) );
pCW->WriteTempFile( szMoreInfo );
}
}
CoTaskMemFree( szMoreInfo );
szMoreInfo = NULL;
}
else
{
if( csLink.LoadString(IDS_LINK_TEXT) )
{
if( pCW->m_hWndLink )
{
::SendMessage( pCW->m_hWndLink, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(static_cast<LPCTSTR>(csLink)) );
LPTSTR szMoreInfoHtml = NULL;
hr = FinishTextToHTML( szFinish, &szMoreInfoHtml );
if( SUCCEEDED(hr) )
{
pCW->WriteTempFile( szMoreInfoHtml );
if( szMoreInfoHtml )
{
delete [] szMoreInfoHtml;
szMoreInfoHtml = NULL;
}
}
}
}
}
}
//
// We need to disable the Back button on the finish page
// if the wizard asked us to do so...
//
if ((pCW->m_dwWizardStyle) & CHAINWIZ_FINISH_BACKDISABLED)
{
::SendMessage(::GetParent(hwndDlg), PSM_SETWIZBUTTONS, 0, PSWIZB_FINISH | (~PSWIZB_BACK));
}
else
{
// setup buttons
::SendMessage( GetParent(hwndDlg), PSM_SETWIZBUTTONS, 0, PSWIZB_BACK | PSWIZB_FINISH );
}
break;
}
default:
{
break;
}
}
} //if( pCW )
break;
} // WM_NOTIFY
default:
{
break;
}
}
return FALSE;
}
struct CDataHolder
{
public:
BSTR m_szHeader;
BSTR m_szText;
HFONT m_hf;
CChainWiz* m_pCW;
CDataHolder( LPOLESTR szHeader, LPOLESTR szText, HFONT hf, CChainWiz* pCW )
{
m_szHeader = SysAllocString( szHeader );
m_szText = SysAllocString( szText );
m_hf = hf;
m_pCW = pCW;
}
~CDataHolder( )
{
if( m_szHeader )
{
SysFreeString( m_szHeader );
}
if( m_szText )
{
SysFreeString( m_szText );
}
// don't delete hf
}
};
INT_PTR CALLBACK WelcomeDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_DESTROY:
{
CDataHolder* pdh = (CDataHolder*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
if( pdh )
{
delete pdh;
}
SetWindowLongPtr( hwndDlg, GWLP_USERDATA, 0 );
}
break;
case WM_INITDIALOG:
{
// center on desktop
RECT r1;
RECT r2;
::GetWindowRect( GetParent(hwndDlg), &r1 );
::GetWindowRect( GetDesktopWindow(), &r2 );
int x = ((r2.right - r2.left) - (r1.right - r1.left)) / 2;
int y = ((r2.bottom - r2.top) - (r1.bottom - r1.top)) / 2;
::MoveWindow( GetParent(hwndDlg), x, y, (r1.right - r1.left), (r1.bottom - r1.top), TRUE );
PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)lParam;
// get whatever's hanging on psp->lParam
// and hang it on hwndDlg via SetWindowLong
SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)psp->lParam );
// add the rest of the pages now:
// this allows me to force all the pages to a
// fixed size.
CDataHolder* pdh = (CDataHolder*)psp->lParam;
if( pdh )
{
HWND hwndParent = ::GetParent( hwndDlg );
PropSheet_RemovePage( hwndParent, 1, NULL );
PROPSHEETHEADERW* psh = pdh->m_pCW->GetPropSheetHeader();
for( int i = 2; i < psh->nPages; i++ )
{
PropSheet_AddPage( hwndParent, psh->phpage[i] );
}
}
BOOL bCommctrlV6 = IsComctrlVersion6orGreater();
if( bCommctrlV6 )
{
if( pdh && pdh->m_pCW )
{
HWND hwnd = GetDlgItem( hwndDlg, IDC_STATIC_2 );
RECT rect;
// Get the size of the static control
GetClientRect( hwnd, &rect );
// Map rect coordinates to the dialog
MapWindowPoints( hwnd, hwndDlg, reinterpret_cast<LPPOINT>(&rect), 2 );
pdh->m_pCW->m_hWndWelcomeLink = CreateWindow( WC_LINK, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP,
rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top),
hwndDlg, NULL, NULL, NULL );
}
}
break;
}
case WM_NOTIFY:
{
NMHDR* pNMHDR = (NMHDR*)lParam;
if( !pNMHDR )
{
return FALSE;
}
switch( pNMHDR->code )
{
case NM_RETURN:
case NM_CLICK:
{
// No links on the Welcome page for CYS.
break;
}
case PSN_SETACTIVE:
{
CDataHolder* pdh = (CDataHolder*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
if( pdh )
{
HWND hwnd;
if( pdh->m_szHeader )
{
hwnd = GetDlgItem( hwndDlg, IDC_STATIC_1 );
if( hwnd )
{
// set font
::SendMessage ( hwnd, WM_SETFONT, (WPARAM)pdh->m_hf, MAKELPARAM(TRUE, 0) );
// set header text
::SetWindowTextW( hwnd, (LPWSTR)pdh->m_szHeader );
}
}
if( pdh->m_szText )
{
hwnd = NULL;
if( pdh->m_pCW && pdh->m_pCW->m_hWndWelcomeLink )
{
hwnd = pdh->m_pCW->m_hWndWelcomeLink;
}
else
{
hwnd = GetDlgItem( hwndDlg, IDC_STATIC_2 );
}
if( hwnd )
{
::SetWindowText( hwnd, pdh->m_szText );
}
}
}
//
// If we show the link on the welcome page, it will have the keyboard focus
// thus pressing enter will activate the link and launch the more info stuff
// To work around this problem, we are telling the wizard (parent window)
// to set the focus to the Next button in this particular case.
//
//
// Make sure the keyboard focus is set to the Next button
//
HWND hWndParent = GetParent(hwndDlg);
if( hWndParent )
{
HWND hWndNext = GetDlgItem( hWndParent, IDD_NEXT );
if( hWndNext )
{
// SendMessage will not work
PostMessage( hWndParent, WM_NEXTDLGCTL, reinterpret_cast<WPARAM> (hWndNext), TRUE );
}
}
::SendMessage( GetParent(hwndDlg), PSM_SETWIZBUTTONS, 0, PSWIZB_NEXT );
break;
}
default:
{
break;
}
}
break;
} // WM_NOTIFY
default:
{
break;
}
}
return FALSE;
}
INT_PTR CALLBACK DummyDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// we should never get here!!!
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CChainWiz
STDMETHODIMP CChainWiz::Initialize( HBITMAP hbmWatermark, HBITMAP hbmHeader, LPOLESTR szTitle, LPOLESTR szWelcomeHeader, LPOLESTR szWelcomeText, LPOLESTR szFinishHeader, LPOLESTR szFinishIntroText, LPOLESTR szFinishText )
{
// make sure this is called only once.
if( m_psh )
{
return E_UNEXPECTED;
}
// validate parameters
if( hbmWatermark != NULL )
{
if( GetObjectType( (HGDIOBJ)hbmWatermark ) != OBJ_BITMAP )
{
return ERROR_INVALID_PARAMETER;
}
}
if( hbmHeader != NULL )
{
if( GetObjectType( (HGDIOBJ)hbmHeader ) != OBJ_BITMAP )
{
return ERROR_INVALID_PARAMETER;
}
}
if ( !szTitle || !szWelcomeHeader || !szWelcomeText || !szFinishHeader || !szFinishIntroText || !szFinishText )
{
return E_POINTER;
}
HRESULT hr = S_OK;
m_psh = new PROPSHEETHEADERW;
if( !m_psh )
{
hr = E_OUTOFMEMORY;
}
else
{
// save title in case a page doesn't have one.
if (szTitle[0] == 0)
{
szTitle = L" "; // use space instead of empty string (for jmenter)
}
m_szWelcomeTitle = SysAllocString( szTitle );
m_szFinishHeader = SysAllocString( szFinishHeader );
m_szFinishSubHeader = SysAllocString( szFinishIntroText );
m_szFirstFinishTextLine = SysAllocString( szFinishText );
// create wizard (property sheet header) here
ZeroMemory( m_psh, sizeof(PROPSHEETHEADERW) );
m_psh->dwSize = sizeof(PROPSHEETHEADERW);
m_psh->dwFlags |= PSH_WIZARD97;
if( hbmWatermark )
{
m_psh->dwFlags |= (PSH_USEHBMWATERMARK | PSH_WATERMARK);
m_psh->hbmWatermark = hbmWatermark;
}
if( hbmHeader )
{
m_psh->dwFlags |= ( PSH_USEHBMHEADER | PSH_HEADER);
m_psh->hbmHeader = hbmHeader;
}
// make an array of HPROPSHEETPAGE FAR *phpage, to hold all the pages
m_psh->phpage = new HPROPSHEETPAGE[MAXPROPPAGES]; // just handles....
m_psh->nPages = 0; // so far
m_psh->nStartPage = 0; // my Welcome page (see below)
// TODO: do I need these? may need to pass in more params...
// ? HWND hwndParent;
// ? HINSTANCE hInstance;
// not using PFNPROPSHEETCALLBACK pfnCallback;
// create welcome page here from parameters above
PROPSHEETPAGEW psp;
ZeroMemory( &psp, sizeof(psp) );
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_HIDEHEADER; // welcome page: use watermark
psp.dwFlags |= PSP_USETITLE;
psp.pszTitle = szTitle;
psp.hInstance = (HINSTANCE)_Module.GetModuleInstance();
psp.pszTemplate = (LPCWSTR)MAKEINTRESOURCE(IDD_PROPPAGE_WELCOME);
psp.pfnDlgProc = WelcomeDlgProc;
CDataHolder* pDataHolder = new CDataHolder( szWelcomeHeader, szWelcomeText, m_hf, this );
if( !pDataHolder )
{
hr = E_OUTOFMEMORY;
}
else
{
psp.lParam = (LPARAM)pDataHolder;
// ~CDataHolder is called after first PSN_SETACTIVE notification
psp.pfnCallback = 0;
}
if( SUCCEEDED(hr) )
{
hr = Add( &psp );
}
if( SUCCEEDED(hr) )
{
// add dummy entry to list of APSs
CComPtr<IAddPropertySheets> spDummyAPS;
CComponents* pComponents = new CComponents(spDummyAPS);
spDummyAPS.Attach( CDummyComponent::Create(TRUE) ); // assignment causes AddRef !!!! so use Attach instead (!@$#$%&$%@!!!)
if( !pComponents )
{
hr = E_OUTOFMEMORY;
}
else
{
m_listOfAPSs.push_back( pComponents );
}
}
if( hr == S_OK )
{
// now add a dummy property page so that sizing works right:
// this page will be deleted, first thing, by WelcomeDlgProc.
ZeroMemory( &psp, sizeof(psp) );
psp.dwSize = sizeof(psp);
psp.dwFlags |= PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCE(0);
psp.pszHeaderSubTitle = MAKEINTRESOURCE(0);
psp.hInstance = (HINSTANCE)_Module.GetModuleInstance();
psp.pszTemplate = (LPCWSTR)MAKEINTRESOURCE(IDD_PROPPAGE_DUMMY);
psp.pfnDlgProc = DummyDlgProc;
psp.lParam = 0;
psp.pfnCallback = 0;
hr = Add( &psp );
// TODO: do I need to add to the list of APSs ???
}
}
return hr;
}
STDMETHODIMP CChainWiz::AddWizardComponent( LPOLESTR szClsidOfComponent )
{
// make sure wizard has been created (in Initalize, above)
if( !m_psh ) return E_UNEXPECTED;
// validate parameter(s)
if( !szClsidOfComponent ) return E_POINTER;
// convert string to clsid
CLSID clsid;
HRESULT hr = S_OK;
RPC_STATUS rpcs = CLSIDFromString( szClsidOfComponent, &clsid );
if (rpcs == RPC_S_OK)
{
// create wizard component
IAddPropertySheets* pAPSs = NULL;
hr = CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IAddPropertySheets, (void **)&pAPSs);
if( hr == S_OK )
{
SetCurrentComponent( pAPSs );
CAddPropertySheet* pAPS = new CAddPropertySheet(this);
if( !pAPS )
{
hr = E_OUTOFMEMORY;
}
else
{
pAPS->AddRef();
do
{
// call IAddPropertySheets::EnumPropertySheets until S_FALSE,
// adding pages to wizard
hr = pAPSs->EnumPropertySheets( pAPS );
} while( hr == S_OK );
pAPS->Release();
if( hr == S_FALSE )
{
hr = S_OK; // S_FALSE means no more pages (not an error).
}
// hang onto pAPSs in order to:
// 1. keep code in memory so that each pages dlgproc will work
// 2. be able to call pAPSs->ProvideFinishText for finish page
if( hr == S_OK )
{
CComponents* pComponents = new CComponents(pAPSs);
if( !pComponents )
{
hr = E_OUTOFMEMORY;
}
else
{
m_listOfAPSs.push_back( pComponents );
}
}
pAPSs->Release(); // not using this anymore, but have addref'd copies in my listofAPSs
}
SetCurrentComponent( NULL );
}
}
return hr;
}
STDMETHODIMP CChainWiz::DoModal( long* ret )
{
// make sure wizard has been created.
if( !m_psh ) return E_UNEXPECTED;
// validate parameter(s)
if( !ret ) return E_POINTER;
*ret = 0;
// add finish page
PROPSHEETPAGEW psp;
ZeroMemory( &psp, sizeof(psp) );
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_HIDEHEADER; // finish page: use watermark
psp.hInstance = _Module.GetModuleInstance();
psp.pszTemplate = (LPCWSTR)MAKEINTRESOURCE(IDD_PROPPAGE_FINISH);
psp.pfnDlgProc = FinishDlgProc;
psp.lParam = (LPARAM)this;
HRESULT hr = Add (&psp); // can't continue without a "Finish" button...
{ // add dummy entry to list of APSs
CComponents* pComponents = NULL;
CComPtr<IAddPropertySheets> spDummyAPS;
spDummyAPS.Attach (CDummyComponent::Create (FALSE)); // assignment causes AddRef !!!! so use Attach instead (!@$#$%&$%@!!!)
pComponents = new CComponents(spDummyAPS);
if( !pComponents )
{
hr = E_OUTOFMEMORY;
}
else
{
m_listOfAPSs.push_back ( pComponents );
}
}
if( hr == S_OK )
{
// make a copy so I can muck with the count:
// I need to do this so I can set a fixed size
// for all the property pages.
PROPSHEETHEADERW psh;
memcpy( &psh, m_psh, sizeof(PROPSHEETHEADERW) );
psh.nPages = 2; // welcome and dummy pages
*ret = ::PropertySheet( &psh );
// clean up: so that DoModal can't be called twice
delete m_psh;
m_psh = NULL;
// also, clean up maps
DestroyMaps();
if( *ret == -1 )
{
hr = GetLastError();
}
}
return hr;
}
#ifdef DEBUG
DLGITEMTEMPLATE* DumpItem( DLGITEMTEMPLATE* pdit )
{
TCHAR szBuffer[256];
wsprintf( szBuffer,
_T("\n\nDialog Item:\nstyle: 0x%x\ndwExtendedStyle: 0x%x\nx, y: %d, %d\ncx, cy: %d, %d\nid: %d\n"),
pdit->style,
pdit->dwExtendedStyle,
pdit->x, pdit->y,
pdit->cx, pdit->cy,
pdit->id );
OutputDebugString( szBuffer );
WORD* pw = (WORD*)(pdit + 1);
// window class:
// WORD 0xffff => predefined system class (1 more word)
// anything else => UNICODE string of class
switch( *pw )
{
case 0xffff:
{
wsprintf (szBuffer, _T("predefined system class: %d\n"), pw[1] );
OutputDebugString( szBuffer );
pw += 2;
break;
}
default:
{
OutputDebugString( _T("class: ") );
OutputDebugStringW( pw );
pw += wcslen( pw ) + 1;
OutputDebugString( _T("\n") );
break;
}
}
// title
// WORD 0xffff => 1 more word specifying resource id
// anything else => UNICODE text
switch( *pw )
{
case 0xffff:
{
wsprintf( szBuffer, _T("resource id: %d\n"), pw[1] );
OutputDebugString( szBuffer );
pw += 2;
break;
}
default:
{
OutputDebugString( _T("text: ") );
OutputDebugStringW( pw );
pw += wcslen( pw ) + 1;
OutputDebugString( _T("\n") );
break;
}
}
// creation data array
// first word is size of array (in bytes)
wsprintf( szBuffer, _T("%d bytes of creation data\n"), *pw );
OutputDebugString( szBuffer );
pw = 1 + (WORD*)(*pw + (BYTE*)pw);
// DWORD align
return (DLGITEMTEMPLATE*)(((DWORD_PTR)pw + 3) & ~DWORD_PTR(3));
}
void DumpTemplate( LPDLGTEMPLATE pdt )
{
if( ((WORD *)pdt)[1] == 0xFFFF )
{
return;
}
TCHAR szBuffer[256];
// dump info about DLGTEMPLATE
wsprintf( szBuffer,
_T("\n\nDialog Template:\nstyle: 0x%x\ndwExtendedStyle: 0x%x\ncdit: %d\nx, y: %d, %d\ncx, cy: %d, %d\n"),
pdt->style,
pdt->dwExtendedStyle,
pdt->cdit,
pdt->x, pdt->y,
pdt->cx, pdt->cy );
OutputDebugString( szBuffer );
WORD* pw = (WORD*)(pdt + 1);
// menu: 0000 = no menu; ffff = menu id; else Unicode string
switch( *pw )
{
case 0:
{
OutputDebugString( _T("no menu\n") );
pw += 2;
break;
}
case 0xFFFF:
{
pw++;
wsprintf( szBuffer, _T("menu id: %d\n"), *(DWORD*)pw );
OutputDebugString( szBuffer );
pw += 2;
break;
}
default:
{
OutputDebugStringW( pw );
pw += wcslen( pw ) + 1;
OutputDebugString( _T("\n") );
break;
}
}
// caption string:
OutputDebugString( _T("caption: ") );
OutputDebugStringW( pw );
pw += wcslen( pw ) + 1;
OutputDebugString( _T("\n") );
// extra font information
if (pdt->style & DS_SETFONT)
{
// font size
wsprintf( szBuffer, _T("font size: %d\n"), *pw++ );
OutputDebugString( szBuffer );
// typeface
OutputDebugString( _T("typeface: ") );
OutputDebugStringW( pw );
pw += wcslen( pw ) + 1;
OutputDebugString( _T("\n") );
}
// DWORD align
DLGITEMTEMPLATE* pdit = (DLGITEMTEMPLATE*)(((DWORD_PTR)pw + 3) & ~DWORD_PTR(3));
// dump all dlg items
for( WORD i = 0; i < pdt->cdit; i++ )
{
pdit = DumpItem( pdit );
}
}
#endif
INT_PTR CALLBACK SanityDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
return FALSE;
}
HRESULT SanityTest( PROPSHEETPAGEW* psp )
{
HRESULT hr = S_OK;
assert( psp->dwFlags & PSP_DLGINDIRECT );
LPCDLGTEMPLATE pdt = psp->pResource;
#ifdef DEBUG
DumpTemplate( (LPDLGTEMPLATE)pdt );
#endif
HWND hwndPage = CreateDialogIndirectParam( psp->hInstance, pdt, GetDesktopWindow(), SanityDlgProc, NULL );
if( hwndPage )
{
DestroyWindow( hwndPage );
}
else
{
hr = GetLastError();
if( hr == S_OK )
{
hr = E_FAIL;
}
}
return hr;
}
HRESULT CChainWiz::Add( PROPSHEETPAGEW* psp )
{
if( !psp )
{
return E_POINTER;
}
if( !psp->hInstance )
{
return ERROR_INVALID_PARAMETER;
}
// allocate my data to do thunking
CThunkData* pTD = new CThunkData( this, psp, NULL, m_CurrentComponent );
if( !pTD )
{
return E_OUTOFMEMORY;
}
// swap my data for their data
DWORD dwFlags = psp->dwFlags;
if( !(dwFlags & PSP_DLGINDIRECT) )
{
LPDLGTEMPLATE lpdt = GetDialogTemplate( psp->hInstance, psp->pszTemplate );
if( lpdt )
{
psp->dwFlags |= PSP_DLGINDIRECT;
LPDLGTEMPLATE lpdt2 = _DialogSplitHelper::SplitDialogTemplate( (LPDLGTEMPLATE)lpdt, NULL );
if( lpdt2 == lpdt )
{
psp->pResource = lpdt;
}
else
{
psp->pResource = lpdt2;
// add to map so I can remap resource id to new template ptr
m_mapOfTemplates[lpdt] = lpdt2;
}
}
}
psp->dwFlags |= PSP_USECALLBACK | PSP_USETITLE;
psp->pfnDlgProc = ChainDlgProc;
psp->lParam = (LPARAM)pTD;
psp->pfnCallback = ChainCallback;
// this is not used in wizard pages....
// remove it just in case comctl32.dll wants to use the hinstance that
// I'm about to replace, below
if( psp->dwFlags & PSP_USEICONID )
{
psp->dwFlags &= ~PSP_USEICONID;
}
// similarly, lock string resources (title, header, subtitle)
if( !(psp->pszTitle = DupStringResource (psp->hInstance, psp->pszTitle)) )
{
psp->pszTitle = _wcsdup (m_szWelcomeTitle); // use default from welcome page
}
psp->pszHeaderTitle = DupStringResource( psp->hInstance, psp->pszHeaderTitle );
psp->pszHeaderSubTitle = DupStringResource( psp->hInstance, psp->pszHeaderSubTitle );
// in order to get OCXs to be created we need:
// 1. to register AtlAxWin class (this is done in DllGetClassObject)
// 2. to pass our hinstance, not their's, as RegisterClass is not system-wide.
// So, I've fixed up all resource ids to be pointers and so now can
// replace their hinstance with mine.
psp->hInstance = (HINSTANCE)_Module.GetModuleInstance();
// run some sanity checks
HRESULT hr = SanityTest( psp );
if( hr != S_OK )
{
// oops! something didn't work:
// change everything back
FreeStringResources( psp );
if( pTD->m_theirPSP )
{
MoveMemory( psp, pTD->m_theirPSP, pTD->m_theirPSP->dwSize );
}
delete pTD; // won't be using this....
return hr;
}
// create the page
HPROPSHEETPAGE hpsp = CreatePropertySheetPage( psp );
if( hpsp == NULL )
{
hr = GetLastError( );
if( hr == S_OK ) // no docs on this....
{
hr = E_FAIL;
}
}
else
{
// all is well
m_psh->phpage[m_psh->nPages++] = hpsp;
}
// change everything back
FreeStringResources( psp );
MoveMemory( psp, pTD->m_theirPSP, pTD->m_theirPSP->dwSize );
return hr;
}
HRESULT CChainWiz::GetAllFinishText(LPOLESTR* pstring, LPOLESTR* ppMoreInfoText )
{
if( !pstring || !ppMoreInfoText )
{
return E_POINTER;
}
*pstring = NULL; // in case of errors
*ppMoreInfoText = NULL;
if( m_szFinishText )
{
CoTaskMemFree( m_szFinishText );
m_szFinishText = NULL;
}
m_szFinishText = (LPOLESTR)CoTaskMemAlloc( 1 * sizeof(OLECHAR) );
if( !m_szFinishText )
{
return E_OUTOFMEMORY;
}
m_szFinishText[0] = 0; // so wcscat works
OLECHAR szCRLF[] = L"\r\n";
DWORD dwSize = 0;
DWORD dwSizeMoreInfoText = 0;
// First line will precede any finish text from wizard components.
if( m_szFirstFinishTextLine && (0 < _tcslen( m_szFirstFinishTextLine )) )
{
dwSize += (sizeof(OLECHAR) * (wcslen( m_szFirstFinishTextLine ) + 1 )) + (sizeof(szCRLF) * 2);
LPOLESTR szTemp = (LPOLESTR)CoTaskMemRealloc( m_szFinishText, dwSize );
if( szTemp )
{
m_szFinishText = szTemp;
wcscat( m_szFinishText, m_szFirstFinishTextLine );
wcscat( m_szFinishText, szCRLF );
wcscat( m_szFinishText, szCRLF );
}
}
std::list<CComponents*>::iterator iterAPSs = m_listOfAPSs.begin();
CComponents* pLastComp = *iterAPSs;
for (CComponents* pComps = *iterAPSs; iterAPSs != m_listOfAPSs.end(); pComps = *++iterAPSs)
{
if( pLastComp->GetComponent() == pComps->GetComponent() )
{
continue; // look for unique APSs only
}
pLastComp = pComps; // for next time
IAddPropertySheets* pAPSs = pComps->GetComponent();
// have 'em re-read their properties (esp. read-write properties)
m_PPPBag->SetReadOnly( TRUE );
m_PPPBag->SetOwner ( (LONG_PTR)pAPSs );
IPropertyPagePropertyBag* pOPPPBag = COwnerPPPBag::Create( m_PPPBag, (LONG_PTR)pAPSs );
if( pOPPPBag )
{
pAPSs->ReadProperties( pOPPPBag );
pOPPPBag->Release();
}
m_PPPBag->SetReadOnly( FALSE ); // in case committers want to write
// get the finish text
LPOLESTR szFinishPiece = NULL;
LPOLESTR szMoreInfoPiece = NULL;
pAPSs->ProvideFinishText( &szFinishPiece, &szMoreInfoPiece );
if( szFinishPiece )
{
dwSize += (sizeof(OLECHAR) * (wcslen( szFinishPiece ) + 1)) + sizeof(szCRLF);
LPOLESTR szTemp = (LPOLESTR)CoTaskMemRealloc( m_szFinishText, dwSize );
if (szTemp)
{
m_szFinishText = szTemp;
wcscat( m_szFinishText, szFinishPiece );
wcscat( m_szFinishText, szCRLF );
}
CoTaskMemFree( szFinishPiece );
}
if( szMoreInfoPiece )
{
dwSizeMoreInfoText += (sizeof(OLECHAR) * (wcslen(szMoreInfoPiece) + 1)) + sizeof(szCRLF);
LPOLESTR szTemp = (LPOLESTR)CoTaskMemRealloc( *ppMoreInfoText, dwSizeMoreInfoText );
if( szTemp )
{
*ppMoreInfoText = szTemp;
wcscat( *ppMoreInfoText, szMoreInfoPiece );
wcscat( *ppMoreInfoText, szCRLF );
}
CoTaskMemFree( szMoreInfoPiece );
}
}
*pstring = m_szFinishText;
return S_OK;
}
STDMETHODIMP CChainWiz::get_PropertyBag( IDispatch** pVal )
{
if( !pVal ) return E_POINTER;
*pVal = NULL;
HRESULT hr = S_OK;
// don't give anybody a raw bag: wrap it up in an owner bag
IPropertyPagePropertyBag* pOPPPBag = COwnerPPPBag::Create( m_PPPBag, PPPBAG_SYSTEM_OWNER );
if( !pOPPPBag )
{
hr = E_OUTOFMEMORY;
}
else
{
hr = pOPPPBag->QueryInterface( IID_IDispatch, (void**)pVal );
pOPPPBag->Release();
}
return hr;
}
STDMETHODIMP CChainWiz::get_MoreInfoFileName( BSTR* pbstrMoreInfoFileName )
{
if( !pbstrMoreInfoFileName ) return E_POINTER;
HRESULT hr = S_OK;
if( NULL == m_bstrTempFileName )
{
return E_UNEXPECTED;
}
else
{
*pbstrMoreInfoFileName = SysAllocString( m_bstrTempFileName );
if( NULL == *pbstrMoreInfoFileName )
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
STDMETHODIMP CChainWiz::put_WizardStyle(VARIANT * pVarWizardStyle)
{
if (NULL == pVarWizardStyle)
{
return E_POINTER;
}
if (VT_UI4 != V_VT(pVarWizardStyle))
{
return E_INVALIDARG;
}
m_dwWizardStyle = V_UI4(pVarWizardStyle);
return S_OK;
}
HRESULT CChainWiz::WriteTempFile( LPCTSTR pszText )
{
if( !pszText ) return E_POINTER;
HRESULT hr = S_OK;
HANDLE hFile = NULL;
TCHAR szTempFileName[MAX_PATH] = {0};
BOOL bGenerateFileName = FALSE;
if( NULL == m_bstrTempFileName )
{
bGenerateFileName = TRUE;
}
else
{
szTempFileName[MAX_PATH - 1] = NULL;
_tcsncpy(szTempFileName, m_bstrTempFileName, MAX_PATH);
if (NULL != szTempFileName[MAX_PATH - 1])
{
hr = E_UNEXPECTED;
}
}
hFile = _CreateTempFile( szTempFileName, _T(".html"), bGenerateFileName );
if( INVALID_HANDLE_VALUE == hFile )
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
else
{
if( 0 == _tcslen( szTempFileName ) )
{
hr = E_UNEXPECTED;
}
}
if( SUCCEEDED(hr) )
{
DWORD dwcBytesWritten;
#ifdef UNICODE
// write UNICODE signature
// IE is doing fine without this but, anyway...
unsigned char sig[2] = { 0xFF, 0xFE };
if( !WriteFile( hFile, reinterpret_cast<LPVOID>(sig), 2, &dwcBytesWritten, NULL ) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
#endif
if( SUCCEEDED(hr) )
{
if( !WriteFile( hFile, pszText, (sizeof(TCHAR) * _tcslen( pszText )), &dwcBytesWritten, NULL ) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
}
if (SUCCEEDED(hr))
{
m_bstrTempFileName = SysAllocString(szTempFileName);
if (NULL == m_bstrTempFileName)
{
hr = E_OUTOFMEMORY;
}
}
if( hFile )
{
CloseHandle( hFile );
}
return hr;
}
HRESULT CChainWiz::LaunchMoreInfo( )
{
HRESULT hr = S_OK;
if( !m_bstrTempFileName )
{
return E_FAIL;
}
INT_PTR hRet = (INT_PTR)ShellExecute( NULL, _T("open"), m_bstrTempFileName, NULL, _T("."), SW_SHOW );
if( 32 >= hRet )
{
hr = E_FAIL;
}
return hr;
}