Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

Часть, заключенная в знаки процента, является подставляемой. В данном случае в строку должно быть подставлено значение неременной окружения USERPROFILE На моей машине эта переменная выглядит так:

C:\Documents and Settings\Administrator

После подстановки переменной в строку реестра получим

C:\Documents and Settings\Admimstrator\My Documents

Поскольку такие подстановки делаются очень часто, в Windows есть функция

ExpandEnvironmentStrings.

DWORD ExpandEnvironmentStrings( PCTSTR pszSrc, PTSTR pszDst, DWORD nSize);

Параметр pszSrc принимает адрес строки, содержащей подставляемые части, а пареметр pszDsf — адрес буфера, в который записывается развернутая строка Пара метр nSize определяет максимальный размер буфера в символах.

Наконец, функция SetEnvironmentVariable позволяет добавлять, удалять и модифи цировать значение переменной

DWORD SetEnvironmentVariable(

PCTSTR pszName,

PCTSTR pszValue);

Она устанавливает ту переменную, на чье имя указывает параметр pszName, и присваивает ей значение, заданное параметром pszValue. Если такая переменная уже существует, функция модифицирует ее значение. Если же spszValue содержится NULL, переменная удаляется из блока

Для манипуляций с блоком переменных окружения всегда используйте именно эти функции. Как я уже говорил, строки в блоке переменных нужно отсортировать в ал фавитном порядке по именам псрсмснных (тогда GetEnvironmentVariable быстрее находит нужные переменные), a SetEnvironmentVariable как раз и следит за порядком расположения переменных.

Привязка к процессорам

Обычно потоки внутри процесса могут выполняться на любом процессоре компью тера. Однако их можно закрепить за определенным подмножеством процессоров из числа имеющихся на компьютере Это свойство называется привязкой к процессорам (processor affinity) и подробно обсуждается в главе 7. Дочерние процессы наследуют привязку к процессорам от родительских.

Режим обработки ошибок

С каждым процессом связан набор флагов, сообщающих системе, каким образом про цесс должен реагировать на серьезные ошибки: повреждения дисковых носителей, необрабатываемые исключения, ошибки операций поиска файлов и неверное вырав нивание данных. Процесс может указать системе, как обрабатывать каждую из этих ошибок, через функцию SetErrorMode\

UINT SetErrorMode(UINT fuErrorMode) ;

Параметр fuErrorMode — это набор флагов, комбинируемых побитовой операцией OR

Флаг

Описание

 

 

SEM FAILCRITICALERRORS

Система не выводит окно с сообщением от обра

 

ботчика критических ошибок и возвращает ошибку в

 

вызывающий процесс

 

 

SEM_NOGPFAULTERRORBOX

Система не выводит окно с сообщением о наруше нии

 

общей защиты; этим флагом манипулируют только

 

отладчики, самостоятельно обрабатывающие

 

нарушения общей защиты с помощью обработчика

 

исключений

 

 

SEM_NOOPENFILEERRORBOX

Система не выводит окно с сообщением об отсут ствии

 

искомого файла

 

 

SEM_NOALIGNMENTFAULTEXCEPT

Система автоматически исправляет нарушения в вы

 

равнивании данных, и они становятся невидимы при

 

ложению: этот флаг не действует на процессорах x8б

 

 

По умолчанию дочерний процесс наследует от родительского флаги, указываю щие на режим обработки ошибок. Иначе говоря, если у процесса в данный момент установлен флаг SEM_NOGPFAULTERRORBOX и он порождает другой процесс, этот

флаг будет установлен и у дочернего процесса. Однако "наследник" об этом не уве домляется, и он вообще может быть нс рассчитан на обработку ошибок такого типа данном случае — нарушений общей защиты). В результате, если в одном из пото ков дочернего процесса все-таки произойдет подобная ошибка, этот процесс может завершиться, ничего не сообщив пользователю. Но родительский процесс способен предотвратить наследование дочерним процессом своего режима обработки ошибок, указав при вызове функции CreateProcess флаг CREATE_DEFAULT_ERROR_MODE (о

CreateProcess чуть позже).

