Y_Mur Да и про CloseClipboard, чем чревато если не вызвать эту функцию? У меня вроде все работает при снимке экрана, только второй уже не получается...
пока ты держишь clipoboard открытым никто не имеет нему доступа кроме твоей проги, впрочем при закрытии программы все её ресурсы освобождаются автоматически, но лучше этим не злоупотреблять, а приучить себя сразу закрывать всё ненужное
Код (Text): Описание формата BMP для Window 3.0 (DIB) ========================================= Основным отличием файлов нового формата (DIB) - Device Independent Bitmap (аппаратно-независимый битовый образ) является то, что в них используется кодировка цветов с одной битовой плос- костью. Файлы битовых образов нового формата начинаются со структуры BITMAPFILEHEADER: typedef struct tagBITMAPFILEHEADER { word bfType; //тип файла (для битового образа - BM) dword bfSize; //размер файла в dword word bfReserved1; //не используется word bfReserved2; //не используется dword bfOffbits; //смещение данных битового образа от //заголовка в байтах } Непосредственно за ней располагается структура BITMAPINFO, со- держащая всю информацию о битовом образе. Она делится на две час- ти: структуру BITMAPINFOHEADER, описывающей размеры и цветовой формат битового образа, и массив структур RGBQUAD, определяющей цветовую палитру: typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } typedef struct tagBITMAPINFOHEADER { dword biSize; //число байт, занимаемых структурой //BITMAPINFOHEADER dword biWidth; //ширина битового образа в пикселах dword biHeight; //высота битового образа в пикселах word biPlanes; //число битовых плоскостей устройства word biBitCount; //число битов на пиксель dword biCompression; //тип сжатия dword biSizeImage; //размер картинки в байтах dword biXPelsPerMeter;//горизонтальное разрешение устройства, //пиксел/м dword biYPelPerMeter; //вертикальное разрешение устройства, //пиксел/м dword biClrUsed; //число используемых цветов dword biClrImportant; //число "важных" цветов } BITMAPINFOHEADER; Более подробно: biSize - обычно используется для облегчения доступа к таблице цветов. biPlanes - определяет число битовых плоскостей; однако, по- скольку цвет кодируется последовательными битами, это число всег- да равно 1. biBitCount - этим полем определяется число цветов, используе- мых битовым образом. В зависимости от способа кодирования, может принимать значения: 1 - битовый образ монохромный, и таблица цветов должна содер- жать два элемента. Каждый бит в массиве данных кодирует один пик- сел. Если значение бита - 0, то пиксел становится первым цветом таблицы; если - 1, пиксел становится вторым цветом таблицы. 4 - битовый образ имеет максимум 16 цветов, и массив bmiColors (таблица цветов) имеет до 16 элементов. Цвет каждого пиксела оп- ределяется по таблице цветов при помощи четырехбитного индекса. Например, если первый байт данных имеет значение 3Ah, то при отображении битового образа цвет первого пиксела определяет чет- вертый элемент таблицы цветов, а цвет второго - одиннадцатый. 8 - битовый образ имеет максимум 256 цветов, и таблица цветов имеет до 256 элементов. Каждый байт массива данных определяет цвет одного пиксела. 24 - битовый образ имеет максимум 2 в 24-й степени цветов. Таблица цветов пуста, а цвет пикселов определяется пакетами из трех байтов, описывающими цветовые интенсивности красного, зеле- ного и голубого цветов. biCompression - тип сжатия. Может принимать значения: BI_RGB - сжатие отсутствует; BI_RLE8 - сжатие для формата 8 бит на пиксел; BI_RLE4 - сжатие для формата 4 бита на пиксел. biXPelsPerMeter и biYPelsPerMeter - могут использоваться для выбора из списка ресурсов пиктограммы, наиболее подходящей для данного устройства. biClrUsed - число цветов, используемых данныи битовым образом. Если 0, то используются все цвета палитры (указанные в массиве bmiColors). biClrImportant - используется для управления алгоритмом отоб- ражения цветов. Так, если четыре различных приложения отображают на экране по одному битовому образу с 75 цветами каждый, то адап- тер, выводящий 256 цветов одновременно, не сможет полностью ау- тентично отобразить на экране все 4 картинки. В этом случае ис- пользуется механизм замены цветов - ищется битовый образ с наи- меньшим приоритетом и его "лишние" цвета заменяются наиболее под- ходящими. typedef struct tagRGBQUAD { byte rgbRed; //интенсивность красного byte rgbGreen; //интенсивность зеленого byte rgbBlue; //интенсивность голубого byte rgbRserved; //не используется } RGBQUAD; После того, как все параметры битового образа определены, в файле идут сами скан-строки битовой плоскости, причем первой скан-строкой в формате DIB считается нижняя скан-строка (т.е. на- чало координат находится в левом нижнем углу изображения). Скан-строки выровнены по 32-битной границе - dword !!! Получишь адрес BITMAPINFOHEADER, через biSize от него будет палитра RGBQUAD размером biClrUsed*4 или 256*4 если biClrUsed=0 или её не будет если biBitCount=24 Затем будет сама картинка выравненнная на 4 байтную границу размером biSizeImage (про возможность другого порядка эт я малость перепутал - давно с этим не играл, единственное что - в современном варианте больше полей чем в этом древнем переводе, но biSize это учитывает . Тебе нужно на основании этого посчитать и заполнить BITMAPFILEHEADER, записать его, затем записать блок памяти начинающийся с полученного из буфера BITMAPINFOHEADER и размером который посчитаешь Ну и закрыть файл Успешной медитации
Ну и зачем этот геморрой, если есть GdipCreateBitmapFromHBITMAP? К тому же можно при этом также обойтись без CreateFile и иже с ним, а использовать GdipSaveImageToFile. И вообще, если цель - сохранение снимка экрана, то буфер обмена - далеко не лучший способ.
l_inc Я так понял цель - освоить работу с буфером, файлами и картинками - потому и указываю путь для медитации А русскую справку по нюансам GDI+ для асма он имхо всё равно не найдёт, да и на английском msdn вся основная справка в объектно плюсованной форме , посему бегиннеерсу разобраться сначала в этой справке, а потом в табличке перевода ++ в доступную из асма flat версию функций геморою точно не меньше, а познавательная сторона медитации над устройством обычного GDI имхо в любом случае штука пользительная
Теперь я начил понимать о чем говорил Subrealist: Код (Text): void SaveScreen(HWND hWnd) { HDC hDC, CompDC; SYSTEMTIME SysTm; BITMAP bmp; PBITMAPINFO pbmi; HBITMAP hBitmap; BITMAPFILEHEADER BFHeader; WORD ClrBits; DWORD dwTmp; LPBYTE lpBits; HANDLE hFile; char FileName[22], dir[MAX_PATH]; hDC=GetDC(hWnd); CompDC=CreateCompatibleDC(hDC); hBitmap=CreateCompatibleBitmap(hDC, XSize, YSize); SelectObject(CompDC,hBitmap); BitBlt(CompDC,0,0,XSize,YSize,hDC,0,0,SRCCOPY); ReleaseDC(hWnd, hDC); GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp); ClrBits=(WORD)bmp.bmPlanes*bmp.bmBitsPixel; strcpy(dir,ScreenSaveDir); if(ClrBits<=16) { ClrBits=16; } else { if(ClrBits<=24) { ClrBits=24; } else { ClrBits=32; } } pbmi=(PBITMAPINFO)new BYTE [sizeof(BITMAPINFOHEADER)]; pbmi->bmiHeader.biClrUsed=0; pbmi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth=bmp.bmWidth; pbmi->bmiHeader.biHeight=bmp.bmHeight; pbmi->bmiHeader.biPlanes=bmp.bmPlanes; pbmi->bmiHeader.biBitCount=bmp.bmBitsPixel; if(ClrBits<24) { pbmi->bmiHeader.biClrUsed=2^ClrBits; } pbmi->bmiHeader.biCompression=BI_RGB; pbmi->bmiHeader.biSizeImage=(pbmi->bmiHeader.biWidth)*pbmi->bmiHeader.biHeight*(ClrBits/8); pbmi->bmiHeader.biClrImportant=0; lpBits= (UCHAR*)VirtualAlloc(NULL, pbmi->bmiHeader.biSizeImage, MEM_TOP_DOWN | MEM_COMMIT, PAGE_READWRITE); GetDIBits(CompDC, hBitmap,0,pbmi->bmiHeader.biHeight,lpBits,pbmi,DIB_RGB_COLORS); GetLocalTime(&SysTm); sprintf(FileName,"\\%hu%02hu%02hu%02hu%02hu%02hu%03hu.bmp", SysTm.wYear,SysTm.wMonth,SysTm.wDay,SysTm.wHour,SysTm.wMinute,SysTm.wSecond,SysTm.wMilliseconds); strcat(dir,FileName); hFile=CreateFile(dir,GENERIC_READ|GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); BFHeader.bfType=0x4d42; BFHeader.bfSize=sizeof(BITMAPFILEHEADER)+pbmi->bmiHeader.biSize+pbmi->bmiHeader.biClrUsed*sizeof(RGBQUAD)+pbmi->bmiHeader.biSizeImage; BFHeader.bfReserved1=0; BFHeader.bfReserved2=0; BFHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+pbmi->bmiHeader.biSize+pbmi->bmiHeader.biClrUsed*sizeof(RGBQUAD); WriteFile(hFile, &BFHeader, sizeof(BITMAPFILEHEADER),(LPDWORD)&dwTmp,0); WriteFile(hFile, pbmi,sizeof(BITMAPINFOHEADER)+pbmi->bmiHeader.biClrUsed*sizeof(RGBQUAD),(LPDWORD)&dwTmp,0); WriteFile(hFile,lpBits,pbmi->bmiHeader.biSizeImage,&dwTmp,0); CloseHandle(hFile); DeleteDC(CompDC); DeleteObject(hBitmap); VirtualFree(lpBits, 0, MEM_RELEASE) ; delete pbmi; }
Y_Mur Аргумент засчитан. А в качестве справки можно использовать http://com.it-berater.org/gdiplus/GdiPlus.htm По крайней мере мне вполне хватало. Там есть прототипы в Basic-синтаксисе, которые редко кому непонятны.
Правильно, ли я определил адрес ? Код (Text): .data ? bmfh BITMAPFILEHEADER ? bmih BITMAPINFOHEADER ? .code mov eax, bmih add eax, sizeof BITMAPFILEHEADER mov bmfh, [eax]
Y_Mur Вобщем застрял я; Код (Text): .386 .model flat, stdcall option casemap :none ; case sensitive ;------------------------------------------------------------------------ include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ .data File_Name db "MyBMP.bmp", 0 ;------------------------------------------------------------------------ .data ? dwTmp dw ? bmfh BITMAPFILEHEADER ?; Заголовок файла изображения bmih BITMAPINFOHEADER ?; Информационная структура изображения hFile db ? ;------------------------------------------------------------------------ .code start: invoke keybd_event,VK_SNAPSHOT,0,0,0; Имитируем нажатие клавиши Print Screen ; читаем из буфера invoke OpenClipboard, hWnd; Открываем буфер invoke GetClipboardData, CF_DIB; mov bmih,eax; В eax -адрес BITMAPINFO invoke CloseClipboard; Закрываем буфер xor eax,eax ; обнуляем eax ;------------------------------------------------------------------------ ;Заполняем структуру BITMAPFILEHEADER mov bmfh.bfType, BM ; тип файла (для битового образа - BM) mov bmfh.bfSize, sizeof BITMAPFILEHEADER ; размер файла mov bmfh.bfReserved1, 0 mov bmfh.bfReserved2, 0 ;------------------------------------------------------------------------ add eax, sizeof BITMAPFILEHEADER add eax, sizeof BITMAPINFOHEADER add eax, sizeof RGBQUAD ;------------------------------------------------------------------------ mov bmfh.bfOffBits,eax; смещение данных битового образа от заголовка xor eax,eax; обнуляем eax mov eax, bmih add eax, sizeof BITMAPFILEHEADER mov bmfh, [eax] xor eax,eax; обнуляем eax ;------------------------------------------------------------------------ ;Заполняем структуру BITMAPINFOHEADER mov bmih.biSize, sizeof BITMAPINFOHEADER mov bmih.biWidth, ? mov bmih.biHeight, ? mov bmih.biPlanes, 1;для цветов кодируемых последовательно , всегда 1 mov bmih.biBitCount, 32 mov bmih.biCompression, BI_RGB; так, как сжатия нет mov bmih.biSizeImage, 0; Если компрессия не используется (в поле biCompression находится значение BI_RGB), содержимое поля biSizeImage может быть равно 0 mov bmih.biXPelsPerMeter,0 mov bmih.biYPelsPerMeter,0 mov bmih.biClrUsed, 0 mov bmih.biClrImportant, 0 ;------------------------------------------------------------------------ ;Заполняем структуру RGBQUAD ;------------------------------------------------------------------------ invoke CreateFile,offset File_Name , GENERIC_READ or GENERIC_WRITE, 0, 0, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, 0; Содаем файл для битмапа mov hFile,eax ;------------------------------------------------------------------------ invoke WriteFile hFile, здесь будет адрес BITMAPFILEHEADER , sizeof BITMAPFILEHEADER, offset dwTmp, 0; Записываем заголовок invoke WriteFile hFile, bmih, sizeof BITMAPINFOHEADER+sizeof RGBQUAD, offset dwTmp, 0; Записываем информацию о битмапе invoke WriteFile hFile, здесь будет адрес массива бит, bmih.biSizeImage, offset dwTmp, 0; Записываем массив битов ;------------------------------------------------------------------------ invoke CloseHandle, hFile; закрываем хэндл файла invoke ExitProcess, 0 end start
hakeem BITMAPFILEHEADER ты определяешь и заполняешь самостоятельно (поскольку ты сам создаёшь файл, а не редактируешь готовый). Располагается BITMAPFILEHEADER в файле перед BITMAPINFOHEADER. BITMAPINFOHEADER тебе как раз определять и заполнять не нужно - это сделает тот кто помещает картинку в ClipBoard, а тебе нужно добыть из него информацию Код (Text): .686 .model flat,stdcall option casemap :none ; case sensitive include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\comdlg32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\comdlg32.lib ;------------------------------------------------------------------------------------------ ; Макрофункция для определения строк ;------------------------------------------------------------------------------------------ ; Строка с завершающим нулём zSTR MACRO P1, P2 LOCAL zSTRname IFB <P2>; Строка без имени .data zSTRname db P1, 0 .code EXITM <offset zSTRname> ELSE ; Строка с именем .data P1 db P2, 0 .code EXITM <offset P1> ENDIF ENDM ; можно вместо этого определять строки в сегменте ; .data ; FileName db 'TestBMP.bmp', 0 ; и обращаться к ним по offset FileName ; но имхо с макросом удобнее, кроме того он позволяет определять и именованные переменные ; zSTR(szError, 'Ошибка'), к котороым потом можно обращаться offset szError ;====================================================================================================== .data? h_File dd ? write_result dd ? ; результат записи (количество записанных байт) szRezult db 1024 dup (?) ; Строка с результатами BMP_File_Header BITMAPFILEHEADER <?> ; Структура заголовка файла содержащего BITMAP .code start: invoke OpenClipboard, 0 invoke GetClipboardData, CF_DIB .if eax == 0 invoke MessageBox, NULL, zSTR('Поместите BitMap в буфер обмена'),\ zSTR(szError, 'Ошибка'), MB_ICONERROR or MB_OK jmp EndProg .endif mov edi, eax ; запомнили адрес BitMap (регистры ebx, edi, esi не изменяются при вызове API) ; === Вычисляем размер файла === mov esi, [edi.BITMAPINFOHEADER.biSize] ; размер заголовка BitMap (для картинки из буфера) ; Размер палитры mov eax, [edi.BITMAPINFOHEADER.biClrUsed] shl eax, 2 ; biClrUsed * 4 байта = размер палитры add esi, eax ; сложить размер заголовка и палитры ; ==== Заполняем заголовок файла ==== mov WORD PTR [BMP_File_Header.bfType], 'MB' ; Сигнатура BitMap файла add esi, sizeof BITMAPFILEHEADER ; размер заголовка с учётом выравнивания mov [BMP_File_Header.bfOffBits], esi ; смещение начала картинки в файле movzx ebx, WORD PTR [edi.BITMAPINFOHEADER.biBitCount] ; Количество бит на цвет mov eax, [edi.BITMAPINFOHEADER.biSizeImage] ; Размер самой картинки .if eax == 0 ; значит тот кто писал в буфер схалтурил и придётся считать размер картинки самостоятельно ;) mov eax, [edi.BITMAPINFOHEADER.biWidth] .if ebx == 1 ; бит на цвет shr eax, 3 ; /8 поскольку 8 пикселей на байт ; учитываем выравнивание строк на границу DWORD mov ecx, eax and eax, not 3 ; отбросить младшие два бита .if ecx & 3 ; если в них был не ноль add eax, 4 ; то учесть выравнивающие байты .endif .elseif ebx == 4 ; бита на цвет shr eax, 1 ; /2 поскольку 4 бита это 2 пиксела на байт ; учитываем выравнивание строк на границу DWORD mov ecx, eax and eax, not 3 ; отбросить младшие два бита .if ecx & 3 ; если в них был не ноль add eax, 4 ; то учесть выравнивающие байты .endif .elseif ebx == 8 ; бит на цвет (1 байт на пиксел) ; учитываем выравнивание строк на границу DWORD mov ecx, eax and eax, not 3 ; отбросить младшие два бита .if ecx & 3 ; если в них был не ноль add eax, 4 ; то учесть выравнивающие байты .endif .elseif ebx == 24 ; бита на цвет shl eax, 1 ; *2 add eax, [edi.BITMAPINFOHEADER.biWidth] ; *3 поскольку 24 бита это 3 байта на цвет ; учитываем выравнивание строк на границу DWORD mov ecx, eax and eax, not 3 ; отбросить младшие два бита .if ecx & 3 ; если в них был не ноль add eax, 4 ; то учесть выравнивающие байты .endif .elseif ebx == 32 shl eax, 2 ; *4 - здесь длина строк выравнивается сама собой ;) ; зато специфическое выравнивание для начала картинки на 16 байт см. ниже .endif xor edx, edx ; неявно используется в mul mul [edi.BITMAPINFOHEADER.biHeight] ; edx:eax * biHeight = размер картинки в байтах mov [edi.BITMAPINFOHEADER.biSizeImage], eax ; пусть теперь хоть в файле сохранится :) .endif .if ebx == 32 ; учитываем выравнивание для начала 32битной картинки add esi, 12 mov [BMP_File_Header.bfOffBits], esi .endif add esi, eax ; наконец добавляем размер картинки mov [BMP_File_Header.bfSize], esi ; размер файла сохраняем в его заголовке ; ===== Информация о результатах ===== invoke wsprintf, addr szRezult, zSTR(<'Размер файла %d байт', 13, 10, 'Глубина цвета %d бит', 13, 10, 'В палитре %d цветов'>), \ [BMP_File_Header.bfSize], ebx, [edi.BITMAPINFOHEADER.biClrUsed] invoke MessageBox, NULL, addr szRezult, zSTR('Параметры BitMap'), MB_OK invoke CreateFile, zSTR('TestBMP.bmp'), GENERIC_READ or GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 .if eax == INVALID_HANDLE_VALUE invoke MessageBox, NULL, zSTR('Не удалось создать файл'),\ offset szError, MB_ICONERROR or MB_OK jmp EndProg .endif mov [h_File], eax ; --- Запись подготовленного заголовка файла --- invoke WriteFile, [h_File], offset BMP_File_Header, sizeof BMP_File_Header, offset write_result, 0 ; --- Запись BITMAP (непосредственно из буфера обмена) --- invoke WriteFile, [h_File], edi, [BMP_File_Header.bfSize], offset write_result, 0 invoke CloseHandle, [h_File] ; Файл записан закрываем ; если кроме записи BitMap нужно что-то ещё, то обязательно сначала скопировать BitMap в свою память и сразу закрыть ClipBoard invoke CloseClipboard EndProg: invoke ExitProcess, NULL end start l_inc А ты енту GdipSaveImageToFile сам пробовал? У меня она создаёт файл и обламывается с левыми кодами ошибки ничего туда не записав... Правда может потому что GUID ImageFormatBMP, 0b96b3cabh, 0728h, 11d3h, 9dh, 7bh, 00h, 00h, 0f8h, 1eh, 0f3h, 2eh брал из инклюд, поскольку добывать его перечислением как советует M$ чего-то не очень радует
Y_Mur Пробовал вообще. И вполне прокатывал стандартный GUID. На сях (в предписаном ООП-шном виде) отрабатывал и на VB отрабатывал (в функциональном виде). На асме не пробовал: сегодня к вечеру попробую.
Охладили пыл Спасибо, скомпилил Ваш пример. Довольно эфектно, с информацией о параметрах bitmap Значит я не в ту сторону копал... И особенно понравилось про пляску с бубном...(представить только)
hakeem Это совершенно напрасно Просто ты случайно ступил на скользкую почву BitMap-ов А так большинство задач решается просто и изящно Осваивай Исцелиона - многое прояснится Русское описание API конечно в разных видах попадается, но и английского MSDN бояться не стоит - он там писан индусами для индусов потому упрощён дальше некуда и осваивается легко, тем более наглядных примеров полно. Только лучше установить его локально, а то в онлайне дико трафик жрёт. Можно отсюда (там версия для XP и Server 2003, соответственно нет данных для Висты) или поискать посвежее, если Виста нужна.