Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Работа с буфером обмена средствами WinAPI


Автор: lollollollol 27.3.2013, 10:34
Здравствуйте.

Есть задача - Сделать скриншот экрана.
Условие - Текстовое значение буфера обмена должно остаться неизменным.

Я решил сделать так:
Код

//Копирую значение буфера в string
CopyScreenToClipboard(GetSystemMetrics(0),GetSystemMetrics(1)); //делаю скриншот
//Сохраняю его
//Из переменной копирую значение в буфер обмена


Если добавить uses clipbrd, то решение задачи не занимает больше пяти минут. 
Но необходимо сделать это использую лишь uses windows. 

Пытался получить текст так:

Код

      while (GetOpenClipboardWindow>0) do sleep(50); //Жду пока буфер освободится, то есть с ним перестанет работать другое приложение
      OpenClipboard(GetCurrentProcess); //Начинаю работу с буфером. Формы у ехе нет, поэтому вместо Application.Handle использую GetCurrentProcess
      TextBuf:=''; //Обнуляю значение текстовой переменной типа String
      if (IsClipboardFormatAvailable(CF_TEXT)=True) then begin //Если в буфере текст
        h:=GetClipboardData(CF_TEXT); //Получаю хендл позиции блока данных 
        {Проблема! Не могу понять как теперь скопировать эти данные в переменную. Ведь я не знаю даже длины данных}
      end;
      CloseClipboard; //Заканчиваю работать с буфером


Прошу помочь с вопросом, спасибо

Добавлено @ 10:45
Заглянул в USES, выдрал оттуда код


Код

function GetAsText: string;
var
  Data: THandle;
begin
  OpenClipboard(GetCurrentProcess); //Это я поменял, было Open
  Data := GetClipboardData(CF_TEXT);
  try
    if Data <> 0 then
      Result := PChar(GlobalLock(Data))
    else
      Result := '';
  finally
    if Data <> 0 then GlobalUnlock(Data);
    CloseClipboard;
  end;
end;


Но к моему удивлению Data=0. Видимо нельзя передавать GetCurrentProcess. А как можно получить хендл процесса, без формы?

Автор: Akella 27.3.2013, 12:56
Нужно обязательно через winapi?

Автор: Evjeny 27.3.2013, 13:06
CF_TEXT - кодировка ANSI, каждая строка заканчивается CRLF, в конце буфера 0

по поводу OpenClipboard - исходя из документации надо передавать 0:
Код

hWndNewOwner [in, optional]

Type: HWND

A handle to the window to be associated with the open clipboard. If this parameter is NULL, the open clipboard is associated with the current task.

Автор: lollollollol 27.3.2013, 18:29
Цитата

Нужно обязательно через winapi? 

Обязательно без использования любых uses кроме windows

Цитата

CF_TEXT - кодировка ANSI, каждая строка заканчивается CRLF, в конце буфера 0

Да, вот это я стормозил. Передал ноль, и смог получить текст из буфера
Посмотрев повнимательней Clipbrd, удалось и записать текст в буфер


Остался финальный вопрос, как записать скриншот, который находится в буфере в переменные
buff: array[0..20971520] of byte; //Массив байт
buff_size:integer; //Размер изображения


Может получить указатель на адрес памяти, а потом просто скопировать, но для этого нужно размер знать, да и как быть с типом данных?

Автор: Evjeny 27.3.2013, 20:25
Я в делфи не силен - CopyScreenToClipboard это функция делфи или самописаная?

как вариант узнать формат содержимого буфера обмена (GetClipboardFormatName), и уже от этого отталкиваться - найти описание формата и т.д.

Автор: lollollollol 27.3.2013, 20:59
Код

procedure CopyScreenToClipboard(x,y:integer);
var
  dx,dy: integer;
  hSourcDC, hDestDC, hBM, hbmOld: THandle;
