// MainDlg.cpp : implementation of the CMainDlg class
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SimpleAlyViewer.h"
#include "MainDlg.h"
#include "aboutdlg.h"
/////////////////////////////////////////////////////////////////////////////
// Message handlers
BOOL CMainDlg::PreTranslateMessage ( MSG* pMsg )
{
return CWindow::IsDialogMessage ( pMsg );
}
BOOL CMainDlg::OnInitDialog ( HWND hwndFocus, LPARAM lParam )
{
// center the dialog on the screen
CenterWindow();
// set icons
HICON hIcon = AtlLoadIconImage ( IDR_MAINFRAME, LR_DEFAULTCOLOR, 32, 32 );
HICON hIconSmall = AtlLoadIconImage ( IDR_MAINFRAME, LR_DEFAULTCOLOR, 16, 16 );
SetIcon(hIcon, TRUE);
SetIcon(hIconSmall, FALSE);
// register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
DoDataExchange();
// Check the command line. If the first argument is a path to an .aly file,
// open it.
if ( __argc > 1 )
{
LPCTSTR szArg = __targv[1];
if ( PathMatchSpec ( szArg, _T("*.aly") ) && PathFileExists ( szArg ) )
ViewAlyFile ( szArg );
}
// Let the system set the focus.
return TRUE;
}
void CMainDlg::OnDestroy()
{
// unregister message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->RemoveMessageFilter(this);
}
void CMainDlg::OnDropFiles ( HDROP hDrop )
{
TCHAR szFilePath[MAX_PATH] = {0};
// Get the full path of the first dragged file, and check that it's any .aly file.
if ( DragQueryFile ( hDrop, 0, szFilePath, _countof(szFilePath) ) > 0 &&
PathMatchSpec ( szFilePath, _T("*.aly") ))
{
ViewAlyFile ( szFilePath );
}
DragFinish ( hDrop );
}
void CMainDlg::OnCancel ( UINT nCode, int nID, HWND hwndCtrl )
{
DestroyWindow();
PostQuitMessage ( nID );
}
void CMainDlg::OnAppAbout ( UINT nCode, int nID, HWND hwndCtrl )
{
CAboutDlg dlg;
dlg.DoModal ( m_hWnd );
}
void CMainDlg::OnOpenAlyFile ( UINT uCode, int nID, HWND hwndCtl )
{
const COMDLG_FILTERSPEC aFilters[] =
{
{ L"Aly test files", L"*.aly" },
{ L"All files", L"*.*" }
};
HRESULT hr;
CString sSelectedFilePath;
CShellFileOpenDialog dlg ( L"test1.aly",
FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST,
L".aly", aFilters, _countof(aFilters) );
if ( IDOK != dlg.DoModal ( m_hWnd ) )
return;
hr = dlg.GetFilePath ( sSelectedFilePath );
// Show the selected file as the current file. The file open dialog automatically
// calls SHAddToRecentDocs for us because we didn't use the FOS_DONTADDTORECENT
// flag. As a result, the file shows up in our jump list.
if ( SUCCEEDED(hr) )
ViewAlyFile ( sSelectedFilePath );
}
void CMainDlg::OnRegisterAsHandler ( UINT uCode, int nID, HWND hwndCtl )
{
CString sCaption ( LPCTSTR(IDR_MAINFRAME) );
// Set this app as the one associated with *.aly files.
if ( RegisterAsHandler() )
{
// That succeeded, so tell the shell that a file association has changed.
SHChangeNotify ( SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0 );
// Create a couple of test files for you to use with this app.
CreateTestFiles();
MessageBox ( _T("The file association was created successfully."),
sCaption, MB_ICONINFORMATION );
}
else
MessageBox ( _T("Error creating the file association"),
sCaption, MB_ICONERROR );
}
void CMainDlg::OnUnregisterAsHandler ( UINT uCode, int nID, HWND hwndCtl )
{
CString sCaption ( LPCTSTR(IDR_MAINFRAME) );
// Remove the association with *.aly files.
if ( UnregisterAsHandler() )
{
// Delete the test files that were created in OnRegisterAsHandler().
DeleteTestFiles();
// Tell the shell that a file association has changed.
SHChangeNotify ( SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0 );
MessageBox ( _T("The file association was removed successfully."),
sCaption, MB_ICONINFORMATION );
}
else
MessageBox ( _T("Error removing the file association"),
sCaption, MB_ICONERROR );
}
/////////////////////////////////////////////////////////////////////////////
// Other methods
bool CMainDlg::ShowCategory ( KNOWNDESTCATEGORY category )
{
HRESULT hr;
CComPtr<ICustomDestinationList> pDestList;
// Create an ICustomDestinationList interface.
hr = pDestList.CoCreateInstance ( CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER );
if ( FAILED(hr) )
return false;
// Tell the jump list our AppID.
hr = pDestList->SetAppID ( g_wszAppID );
if ( FAILED(hr) )
return false;
UINT cMaxSlots;
CComPtr<IObjectArray> pRemovedItems;
// Create a new empty jump list. The output parameters aren't important here
// since they are only relevant if you're adding custom items to the list.
hr = pDestList->BeginList ( &cMaxSlots, IID_PPV_ARGS(&pRemovedItems) );
if ( FAILED(hr) )
return false;
// Add a category to the list.
hr = pDestList->AppendKnownCategory ( category );
if ( FAILED(hr) )
return false;
// Save the new list.
return SUCCEEDED( pDestList->CommitList() );
}
void CMainDlg::OnShowRecentCategory ( UINT uCode, int nID, HWND hwndCtl )
{
CString sCaption ( LPCTSTR(IDR_MAINFRAME) );
if ( ShowCategory ( KDC_RECENT ) )
MessageBox ( _T("The jump list now shows the Recent category."),
sCaption, MB_ICONINFORMATION );
else
MessageBox ( _T("Error changing the jump list"), sCaption, MB_ICONERROR );
}
void CMainDlg::OnShowFrequentCategory ( UINT uCode, int nID, HWND hwndCtl )
{
CString sCaption ( LPCTSTR(IDR_MAINFRAME) );
if ( ShowCategory ( KDC_FREQUENT ) )
MessageBox ( _T("The jump list now shows the Frequent category."),
sCaption, MB_ICONINFORMATION );
else
MessageBox ( _T("Error changing the jump list"), sCaption, MB_ICONERROR );
}
bool CMainDlg::ClearJumpList()
{
HRESULT hr;
CComPtr<IApplicationDestinations> pDests;
// Create an IApplicationDestinations interface.
hr = pDests.CoCreateInstance ( CLSID_ApplicationDestinations, NULL,
CLSCTX_INPROC_SERVER );
if ( FAILED(hr) )
return false;
// Tell the jump list our AppID.
hr = pDests->SetAppID ( g_wszAppID );
if ( FAILED(hr) )
return false;
// Clear the list of files.
return SUCCEEDED( pDests->RemoveAllDestinations() );
}
void CMainDlg::OnClearJumpList ( UINT uCode, int nID, HWND hwndCtl )
{
CString sCaption ( LPCTSTR(IDR_MAINFRAME) );
if ( ClearJumpList() )
MessageBox ( _T("The jump list was cleared successfully."),
sCaption, MB_ICONINFORMATION );
else
MessageBox ( _T("Error clearing the jump list"), sCaption, MB_ICONERROR );
}
bool CMainDlg::WriteRegString ( HKEY hkeyParent, LPCTSTR szSubkey, LPCTSTR szValueName,
LPCTSTR szData, HANDLE hTransaction )
{
HKEY hKey;
CRegKey reg;
LONG lRet;
lRet = RegCreateKeyTransacted (
hkeyParent, szSubkey, 0, REG_NONE,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL,
hTransaction, NULL );
if ( ERROR_SUCCESS != lRet )
return false;
reg.Attach ( hKey );
lRet = reg.SetStringValue ( szValueName, szData );
return ERROR_SUCCESS == lRet;
}
// This function registers this app as the default handler for *.aly files.
// The association is written to HKEY_CURRENT_USER instead of HKEY_CLASSES_ROOT
// so we can do this without requiring elevation. The jump list would work just
// as well if we did write to HKEY_CLASSES_ROOT, or if we had an installer that
// wrote the association there.
bool CMainDlg::RegisterAsHandler()
{
CHandle hTransaction;
CString sIconPath, sCommandLine, sProgIDKey;
TCHAR szModulePath[MAX_PATH] = {0};
GetModuleFileName ( NULL, szModulePath, _countof(szModulePath) );
sIconPath.Format ( _T("\"%s\",-%d"), szModulePath, IDI_ALY_FILETYPE );
sCommandLine.Format ( _T("\"%s\" \"%%1\""), szModulePath );
sProgIDKey.Format ( _T("software\\classes\\%s"), g_wszProgID );
// Create a transaction for these registry changes.
hTransaction.Attach ( CreateTransaction ( NULL, 0, TRANSACTION_DO_NOT_PROMOTE,
0, 0, 0, NULL ) );
if ( hTransaction == NULL )
return false;
struct { CString sKey; LPCTSTR szValue, szData; } aEntries[] =
{
{ _T("software\\classes\\.aly"), NULL, g_wszProgID },
{ _T("software\\classes\\.aly\\OpenWithProgIDs"), g_wszProgID, _T("") },
{ sProgIDKey, _T("FriendlyTypeName"), _T("ALY test file type") },
{ sProgIDKey, _T("AppUserModelID"), g_wszAppID },
{ sProgIDKey + _T("\\DefaultIcon"), NULL, sIconPath },
{ sProgIDKey + _T("\\CurVer"), NULL, g_wszProgID },
{ sProgIDKey + _T("\\shell\\open\\command"), NULL, sCommandLine }
};
for ( int i = 0; i < _countof(aEntries); i++ )
{
if ( !WriteRegString ( HKEY_CURRENT_USER, aEntries[i].sKey, aEntries[i].szValue,
aEntries[i].szData, hTransaction ) )
return false;
}
return 0 != CommitTransaction ( hTransaction );
}
// Remove the file association that was created in RegisterAsHandler().
bool CMainDlg::UnregisterAsHandler()
{
CHandle hTransaction;
LONG lRet;
HKEY hKey;
CRegKey rk;
// Create a transaction for these registry changes.
hTransaction.Attach ( CreateTransaction ( NULL, 0, TRANSACTION_DO_NOT_PROMOTE,
0, 0, 0, NULL ) );
if ( hTransaction == NULL )
return false;
// Delete the .aly key.
lRet = RegOpenKeyTransacted (
HKEY_CURRENT_USER, _T("software\\classes"), 0,
DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
&hKey, hTransaction, NULL );
if ( ERROR_SUCCESS != lRet )
return false;
rk.Attach ( hKey );
lRet = RegDeleteTree ( rk, _T(".aly") );
if ( ERROR_SUCCESS != lRet )
return false;
// Delete the alyfile key.
lRet = RegDeleteTree ( rk, g_wszProgID );
if ( ERROR_SUCCESS != lRet )
return false;
return CommitTransaction ( hTransaction ) != 0;
}
LPWSTR g_awszTestFileNames[] = { L"test1.aly", L"willow.aly", L"buffy.aly" };
// Create some test files in the user's My Documents directory.
void CMainDlg::CreateTestFiles()
{
HRESULT hr;
LPWSTR pwszMyDocsPath = NULL;
hr = SHGetKnownFolderPath ( FOLDERID_Documents, 0, NULL, &pwszMyDocsPath );
if ( FAILED(hr) )
return;
for ( int i = 0; i < _countof(g_awszTestFileNames); i++ )
{
WCHAR wszTestFilePath[MAX_PATH] = {0};
CHandle hFile;
PathCombineW ( wszTestFilePath, pwszMyDocsPath, g_awszTestFileNames[i] );
hFile.Attach ( CreateFileW ( wszTestFilePath, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ) );
if ( hFile != NULL )
{
LPCSTR szMsg = "Just a boring ol' test file, part deux.";
DWORD cbyToWrite = strlen ( szMsg ), cbyWritten = 0;
WriteFile ( hFile, szMsg, cbyToWrite, &cbyWritten, NULL );
}
}
CoTaskMemFree ( pwszMyDocsPath );
}
// Delete the test files that were created in CreateTestFiles().
void CMainDlg::DeleteTestFiles()
{
HRESULT hr;
LPWSTR pwszMyDocsPath = NULL;
hr = SHGetKnownFolderPath ( FOLDERID_Documents, 0, NULL, &pwszMyDocsPath );
if ( FAILED(hr) )
return;
for ( int i = 0; i < _countof(g_awszTestFileNames); i++ )
{
WCHAR wszTestFilePath[MAX_PATH] = {0};
PathCombineW ( wszTestFilePath, pwszMyDocsPath, g_awszTestFileNames[i] );
DeleteFileW ( wszTestFilePath );
}
CoTaskMemFree ( pwszMyDocsPath );
}
void CMainDlg::ViewAlyFile ( LPCTSTR szFilePath )
{
// Since this app doesn't do anything with its files, there isn't much here.
// In a more substantal app, this would be where you open and parse your
// documents.
// We'll just check that the file exists, and bail out if not.
if ( !PathFileExists ( szFilePath ) )
return;
// Show the file as the current file.
m_cCurrFilePath.SetWindowText ( szFilePath );
// Add it to the recent files list. This also makes Win 7 add it to the
// app's jump list.
HRESULT hr;
SHARDAPPIDINFO info;
CComPtr<IShellItem> pItem;
hr = SHCreateItemFromParsingName ( szFilePath, NULL, IID_PPV_ARGS(&pItem) );
if ( SUCCEEDED(hr) )
{
info.psi = pItem;
info.pszAppID = g_wszAppID;
SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
}
}