Текущие диск и каталог для процесса

Текущий каталог текущего диска — то место, где Windows-функции ищут файлы и подкаталоги, если полные пути в соответствующих параметрах не указаны. Например, если поток в процессе вызывает функцию CreateFile, чтобы открыть какой-нибудь файл, а полный путь не задан, система просматривает список файлов в текущем ката логе текущего диска. Этот каталог отслеживается самой системой, и, поскольку такая информация относится ко всему процессу, смена текущего диска или каталога одним из потоков распространяется и на остальные потоки в данном процессе.

Поток может получать и устанавливать текущие каталог и диск для процесса с помощью двух функций:

DWORD GetCurrentDirectory( DWORD cchCurDir, PTSTR pszCurDir);

BOOL SetCurrentDirectory(PCTSTR pszCurDir);

Текущие каталоги для процесса

Система отслеживает текущие диск и каталог для процесса, но не текущие каталоги на каждом диске. Однако в операционной системе предусмотрен кое-какой сервис для манипуляций с текущими каталогами на разных дисках. Он реализуется через пере менные окружения конкретного процесса. Например:

=C:=C-\Utility\Bin =D:=D:\Program Files

Эти переменные указывают, что текущим каталогом на диске С является \Utllity\ Bin, а на диске D — Program Files.

Если Вы вызываете функцию, передавая ей путь с именем диска, отличного от текущего, система сначала просматривает блок переменных окружения и пытается найти переменную, связанную с именем указанного диска. Если таковая есть, система выбирает текущий каталог на заданном диске в соответствии с ее значением, нет — текущим каталогом считается корневой.

Скажем, если текущий каталог для процесса — C:\Uiiltty\Bin и Вы вызываете фун кцию CreateFile, чтобы открыть файл D:\ReadMe.txt, система ищет переменную =D:. Поскольку переменная =D: существует, система пытается открыть файл ReadMe txt в каталоге D:\Program Files. А если бы таковой переменной не было, система искала бы файл ReadMe.txt в корневом каталоге диска D. Кстати, файловые Windows-функции никогда не добявляют и не изменяют переменные окружения, связанные с именами дисков, а лишь считывают их значения.

NOTE:

Для смены текущего каталога вместо Windows-функции SetCurrentDirectory можно использовать функцию _chdir из библиотеки С Внутренне она тоже обращается к SetCurrentDirettory, но, кроме того, способна добавлять или мо дифицировать переменные окружения, что позволяет запоминать в програм ме текущие каталоги на различных дисках.

Если родительский процесс создает блок переменных окружения и хочст пере дать его дочернему процессу, тот не наследует текущие каталоги родительского про цесса автоматически. Вместо этого у дочернего процесса текущими на всех дисках становятся корневые каталоги. Чтобы дочерний процесс унаследовал текущие ката логи родительского, последний должен создать соответствующие переменные окру жения (и сделать это до порождения другого процесса). Родительский процесс мо жет узнать, какие каталоги являются текущими, вызвав GetFullPathName:

DWORD GetFullPathName( PCTSTR pszFile, DWORD cchPath, PTSTR pszPath, PTSTR *ppszFilePart);

Например, чтобы получить текущий каталог на диске С, функцию вызывают так:

TCHAR szCurDir[MAX_PATH];

DWORD GetFullPathName(TEXT("C."), MAX_PATH, szCurDir, NULL);

Не забывайте, что переменные окружения процесса должны всегда храниться в алфавитном порядке Позтому переменные, связанные с дисками, обычно приходит ся размещать в самом начале блока.

Определение версии системы

Весьма часто приложению требуется определять, в какой версии Windows оно выпол няется. Причин тому несколько Например, программа может использовать функции защиты, заложенные в Windows API, но в полной мерс эти функции реализованы лишь в

Windows 2000.

Насколько я помню, функция GetVersion есть в API всех версий Windows:

DWORD GetVersion();

