Reputation: 2005
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
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
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