6

元のプラグインより3年遅れて公開されたコードを追加しましたが、それでもエラーが返されます...

コードは単純明快です...しかし、それでも私はおそらくいくつかの側面を見逃しています...

このコードを参照してください:

{
        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,H: integer;
begin
  Result := 0;
  // set up global variables
  Init(hwndParent,string_size,variables,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,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 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, 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, 0, 0, Bmp.Width, Bmp.Height, GetDc(0), 0, 0, GetDeviceCaps(GetDc(0), 8), GetDeviceCaps(GetDc(0), 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,string_size,variables,stacktop);
  try
    PopString;
    Filename := buf;
    PopString;
    grabWnd := StrToInt(buf);
  except
    PushString(PChar('error'));
    exit;
  end;
  if GetScreenToFile(Filename,W,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ます。

ご入力いただきありがとうございます。このプラグインは、インストーラーのドキュメント生成の自動化に不可欠です。

4

3 に答える 3

7

1. NSIS プラグイン コア ユニットの問題:

1.1。間違った文字列について:

あなた自身の回答投稿から、NSISのANSIバージョンを使用していることがわかりました。Delphi XE でコンパイルされたライブラリ コードで使用しているためstringCharPCharが Unicode 文字列にマップされているため、NSIS セットアップ アプリケーションとライブラリの間で間違ったデータを渡していました。

1.2. コア プラグイン ユニットの別のビュー:

わずかに変更されたプラグイン コア ユニットNSIS.pasを確認しましたが、プラグインが正常に動作しないいくつかの問題があります。しかし、この単元を見て最初に頭に浮かんだのは、スタンドアロンのプロシージャと関数をクラスにラップすることでした。そして、それが私がやったことです。

1.3。NSIS.pas v2.0:

現在、元のコア ユニットの関数を 3 つしか使用していないため、これらの関数your codeのみを使用するようにクラスを単純化しました (メッセージ ボックスの表示用に 1 つの追加機能を追加しました)。これが、変更されたプラグイン コア ユニットのコードです。私はデータ操作の専門家ではないので、次のコードは単純化できるかもしれませんが、少なくとも 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.

1.4。変更されたプラグイン コア ユニットの使用法:

ご覧のとおり、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コード内です。DropShadowAero コンポジションが有効な場合、ウィンドウのドロップ シャドウを含むスクリーンショットを取得する必要がある追加のパラメーターが必要です。ただし、キャプチャしたウィンドウの後ろに偽のウィンドウを配置する以外の方法でそれを行う方法を見つけることができませんでした。1 つの大きな弱点があります。キャプチャが完了したときに偽のウィンドウが完全に表示されないことがあるため、背後にある白い偽のウィンドウ (まだ表示されていない) ではなく、キャプチャされたウィンドウの周りの現在のデスクトップのスクリーンショットが取得されます。そのため、 を True に設定するDropShadowことは、現在実験段階にあります。

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, 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, 0, 0, R.Right - R.Left, R.Bottom - R.Top,
          DeviceContext, R.Left, R.Top, 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, 0, R.Right - R.Left, 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, 0, 0, R.Right - R.Left, R.Bottom - R.Top,
          DeviceContext, 0, 0, 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, ParamSize, 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.
于 2012-08-05T11:42:06.813 に答える
3

GetDesktopWindowおそらくそうあるべきGetDesktopWindow()ですが、多くの場合、GetDesktopWindow() ではなく NULL を使用できます (また使用する必要があります)。また、1 つの関数は GetDC を使用し、もう 1 つの関数は GetWindowDC を使用します...

于 2012-06-29T10:22:15.730 に答える
3

いくつかの検索の後、次の SO の質問から動作する次のコードを見つけました。

Delphiでアクティブウィンドウのスクリーンショットを撮る方法は?

NSIS を含む inclusin の他のすべてのオプションは、おそらく Aero とそれに関連する DWM フォグが原因で、BitBtl 関数でクラッシュを引き起こしました ...

また、この機能を使用するための提案があります。ジェットテストされていません...

http://msdn.microsoft.com/en-us/library/dd162869.aspx

それでも、いくつかの問題があります。

  • ガラスフレームは透明なものとして描画されます
  • NSIS からのファイル名は、多少破損したワイドストリングに変換されます ...
  • ページを変更すると( nsdialogs と MUI2 を使用して)、ダイアログの背景色だけでファイルを描画できます...
于 2012-07-28T12:28:05.473 に答える