С этой простой функцией связана целая история. Сначала ее разработали для 16 разрядной Windows, и она должна была я старшем слове возвращать номер версии MS-DOS, а в младшем — номер версии Windows. Соответственно в каждом слове старший байт сообщал основной номер версии, младший — дополнительный но мер версии

Увы, программист, писавший ее код, слегка ошибся, и получилось так, что номера версии Windows поменялись местами: в старший байт попадал дополнительный но мер, а в младший - основной. Поскольку многие программисты уже начали пользо ваться этой функцией, Microsoft пришлось оставить все, как есть, и изменить доку ментацию с учетом ошибки.

Из-за всей этой неразберихи вокруг GetVersion в Windows API включили новую функцию

GetVersionEx:

BOOL GetVersionEx(POSVERSIONINFO pVersionInformation);

Перед обращением к GetVersionEx профамма должна создать структуру OSVER SIONINFOEX, показанную ниже, и передать ее адрес этой функции

typedef struct {

DWORD dwOSVersionInfoSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber;

DWORD dwPlatformId; TCHAR szCSDVersion[128]; WORD wServicePackMajor; WORD wServicePackMinor; WORD wSuiteMask;

BYTE wProductType; BYTE wReserved;

} OSVERSIONINFOEX, *POSVERSIONINFOEX;

Эта структура — новинка Windows 2000 В остальных версиях Windows использу ется структура OSVERSIONINFO, в которой нет последних пяти элементов, присутству ющих в структуре OSVERSIONINFOEX

Обратите внимание, что каждому компоненту номера версии операционной сис темы соответствует свой элемент структуры это сделано специально — чтобы про граммисты не возились с выборкой данных ш всяких там старших-младших байтов слов (и не путались в них), тeпepь программе гораздо проще сравнивать ожидаемый номер версии операционной системы с действительным Назначение каждою элемен та структуры

OSVERSIONTNFOFX описано в таблице 4-2

Элемент

Описание

 

 

dwOSVersionInfoSjze

Размер структуры, перед обращением к функции GetVertsionEx дол жен быть заполнен

 

 

вызовом sizeof(OSVERSIONINFO) или Sizeof(OSVERSIONINFOEX)

 

 

 

 

 

dwMajorVersion

Основной номер версии операционной системы

 

 

 

 

 

dwMinorVersion

Дополнительный номер версии операционной системы

 

 

 

 

 

dwBuildNumber

Версия сборки данной системы

 

 

 

 

 

dwPlatformId

Идентификатор платформы, поддерживаемой данной системой, его возможные шачепия

 

 

VFR_PLATFORM_WIN32s (Win32s), VER_PLATFORM_WIN32_WINDOWS (Windows

 

 

95/98), VER_PLATFORM_WIN32_NT (Windows NT или Windows 2000),

 

 

VER_PLATFORM_WIN32_CEHH (Windows CE)

szCSDVersion

Этот элемент содержит текст — дополнительную информацию об установленной

 

 

операционной системе

 

 

 

 

 

wServicePackMajor

Основной номер версии последнего установленного пакета исправ лений (service pack)

 

 

 

 

 

wServicePackMinor

Дополнительный номер версии последнего установленного пакета исправлений

Таблица 4-2. Элементы структуры OSVERSIONINFOEX

 

 

 

 

 

 

Элемент

 

Описание

 

 

 

 

 

 

 

wSuiteMask

 

Сообщает, какие программные пакеты (suites) доступны в системе;

 

 

 

 

его возможные значения

 

 

 

 

VER_SUITE_SMALLBUSINESS,

 

 

 

 

VER_SUITE_ENTERPRISE,

 

 

 

 

VER_SUITE_BACKOFFICE,

 

 

 

 

VER_SUITE_COMMUNICATIONS,

 

 

 

 

VER_SUITE_TERMINAL,

 

 

 

 

VER_SUITE_SMALLBUSINESS_RESTRICTED,

 

 

 

 

VER_SUITE_EMBEDDEDNT,

 

 

 

 

VER_SUITE_DATACENTER

 

 

 

 

 

 

 

wProductType

Сообщает, какой именно вариант операционной системы установлен; его

 

 

 

 

