// ThumbCtl.cpp : Implementation of CThumbCtl #include "priv.h" #include "shdguid.h" #include "strsafe.h" const CLSID CLSID_ThumbCtlOld = {0x1d2b4f40,0x1f10,0x11d1,{0x9e,0x88,0x00,0xc0,0x4f,0xdc,0xab,0x92}}; // retired from service, so made private // global // for LoadLibrary/GetProcAddress on SHGetDiskFreeSpaceA typedef BOOL (__stdcall * PFNSHGETDISKFREESPACE)(LPCTSTR pszVolume, ULARGE_INTEGER *pqwFreeCaller, ULARGE_INTEGER *pqwTot, ULARGE_INTEGER *pqwFree); const TCHAR * const g_szWindowClassName = TEXT("MSIE4.0 Webvw.DLL ThumbCtl"); STDAPI IsSafePage(IUnknown *punkSite) { // Return S_FALSE if we don't have a host site since we have no way of doing a // security check. This is as far as VB 5.0 apps get. if (!punkSite) return S_FALSE; HRESULT hr = E_ACCESSDENIED; CComPtr spDefViewSafety; if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IDefViewSafety, &spDefViewSafety)))) { hr = spDefViewSafety->IsSafePage(); } return hr; } // === INTERFACE === // *** IThumbCtl *** STDMETHODIMP CThumbCtl::displayFile(BSTR bsFileName, VARIANT_BOOL *pfSuccess) { HRESULT hr = E_FAIL; *pfSuccess = VARIANT_FALSE; if (S_OK != _IsSafe()) { // We don't trust this host, so we are going to not carry // out the action. We are going to return E_ACCESSDENIED so they can't // determine if the path exists or not. // return S_FALSE -- // this is because webvw has a customization feature letting people choose // a intranet htt file as their folder.htt, but for security we generally need // to block random intranet web pages from calling this method. This will break // a case where the customization is done on a NT machine, but the user tries to // view it using Millennium, it will not show any image and pop up error messages // if we return E_ACCESSDENIED. hr = S_FALSE; } else { // Cancel pending bitmap request if in thumbnail mode && have a functioning IThumbnail // && haven't yet received our bitmap if(!m_fRootDrive && m_fHaveIThumbnail && m_hbm == NULL) { m_pthumb->GetBitmap(NULL, 0, 0, 0); } // change ID to catch late bitmap computed ++m_dwThumbnailID; // if already displaying something, refresh if(m_fRootDrive || m_hbm) { if(m_hbm) { DeleteObject(m_hbm); m_hbm = NULL; } FireViewChange(); } // Now work on new thumbnail m_fRootDrive = FALSE; // check for non-empty file name if(bsFileName && bsFileName[0]) { TCHAR szFileName[INTERNET_MAX_URL_LENGTH]; SHUnicodeToTChar(bsFileName, szFileName, ARRAYSIZE(szFileName)); DWORD dwAttrs = GetFileAttributes(szFileName); // Pie Chart if(PathIsRoot(szFileName)) { if(SUCCEEDED(ComputeFreeSpace(szFileName))) { m_fRootDrive = TRUE; *pfSuccess = VARIANT_TRUE; } } // Thumbnail else if(!(dwAttrs & FILE_ATTRIBUTE_DIRECTORY) && !PathIsSlow(szFileName, dwAttrs)) // should really be calling this from Shell32 private functions { if(!m_fInitThumb) { m_fHaveIThumbnail = SUCCEEDED(SetupIThumbnail()); m_fInitThumb = TRUE; } if(m_fHaveIThumbnail) { SIZE size; AtlHiMetricToPixel(&m_sizeExtent, &size); if(EVAL(size.cx > 0 && size.cy > 0)) { if(SUCCEEDED(m_pthumb->GetBitmap(bsFileName, m_dwThumbnailID, size.cx, size.cy))) { *pfSuccess = VARIANT_TRUE; } } } } } hr = S_OK; } return hr; } // displayFile STDMETHODIMP CThumbCtl::haveThumbnail(VARIANT_BOOL *pfRes) { HRESULT hr; *pfRes = VARIANT_FALSE; if (S_OK != _IsSafe()) { // We don't trust this host, so we are going to not carry // out the action. We are going to return E_ACCESSDENIED so they can't // determine if the path exists or not. // return S_FALSE -- // this is because webvw has a customization feature letting people choose // a intranet htt file as their folder.htt, but for security we generally need // to block random intranet web pages from calling this method. This will break // a case where the customization is done on a NT machine, but the user tries to // view it using Millennium, it will not show any image and pop up error messages // if we return E_ACCESSDENIED. hr = S_FALSE; } else { *pfRes = (m_fRootDrive || m_hbm) ? VARIANT_TRUE : VARIANT_FALSE; hr = S_OK; } return hr; } STDMETHODIMP CThumbCtl::get_freeSpace(BSTR *pbs) { HRESULT hr; if (S_OK != _IsSafe()) { // We don't trust this host, so we are going to not carry // out the action. We are going to return E_ACCESSDENIED so they can't // determine if the path exists or not. *pbs = SysAllocString(L""); hr = (*pbs) ? S_FALSE : E_OUTOFMEMORY; } else { get_GeneralSpace(m_dwlFreeSpace, pbs); hr = S_OK; } return hr; } // get_freeSpace STDMETHODIMP CThumbCtl::get_usedSpace(BSTR *pbs) { HRESULT hr; if (S_OK != _IsSafe()) { // We don't trust this host, so we are going to not carry // out the action. We are going to return E_ACCESSDENIED so they can't // determine if the path exists or not. *pbs = SysAllocString(L""); hr = (*pbs) ? S_FALSE : E_OUTOFMEMORY; } else { get_GeneralSpace(m_dwlUsedSpace, pbs); hr = S_OK; } return hr; } // get_usedSpace STDMETHODIMP CThumbCtl::get_totalSpace(BSTR *pbs) { HRESULT hr; if (S_OK != _IsSafe()) { // We don't trust this host, so we are going to not carry // out the action. We are going to return E_ACCESSDENIED so they can't // determine if the path exists or not. *pbs = SysAllocString(L""); hr = (*pbs) ? S_FALSE : E_OUTOFMEMORY; } else { get_GeneralSpace(m_dwlTotalSpace, pbs); hr = S_OK; } return hr; } // get_totalSpace // *** IObjectSafety *** STDMETHODIMP CThumbCtl::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions) { ATLTRACE(_T("IObjectSafetyImpl::GetInterfaceSafetyOptions\n")); if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL) return E_POINTER; HRESULT hr = S_OK; if (riid == IID_IDispatch) { *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA; *pdwEnabledOptions = m_dwCurrentSafety & (INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA); } else { *pdwSupportedOptions = 0; *pdwEnabledOptions = 0; hr = E_NOINTERFACE; } return hr; } // *** ISupportsErrorInfo *** STDMETHODIMP CThumbCtl::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IThumbCtl, }; for (int i=0;i::TranslateAccelerator(pMsg); // If we did not handle this and if it is a tab (and we are not getting it in a cycle), forward it to trident, if present. if (hres != S_OK && pMsg && (pMsg->wParam == VK_TAB || pMsg->wParam == VK_F6) && m_spClientSite) { if (GetFocus() != m_hwnd) { ::SetFocus(m_hwnd); hres = S_OK; } else { IOleControlSite* pocs = NULL; if (SUCCEEDED(m_spClientSite->QueryInterface(IID_IOleControlSite, (void **)&pocs))) { DWORD grfModifiers = 0; if (GetKeyState(VK_SHIFT) & 0x8000) { grfModifiers |= 0x1; //KEYMOD_SHIFT } if (GetKeyState(VK_CONTROL) & 0x8000) { grfModifiers |= 0x2; //KEYMOD_CONTROL; } if (GetKeyState(VK_MENU) & 0x8000) { grfModifiers |= 0x4; //KEYMOD_ALT; } m_fTabRecieved = TRUE; hres = pocs->TranslateAccelerator(pMsg, grfModifiers); m_fTabRecieved = FALSE; } } } } return hres; } // === PUBLIC FUNCTIONS === // CONSTRUCTOR/DESTRUCTOR CThumbCtl::CThumbCtl(void): m_fRootDrive(FALSE), m_fInitThumb(FALSE), m_fHaveIThumbnail(FALSE), m_pthumb(NULL), m_hwnd(NULL), m_hbm(NULL), m_dwThumbnailID(0), m_dwlFreeSpace(0), m_dwlUsedSpace(0), m_dwlTotalSpace(0), m_dwUsedSpacePer1000(0), m_fUseSystemColors(TRUE) { m_fTabRecieved = FALSE; } CThumbCtl::~CThumbCtl(void) { if(m_hbm) { DeleteObject(m_hbm); m_hbm = NULL; } if(m_pthumb) { m_pthumb->Release(); // will cancel pending bitmap requests m_pthumb = NULL; } if(m_hwnd) { EVAL(::DestroyWindow(m_hwnd)); m_hwnd = NULL; } } // === PRIVATE FUNCTIONS === // Thumbnail drawing functions HRESULT CThumbCtl::SetupIThumbnail(void) { HRESULT hr = E_FAIL; // Create Window Class WNDCLASS wc; if (!::GetClassInfoWrap(_Module.GetModuleInstance(), g_szWindowClassName, &wc)) { wc.style = 0; wc.lpfnWndProc = CThumbCtl::WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = _Module.GetModuleInstance(); wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = g_szWindowClassName; RegisterClass(&wc); } m_hwnd = CreateWindow(g_szWindowClassName, NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, _Module.GetModuleInstance(), this); if(m_hwnd) { if(SUCCEEDED(CoCreateInstance(CLSID_Thumbnail, NULL, CLSCTX_INPROC_SERVER, IID_IThumbnail, (void **)&m_pthumb))) { if(SUCCEEDED(m_pthumb->Init(m_hwnd, WM_HTML_BITMAP))) { hr = S_OK; } } if(FAILED(hr)) { EVAL(::DestroyWindow(m_hwnd)); m_hwnd = NULL; } } return hr; } LRESULT CALLBACK CThumbCtl::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CThumbCtl *ptc = (CThumbCtl *)::GetWindowLongPtr(hWnd, GWLP_USERDATA); switch(uMsg) { case WM_CREATE: { ptc = (CThumbCtl *)((CREATESTRUCT *)lParam)->lpCreateParams; ::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LPARAM)ptc); } break; case WM_HTML_BITMAP: // check that ptc is still alive, bitmap is current using ID if(ptc && ptc->m_dwThumbnailID == wParam) { // ptc->displayFile() should've destroyed old bitmap already, but doesn't hurt to check. if(!EVAL(ptc->m_hbm == NULL)) { DeleteObject(ptc->m_hbm); } ptc->m_hbm = (HBITMAP)lParam; ptc->InvokeOnThumbnailReady(); } else if(lParam) { DeleteObject((HBITMAP)lParam); } break; case WM_DESTROY: // ignore late messages if(ptc) { MSG msg; while(PeekMessage(&msg, hWnd, WM_HTML_BITMAP, WM_HTML_BITMAP, PM_REMOVE)) { if(msg.lParam) { DeleteObject((HBITMAP)msg.lParam); } } ::SetWindowLongPtr(hWnd, GWLP_USERDATA, NULL); } break; default: return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } // Pie Chart functions HRESULT CThumbCtl::ComputeFreeSpace(LPTSTR pszFileName) { ULARGE_INTEGER qwFreeCaller; // use this for free space -- this will take into account disk quotas and such on NT ULARGE_INTEGER qwTotal; ULARGE_INTEGER qwFree; // unused static PFNSHGETDISKFREESPACE pfnSHGetDiskFreeSpace = NULL; if (NULL == pfnSHGetDiskFreeSpace) { HINSTANCE hinstShell32 = LoadLibrary(TEXT("SHELL32.DLL")); if (hinstShell32) { #ifdef UNICODE pfnSHGetDiskFreeSpace = (PFNSHGETDISKFREESPACE)GetProcAddress(hinstShell32, "SHGetDiskFreeSpaceExW"); #else pfnSHGetDiskFreeSpace = (PFNSHGETDISKFREESPACE)GetProcAddress(hinstShell32, "SHGetDiskFreeSpaceExA"); #endif } } // Compute free & total space and check for valid results // if have a fn pointer call SHGetDiskFreeSpaceA if( pfnSHGetDiskFreeSpace && pfnSHGetDiskFreeSpace(pszFileName, &qwFreeCaller, &qwTotal, &qwFree) ) { m_dwlFreeSpace = qwFreeCaller.QuadPart; m_dwlTotalSpace = qwTotal.QuadPart; m_dwlUsedSpace = m_dwlTotalSpace - m_dwlFreeSpace; if ((m_dwlTotalSpace > 0) && (m_dwlFreeSpace <= m_dwlTotalSpace)) { // some special cases require interesting treatment if(m_dwlTotalSpace == 0 || m_dwlFreeSpace == m_dwlTotalSpace) { m_dwUsedSpacePer1000 = 0; } else if(m_dwlFreeSpace == 0) { m_dwUsedSpacePer1000 = 1000; } else { // not completely full or empty m_dwUsedSpacePer1000 = (DWORD)(m_dwlUsedSpace * 1000 / m_dwlTotalSpace); // Trick: if user has extremely little free space, the user expects to still see // a tiny free slice -- not a full drive. Similarly for almost free drive. if(m_dwUsedSpacePer1000 == 0) { m_dwUsedSpacePer1000 = 1; } else if(m_dwUsedSpacePer1000 == 1000) { m_dwUsedSpacePer1000 = 999; } } return S_OK; } } return E_FAIL; } // 32 should be plenty #define STRLENGTH_SPACE 32 HRESULT CThumbCtl::get_GeneralSpace(DWORDLONG dwlSpace, BSTR *pbs) { ASSERT(pbs != NULL); WCHAR wszText[STRLENGTH_SPACE]; if(m_fRootDrive) { StrFormatByteSizeW(dwlSpace, wszText, ARRAYSIZE(wszText)); *pbs = SysAllocString(wszText); } else { *pbs = SysAllocString(L""); } return *pbs? S_OK: E_OUTOFMEMORY; } HRESULT CThumbCtl::Draw3dPie(HDC hdc, LPRECT lprc, DWORD dwPer1000, const COLORREF *lpColors) { ASSERT(lprc != NULL && lpColors != NULL); enum { COLOR_UP = 0, COLOR_DN, COLOR_UPSHADOW, COLOR_DNSHADOW, COLOR_NUM // #of entries }; // The majority of this code came from "drawpie.c" const LONG c_lShadowScale = 6; // ratio of shadow depth to height const LONG c_lAspectRatio = 2; // ratio of width : height of ellipse // We make sure that the aspect ratio of the pie-chart is always preserved // regardless of the shape of the given rectangle // Stabilize the aspect ratio now... LONG lHeight = lprc->bottom - lprc->top; LONG lWidth = lprc->right - lprc->left; LONG lTargetHeight = (lHeight * c_lAspectRatio <= lWidth? lHeight: lWidth / c_lAspectRatio); LONG lTargetWidth = lTargetHeight * c_lAspectRatio; // need to adjust because w/c * c isn't always == w // Shrink the rectangle on both sides to the correct size lprc->top += (lHeight - lTargetHeight) / 2; lprc->bottom = lprc->top + lTargetHeight; lprc->left += (lWidth - lTargetWidth) / 2; lprc->right = lprc->left + lTargetWidth; // Compute a shadow depth based on height of the image LONG lShadowDepth = lTargetHeight / c_lShadowScale; // check dwPer1000 to ensure within bounds if(dwPer1000 > 1000) dwPer1000 = 1000; // Now the drawing function int cx, cy, rx, ry, x, y; int uQPctX10; RECT rcItem; HRGN hEllRect, hEllipticRgn, hRectRgn; HBRUSH hBrush, hOldBrush; HPEN hPen, hOldPen; rcItem = *lprc; rcItem.left = lprc->left; rcItem.top = lprc->top; rcItem.right = lprc->right - rcItem.left; rcItem.bottom = lprc->bottom - rcItem.top - lShadowDepth; rx = rcItem.right / 2; cx = rcItem.left + rx - 1; ry = rcItem.bottom / 2; cy = rcItem.top + ry - 1; if (rx<=10 || ry<=10) { return S_FALSE; } rcItem.right = rcItem.left+2*rx; rcItem.bottom = rcItem.top+2*ry; /* Translate to first quadrant of a Cartesian system */ uQPctX10 = (dwPer1000 % 500) - 250; if (uQPctX10 < 0) { uQPctX10 = -uQPctX10; } /* Calc x and y. I am trying to make the area be the right percentage. ** I don't know how to calculate the area of a pie slice exactly, so I ** approximate it by using the triangle area instead. */ // NOTE-- *** in response to the above comment *** // Calculating the area of a pie slice exactly is actually very // easy by conceptually rescaling into a circle but the complications // introduced by having to work in fixed-point arithmetic makes it // unworthwhile to code this-- CemP if (uQPctX10 < 120) { x = IntSqrt(((DWORD)rx*(DWORD)rx*(DWORD)uQPctX10*(DWORD)uQPctX10) /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10))); y = IntSqrt(((DWORD)rx*(DWORD)rx-(DWORD)x*(DWORD)x)*(DWORD)ry*(DWORD)ry/((DWORD)rx*(DWORD)rx)); } else { y = IntSqrt((DWORD)ry*(DWORD)ry*(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10) /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10))); x = IntSqrt(((DWORD)ry*(DWORD)ry-(DWORD)y*(DWORD)y)*(DWORD)rx*(DWORD)rx/((DWORD)ry*(DWORD)ry)); } /* Switch on the actual quadrant */ switch (dwPer1000 / 250) { case 1: y = -y; break; case 2: break; case 3: x = -x; break; default: // case 0 and case 4 x = -x; y = -y; break; } /* Now adjust for the center. */ x += cx; y += cy; // Hack to get around bug in NTGDI x = x < 0 ? 0 : x; /* Draw the shadows using regions (to reduce flicker). */ hEllipticRgn = CreateEllipticRgnIndirect(&rcItem); OffsetRgn(hEllipticRgn, 0, lShadowDepth); hEllRect = CreateRectRgn(rcItem.left, cy, rcItem.right, cy+lShadowDepth); hRectRgn = CreateRectRgn(0, 0, 0, 0); CombineRgn(hRectRgn, hEllipticRgn, hEllRect, RGN_OR); OffsetRgn(hEllipticRgn, 0, -(int)lShadowDepth); CombineRgn(hEllRect, hRectRgn, hEllipticRgn, RGN_DIFF); /* Always draw the whole area in the free shadow/ */ hBrush = CreateSolidBrush(lpColors[COLOR_DNSHADOW]); if (hBrush) { FillRgn(hdc, hEllRect, hBrush); DeleteObject(hBrush); } /* Draw the used shadow only if the disk is at least half used. */ if (dwPer1000>500 && (hBrush = CreateSolidBrush(lpColors[COLOR_UPSHADOW]))!=NULL) { DeleteObject(hRectRgn); hRectRgn = CreateRectRgn(x, cy, rcItem.right, lprc->bottom); CombineRgn(hEllipticRgn, hEllRect, hRectRgn, RGN_AND); FillRgn(hdc, hEllipticRgn, hBrush); DeleteObject(hBrush); } DeleteObject(hRectRgn); DeleteObject(hEllipticRgn); DeleteObject(hEllRect); hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME)); hOldPen = (HPEN__*) SelectObject(hdc, hPen); // if per1000 is 0 or 1000, draw full elipse, otherwise, also draw a pie section. // we might have a situation where per1000 isn't 0 or 1000 but y == cy due to approx error, // so make sure to draw the ellipse the correct color, and draw a line (with Pie()) to // indicate not completely full or empty pie. hBrush = CreateSolidBrush(lpColors[dwPer1000 < 500 && y == cy && x < cx? COLOR_DN: COLOR_UP]); hOldBrush = (HBRUSH__*) SelectObject(hdc, hBrush); Ellipse(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); if(dwPer1000 != 0 && dwPer1000 != 1000) { // display small sub-section of ellipse for smaller part hBrush = CreateSolidBrush(lpColors[COLOR_DN]); hOldBrush = (HBRUSH__*) SelectObject(hdc, hBrush); // NTRAID#087993-2000/02/16-aidanl: Pie may malfunction when y approaches cy // If y == cy (when the disk is almost full)and if x approaches // rcItem.left, on win9x, Pie malfunctions. It draws the larger portion // of the pie, instead of the smaller portion. We work around it by // adding 1 to y. Pie(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, rcItem.left, cy, x, (y == cy) ? (y + 1) : y); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); } Arc(hdc, rcItem.left, rcItem.top+lShadowDepth, rcItem.right - 1, rcItem.bottom+lShadowDepth - 1, rcItem.left, cy+lShadowDepth, rcItem.right, cy+lShadowDepth-1); MoveToEx(hdc, rcItem.left, cy, NULL); LineTo(hdc, rcItem.left, cy+lShadowDepth); MoveToEx(hdc, rcItem.right-1, cy, NULL); LineTo(hdc, rcItem.right-1, cy+lShadowDepth); if(dwPer1000 > 500 && dwPer1000 < 1000) { MoveToEx(hdc, x, y, NULL); LineTo(hdc, x, y+lShadowDepth); } SelectObject(hdc, hOldPen); DeleteObject(hPen); return S_OK; // Everything worked fine } // Draw3dPie // General functions void CThumbCtl::InvokeOnThumbnailReady(void) { // Fire off "OnThumbnailReady" event to our connection points to indicate that // either a thumbnail has been computed or we have no thumbnail for this file. DISPPARAMS dp = {0, NULL, 0, NULL}; // no parameters IUnknown **pp = NULL; // traverses connection points, where it is interpreted as IDispatch* Lock(); for(pp = m_vec.begin(); pp < m_vec.end(); ++pp) { if(pp) { ((IDispatch *)*pp)->Invoke(DISPID_ONTHUMBNAILREADY, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dp, NULL, NULL, NULL); } } Unlock(); FireViewChange(); } HRESULT CThumbCtl::OnDraw(ATL_DRAWINFO& di) { HDC hdc = di.hdcDraw; RECT rc = *(LPRECT)di.prcBounds; HRESULT hr = S_OK; if(m_fRootDrive || m_hbm) { HPALETTE hpal = NULL; // Create pallete appropriate for this HDC if(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { hpal = SHCreateShellPalette(hdc); HPALETTE hpalOld = SelectPalette(hdc, hpal, TRUE); RealizePalette(hdc); // Old one needs to be selected back in SelectPalette(hdc, hpalOld, TRUE); } if(m_fRootDrive) { // Draw a pie chart if(m_fUseSystemColors) { // system colors can change! m_acrChartColors[PIE_USEDCOLOR] = GetSysColor(COLOR_3DFACE); m_acrChartColors[PIE_FREECOLOR] = GetSysColor(COLOR_3DHILIGHT); m_acrChartColors[PIE_USEDSHADOW] = GetSysColor(COLOR_3DSHADOW); m_acrChartColors[PIE_FREESHADOW] = GetSysColor(COLOR_3DFACE); } else if(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { // Call GetNearestColor on the colors to make sure they're on the palette // Of course, system colors ARE on the palette (I think) DWORD dw = 0; // index for(dw = 0; dw < PIE_NUM; dw++) { m_acrChartColors[dw] = GetNearestColor(hdc, m_acrChartColors[dw]); } } hr = Draw3dPie(hdc, &rc, m_dwUsedSpacePer1000, m_acrChartColors); } else { // Draw the Thumbnail bitmap HDC hdcBitmap = CreateCompatibleDC(hdc); if (hdcBitmap) { BITMAP bm; SelectObject(hdcBitmap, m_hbm); GetObject(m_hbm, SIZEOF(bm), &bm); if(bm.bmWidth == rc.right - rc.left && bm.bmHeight == rc.bottom - rc.top) { BitBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcBitmap, 0, 0, SRCCOPY); } else { SetStretchBltMode(hdc, COLORONCOLOR); StretchBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcBitmap, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); } DeleteDC(hdcBitmap); } } // clean up DC, palette if(hpal) { DeleteObject(hpal); } } else { SelectObject(hdc, GetStockObject(WHITE_PEN)); SelectObject(hdc, GetStockObject(WHITE_BRUSH)); // Just draw a blank rectangle Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); } return hr; }