Работа с буфером обмена средствами WinAPI

 
0
 
Delphi, Kylix & Pascal
ava
lollollollol | 27.03.2013, 11: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; //Заканчиваю работать с буфером


Прошу помочь с вопросом, спасибо
Kommentare (11)
ava
Akella | 27.03.2013, 12:56 #
Нужно обязательно через winapi?
ava
Evjeny | 27.03.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.
ava
lollollollol | 27.03.2013, 18:29 #
Цитата


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


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

Цитата


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


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


А вот обратно записать не получается. Попытался также вытащить код из этого uses.
Но в результате в буфере появляется чтото непонятное, типа
Цитата


XпJ€яJ’[email protected]



Делаю так:

procedure SetBuffer(Format:Word; Buffer:pointer; Size:Integer);
var
  Data:THandle;
  DataPtr:Pointer;
begin
  OpenClipboard(0);
  try
    Data := GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, Size);
    try
      DataPtr := GlobalLock(Data);
      try
        Move(Buffer, DataPtr^, Size); 
        EmptyClipboard;
        SetClipboardData(Format, Data);
      finally
        GlobalUnlock(Data);
      end;
    except
      GlobalFree(Data);
      raise;
    end;
  finally
    CloseClipboard;
  end;
end;

procedure SetAsText(const Value: string);
begin
  SetBuffer(CF_TEXT, @Value, Length(Value) + 1);
end;
ava
Evjeny | 27.03.2013, 20:25 #
Я в делфи не силен - CopyScreenToClipboard это функция делфи или самописаная?

как вариант узнать формат содержимого буфера обмена (GetClipboardFormatName), и уже от этого отталкиваться - найти описание формата и т.д.
ava
lollollollol | 27.03.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, но мне нельзя использовать это.
ava
Evjeny | 27.03.2013, 21:21 #
да это HBITMAP, соответственно и работать с ним можно через win api...

здесь пример (правда на с++), как сохранить это все в файл....
ava
lollollollol | 27.03.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 записать, который сейчас в буфере
ava
Evjeny | 27.03.2013, 22:33 #
нет так не получится...картинка хранится в буфере в определенном формате (bmp в данном случае), т.е. чтобы вычислить тот же размер, необходимо обращаться к полям заголовка файла...
думаю в сети можно найти примеры работы с bmp форматом, на делфи в том числе...на крайний случай можно почитать описание формата и написать самому...

я бы помог кодом, но с делфи давно не общался, да и иде самой нету...
ava
lollollollol | 28.03.2013, 07:04 #
В любом случае это просто набор байт, нужно знать лишь начало, и размер. Так что думаю получится.

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

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

//len:=40+(GetSystemMetrics(0)*GetSystemMetrics(1)*3;
procedure SaveBitmap(buff:pointer;len:integer);
var
  Data: THandle;
  DataPtr: Pointer;
begin
  OpenClipboard(0);
  try
    Data := GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE,len);
    try
      DataPtr := GlobalLock(Data);
      try
        CopyMemory(buff,DataPtr,len);
      finally
        GlobalUnlock(Data);
      end;
    except
      GlobalFree(Data);
      raise;
    end;
  finally
    CloseClipboard;
  end;
end;

Почемут одни нули в в результате получаю.
ava
lollollollol | 28.03.2013, 20:44 #

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

После res:=GlobalLock(Data); показало
Неверный дескриптор окна, непойму что ему не нравится, остальные функции без ошибок отработали, проверил этим же кодом
Registrieren Sie sich oder melden Sie sich an, um schreiben zu können.
Unternehmen des Tages
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Mitwirkende
advanced
Absenden