возможные значения:

 

 

 

 

VER_NT_WORKSTATION,

 

 

 

 

VER_NT_SERVER,

 

 

 

 

VER_NT_DOMAIN_CONTROLLER

 

 

wReserved

 

Зарезервирован на будущее

 

 

 

 

 

 

В Windows 2000 появилась новая функция, VerifyVersionInfo, которая сравнивает версию установленной операционной системы с тем, что требует Ваше приложение:

BOOL VerifyVersionInfo(

POSVERSIONINFOEX pVersionInformation;

DWORD dwTypeMask;

DWORDLONG dwlConditionMask);

Чтобы использовать эту функцию, соэдайте структуру OSVERSIONINFOEX, запи шите в се элемент dwOSVersionInfoSize размер структуры, а потом инициализируйте любые другие элементы, важные для Вашей программы, При вызове VerifyVersionInfo параметр dwTypeMask указывает, какие элементы структуры Вы инициализировали. Этот параметр принимает любые комбинации следующих флагов: VER_MINORVER SION,

VER_MAJORVERSION, VER_BUILDNUMBER, VER_PLATFORMID, VER_SERVICEPACK MINOR, VER_SERVICEPACKMAJOR, VER_SUITENAME и

VER_PRODUCT_TYPE, Последний параметр, dwlConditionMask, является 64-разрядным значением, которое управляет тем, как именно функция сравнивает информацию о версии системы с нужными Вам дан ными.

Параметр dwlConditionMask устанавливает правила сравнения через сложный на бор битовых комбинаций. Для создания требуемой комбинации используйте макрос

VER_SET_CONDITION:

VER_SET_CONDITION(

DWORDLONG dwlConditionMask, ULONG dwTy0eBiLMask, ULONG dwConditionMask);

Первый параметр, dwlConditionMask, идентифицирует переменную, битами кото рой Вы манипулируете. Вы не передаете адрес этой переменной, потому что VER_SET_ CONDITION — макрос, а не функция. Параметр dwTypeBitMask указывает один элемент в структуре OSVERSIONINFOEX, который Вы хотите сравнить со своими данными. (Для сравнения нескольких элементов придется обращаться к VER_SETCONDITION не сколько раз подряд.) Флаги, передаваемые в этом параметре, идентичны передавае мым в параметре dwTypeMask функции VerifyVersionInfo.

Последний параметр макроса VER_SET_CONDITION, dwConditionMask, сообщает, как Вы хотите проводить сравнение. Он принимает одно из следующих значений. VER_EQUAL, VER_GREATER, VER_GREATER_EQUAL, VER_LESS или

VER_LESS_EQUAL, Вы можете использовать эти значения в сравнениях по

VER_PRODUCT_TYPE. Например, значение VER_NT_WORKSTATION меньше, чем

VER_NT_SERVER. Но в сравнениях по VER_SUITENAME вместо этих значений применяется VER_AND (должны быть установ лены все программные пакеты) или VER_OR (должен быть установлен хотя бы один из программных пакетов).

Подготовив набор условий, Вы вызываете VerifyVersionlnfo и получаете ненулевое значение, если система отвечает требованиям Вашего приложения, или 0, если она не удовлетворяет этим требованиям или если Вы неправильно вызвали функцию Чтобы определить, почему VenfyVersionlnfo вернула 0, вызовше GetLastError. Если та вернет ERROR_OLD_WIN_VERSION, значит, Вы правильно вызвали функцию Venfy VersionInfo, но система не соответствует предъявленным требованиям.

Вот как проверить, установлена ли Windows 2000;

//готовим структуру OSVERSIONINFOEX, сообщая, что нам нужна

Windows 2000

OSVERSIONINFOEX osver = { 0 }; osver.dwOSVersionInfoSize = sizeof(osver); osver.dwMdjorVersion = 5; osver.dwMinorVersion = 0; osver.dwPlatformId = VER_PLATFORM_WIN32_NT;

//формируем маску условии

DWORDLONG dwlConditionMask = 0;

// всегда инициализируйте это элемент так

VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);