Системное_программирование. Практикум для студентов специальностей 140 01 02 Информационные системы и технологии
Скачать 1.66 Mb.
|
CreateFile позволяет процессу открывать файл, про- ецируемый в память другим процессом: hTxtFile = CreateFileW(L"kate.txt",GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 55 Функция CreateFileMapping создаёт уже другие, «проецирую- щие» внутренние структуры и связывает «сущность на диске» с « сущностью в адресном пространстве»: //Создание объекта ядра «проекция файла» hTxtMappingFile=CreateFileMappingA(hTxtFile,NULL,PAGE_READO NLY,0,0,"kate"); По своему месту в последовательности действий она создаёт что-то наподобие «адресного пространства из файла». Этот объект поддерживает соответствие между содержимым файла и адресным пространством процесса, использующего этот файл (“kate” – имя объекта файлового отображения). Так как мы хотим отобразить весь файл, то четвертый и пятый параметры должны быть равны нулю. Функция MapViewOfFile делает "проекцию в память": //Проецирование данных файла на адресное пространство процесса hTxtMapFileStartAddr = MapViewOfFile(hTxtMappingFile,FILE_MAP_READ,0,0,0); Здесь hTxtMappingFile – дескриптор созданного объекта файло- вого отображения. Второй параметр определяет режим доступа к файлу. Значение, возвращаемое функцией MapViewOfFile, имеет тип "указатель". Если функция отработала успешно, то она вернет начальный адрес данных объекта файлового отображения. Функция OpenFileMapping используется для открытия объекта ядра «проекция файла»: //Открытие объекта ядра «проекция файла» hTxtMappingFile = OpenFileMappingA(FILE_MAP_READ,FALSE,"kate"); Функция UnmapViewOfFile прекращает отображение в адресное пространство процесса того файла, который перед этим был отоб- ражен при помощи функции MapViewOfFile: UnmapViewOfFile(hTxtMapFileStartAddr); 56 Файлы данных, проецируемые в память Операционная система позволяет проецировать на адресное про- странство процесса и файл данных. Это очень удобно при манипу- ляциях с большими потоками данных. Для проецирования файла данных нужно выполнить три операции: 1. Создать или открыть объект ядра "файл", идентифицирующий дисковый файл, который Вы хотите использовать как проецируе- мый в память. Для создания объекта “файл” используется функция CreateFile . 2. С помощью функции CreateFileMapping создается объект ядра “проецируемый файл”, чтобы сообщить системе размер файла и способ доступа к нему. При этом используется описатель файла (handle), возвращенный функцией CreateFile. Теперь файл готов к проецированию. 3. Производится отображение объекта “проецируемый файл” или его части на адресное пространство процесса. Для этого применяет- ся функция MapViewOfFile. Для открепления файла от адресного пространства процесса ис- пользуется функция UnmapViewOfFile, а для уничтожения объек- тов “файл” и “проецируемый файл” – функция CloseHandle. Общая схема работы с проецированными файлами такова: //Открытие объекта ядра «файл» hTxtFile = CreateFileW(L"kate.txt",…); //Создание объекта ядра «проекция файла» hTxtMappingFile=CreateFileMappingA(hTxtFile,…,"kate"); //Проецирование данных файла на адресное пространство процесса hTxtMapFileStartAddr = MapViewOfFile(hTxtMappingFile,…); Совместный доступ процессов к данным через механизм проецирования Совместное использование данных в этом случае происходит так: два или более процесса проецируют в память представления одного и того же объекта "проекция файла", т.е. одни и те же стра- 57 ницы физической памяти. В результате, когда один процесс запи- сывает данные в представление общего объекта "проекция файла", изменения немедленно отражаются на представлениях в других процессах. Но при этом все процессы должны использовать одина- ковое имя объекта "проекция файла". 1.exe case WM_INITDIALOG: hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1000, TEXT("katarina")); if(hFileMapping == NULL) MessageBox(hDlg,TEXT("Невозможно создать проекцию файла в Page-файле..."),TEXT("Error"),MB_ICONERROR); else { sharedBuffer = (LPTSTR) MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS,0,0,0); } 2.exe hFileMapping = OpenFileMapping(FILE_MAP_READ,FALSE,TEXT("katarina")); if(hFileMapping == NULL) MessageBox(hDlg,TEXT("Невозможно создать проекцию файла..."),TEXT("Error"),MB_ICONERROR); else { sharedBuffer = (LPTSTR) MapViewOfFile(hFileMapping, FILE_MAP_READ,0,0,0); DWORD dwError = GetLastError(); } Обработка текстового и графического файлов двумя процессами При обработке текстовых и графических файлов небольшого размера можно спроецировать весь файл в память. Затем произве- сти его обработку. По завершении обработки файла необходимо прекратить отображение на адресное пространство представления объекта «проекция файла» и закрыть описатель всех объектов ядра. 58 Текстовый файл: //Открытие объекта ядра «проекция файла» hTxtMappingFile = OpenFileMappingA(FILE_MAP_READ,FALSE,"kate"); if(hTxtMappingFile != NULL) { //Проецирование данных файла на адресное пространство процесса hTxtMapFileStartAddr = MapViewOfFile(hTxtMappingFile, FILE_MAP_READ,0,0,0); //Вывод данных из текстового файла в эдит SetDlgItemTextA(hDlg, IDC_EDIT_TEXT,(char*)hTxtMapFileStartAddr); //Отключение файла данных от адресного пространства процесса UnmapViewOfFile(hTxtMapFileStartAddr); //Закрытие объекта «проекция файла» CloseHandle(hTxtMappingFile); Графический файл: //Открытие объекта ядра «файл» hBmpFile = CreateFileA("LOVE.bmp",GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); GetClientRect(GetDlgItem(hDlg,IDC_BMPFRAME),&rect); //Создание объекта ядра «проекция файла» hBmpMappingFile = CreateFileMappingA( hBmpFile, //описатель файла NULL, //аттрибуты защиты объекта ядра PAGE_READONLY, //атрибут защиты, присваиваемый // страницам физической памяти 0,0, //максимальный размер файла в // байтах (0 -- размер меньше 4Гб) "BmpFile"); //имя объекта ядра //Проецирование данных файла на адресное пространство процесса hBmpMapFileAddr = MapViewOfFile( hBmpMappingFile, // описатель объекта «проекция файла» FILE_MAP_READ, // вид доступа к данным 0,0, // смещение в файле до байта файла данных, //который нужно считать в представлении первым 59 0); // сколько байтов файла данных должно быть //спроецировано на адресное пространство //(0 -- от смещения и до конца файла) BITMAPFILEHEADER *bFileHeader= (BITMAPFILEHEADER*)hBmpMapFileAddr; BITMAPINFO *bInfo=(BITMAPINFO*)((char*)hBmpMapFileAddr+14); hdc=GetDC(GetDlgItem(hDlg,IDC_BMPFRAME)); hBmpFile=CreateDIBitmap(hdc,&(bInfo->bmiHeader), CBM_INIT,(char*)hBmpMapFileAddr+bFileHeader->bfOffBits, bInfo,DIB_PAL_COLORS); hMemDC=CreateCompatibleDC(hdc); SelectObject(hMemDC,hBmpFile); StretchBlt(hdc,0,0,rect.right,rect.bottom,hMemDC, 0,0,bInfo->bmiHeader.biWidth,bInfo->bmiHeader.biHeight, SRCCOPY); ReleaseDC(GetDlgItem(hDlg,IDC_BMPFRAME),hdc); DeleteDC(hMemDC); DeleteObject(hBmpFile); return (INT_PTR)TRUE; Работа с файлами больших размеров //*****Работа с большим файлом******************* if (LOWORD(wParam) == IDC_HUGE) { // начальные границы представлений всегда начинаются no // адресам, кратным гранулярности выделения памяти SYSTEM_INFO sinf; GetSystemInfo(&sinf); / / открываем файл данных HANDLE hBigFile = CreateFileA("ТЕСТ.txt", GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,NULL); // создаем объект проекции файла HANDLE hHugeFileMapping = CreateFileMapping(hBigFile, NULL,PAGE_READONLY, 0, 0, NULL); DWORD dwFileSizeHigh; __int64 dwFileSize = GetFileSize(hBigFile, &dwFileSizeHigh); dwFileSize += (((__int64) dwFileSizeHigh) << 32); // доступ к описателю объекта файл нам больше не нужен 60 CloseHandle(hBigFile); __int64 dwFileOffset = 0; DWORD dwNumOfOs = 0; char symbol[100] = ""; DWORD dwPrBarRange = dwFileSize/sinf.dwAllocationGranularity; while (dwFileSize > 0) { // определяем, сколько байтов надо спроецировать DWORD dwBytesInBlock = sinf.dwAllocationGranularity; if (dwFileSize < sinf.dwAllocationGranularity) dwBytesInBlock = (DWORD)dwFileSize; //Проецирование данных файла на адресное // пространство процесса PBYTE hHugeMapFileStartAddr = (PBYTE) MapViewOfFile(hHugeFileMapping,FILE_MAP_READ, (DWORD) (dwFileOffse t >> 32), // начальный байт (DWORD) (dwFileOffset & 0xFFFFFFFF), // в файле dwBytesInBlock); // число проецируемых байтов SetDlgItemTextA(hDlg, IDC_EDIT_HUGEFILE, (char*) hHugeMapFileStartAddr); SendMessage(GetDlgItem(hDlg, IDC_PROGRESS), (UINT)PBM_STEPIT, 0, 0); // прекращаем проецирование представления, // чтобы в адресном пространстве // не образовалось несколько представлений одного файла UnmapViewOfFile(hHugeMapFileStartAddr); // переходим к следующей группе байтов в файле dwFileOffset += dwBytesInBlock; dwFileSize -= dwBytesInBlock; } CloseHandle(hHugeFileMapping); return (INT_PTR)TRUE; } 61 Пример взаимодействия процессов через Page файл. 1.exe case WM_INITDIALOG: hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1000, TEXT("katarina")); if(hFileMapping == NULL) MessageBox(hDlg,TEXT("Невозможно создать проекцию файла в Page- файле..."),TEXT("Error"),MB_ICONERROR); else { sharedBuffer = (LPTSTR) MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS,0,0,0); } 2.exe hFileMapping = OpenFileMapping(FILE_MAP_READ,FALSE,TEXT("katarina")); if(hFileMapping == NULL) MessageBox(hDlg,TEXT("Невозможно создать проекцию файла..."),TEXT("Error"),MB_ICONERROR); else { sharedBuffer = (LPTSTR) MapViewOfFile(hFileMapping, FILE_MAP_READ,0,0,0); DWORD dwError = GetLastError(); } 62 Лабораторная работа № 8 БУФЕР ОБМЕНА Цель работы: изучить основы работы с объектом – буфер обмена. Изучаемые вопросы 1. Форматы данных. 2. Запись информации в буфер обмена. 3. Чтение информации из буфера обмена. 4. Передача информации пользовательского типа *. Постановка задачи Создать приложение, состоящее из двух процессов: − первый процесс записывает текстовый файл и растровый рисунок в буфер обмена; − второй процесс считывает информацию из буфера обмена и отображает в окне процесса. Текстовый файл и файл с растровым рисунком взять из преды- дущих лабораторных работ. Теоретические сведения Форматы данных Ниже в таблице 8.1 представлены типы данных и соответствую- щие им форматы данных. 63 Таблица 8.1 Форматы данных Формат Тип данных CF_BITMAP Растр (bitmap) в чистом виде CF_DIB Растр (bitmap) с заголовком BITMAPINFO CF_DIF Универсальный формат обмена (Data Interchange Format) CF_DSPBITMAP Пользовательское растровое изображение CF_DSPENHMETAFILE Пользовательский расширенный метафайл CF_DSPMETAFILEPICT Пользовательский метафайл CF_DSPTEXT Пользовательский текст СF_ENHMETAFILE Расширенный метафайл CF_METAFILEPICT Метафайл в стиле METAFILEPICT CF_OEMTEXT Текст в кодировке OEM CF_OWNERDISPLAY Пользовательский формат данных CF_PALETTE Цветовая палитра CF_PENDATA Формат для данных, связанных с элек- тронным пером CF_RIFF Файл ресурсов (Resource Interchange File Format) CF_SYLK Символическая ссылка CF_TEXT Текст CF_TIFF Графика в формате TIFF CF_WAVE Звук в формате WAVE CF_UNICODETEXT Текст в кодировке UNICODE Запись информации в буфер обмена Общая процедура записи данных в буфер обмена состоит из сле- дующих шагов: 64 a) Прежде чем поместить в буфер обмена какую-либо инфор- мацию, ваша программа (далее просто окно) должна его от- крыть, используя функцию OpenClipboard: if (OpenClipboard(hDlg)) //открываем буфер обмена b) После того, как программа открыла буфер обмена, она должна его очистить от предыдущего содержания, для чего следует вызвать функцию EmptyClipboard. if (EmptyClipboard()) //очищение буфера обмена c) Выделяем блок глобальной памяти, достаточный для того, чтобы хранить в нем данные, которые необходимо поме- стить в буфер обмена. HGLOBAL hGl; hGl = GlobalAlloc(GMEM_DDESHARE, strlen(buffer2)); //заказываем блок памяти Функция GlobalAlloc() выделяет память и возвращает дескрип- тор выделенного блока. Для получения указателя на область памяти, выделенную при помощи GlobalAlloc(), следует использовать функцию GlobalLock(): LPVOID lpstr = (char *) GlobalLock(hGl); //блокируем его Функция GlobalLock() фиксирует в памяти объект (блок), де- скриптор которого передается в параметре hGl. Зафиксированный объект не перемещается в памяти и не выгружается. Функция GlobalLock () возвращает адрес начала блока в случае успешного завершения или NULL при возникновении ошибки. После получения указателя на глобальный блок памяти необхо- димо скопировать в этот блок данные, которые Вы хотите поме- стить в буфер обмена. memcpy(lpstr, buffer2, strlen(buffer2)); //записываем данные 65 Когда копирование завершится, блок памяти можно разблокиро- вать, вызвав функцию GlobalUnlock(): GlobalUnlock(hGl); //разблокировать блок Теперь мы имеем полное право помещать в него свои данные в различных форматах, используя функцию SetClipboardData. SetClipboardData(CF_TEXT, hGl); //помещаем данные в буфер обмена Чтение информации из буфера обмена Для чтения данных из буфера обмена используется следующая последовательность шагов: a) Необходимо открыть буфер обмена; if (OpenClipboard(hDlg)) Чтобы получить доступ к данным, хранящимся в буфере обмена, последний должен быть открыт. b) Чтобы извлечь информацию из буфера обмена, окно должно вызвать функцию GetClipboardData. HANDLE hData = GetClipboardData(CF_TEXT); //извлекаем информацию из буфера Данная функция в качестве параметра принимает формат буфера обмена, для того чтобы извлечь данные в этом формате. Если в буфер обмена данные поместила другая программа, вы мо- жете проверить доступные форматы данных перед их непосредствен- ным извлечением, используя функцию IsClipboardFormatAvailable. if (IsClipboardFormatAvailable(CF_TEXT)) //проверка на доступные файлы c) Копируем данные из буфера обмена d) Закрываем буфер обмена CloseClipboard(); 66 Пользовательский формат данных Возможно ситуация, когда приложению необходимо поместить данные в буфер обмена, а стандартные форматы для этого не под- ходят. Выходом из такой ситуации является возможность регистра- ции собственного формата данных. Для того чтобы зарегистрировать новый формат буфера обмена, используется функция RegisterClipboardFormat. В качестве пара- метра этой функции следует передать указатель на текстовую стро- ку, закрытую двоичным нулем и содержащую имя регистрируемого нестандартного формата данных для буфера обмена. Функция возвращает нулевое значение при ошибке или иденти- фикатор зарегистрированного формата данных, который можно ис- пользовать аналогично идентификаторам стандартных форматов в качестве параметра функции SetClipboardData. Два различных приложения или две копии одного приложения могут зарегистрировать формат с одним и тем же именем, при этом функция RegisterClipboardFormat вернет один и тот же идентифика- тор формата. Поэтому два приложения всегда смогут "договорить- ся", если они знают имя нестандартного формата данных. В приведенном ниже коде регистрируется новый формат дан- ных, представленный структурой MyClipboardData, а затем запол- ненная структура помещается в буфер обмена: if (LOWORD(wParam) == IDC_BUTTON1) { UINT format = RegisterClipboardFormat(L"MyClipboardData"); //регистрируем наш формат данных MyClipboardData MCD; SendDlgItemMessageA(hDlg, IDC_EDIT2, WM_GETTEXT, 50, (LPARAM)MCD.InfData); if (OpenClipboard(hDlg)) //для работы с буфером обмена его нужно открыть { if (EmptyClipboard()) { HGLOBAL hGl; EmptyClipboard(); //очищаем буфер hGl = GlobalAlloc(GMEM_DDESHARE, sizeof(MyClipboardData)); //выделим память 67 MyClipboardData * buffer = (MyClipboardData *) GlobalLock(hGl); //запишем данные в память *buffer = MCD; //поместим данные в буфер обмена GlobalUnlock(hGl); SetClipboardData(format, hGl); //помещаем данные в буфер обмена CloseClipboard(); //после работы с буфером, его нужно закрыть } return (INT_PTR)TRUE; }} В приведенном ниже коде извлекается из буфера обмена данные, представленные структурой MyClipboardData: case IDC_BUTTON1: { if (OpenClipboard(hDlg)) { UINT format = RegisterClipboardFormat(L"MyClipboardData"); //вызываем второй раз, чтобы просто получить формат if (IsClipboardFormatAvailable(format)) { MyClipboardData MCD; //извлекаем данные из буфера HANDLE hData = GetClipboardData(format); MyClipboardData* buffer = (MyClipboardData *) GlobalLock(hData); //заполняем нашу структуру полученными данными MCD = *buffer; GlobalUnlock(hData) SetDlgItemTextA(hDlg, IDC_EDIT1, MCD.In fData); CloseClipboard(); } } CloseClipboard(); return TRUE; } |