Copy paste shame
Не думал, что можно настолько тупо копипастить.
Уже что-то около полугода мне не удается привычным способом обновить двайвера ATI Catalyst, установищик падает с сообщением CATALYST™ Install Manager has stopped working
. Грешу на .NET Framework 3.5, но удалять его совершенно нет никакого желания. Приходится извращаться:
c:\ATI\SUPPORT\8-12_vista32_dd_ccc_wdm_enu_72275\Driver
(зависит от версии);Setup.exe -Uninstall -output screen
;Setup.exe -Install -output screen
Редактор диалогов Visual Studio позволяет задать базовый шрифт для диалога.
По умолчанию, это MS Shell Dlg
8-го размера. А что, если я хочу выставить Segoe UI
? Точнее, что будет на системах, где этого шрифта физически нет? Не известно.
Уже понятно, что сию беду нужно, как обычно, писать в коде. Ниже - два универсальных способа решить проблему.
У класса CDialog
имеется следующая функция-член: virtual BOOL CreateIndirect(LPCDLGTEMPLATE, CWnd*, void*); Создание любого диалога проходит через нее, поэтому для изменения шрифта в шаблоне диалога это очень подходящее место. В результате - создаем класс-потомок CDialog
, переопределяем CreateIndirect
, меняем шрифт, вызываем базовый метод. Недостаток этого подхода - все классы диалогов нужно будет наследовать от него. Вариант подходит для нового кода, а если в проекте уже 120 диалогов - что делать?
Хак, а как же еще?
class CFontOccManager : public COccManager { virtual const DLGTEMPLATE *PreCreateDialog(_AFX_OCC_DIALOG_INFO *pOccDialogInfo, const DLGTEMPLATE *pOrigTemplate) { const DLGTEMPLATE *lpNewTemplate = __super::PreCreateDialog(pOccDialogInfo, pOrigTemplate); OSVERSIONINFO osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); if ((osvi.dwMajorVersion >= 6) && !pOccDialogInfo->m_pNewTemplate) { CDialogTemplate dlgTemplate(lpNewTemplate); CString origFontFaceName; WORD wOrigFontSize; dlgTemplate.GetFont(origFontFaceName, wOrigFontSize); NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0); LOGFONT lf = ncm.lfMessageFont; HDC hDC = ::GetDC(NULL); const int logPixelSY = ::GetDeviceCaps(hDC, LOGPIXELSY); ::ReleaseDC(NULL, hDC); const WORD wProposedFontSize = static_cast<WORD>(MulDiv(-lf.lfHeight, 72, logPixelSY)); dlgTemplate.SetFont(lf.lfFaceName, max(wOrigFontSize, wProposedFontSize)); pOccDialogInfo->m_pNewTemplate = reinterpret_cast<DLGTEMPLATE*>(dlgTemplate.Detach()); return(pOccDialogInfo->m_pNewTemplate); } else return(lpNewTemplate); } }; AfxEnableControlContainer(new CFontOccManager());
Класс COccManager
не документирован, но создание диалога проходит в том числе через него. Логика простая:
Segoe UI
);В качестве метрик я взял характеристики lfMessageFont
- LOGFONT structure that contains information about the font used in message boxes
. Message box - это же диалог, остальное не подходит. Код простой, объяснений не требует. Единственный нюанс, я беру максимум из старого размера и посчитанного.
В результате - нормального размера диалоги с обалденным шрифтом. Удачи.
А как же еще назвать пост, если эти классы написаны уже 1,000,000 раз?
#pragma once #include <string> #include <map> #include <boost/any.hpp> namespace win32 { class Exception { protected: typedef std::wstring properties_key_t; typedef std::map<properties_key_t, boost::any> properties_t; protected: static const properties_key_t propertyMessageKey; protected: Exception(); Exception(const std::wstring &message); public: virtual ~Exception() = 0 {}; public: __declspec(property(get=get_Message)) std::wstring Message; virtual std::wstring get_Message() const; protected: properties_t _properties; }; }
#include "Exception.h" namespace win32 { const Exception::properties_key_t Exception::propertyMessageKey(L"msg"); Exception::Exception() { } Exception::Exception(const std::wstring &message) { _properties.insert(std::make_pair(propertyMessageKey, boost::any(message))); } /*virtual*/ std::wstring Exception::get_Message() const { properties_t::const_iterator i = _properties.find(propertyMessageKey); if (i != _properties.end()) return(boost::any_cast<std::wstring>(i->second)); return(std::wstring()); } }
#pragma once #include "Exception.h" #include <Windows.h> #include <string> namespace win32 { class Win32Exception : public virtual Exception { protected: static const properties_key_t propertyErrorCodeKey; public: Win32Exception(DWORD errorCode = GetLastError()); virtual ~Win32Exception(); public: __declspec(property(get=get_ErrorCode)) DWORD ErrorCode; DWORD get_ErrorCode() const; public: static std::wstring FormatMessage(DWORD errorCode); }; }
#include "Win32Exception.h" #include <sstream> #include <cassert> namespace win32 { const Exception::properties_key_t Win32Exception::propertyErrorCodeKey(L"dwErrorCode"); Win32Exception::Win32Exception(DWORD errorCode/* = GetLastError()*/) : Exception(FormatMessage(errorCode)) { _properties.insert(std::make_pair(propertyErrorCodeKey, boost::any(errorCode))); } /*virtual*/ Win32Exception::~Win32Exception() { } DWORD Win32Exception::get_ErrorCode() const { properties_t::const_iterator i = _properties.find(propertyErrorCodeKey); assert(i != _properties.end()); return(boost::any_cast<DWORD>(i->second)); } std::wstring Win32Exception::FormatMessage(DWORD errorCode) { std::wstring returnValue; LPWSTR pwszMsg(NULL); DWORD nChars = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&pwszMsg), 0, NULL ); if (nChars == 0) { std::wstringstream ss; ss << L"Error " << std::showbase << std::hex << errorCode; returnValue = ss.str(); } if (pwszMsg) { int i = wcslen(pwszMsg); while (--i > 0) if (pwszMsg[i] == L'\r' || pwszMsg[i] == L'\n') pwszMsg[i] = L'\0'; returnValue = pwszMsg; ::LocalFree(pwszMsg); } return(returnValue); } }
#pragma once #include "Exception.h" #include <Windows.h> namespace win32 { class COMException : public virtual Exception { private: enum { WCODE_HRESULT_FIRST = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x200), WCODE_HRESULT_LAST = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF + 1, 0) - 1 }; protected: static const properties_key_t propertyHrKey; static const properties_key_t propertyDescriptionKey; static const properties_key_t propertyInterfaceKey; static const properties_key_t propertyHelpContextKey; static const properties_key_t propertyHelpFileKey; static const properties_key_t propertySourceKey; public: COMException(HRESULT hr); virtual ~COMException(); public: __declspec(property(get=get_HResult)) HRESULT HResult; HRESULT get_HResult() const; __declspec(property(get=get_Description)) std::wstring Description; std::wstring get_Description() const; __declspec(property(get=get_Interface)) GUID Interface; GUID get_Interface() const; __declspec(property(get=get_HelpContext)) DWORD HelpContext; DWORD get_HelpContext() const; __declspec(property(get=get_HelpFile)) std::wstring HelpFile; std::wstring get_HelpFile() const; __declspec(property(get=get_Source)) std::wstring Source; std::wstring get_Source() const; public: static std::wstring FormatMessage(HRESULT hr); static HRESULT WCodeToHRESULT(WORD wCode); static WORD HRESULTToWCode(HRESULT hr); }; }
#include "COMException.h" #include <sstream> #include <cassert> namespace win32 { const Exception::properties_key_t COMException::propertyHrKey(L"hr"); const Exception::properties_key_t COMException::propertyDescriptionKey(L"IErrorInfo_Description"); const Exception::properties_key_t COMException::propertyInterfaceKey(L"IErrorInfo_GUID"); const Exception::properties_key_t COMException::propertyHelpContextKey(L"IErrorInfo_HelpContext"); const Exception::properties_key_t COMException::propertyHelpFileKey(L"IErrorInfo_HelpFile"); const Exception::properties_key_t COMException::propertySourceKey(L"IErrorInfo_Source"); COMException::COMException(HRESULT hr) : Exception(FormatMessage(hr)) { _properties.insert(std::make_pair(propertyHrKey, boost::any(hr))); IErrorInfo *pErrorInfo(NULL); if (::GetErrorInfo(0, &pErrorInfo) == S_OK) { BSTR bstr; if (SUCCEEDED(pErrorInfo->GetDescription(&bstr))) { _properties.insert(std::make_pair(propertyDescriptionKey, boost::any(std::wstring(bstr)))); SysFreeString(bstr); } GUID guid; if (SUCCEEDED(pErrorInfo->GetGUID(&guid))) { _properties.insert(std::make_pair(propertyInterfaceKey, boost::any(guid))); } DWORD dw; if (SUCCEEDED(pErrorInfo->GetHelpContext(&dw))) { _properties.insert(std::make_pair(propertyHelpContextKey, boost::any(dw))); } if (SUCCEEDED(pErrorInfo->GetHelpFile(&bstr))) { _properties.insert(std::make_pair(propertyHelpFileKey, boost::any(std::wstring(bstr)))); SysFreeString(bstr); } if (SUCCEEDED(pErrorInfo->GetSource(&bstr))) { _properties.insert(std::make_pair(propertySourceKey, boost::any(std::wstring(bstr)))); SysFreeString(bstr); } pErrorInfo->Release(); } } /*virtual*/ COMException::~COMException() { } HRESULT COMException::get_HResult() const { properties_t::const_iterator i = _properties.find(propertyHrKey); assert(i != _properties.end()); return(boost::any_cast<HRESULT>(i->second)); } std::wstring COMException::get_Description() const { properties_t::const_iterator i = _properties.find(propertyDescriptionKey); if (i != _properties.end()) return(boost::any_cast<std::wstring>(i->second)); else return(std::wstring()); } GUID COMException::get_Interface() const { properties_t::const_iterator i = _properties.find(propertyInterfaceKey); if (i != _properties.end()) return(boost::any_cast<GUID>(i->second)); else return(GUID_NULL); } DWORD COMException::get_HelpContext() const { properties_t::const_iterator i = _properties.find(propertyHelpContextKey); if (i != _properties.end()) return(boost::any_cast<DWORD>(i->second)); else return(0); } std::wstring COMException::get_HelpFile() const { properties_t::const_iterator i = _properties.find(propertyHelpFileKey); if (i != _properties.end()) return(boost::any_cast<std::wstring>(i->second)); else return(std::wstring()); } std::wstring COMException::get_Source() const { properties_t::const_iterator i = _properties.find(propertySourceKey); if (i != _properties.end()) return(boost::any_cast<std::wstring>(i->second)); else return(std::wstring()); } std::wstring COMException::FormatMessage(HRESULT hr) { std::wstring returnValue; LPWSTR pwszMsg(NULL); DWORD nChars = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, static_cast<DWORD>(hr), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&pwszMsg), 0, NULL ); if (nChars == 0) { std::wstringstream ss; WORD wCode = HRESULTToWCode(hr); if (wCode != 0) ss << L"IDispatch error #" << wCode; else ss << L"Unknown error " << std::showbase << std::hex << hr; returnValue = ss.str(); } if (pwszMsg) { int i = wcslen(pwszMsg); while (--i > 0) if (pwszMsg[i] == L'\r' || pwszMsg[i] == L'\n') pwszMsg[i] = L'\0'; returnValue = pwszMsg; ::LocalFree(pwszMsg); } return(returnValue); } HRESULT COMException::WCodeToHRESULT(WORD wCode) { return (wCode >= 0xFE00 ? WCODE_HRESULT_LAST : WCODE_HRESULT_FIRST + wCode); } WORD COMException::HRESULTToWCode(HRESULT hr) { return((hr >= WCODE_HRESULT_FIRST && hr <= WCODE_HRESULT_LAST) ? static_cast<WORD>(hr - WCODE_HRESULT_FIRST) : 0); } }
Очередная версия BITS Manager. Окно Job profile опять пострадало
- появились закладки с разными частями профиля задачи. Исправил несколько ошибок. Заголовок списка задач обзавелся менюхой и возможностями по группировке, сортировке и т.д. Появилась возможность менять настройки безопасности HTTP, а также устанавливать дополнительные HTTP заголовки.
Download BITS Manager v1.02.0003
Next version: BITS Manager v1.02.0004.
Background Intelligent Transfer Service - сложная и заковыристая часть Windows. Сложная она потому, что малоизвестная, малоиспользуемая простыми пользователями, слабо документированная и при этом я насчитал уже 5 ее версий! Думаю, основная причина ее создания - Windows Update. Она же - основная причина появления новых версий и такого программного интерфейса, как мы имеем сйечас.
Недавно я разбирался с настройками proxy для задачи, которые задаются методом IBackgroundCopyJob::SetProxySettings
. Из документации можно предположить, что в параметр pProxyList
можно передать сроку следующего вида: [protocol=][protocol://]server[:port] [protocol=][protocol://]server[:port] ...
. О точном формате строки и о реакции на нее самой службой не сказано ничегошеньки. Пришлось потратить прядочно времени на вскрытие черного ящика.
Мое исследование началось с двух вещей:
Как это сделать описано здесь.
set tracing output=file trace-file-prefix=D:\Development\Logs\BITS\ level=default format=ansi max-trace-file-size=512000000 state=enabled
Единственный нюанс, для того, чтобы логирование службы заработало, - необходимо перегрузить машину.
Строка делится на части (назову proxy item) по разделителю " " (пробел). Далее, по порядку BITS проверяет, можно ли использовать proxy item для текущего файла. Если да - передает управление WinHTTP, установив proxy item в качестве строки proxy (см. структуру WINHTTP_PROXY_INFO
- ничего не напоминает?). Если скачать файл не удалось, служба с радостью переходит к следующему proxy item, а если они закончились - получаем BG_JOB_STATE_TRANSIENT_ERROR
. А вот если proxy item каким-то образом файлу не подошел - начинается самодеятельность: BITS пытается использовать режим BG_JOB_PROXY_USAGE_AUTODETECT
, а если и с этим пролет - качает напрямую (BG_JOB_PROXY_USAGE_NO_PROXY
)!
Судя по всему, не подошел
- это если протокол proxy item не совпадает с протоколом файла. В результате, если строка прокси будет https://someproxy somegenericproxy
или x=y http://someproxy somegenericproxy
, то файл по протоколу http
не будет качаться через somegenericproxy
или someproxy
!
Выводы напрашиваются следующие:
pProxyList
автоматически ведет к предыдущему пункту.Тем более, если задача состоит из файлов с разными протоколами загрузки. https=...
не подходит файлам http://...
, а http=...
- файлам https://...
. Поэтому, часть файлов будет гарантированно качаться в обход proxy.
Здесь, правда, можно обмануть BITS - воспользоваться тем, что есть еще один пробельный разделитель - \t
(TAB). Если строка pProxyList
выглядит, как \thttp=http_proxy1\thttps=https_proxy1 \thttp=http_proxy2\thttps=https_proxy2
, то в качестве строки proxy в WinHTTP попадет сначала \thttp=http_proxy1\thttps=https_proxy1
, а если произошла ошибка, то после и \thttp=http_proxy2\thttps=https_proxy2
. А хитрость в том, что WinHTTP считает \t
пробелом и выберет нужный сервер с учетом протокола. Использовать этот метод я не рекомендую, т.к. оно может поломаться с очередным пакетом обновлений. Да и не известно, как это будет работать с другими версиями службы (у меня 3.0 на Windows Vista) и не с WinHTTP, которую Background Intelligent Transfer Service начала использовать только с версии 2.0!
Очередная версия BITS Manager. Список задач пополнился двумя колонками - Created и Modified/Completed. Окно Job profile также пострадало
- добавились поля Created и Modified, изменилось расположение элементов управления. Появились информативные подсказки. Реализована функциональность Take Ownership (кнопка TO), а также полноценный редактор настроек proxy!
Download BITS Manager v1.02.0002
Next version: BITS Manager v1.02.0003.
Всем рекомендую к просмотру итальянский(!) фильм Прости за любовь / Sorry, If I Love You / Scusa ma ti chiamo amore (2008). Один из самых ярких и душевных фильмов за длительное время! А актриса просто красавица. Все-таки, итальянки очень красивые женщины.
Всех с новым годом!