Danny Rancher
Danny Rancher

Reputation: 2005

Saving images from the Clipboard using pure API

I'm having trouble saving images from the clipboard using pure API.

Here is my code. It creates 20 empty bmp files. I have checked dwSize and it is correct so it seems I am retreiving the data correctly, but I am writing to a file incorrectly.

  program WebcamTest;
  //cswi

  uses
    Windows;

  const
    WM_CAP_DRIVER_CONNECT = 1034;
    WM_CAP_GRAB_FRAME = 1084;
    //WM_CAP_SAVEDIB = 1049;
    WM_CAP_EDIT_COPY = 1054;
    WM_CAP_DRIVER_DISCONNECT = 1035;

  function SendMessageA(hWnd: Integer;
                        Msg: Integer;
                        wParam: Integer;
                        lParam: Integer): Integer;
                        stdcall;
                        external 'user32.dll' name 'SendMessageA';

  function capGetDriverDescriptionA(DrvIndex: Cardinal;
                                    Name: PAnsiChar;
                                    NameLen: Integer;
                                    Description: PAnsiChar;
                                    DescLen: Integer) : Boolean;
                                    stdcall;
                                    external 'avicap32.dll' name 'capGetDriverDescriptionA';

  function capCreateCaptureWindowA(lpszWindowName: PAnsiChar;
                                   dwStyle: Integer;
                                   x : Integer;
                                   y : Integer;
                                   nWidth : Integer;
                                   nHeight : Integer;
                                   ParentWin: Integer;
                                   nId: Integer): Integer;
                                   stdcall;
                                   external 'avicap32.dll' name 'capCreateCaptureWindowA';

  function IntToStr(i: Integer): String;
  begin
    Str(i, Result);
  end;

  var
    WebCamId : Integer;
    CaptureWindow : Integer;
    x : Integer;
    FileName : PAnsiChar;
    hData:  DWORD;
    pData:  Pointer;
    dwSize: DWORD;
    szText : AnsiString;
    FileHandle, BytesWritten : LongWord;
  begin
    WebcamId := 0;
    CaptureWindow := capCreateCaptureWindowA('CaptureWindow', 0, 0, 0, 0, 0, 0, 0);
    if CaptureWindow <> 0 then
    begin
      if SendMessageA(CaptureWindow, WM_CAP_DRIVER_CONNECT, WebCamId, 0) <> 1 then
      begin
        SendMessageA(CaptureWindow, WM_CAP_DRIVER_DISCONNECT, 0, 0);
      end
      else
      begin
        for x := 1 to 20 do // Take 20 photos.
        begin
          SendMessageA(CaptureWindow, WM_CAP_GRAB_FRAME, 0, 0);
          FileName := PAnsiChar('C:\Test' + IntToStr(x) + '.bmp');
          //SendMessageA(CaptureWindow, WM_CAP_SAVEDIB, 0, LongInt(FileName));
          SendMessageA(CaptureWindow, WM_CAP_EDIT_COPY, 0, LongInt(FileName));
          if OpenClipBoard(0) then
          begin
            hData := GetClipBoardData(CF_DIB);
            if hData <> 0 then
            begin
              pData := GlobalLock(hData);
              if pData <> nil then
              begin
                dwSize := GlobalSize(hData);
                if dwSize <> 0 then
                begin
                  FileHandle := CreateFileA(FileName, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN, 0);
                  WriteFile(FileHandle, pData, dwSize, BytesWritten, nil);
                  CloseHandle(FileHandle);
                end;
                GlobalUnlock(DWORD(pData));
              end;
            end;
            CloseClipBoard;
          end;
        end;
      end;
      SendMessageA(CaptureWindow, WM_CAP_DRIVER_DISCONNECT, 0, 0);
    end;
  end.

Upvotes: 1

Views: 2005

Answers (2)

Stijn Sanders
Stijn Sanders

Reputation: 36840

Since using the clipboard for something not in response to user interaction is so bad (very very bad), I'll provide another alternative to get to the grabbed frame's data. If you pass capCreateCaptureWindow a handle to a control of a form, after you send the WM_CAP_GRAB_FRAME message, you can use BitBlt to copy this data to a bitmap using the Canvas.Handle on the form and the bitmap. An important downside to this method is that the form is required to be visible on screen.

Upvotes: 1

Stijn Sanders
Stijn Sanders

Reputation: 36840

It is a very (very!) bad idea to (ab)use the clipboard to transfer image data from a capture window just because you want to get to the data in-memory, since you're writing to a file anyway I very warmly suggest you stick with the WM_CAP_SAVEDIB option. (Just to give you two very good reasons: in the clipboard it's out there accessible to other processes: security!; it drops whatever was in the clipboard: inconvenient!)

But to give an answer to your question: WriteFile takes Buffer as an untyped argument, so if you pass it a pointer it takes the value of the pointer, not the data pointed to, unless you specify it should do so, like this (caret suffix):

WriteFile(FileHandle, pData^, dwSize, BytesWritten, nil);

Upvotes: 4

Related Questions