Figure 4 Scribble.reg
[HKEY_CLASSES_ROOT\.SCB]
@="Scribble.Document"
[HKEY_CLASSES_ROOT\.SCB\ShellNew]
"NullFile"=""
[HKEY_CLASSES_ROOT\Scribble.Document]
@="Scribble Drawing"
[HKEY_CLASSES_ROOT\Scribble.Document\DefaultIcon]
@="C:\\Mumble\\Bumble\\Fumble\\SCRIBBLE.EXE,0"
[HKEY_CLASSES_ROOT\Scribble.Document\shell]
[HKEY_CLASSES_ROOT\Scribble.Document\shell\open]
[HKEY_CLASSES_ROOT\Scribble.Document\shell\open\command]
@="C:\\Mumble\\Bumble\\Fumble\\SCRIBBLE.EXE \"%1\""
[HKEY_CLASSES_ROOT\Scribble.Document\shell\print]
[HKEY_CLASSES_ROOT\Scribble.Document\shell\print\command]
@="C:\\Mumble\\Bumble\\Fumble\\SCRIBBLE.EXE /p \"%1\""
[HKEY_CLASSES_ROOT\Scribble.Document\shell\printto]
[HKEY_CLASSES_ROOT\Scribble.Document\shell\printto\command]
@="C:\\Mumble\\Bumble\\Fumble\\SCRIBBLE.EXE /pt \"%1\" \"%2\" \"%3\" \"%4\""
Figure 6 Recognized Values for ShellNew
HKEY_CLASSES_ROOT\.ext\ShellNew\
Value Name = Value
NullFile "" Runs app with no command
line arguments
FileName pathname Runs app, passing pathname
as FileName argument
Command command Executes the command
Data binary data Creates a new file containing
the binary data
Figure 8 Scribble
SCRIBBLE.CPP
////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal.
// Portions Copyright (C) 1992-1995 Microsoft Corporation
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// This program is based on the SCRIBBLE program that comes with
// Microsoft Visual C++, as part of the MFC tutorial.
//
// This modified SCRIBBLE shows
//
// -how to write an SDI app that starts with an empty frame instead
// of a new document;
//
// -how to setup the systemp registry to call your app with a
// special switch when the user does "File New" from the
// Windows 95 shell;
//
// -how to parse the switch in your InitInstance function;
//
// -how to write an exception handler to handle any uncaught
// exception that occurs while the app is running.
//
#include "stdafx.h"
#include "Scribble.h"
#include "MainFrm.h"
#include "ScribDoc.h"
#include "ScribVw.h"
#include <eh.h> // set_terminate
#include "TraceWin.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////
// CScribbleApp
BEGIN_MESSAGE_MAP(CScribbleApp, CWinApp)
//{{AFX_MSG_MAP(CScribbleApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_EX_USER, OnExUser)
ON_COMMAND(ID_EX_MEMORY, OnExMemory)
ON_COMMAND(ID_EX_RESOURCE, OnExResource)
ON_COMMAND(ID_INSTALL, OnInstall)
ON_COMMAND(ID_REMOVE, OnRemove)
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_NEW, OnMyFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
CScribbleApp theApp;
typedef void (*TERMINATE_FN)();
TERMINATE_FN old_terminate = NULL;
////////////////
// Custom terminate handler just displays message
//
void my_terminate()
{
MessageBox(NULL, "Help me, I'm dying.", "Terminate", MB_OK);
if (old_terminate)
old_terminate();
}
//////////////////
// Custom class to parse command line switches.
// To parse your own switches, just override
// CCommandLineInfo::ParseParam.
class CMyCmdLineInfo : public CCommandLineInfo {
public:
virtual void ParseParam(const char* pszParam, BOOL bFlag, BOOL bLast);
enum { ShellNew=100 };
};
//////////////////
// Custom override to parse my own switches.
// Recognizes /ShellNew
//
void CMyCmdLineInfo::ParseParam(const char* pszParam, BOOL bFlag, BOOL bLast)
{
TRACE("CMyCmdLineInfo::ParseParam(%s)\n",pszParam);
if (bFlag && stricmp(pszParam, "ShellNew")= =0)
(UINT&)m_nShellCommand = ShellNew;
else
CCommandLineInfo::ParseParam(pszParam, bFlag, bLast);
}
CScribbleApp::CScribbleApp()
{
MfxTraceInit(); // initialize TRACEWIN tracing
old_terminate = set_terminate(my_terminate);
m_bRemoveProfile = FALSE;
}
CScribbleApp::~CScribbleApp()
{
set_terminate(old_terminate);
}
//////////////////
// Initialize app
//
BOOL CScribbleApp::InitInstance()
{
SetRegistryKey("MSJ"); // Use HKEY_CURRENT_USER\Software\MSJ for INI
LoadStdProfileSettings(); // Load INI file options (including MRU file)
// create doc template
CDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CScribbleDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CScribbleView));
AddDocTemplate(pDocTemplate);
if (!MaybeRegisterFileTypes()) // Maybe register shell stuff
return FALSE;
// Parse command line. Use my own custom class.
CMyCmdLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo)) // dispatch command
return FALSE;
return TRUE;
}
//////////////////
// Quit SCRIBBLE. If the user chose to remove the installation,
// delete the profile settings. This must happen now since other
// functions update the profile as the app is shutting down, recreating
// the profile entry if it's gone. This will remove it for good
//
int CScribbleApp::ExitInstance()
{
int nRet = CWinApp::ExitInstance();
if (m_bRemoveProfile)
RegDeleteKey(HKEY_CURRENT_USER,"Software\\MSJ\\Scribble");
return nRet;
}
//////////////////
// Override to handle /ShellNew option
//
BOOL CScribbleApp::ProcessShellCommand(CCommandLineInfo& cmdInfo)
{
TRACE("CScribbleApp::ProcessShellCommand(%d)\n",cmdInfo.m_nShellCommand);
if (cmdInfo.m_nShellCommand= =CMyCmdLineInfo::ShellNew) {
OnMyFileNew();
return TRUE;
}
return CWinApp::ProcessShellCommand(cmdInfo); // let MFC do it
}
//////////////////
// REALLY create new document: that is, initialize the empty doc object.
//
void CScribbleApp::OnMyFileNew()
{
OnFileNew(); // do normal thing to create new doc
// (in case this is an open doc, reuse it)
CFrameWnd* pFrame = (CFrameWnd*)m_pMainWnd;
ASSERT_KINDOF(CFrameWnd, pFrame);
CScribbleView* pView = (CScribbleView*)pFrame->GetActiveView();
ASSERT_KINDOF(CScribbleView, pView);
pView->GetDocument()->Initialize(); // initialize new doc
}
void CScribbleApp::OnAppAbout()
{
CDialog(IDD_ABOUTBOX).DoModal();
}
//////////////////
// Commands to throw various kinds of exceptions.
//
void CScribbleApp::OnExUser() { AfxThrowUserException(); }
void CScribbleApp::OnExMemory() { AfxThrowMemoryException(); }
void CScribbleApp::OnExResource() { AfxThrowResourceException(); }
//////////////////
// Exception handler of last resort displays message box describing
// exception, then tries to save all files.
//
LRESULT CScribbleApp::ProcessWndProcException(CException* e, const MSG* pMsg)
{
CString s = "An unhandled error occurred in your program.\n\n";
CString m;
m.Format("type\t%s\n", e->GetRuntimeClass()->m_lpszClassName);
s += m;
m.Format("HWND\t0x%04x\nmsg\t0x%04x\nwParam\t0x%08x\nlParam\t0x%08x",
pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
s += m;
s += "\n\nSelect:\nAbort \tto terminate the program;\n";
s += "Retry \tto let MFC handle the error; or\n";
s += "Ignore \tto do nothing.\n\n";
s += "After this dialog, you'll have a chance to save modified documents.";
int nRes = MessageBox(m_pMainWnd->GetSafeHwnd(), s, "Error!",
MB_ABORTRETRYIGNORE);
// Save any modified docs
SaveAllModified();
if (nRes= =IDABORT)
THROW_LAST(); // will call terminate()
else if (nRes= =IDRETRY)
return CWinApp::ProcessWndProcException(e, pMsg); // do MFC Thing
return 0; // IDIGNORE
}
//////////////////
// Dialog asks user whether to register scribble file types
//
class CRegisterConfirmDialog : public CDialog {
public:
CRegisterConfirmDialog() : CDialog (IDD_REGISTER) { }
DECLARE_MESSAGE_MAP()
afx_msg BOOL OnButton(UINT id) { EndDialog(id); return TRUE; }
};
BEGIN_MESSAGE_MAP(CRegisterConfirmDialog, CDialog)
ON_COMMAND_EX(IDYES, OnButton)
ON_COMMAND_EX(IDNO, OnButton)
END_MESSAGE_MAP()
//////////////////
// Check for proper registration, and maybe prompt user to register.
// Better to do it this way than blithely clobber the registry.
//
BOOL CScribbleApp::MaybeRegisterFileTypes()
{
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT, ".SCB\\ShellNew", &hkey)= =ERROR_SUCCESS)
// if ShellNew key exists, assume already registered.
// Theoretically, should check for proper values of all keys
return TRUE;
switch (CRegisterConfirmDialog().DoModal()) {
case IDCANCEL:
return FALSE; // abort
case IDNO:
return TRUE; // continue, but don't register
}
// User selected OK, so do the installation
OnInstall();
return TRUE;
}
//////////////////
// "Install" scribble (register file types)
//
void CScribbleApp::OnInstall()
{
// EnableShellOpen(); // Only use to support File Manager
RegisterShellFileTypes(TRUE); // TRUE for Win 95
// MFC has set HKEY_CLASSES_ROOT\.SCB\ShellNew\NullFile = ""
// So we must remove and replace with Command =
//
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT,".SCB\\ShellNew",&hkey)= =ERROR_SUCCESS) {
RegDeleteValue(hkey, "NullFile");
CString s = "SCRIBBLE.EXE /ShellNew";
DWORD len = s.GetLength() + 1;
RegSetValueEx(hkey, "Command", 0, REG_SZ, (BYTE*)(LPCSTR)s, len);
RegCloseKey(hkey);
} else {
ASSERT(FALSE);
}
}
//////////////////
// De-install SCRIBBLE app
// Remove registry keys. Profile must be deferred since shutdown code
// will write stuff like MRU files to the profile, recreating it.
//
void CScribbleApp::OnRemove()
{
RegDeleteKey(HKEY_CLASSES_ROOT,".SCB");
RegDeleteKey(HKEY_CLASSES_ROOT,"Scribble.Document");
m_bRemoveProfile = TRUE;
}
SCRIBBLE.H
////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal.
// Portions Copyright (C) 1992-1995 Microsoft Corporation.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// See SCRIBBLE.CPP for Description of program.
//
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // main symbols
class CScribbleApp : public CWinApp {
private:
BOOL m_bRemoveProfile; // delete profile on exit?
BOOL MaybeRegisterFileTypes(); // helper
public:
CScribbleApp();
~CScribbleApp();
virtual BOOL InitInstance();
virtual int ExitInstance();
// Note: non-virtual override
BOOL ProcessShellCommand(CCommandLineInfo& rCmdInfo);
virtual LRESULT ProcessWndProcException(CException* e, const MSG* pMsg);
//{{AFX_MSG(CScribbleApp)
afx_msg void OnAppAbout();
afx_msg void OnMyFileNew();
afx_msg void OnExUser();
afx_msg void OnExMemory();
afx_msg void OnExResource();
afx_msg void OnInstall();
afx_msg void OnRemove();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Figure 12 Pixel Problems
Top border | = | 3 | SM_CYFIXEDFRAME |
Caption | = | 19 | SM_CYCAPTION |
Bottom border | = | 3 | SM_CYFIXEDFRAME |
TOTAL | = | 25 (Desired height) | |
Min window height | = | 27 | SM_CYMIN |
Client | = | 2 | Extra pixels |
Figure 13 Fixing the Buggy Title Bar
////////////////////////////////////////////////////////////////
// Copyright 1996 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 4.1 on Windows 95
//
// MONITOR shows how to create a main window that is only a title bar,
// and how to prevent Windows from drawing size cursors in up/down or
// left/right directions
//
#include "StdAfx.h"
#include "TraceWin.h"
////////////////
// Remove this comment to see the original bug
//
//#define BUG
//////////////////
// Standard application class
//
class CMonitorApp : public CWinApp {
public:
CMonitorApp() : CWinApp() { }
virtual BOOL InitInstance();
};
//////////////////
// Monitor window is main frame
//
class CMonitorWindow : public CFrameWnd {
public:
CMonitorWindow();
afx_msg void OnTimer(UINT nIdEvent);
afx_msg void OnPaint();
afx_msg UINT OnNcHitTest(CPoint point);
DECLARE_MESSAGE_MAP()
};
CMonitorApp theApp;
//////////////////
// Initialize app: create main window
//
BOOL CMonitorApp::InitInstance()
{
MfxTraceInit();
m_pMainWnd = new CMonitorWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMonitorWindow, CFrameWnd)
ON_WM_TIMER()
ON_WM_PAINT()
ON_WM_NCHITTEST()
END_MESSAGE_MAP()
//////////////////
// Create monitor window. Constructor actually creates window too.
//
CMonitorWindow::CMonitorWindow()
{
// Compute size of window: smallest to display text
CString s = _T("Memory: 100%% used [Phys:999999K total/999999K free]");
CWindowDC dc(NULL); // screen DC
CSize sz = dc.GetTextExtent(s, s.GetLength()); // size of text
sz.cx += 2*GetSystemMetrics(SM_CXSMICON) + // plus icon, close box,
2*GetSystemMetrics(SM_CXSIZEFRAME); // and sizable frame
sz.cy += 2*GetSystemMetrics(SM_CYFIXEDFRAME); // should be SIZEFRAME, but
// it doesn't matter
CreateEx(WS_EX_TOPMOST, NULL, NULL,
#ifdef BUG
// The buggy version uses WS_BORDER to disallow sizing,
// resulting in a frame that's too narrow.
WS_BORDER
#else
// The fix uses WS_THICKFRAME to get the fat border, but
// disallows sizing by trapping WM_NCHITTEST
WS_THICKFRAME
#endif
| WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
sz.cx,
sz.cy,
NULL, 0);
TRACE("Desired size = %d x %d\n", sz.cx, sz.cy);
CRect rc;
GetWindowRect(&rc);
TRACE("Actual size = %d x %d\n", rc.Width(), rc.Height());
GetClientRect(&rc);
TRACE("Client size = %d x %d\n", rc.Width(), rc.Height());
TRACE("Minimum window height = %d\n", GetSystemMetrics(SM_CYMIN));
TRACE("Caption height = %d\n", GetSystemMetrics(SM_CYCAPTION));
TRACE("Sizable border height = %d\n",GetSystemMetrics(SM_CYSIZEFRAME));
TRACE("Non-sizable border height = %d\n",
GetSystemMetrics(SM_CYFIXEDFRAME));
SetTimer((UINT) 1, (UINT) 7500, NULL);
OnTimer(1); // first update immediately
}
//////////////////
// Update the window caption by getting new memory stats
//
void CMonitorWindow::OnTimer(UINT nIdEvent)
{
MEMORYSTATUS ms;
ms.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&ms);
CString s;
s.Format(_T("Memory: %2ld%% used [Phys:%ldK total/%ldK free]"),
ms.dwMemoryLoad,
ms.dwTotalPhys>>10,
ms.dwAvailPhys>>10);
SetWindowText(s);
}
//////////////////
// Handle WM_NCCHITTEST:
// Re-map top/bottom to HTBORDER, and corners to HTLEFT or HTRIGHT
//
UINT CMonitorWindow::OnNcHitTest(CPoint point)
{
UINT nRet = CFrameWnd::OnNcHitTest(point);
#ifndef BUG
switch (nRet) {
case HTTOP:
case HTBOTTOM:
return HTBORDER;
case HTTOPLEFT:
case HTBOTTOMLEFT:
return HTLEFT;
case HTTOPRIGHT:
case HTBOTTOMRIGHT:
return HTRIGHT;
}
#endif
return nRet;
}
//////////////////
// Paint client red to make it contrast more with frame/caption
//
void CMonitorWindow::OnPaint()
{
CPaintDC dc(this);
CRect rc;
GetClientRect(&rc);
CBrush b(RGB(255,0,0)); // red
CBrush* pOldBrush = dc.SelectObject(&b);
dc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
dc.SelectObject(pOldBrush);
}