DxCapsViewer/dxprint.cpp

714 строки
22 KiB
C++

//-----------------------------------------------------------------------------
// Name: dxprint.cpp
//
// Desc: DirectX Capabilities Viewer Printing Support
//
// Copyright(c) Microsoft Corporation.
// Licensed under the MIT License.
//
// https://go.microsoft.com/fwlink/?linkid=2136896
//-----------------------------------------------------------------------------
#include "dxview.h"
#include <Windowsx.h>
#include <commdlg.h>
#include <shlobj.h>
BOOL g_PrintToFile = FALSE; // Don't print to printer print to dxview.log
TCHAR g_PrintToFilePath[MAX_PATH]; // "Print" to this file instead of dxview.log
namespace
{
BOOL g_fAbortPrint = FALSE; // Did User Abort Print operation ?!?
HWND g_hAbortPrintDlg = nullptr; // Print Abort Dialog handle
HANDLE g_FileHandle = nullptr; // Handle to log file
DWORD iLastXPos = 0;
//-----------------------------------------------------------------------------
// Local Prototypes
//-----------------------------------------------------------------------------
#define MAX_TITLE 64
#define MAX_MESSAGE 256
VOID DoMessage(DWORD dwTitle, DWORD dwMsg);
BOOL CALLBACK PrintTreeStats(HINSTANCE hInstance, HWND hWnd, HWND hTreeWnd,
HTREEITEM hRoot);
//-----------------------------------------------------------------------------
// Name: PrintDialogProc()
// Desc: Dialog procedure for printing
//-----------------------------------------------------------------------------
BOOL CALLBACK PrintDialogProc(HWND hDlg, UINT msg, WPARAM /*wParam*/, LPARAM /*lParam*/)
{
switch (msg)
{
case WM_INITDIALOG:
// Disable system menu on dialog
EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE, MF_GRAYED);
return TRUE;
case WM_COMMAND:
// User is aborting print operation
g_fAbortPrint = TRUE;
EnableWindow(GetParent(hDlg), TRUE);
DestroyWindow(hDlg);
g_hAbortPrintDlg = nullptr;
return TRUE;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Name: AbortProc()
// Desc: Abort procedure for printing
//-----------------------------------------------------------------------------
BOOL CALLBACK AbortProc(HDC /*hPrinterDC*/, int /*iCode*/)
{
MSG msg;
while (!g_fAbortPrint && PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if ((!g_hAbortPrintDlg) || !IsDialogMessage(g_hAbortPrintDlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return !g_fAbortPrint;
}
//-----------------------------------------------------------------------------
// Name: PrintStartPage
// Desc: Check if we need to start a new page
//-----------------------------------------------------------------------------
HRESULT PrintStartPage(_In_ PRINTCBINFO* pci)
{
if (g_PrintToFile)
return S_OK;
if (!pci)
return E_FAIL;
// Check if we need to start a new page
if (pci->fStartPage)
{
// Check for user abort
if (g_fAbortPrint)
return E_FAIL;
// Start new page
if (StartPage(pci->hdcPrint) < 0)
return E_FAIL;
// Reset line count
pci->fStartPage = FALSE;
pci->dwCurrLine = 0;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: PrintEndPage()
// Desc: Check if we need to start a new page
//-----------------------------------------------------------------------------
HRESULT PrintEndPage(_In_ PRINTCBINFO* pci)
{
if (g_PrintToFile)
return S_OK;
if (!pci)
return E_FAIL;
// Check if we need to end this page
if (!pci->fStartPage)
{
// End page
if (EndPage(pci->hdcPrint) < 0)
return E_FAIL;
pci->fStartPage = TRUE;
// Check for user abort
if (g_fAbortPrint)
return E_FAIL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: DoMessage()
// Desc: Display warning message to user
//-----------------------------------------------------------------------------
VOID DoMessage(DWORD dwTitle, DWORD dwMsg)
{
TCHAR strTitle[MAX_TITLE];
TCHAR strMsg[MAX_MESSAGE];
LoadString(GetModuleHandle(nullptr), dwTitle, strTitle, MAX_TITLE);
LoadString(GetModuleHandle(nullptr), dwMsg, strMsg, MAX_MESSAGE);
MessageBox(nullptr, strMsg, strTitle, MB_OK);
}
//-----------------------------------------------------------------------------
// Name: PrintStats()
// Desc: Print user defined stuff
//-----------------------------------------------------------------------------
BOOL CALLBACK PrintTreeStats(HINSTANCE hInstance, HWND hWnd, HWND hTreeWnd,
HTREEITEM hRoot)
{
static DOCINFO di;
static PRINTDLG pd = {};
// Check Parameters
if (!hInstance || !hWnd || !hTreeWnd)
return FALSE;
// Get Starting point for tree
HTREEITEM hStartTree = (hRoot) ? hRoot : TreeView_GetRoot(hTreeWnd);
if (!hStartTree)
return FALSE;
BOOL fDone = FALSE;
BOOL fFindNext = FALSE;
BOOL fResult = FALSE;
BOOL fStartDoc = FALSE;
BOOL fDisableWindow = FALSE;
LPTSTR pstrTitle = nullptr;
LPTSTR pstrBuff = nullptr;
DWORD dwCurrCopy;
HANDLE hHeap = nullptr;
DWORD buffSize;
DWORD cchLen;
TV_ITEM tvi;
// Initialize Print Dialog structure
pd.lStructSize = sizeof(PRINTDLG);
pd.hwndOwner = hWnd;
pd.Flags = PD_ALLPAGES | PD_RETURNDC;
pd.nCopies = 1;
PRINTCBINFO pci = {};
TEXTMETRIC tm = {};
if (g_PrintToFile)
{
pci.hdcPrint = nullptr;
}
else
{
// Call Common Print Dialog to get printer DC
if (!PrintDlg(&pd) || !pd.hDC)
{
// Print Dialog failed or user canceled
return TRUE;
}
pci.hdcPrint = pd.hDC;
// Get Text metrics for printing
if (!GetTextMetrics(pci.hdcPrint, &tm))
{
// Error, TextMetrics failed
goto lblCLEANUP;
}
}
if (g_PrintToFile)
{
pci.dwLineHeight = 1;
pci.dwCharWidth = 1;
pci.dwCharsPerLine = 80;
pci.dwLinesPerPage = 66;
}
else
{
pci.dwLineHeight = tm.tmHeight + tm.tmExternalLeading;
pci.dwCharWidth = tm.tmAveCharWidth;
pci.dwCharsPerLine = GetDeviceCaps(pci.hdcPrint, HORZRES) / pci.dwCharWidth;
pci.dwLinesPerPage = GetDeviceCaps(pci.hdcPrint, VERTRES) / pci.dwLineHeight;
}
// Get Heap
hHeap = GetProcessHeap();
if (!hHeap)
{
// Error, no heap associated with this process
goto lblCLEANUP;
}
// Create line buffer
buffSize = (pci.dwCharsPerLine + 1) * sizeof(TCHAR);
pstrBuff = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, buffSize);
if (!pstrBuff)
{
// Error, not enough memory
goto lblCLEANUP;
}
*pstrBuff = 0;
// Disable Parent window
EnableWindow(hWnd, FALSE);
fDisableWindow = TRUE;
// Start Printer Abort Dialog
g_fAbortPrint = FALSE;
g_hAbortPrintDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABORTPRINTDLG),
hWnd, (DLGPROC)PrintDialogProc);
if (!g_hAbortPrintDlg)
{
// Error, unable to create abort dialog
goto lblCLEANUP;
}
//
// Set Document title to Root string
//
tvi.mask = TVIF_CHILDREN | TVIF_TEXT;
tvi.hItem = hStartTree;
tvi.pszText = pstrBuff;
tvi.cchTextMax = pci.dwCharsPerLine;
if (TreeView_GetItem(hTreeWnd, &tvi))
{
SetWindowText(g_hAbortPrintDlg, pstrBuff);
SetAbortProc(pd.hDC, AbortProc);
cchLen = static_cast<DWORD>(_tcsclen(pstrBuff));
DWORD cbSize = (cchLen + 1) * sizeof(TCHAR);
pstrTitle = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, cbSize);
if (!pstrTitle)
{
// Error, not enough memory
goto lblCLEANUP;
}
strcpy_s(pstrTitle, cbSize, pstrBuff);
pstrTitle[cchLen] = 0;
}
else
{
SetWindowText(g_hAbortPrintDlg, TEXT("Unknown"));
SetAbortProc(pd.hDC, AbortProc);
cchLen = static_cast<DWORD>(_tcsclen(TEXT("Unknown")));
DWORD cbSize = (cchLen + 1) * sizeof(TCHAR);
pstrTitle = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, cbSize);
if (!pstrTitle)
{
// Error, not enough memory
goto lblCLEANUP;
}
strcpy_s(pstrTitle, cbSize, TEXT("Unknown"));
pstrTitle[cchLen] = 0;
}
// Initialize Document Structure
di.cbSize = sizeof(DOCINFO);
di.lpszDocName = pstrTitle;
di.lpszOutput = nullptr;
di.lpszDatatype = nullptr;
di.fwType = 0;
// Start document
if (g_PrintToFile)
{
const TCHAR* pstrFile;
TCHAR buff[MAX_PATH];
if (strlen(g_PrintToFilePath) > 0)
pstrFile = g_PrintToFilePath;
else
{
HRESULT hr = SHGetFolderPath(nullptr, CSIDL_DESKTOP, nullptr, SHGFP_TYPE_CURRENT, buff);
if (SUCCEEDED(hr))
{
strcat_s(buff, MAX_PATH, TEXT("\\dxview.log"));
pstrFile = buff;
}
else
pstrFile = TEXT("dxview.log");
}
g_FileHandle = CreateFile(pstrFile, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
}
else
if (StartDoc(pd.hDC, &di) < 0)
{
// Error, StartDoc failed
goto lblCLEANUP;
}
fStartDoc = TRUE;
// Print requested number of copies
fResult = FALSE;
for (dwCurrCopy = 0; dwCurrCopy < (DWORD)pd.nCopies; dwCurrCopy++)
{
pci.hCurrTree = hStartTree;
pci.fStartPage = TRUE;
pci.dwCurrIndent = 0;
// Note: We are basically doing an pre-order traversal
// of the Tree. Printing the current node
// before moving on to it's children or siblings
fDone = FALSE;
while (!fDone)
{
// Check if we need to start a new page
if (FAILED(PrintStartPage(&pci)))
{
goto lblCLEANUP;
}
//
// Get Current Item in Tree
// and print it's text info and associated Node caps
//
tvi.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
tvi.hItem = pci.hCurrTree;
tvi.pszText = pstrBuff;
tvi.lParam = 0;
tvi.cchTextMax = pci.dwCharsPerLine;
if (TreeView_GetItem(hTreeWnd, &tvi))
{
cchLen = static_cast<DWORD>(_tcslen(pstrBuff));
cchLen = __min(cchLen, buffSize);
if (cchLen > 0)
{
int xOffset = (int)(pci.dwCurrIndent * DEF_TAB_SIZE * pci.dwCharWidth);
int yOffset = (int)(pci.dwLineHeight * pci.dwCurrLine);
// Print this line
if (FAILED(PrintLine(xOffset, yOffset, pstrBuff, cchLen, &pci)))
{
goto lblCLEANUP;
}
// Advance to next line in page
if (FAILED(PrintNextLine(&pci)))
{
goto lblCLEANUP;
}
// Check if there is any additional node info
// that needs to be printed
if (tvi.lParam != 0)
{
NODEINFO* pni = (NODEINFO*)tvi.lParam;
if (pni && pni->fnDisplayCallback)
{
// Force indent to offset node info from tree info
pci.dwCurrIndent += 2;
if (pni->bUseLParam3)
{
if (FAILED(((DISPLAYCALLBACKEX)(pni->fnDisplayCallback))(pni->lParam1, pni->lParam2, pni->lParam3, &pci)))
{
// Error, callback failed
goto lblCLEANUP;
}
}
else
{
if (FAILED(pni->fnDisplayCallback(pni->lParam1, pni->lParam2, &pci)))
{
// Error, callback failed
goto lblCLEANUP;
}
}
// Recover indent
pci.dwCurrIndent -= 2;
}
}
}
} // End if TreeView_GetItem()
//
// Get Next Item in tree
//
// Get first child, if any
if (tvi.cChildren)
{
// Get First child
HTREEITEM hTempTree = TreeView_GetChild(hTreeWnd, pci.hCurrTree);
if (hTempTree)
{
// Increase Indentation
pci.dwCurrIndent++;
pci.hCurrTree = hTempTree;
continue;
}
}
// Exit, if we are the root
if (pci.hCurrTree == hRoot)
{
// We have reached the root, so stop
PrintEndPage(&pci);
fDone = TRUE;
continue;
}
// Get next sibling in the chain
HTREEITEM hTempTree = TreeView_GetNextSibling(hTreeWnd, pci.hCurrTree);
if (hTempTree)
{
pci.hCurrTree = hTempTree;
continue;
}
// Get next Ancestor yet to be processed
// (uncle, granduncle, etc)
fFindNext = FALSE;
while (!fFindNext)
{
hTempTree = TreeView_GetParent(hTreeWnd, pci.hCurrTree);
if ((!hTempTree) || (hTempTree == hRoot))
{
// We have reached the root, so stop
PrintEndPage(&pci);
fDone = TRUE;
fFindNext = TRUE;
}
else
{
// Move up to the parent
pci.hCurrTree = hTempTree;
// Decrease Indentation
pci.dwCurrIndent--;
// Since we have already processed the parent
// we want to get the uncle/aunt node
hTempTree = TreeView_GetNextSibling(hTreeWnd, pci.hCurrTree);
if (hTempTree)
{
// Found a non-processed node
pci.hCurrTree = hTempTree;
fFindNext = TRUE;
}
}
}
} // End While (! fDone)
} // End for num copies
// Success
fResult = TRUE;
lblCLEANUP:
// End Document
if (fStartDoc)
{
if (g_PrintToFile)
{
CloseHandle(g_FileHandle);
}
else
EndDoc(pd.hDC);
fStartDoc = FALSE;
}
// Re-enable main window
// Note: Do this before destroying abort dialog
// otherwise the main window loses focus
if (fDisableWindow)
{
EnableWindow(hWnd, TRUE);
fDisableWindow = FALSE;
}
// Destroy Abort Dialog
if (g_hAbortPrintDlg)
{
DestroyWindow(g_hAbortPrintDlg);
g_hAbortPrintDlg = nullptr;
}
// Free title memory
if (pstrTitle)
{
HeapFree(hHeap, 0, (VOID*)pstrTitle);
pstrTitle = nullptr;
di.lpszDocName = nullptr;
}
// Free buffer memory
if (pstrBuff)
{
HeapFree(hHeap, 0, (VOID*)pstrBuff);
pstrBuff = nullptr;
}
// Cleanup printer DC
if (pd.hDC)
{
DeleteDC(pd.hDC);
pd.hDC = nullptr;
}
return fResult;
}
}
//-----------------------------------------------------------------------------
// Name: DXView_OnPrint()
// Desc: Print user defined stuff
//-----------------------------------------------------------------------------
BOOL DXView_OnPrint(HWND hWnd, HWND hTreeWnd, BOOL bPrintAll)
{
HINSTANCE hInstance;
HTREEITEM hRoot;
// Check Parameters
if (!hWnd || !hTreeWnd)
return FALSE;
// Get hInstance
hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
if (!hInstance)
return FALSE;
if (bPrintAll)
{
hRoot = nullptr;
}
else
{
hRoot = TreeView_GetSelection(hTreeWnd);
if (!hRoot)
DoMessage(IDS_PRINT_WARNING, IDS_PRINT_NEEDSELECT);
}
g_PrintToFile = FALSE;
// Do actual printing
return PrintTreeStats(hInstance, hWnd, hTreeWnd, hRoot);
}
//-----------------------------------------------------------------------------
// Name: DXView_OnFile()
//-----------------------------------------------------------------------------
BOOL DXView_OnFile(HWND hWnd, HWND hTreeWnd, BOOL bPrintAll)
{
HINSTANCE hInstance;
HTREEITEM hRoot;
// Check Parameters
if (!hWnd || !hTreeWnd)
return FALSE;
// Get hInstance
hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
if (!hInstance)
return FALSE;
if (bPrintAll)
{
hRoot = nullptr;
}
else
{
hRoot = TreeView_GetSelection(hTreeWnd);
if (!hRoot)
DoMessage(IDS_PRINT_WARNING, IDS_PRINT_NEEDSELECT);
}
g_PrintToFile = TRUE;
// Do actual printing
return PrintTreeStats(hInstance, hWnd, hTreeWnd, hRoot);
}
//-----------------------------------------------------------------------------
// Name: PrintLine()
// Desc: Prints text to page at specified location
//-----------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT PrintLine(int xOffset, int yOffset, LPCTSTR pszBuff, size_t cchBuff,
PRINTCBINFO* pci)
{
if (!pci)
return E_FAIL;
// Check if we need to start a new page
if (FAILED(PrintStartPage(pci)))
return E_FAIL;
// Return if there's nothing to print
if (!pszBuff || !cchBuff)
return S_OK;
// Print text out to buffer current line
if (g_PrintToFile)
{
DWORD dwDummy;
TCHAR Temp[80];
int offset = (xOffset - iLastXPos) / pci->dwCharWidth;
if (offset < 0 || offset >= 80)
return S_OK;
memset(Temp, ' ', sizeof(TCHAR) * 79);
Temp[offset] = 0;
WriteFile(g_FileHandle, Temp, (xOffset - iLastXPos) / pci->dwCharWidth, &dwDummy, nullptr);
iLastXPos = (xOffset - iLastXPos) + (pci->dwCharWidth * static_cast<DWORD>(cchBuff));
WriteFile(g_FileHandle, pszBuff, static_cast<DWORD>(cchBuff), &dwDummy, nullptr);
}
else
{
TextOut(pci->hdcPrint, xOffset, yOffset, pszBuff, static_cast<int>(cchBuff));
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: PrintNextLine()
// Desc: Advance to next line on page
//-----------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT PrintNextLine(PRINTCBINFO* pci)
{
if (g_PrintToFile)
{
DWORD dwDummy;
WriteFile(g_FileHandle, "\r\n", 2, &dwDummy, nullptr);
iLastXPos = 0;
return S_OK;
}
if (!pci)
return E_FAIL;
pci->dwCurrLine++;
// Check if we need to end the page
if (pci->dwCurrLine >= pci->dwLinesPerPage)
return PrintEndPage(pci);
return S_OK;
}