begin
  dx := x;
  dy := y;
  hSourcDC := CreateDC('DISPLAY', nil, nil, nil);
  hDestDC := CreateCompatibleDC(hSourcDC);
  hBM := CreateCompatibleBitmap(hSourcDC, dx, dy);
  hbmold:= SelectObject(hDestDC, hBM);
  BitBlt(hDestDC, 0, 0, dx, dy, hSourcDC, 0, 0, SRCCopy);
  OpenClipBoard(0);
  EmptyClipBoard;
  SetClipBoardData(CF_Bitmap, hBM);
  CloseClipBoard;
  SelectObject(hDestDC,hbmold);
  DeleteObject(hbm);
  DeleteDC(hDestDC);
  DeleteDC(hSourcDC);
end;

Должно быть изображение BitMap, всё что есть для этого типа расчитано что я загружу его в TBitmap, но мне нельзя использовать это.

Автор: Evjeny 27.3.2013, 21:21
да это HBITMAP, соответственно и работать с ним можно через win api...

http://www.sql.ru/forum/actualthread.aspx?tid=885895 пример (правда на с++), как сохранить это все в файл....

Автор: lollollollol 27.3.2013, 21:47
А если за основу взять функцию которая копирует текст:

Код

function GetBitMap: string;
var
  Data: THandle;
begin
  OpenClipboard(0);
  Data := GetClipboardData(CF_BITMAP); //Тут указываю тип
  try
    if Data <> 0 then
     Result := PChar(GlobalLock(Data))  //А вот как прописать тут - не пойму. 
//Я ведь даже размера изображения не могу узнать, чтобы скопировать байты

    else
      ZeroMemory(@buff,sizeof(buffer)); //Если ничего в буфере нет, очищу и массив
  finally
    if Data <> 0 then GlobalUnlock(Data);
    CloseClipboard;
  end;
end;


Я верно мыслю? Указываю необходимый тип, теперь осталось получить информацию. Но не пойму как в массив записать BitMap, который сейчас в буфере

Автор: Evjeny 27.3.2013, 22:33
нет так не получится...картинка хранится в буфере в определенном формате (http://ru.wikipedia.org/wiki/BMP в данном случае), т.е. чтобы вычислить тот же размер, необходимо обращаться к полям заголовка файла...
думаю в сети можно найти примеры работы с bmp форматом, на делфи в том числе...на крайний случай можно почитать описание формата и написать самому...

я бы помог кодом, но с делфи давно не общался, да и иде самой нету...

Автор: lollollollol 28.3.2013, 07:04
В любом случае это просто набор байт, нужно знать лишь начало, и размер. Так что думаю получится.

BMP это ведь просто формат где каждый пиксель это цвет, в формате RGP и занимает он 3 байта.

Значит размер изображения должен быть
Длина*Ширина*3. 
Хотя сомневаюсь, ведь в начале .bmp файла есть ряд символов которые уникальны для каждого формата

Автор: lollollollol 28.3.2013, 09:45
Код

//len:=40+(GetSystemMetrics(0)*GetSystemMetrics(1)*3;
procedure SaveBitmap(buff:pointer;len:integer);
var
  Data: THandle;
  res:pointer;
begin
  OpenClipboard(0);
  Data := GetClipboardData(CF_BITMAP);
  try
    if (Data<>0) then begin
      res:=GlobalLock(Data); //res=nil, но в буфере точно есть изображение, без проблем вставляется в любой графический редактор
      if (res<>nil) then begin
        CopyMemory(buff,res,len);
      end;
    end;
  finally
    if Data <> 0 then GlobalUnlock(Data);
    CloseClipboard;
  end;
end;


Автор: lollollollol 28.3.2013, 20:44
Код

MessageBox(0,Pchar(SysErrorMessage(GetLastError)),'',0);

После res:=GlobalLock(Data); показало
Неверный дескриптор окна, непойму что ему не нравится, остальные функции без ошибок отработали, проверил этим же кодом

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)