代码是直截了当的……但我仍然很想念一些方面……
看到这段代码:
{ nsScreenshot NSIS Plugin (c) 2003: Leon Zandman (leon@wirwar.com) Re-compiled by: Linards Liepins (linards.liepins@gmail.com) Code by: http://www.delphitricks.com/source-code/forms/make_a_desktop_screenshot.html (e) 2012. } library nsScreenshot; uses nsis in './nsis.pas',Windows,Jpeg,graphics,types,SysUtils; const USER32 = 'user32.dll'; type HWND = type LongWord; {$EXTERNALSYM HWND} HDC = type LongWord; {$EXTERNALSYM HDC} BOOL = LongBool; {$EXTERNALSYM BOOL} {$EXTERNALSYM GetDesktopWindow} function GetDesktopWindow: HWND; stdcall; external USER32 name 'GetDesktopWindow'; {$EXTERNALSYM GetWindowDC} function GetWindowDC(hWnd: HWND): HDC; stdcall; external USER32 name 'GetWindowDC'; {$EXTERNALSYM GetWindowRect} function GetWindowRect(hWnd: HWND; var lpRect: TRect): BOOL; stdcall; external USER32 name 'GetWindowRect'; {$EXTERNALSYM ReleaseDC} function ReleaseDC(hWnd: HWND; hDC: HDC): Integer; stdcall; external user32 name 'ReleaseDC'; function GetScreenshot(Filename: string; Hwnd: HWND; var Width: integer; var Height: integer): boolean; forward; function ScreenShot(Bild: TBitMap; hWnd: HWND): boolean; forward; function Grab_FullScreen(hwndParent: HWND; string_size: integer; variables: PChar; stacktop: pointer):integer; cdecl; var buf: array[0..1024] of char; W,H: integer; begin Result := 0; // set up global variables Init(hwndParent,string_size,variables,stacktop); // Get filename to save to PopString;//(@buf); // Get a full-screen screenshot if GetScreenShot(buf,GetDesktopWindow,W,H) then begin // Everything went just fine... // Push image dimensions onto stack PushString(PChar(IntToStr(H))); PushString(PChar(IntToStr(W))); // Push result onto stack PushString(PChar('ok')); Result := 1; end else begin // Something went wrong... PushString(PChar('error')); end; end; function Grab(hwndParent: HWND; string_size: integer; variables: PChar; stacktop: pointer):integer; cdecl; var buf: array[0..1024] of char; grabWnd: HWND; Filename: string; W,stacktop); try // Get filename to save to PopString;//(@buwf); Filename := buf; // Get window handle of window to grab PopString;//(@buf); grabWnd := StrToInt(buf); except PushString(PChar('error')); exit; end; // Get screenshot of parent windows (NSIS) if GetScreenShot(Filename,grabWnd,H) then begin // Everything went just fine... // Push image dimensions onto stack PushString(PChar(IntToStr(H))); PushString(PChar(IntToStr(W))); // Push result onto stack PushString(PChar('ok')); Result := 1; end else begin // Something went wrong... PushString(PChar('error')); end; end; function GetScreenshot(Filename: string; Hwnd: HWND; var Width: integer; var Height: integer): boolean; var bmp: TBitmap; begin Result := false; // Get screenshot bmp := TBitmap.Create; try try if ScreenShot(bmp,Hwnd) then begin Width := bmp.Width; Height := bmp.Height; bmp.SaveToFile(Filename); Result := true; end; except // Catch exception and do nothing (function return value remains 'false') end; finally bmp.Free; end; end; function ScreenShot(Bild: TBitMap; hWnd: HWND): boolean; var c: TCanvas; r,t: TRect; h: THandle; begin Result := false; c := TCanvas.Create; c.Handle := GetWindowDC(GetDesktopWindow); h := hWnd; if h <> 0 then begin GetWindowRect(h,t); try r := Rect(0,t.Right - t.Left,t.Bottom - t.Top); Bild.Width := t.Right - t.Left; Bild.Height := t.Bottom - t.Top; Bild.Canvas.CopyRect(r,c,t); finally ReleaseDC(0,c.Handle); c.Free; end; Result := true; end; end; function GetScreenToFile(FileName: string; Quality: Word; Percent: Word): boolean; var Bmp: TBitmap; Jpg: TJpegImage; begin Bmp := TBitmap.Create; Jpg := TJpegImage.Create; try Bmp.Width := GetDeviceCaps(GetDc(0),8) * Percent div 100; Bmp.Height := GetDeviceCaps(GetDc(0),10) * Percent div 100; SetStretchBltMode(Bmp.Canvas.Handle,HALFTONE); StretchBlt(Bmp.Canvas.Handle,Bmp.Width,Bmp.Height,GetDc(0),GetDeviceCaps(GetDc(0),8),10),SRCCOPY); Jpg.Assign(Bmp); Jpg.CompressionQuality := Quality; Jpg.SaveToFile(FileName); finally Jpg.free; Bmp.free; end; end; function ScreenToFile(hwndParent: HWND; string_size: integer; variables: PChar; stacktop: pointer):integer; cdecl; var buf: array[0..1024] of char; grabWnd: HWND; Filename: string; W,H: integer; begin Result := 0; Init(hwndParent,stacktop); try PopString; Filename := buf; PopString; grabWnd := StrToInt(buf); except PushString(PChar('error')); exit; end; if GetScreenToFile(Filename,H) then begin PushString(PChar('ok')); Result := 1; end else begin PushString(PChar('error')); end; end; //ScreenToFile('SHOT.JPG',50,70); exports Grab_FullScreen,Grab,ScreenToFile; begin end.
搜索ScreenToFile.
解决方法
1.1.关于错误的字符串:
从您自己的回答中发现您正在使用ANSI版本的NSIS.由于您在Delphi XE中编译的库代码中使用了字符串,Char和PChar映射到Unicode字符串,因此您在NSIS安装应用程序和库错误数据之间传递.
1.2.关于核心插件单元的另一种观点:
我已经检查了你的略有修改的插件核心单元NSIS.pas
,并且存在一些问题,这会阻止你的插件正常工作.但是,正如我所看到的那样,我首先想到的是将独立的过程和函数包装到一个类中.这就是我所做的.
1.3. NSIS.pas v2.0:
由于您目前在your code
中仅使用了原始核心单元中的3个功能,因此我简化了仅使用这些功能的类(以及一个用于显示消息框的额外功能).所以这是修改后的插件核心单元的代码.我不是数据操作方面的专家,所以也许可以简化以下代码,但它至少在Delphi XE2和Delphi 2009中有效,我在那里测试过它.这是代码:
unit NSIS; interface uses Windows,CommCtrl,SysUtils; type PParamStack = ^TParamStack; TParamStack = record Next: PParamStack; Value: PAnsiChar; end; TNullsoftInstaller = class private FParent: HWND; FParamSize: Integer; FParameters: PAnsiChar; FStackTop: ^PParamStack; public procedure Initialize(Parent: HWND; ParamSize: Integer; Parameters: PAnsiChar; StackTop: Pointer); procedure PushString(const Value: string = ''); function PopString: string; function MessageDialog(const Text,Caption: string; Buttons: UINT): Integer; end; var NullsoftInstaller: TNullsoftInstaller; implementation procedure TNullsoftInstaller.Initialize(Parent: HWND; ParamSize: Integer; Parameters: PAnsiChar; StackTop: Pointer); begin FParent := Parent; FParamSize := ParamSize; FParameters := Parameters; FStackTop := StackTop; end; procedure TNullsoftInstaller.PushString(const Value: string = ''); var CurrParam: PParamStack; begin if Assigned(FStackTop) then begin CurrParam := PParamStack(GlobalAlloc(GPTR,SizeOf(TParamStack) + FParamSize)); StrLCopy(@CurrParam.Value,PAnsiChar(AnsiString(Value)),FParamSize); CurrParam.Next := FStackTop^; FStackTop^ := CurrParam; end; end; function TNullsoftInstaller.PopString: string; var CurrParam: PParamStack; begin Result := ''; if Assigned(FStackTop) then begin CurrParam := FStackTop^; Result := String(PAnsiChar(@CurrParam.Value)); FStackTop^ := CurrParam.Next; GlobalFree(HGLOBAL(CurrParam)); end; end; function TNullsoftInstaller.MessageDialog(const Text,Caption: string; Buttons: UINT): Integer; begin Result := MessageBox(FParent,PChar(Text),PChar(Caption),Buttons); end; initialization NullsoftInstaller := TNullsoftInstaller.Create; finalization if Assigned(NullsoftInstaller) then NullsoftInstaller.Free; end.
正如您所看到的,声明了NullsoftInstaller全局变量,它允许您使用我之前已经使用过的函数的类.使用此变量中的对象实例可以简化初始化和终结部分,其中创建此对象实例并在库被加载时分配给此变量,并在释放库时释放.
所以你需要在代码中做的唯一事情就是像这样使用这个NullsoftInstaller全局变量:
uses NSIS; function ScreenToFile(Parent: HWND; ParamSize: Integer; Parameters: PAnsiChar; StackTop: Pointer): Integer; cdecl; var InputString: string; begin Result := 0; // this is not necessary,if you keep the NullsoftInstaller object instance // alive (and there's even no reason to free it,since this will be done in // the finalization section when the library is unloaded),so the following // statement has no meaning when you won't free the NullsoftInstaller if not Assigned(NullsoftInstaller) then NullsoftInstaller := TNullsoftInstaller.Create; // this has the same meaning as the Init procedure in the original core unit NullsoftInstaller.Initialize(Parent,ParamSize,Parameters,StackTop); // this is the same as in the original,except that returns a native string InputString := NullsoftInstaller.PopString; NullsoftInstaller.MessageDialog(InputString,'PopString Result',0); // and finally the PushString method,this is also the same as original and // as well as the PopString supports native string for your Delphi version NullsoftInstaller.PushString('ok'); end;
2. Aero合成窗口的屏幕截图
这是我尝试的截图程序,代码中的TakeScreenshot.它需要一个额外的参数DropShadow,它应该在启用Aero组合时拍摄包括窗口投影的屏幕截图.然而,我找不到一种方法如何以不同的方式做到这一点,而不是在被捕获的窗口后面放置假窗口.它有一个很大的弱点;有时会发生捕获完成后假窗口没有完全显示的情况,因此它会捕获当前桌面的屏幕截图,而不是后面的白色虚假窗口(尚未显示).因此,将DropShadow设置为True现在正处于试验阶段.
当DropShadow为False(没有投影的屏幕截图)时,它可以正常工作.我的猜测是,由于上面描述的Unicode Delphi与ANSI NSIS问题,您传递了错误的参数.
library nsScreenshot; uses Windows,SysUtils,Types,Graphics,DwmApi,Forms,JPEG,NSIS; procedure CalcCloseCrop(Bitmap: TBitmap; const BackColor: TColor; out CropRect: TRect); var X: Integer; Y: Integer; Color: TColor; Pixel: PRGBTriple; RowClean: Boolean; LastClean: Boolean; begin LastClean := False; CropRect := Rect(Bitmap.Width,Bitmap.Height,0); for Y := 0 to Bitmap.Height-1 do begin RowClean := True; Pixel := Bitmap.ScanLine[Y]; for X := 0 to Bitmap.Width - 1 do begin Color := RGB(Pixel.rgbtRed,Pixel.rgbtGreen,Pixel.rgbtBlue); if Color <> BackColor then begin RowClean := False; if X < CropRect.Left then CropRect.Left := X; if X + 1 > CropRect.Right then CropRect.Right := X + 1; end; Inc(Pixel); end; if not RowClean then begin if not LastClean then begin LastClean := True; CropRect.Top := Y; end; if Y + 1 > CropRect.Bottom then CropRect.Bottom := Y + 1; end; end; with CropRect do begin if (Right < Left) or (Right = Left) or (Bottom < Top) or (Bottom = Top) then begin if Left = Bitmap.Width then Left := 0; if Top = Bitmap.Height then Top := 0; if Right = 0 then Right := Bitmap.Width; if Bottom = 0 then Bottom := Bitmap.Height; end; end; end; procedure TakeScreenshot(WindowHandle: HWND; const FileName: string; DropShadow: Boolean); var R: TRect; Form: TForm; Bitmap: TBitmap; Target: TBitmap; DeviceContext: HDC; DesktopHandle: HWND; ExtendedFrame: Boolean; const CAPTUREBLT = $40000000; begin ExtendedFrame := False; if DwmCompositionEnabled then begin DwmGetWindowAttribute(WindowHandle,DWMWA_EXTENDED_FRAME_BOUNDS,@R,SizeOf(TRect)); if DropShadow then begin ExtendedFrame := True; R.Left := R.Left - 30; R.Top := R.Top - 30; R.Right := R.Right + 30; R.Bottom := R.Bottom + 30; end; end else GetWindowRect(WindowHandle,R); SetForegroundWindow(WindowHandle); Bitmap := TBitmap.Create; try Bitmap.PixelFormat := pf24bit; Bitmap.SetSize(R.Right - R.Left,R.Bottom - R.Top); if ExtendedFrame then begin DesktopHandle := GetDesktopWindow; DeviceContext := GetDC(GetDesktopWindow); Form := TForm.Create(nil); try Form.Color := clWhite; Form.BorderStyle := bsNone; Form.AlphaBlend := True; Form.AlphaBlendValue := 0; ShowWindow(Form.Handle,SW_SHOWNOACTIVATE); SetWindowPos(Form.Handle,WindowHandle,R.Left,R.Top,R.Right - R.Left,R.Bottom - R.Top,SWP_NOACTIVATE); Form.AlphaBlendValue := 255; BitBlt(Bitmap.Canvas.Handle,DeviceContext,SRCCOPY or CAPTUREBLT); finally Form.Free; ReleaseDC(DesktopHandle,DeviceContext); end; Target := TBitmap.Create; try CalcCloseCrop(Bitmap,clWhite,R); Target.SetSize(R.Right - R.Left,R.Bottom - R.Top); Target.Canvas.CopyRect(Rect(0,R.Bottom - R.Top),Bitmap.Canvas,R); Target.SaveToFile(FileName); finally Target.Free; end; end else begin DeviceContext := GetWindowDC(WindowHandle); try BitBlt(Bitmap.Canvas.Handle,SRCCOPY or CAPTUREBLT); finally ReleaseDC(WindowHandle,DeviceContext); end; Bitmap.SaveToFile(FileName); end; finally Bitmap.Free; end; end; function ScreenToFile(Parent: HWND; ParamSize: Integer; Params: PAnsiChar; StackTop: Pointer): Integer; cdecl; var I: Integer; FileName: string; DropShadow: Boolean; Parameters: array[0..1] of string; begin Result := 0; if not Assigned(NullsoftInstaller) then NullsoftInstaller := TNullsoftInstaller.Create; NullsoftInstaller.Initialize(Parent,Params,StackTop); for I := 0 to High(Parameters) do Parameters[I] := NullsoftInstaller.PopString; FileName := Parameters[1]; if not DirectoryExists(ExtractFilePath(FileName)) or not TryStrToBool(Parameters[0],DropShadow) then begin NullsoftInstaller.PushString('error'); NullsoftInstaller.PushString('Invalid parameters!'); Exit; end; try TakeScreenshot(Parent,FileName,DropShadow); NullsoftInstaller.PushString('ok'); Result := 1; except on E: Exception do begin NullsoftInstaller.PushString('error'); NullsoftInstaller.PushString(E.Message); NullsoftInstaller.MessageDialog(E.Message,'Error',0); end; end; end; exports ScreenToFile; begin end.