unit uShellExt;
interface
uses
Winapi.Windows, Winapi.ActiveX, Winapi.ShlObj, Winapi.ShellAPI, System.Classes,
System.SysUtils, System.Win.ComObj, ComServ, Vcl.Forms;
type
TPicaViewExt = class(TComObject, IShellExtInit, IContextMenu)
private
FFiles: TStringList;
FThumbForm: TForm;
protected
function IShellExtInit.Initialize = InitShellExt;
function InitShellExt(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall;
function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
function GetCommandString(idCmd: UINT_PTR; uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; stdcall;
public
procedure Initialize; override;
destructor Destroy; override;
end;
TPicaViewFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
const
CLSID_PicaView: TGUID = '{C1B2D3E4-F5A6-7890-ABCD-EF123456789A}';
implementation
uses
System.Win.Registry, Vcl.Dialogs, uThumbnailForm, ImageViewerForm;
const
SHELLEX_KEY = '*\shellex\ContextMenuHandlers\PicaView';
IMAGE_EXTS: array[0..8] of string = ('.bmp', '.jpg', '.jpeg', '.png', '.gif', '.tiff', '.tif', '.ico', '.wmf');
MENU_VERB = '用PicaView查看';
function IsImageFile(const AFileName: string): Boolean;
var
S, E: string;
begin
E := LowerCase(ExtractFileExt(AFileName));
for S in IMAGE_EXTS do
if S = E then
Exit(True);
Result := False;
end;
{ TPicaViewExt }
procedure TPicaViewExt.Initialize;
begin
inherited;
FFiles := TStringList.Create;
FThumbForm := nil;
end;
destructor TPicaViewExt.Destroy;
begin
FThumbForm := nil;
FFiles.Free;
inherited;
end;
function TPicaViewExt.InitShellExt(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult;
var
FmtEtc: TFormatEtc;
Medium: TStgMedium;
I, FileCount: Integer;
Buffer: array[0..MAX_PATH] of Char;
begin
Result := E_FAIL;
if lpdobj = nil then
Exit;
FmtEtc.cfFormat := CF_HDROP;
FmtEtc.ptd := nil;
FmtEtc.dwAspect := DVASPECT_CONTENT;
FmtEtc.lindex := -1;
FmtEtc.tymed := TYMED_HGLOBAL;
if lpdobj.GetData(FmtEtc, Medium) <> S_OK then
Exit;
try
FileCount := DragQueryFile(Medium.hGlobal, $FFFFFFFF, nil, 0);
for I := 0 to FileCount - 1 do
begin
DragQueryFile(Medium.hGlobal, I, Buffer, MAX_PATH);
if IsImageFile(Buffer) then
FFiles.Add(Buffer);
end;
if FFiles.Count > 0 then
Result := S_OK
else
Result := S_FALSE;
finally
ReleaseStgMedium(Medium);
end;
end;
function TPicaViewExt.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult;
var
Pt: TPoint;
begin
if FFiles.Count = 0 then
begin
Result := 0;
Exit;
end;
if (uFlags and (CMF_DEFAULTONLY or CMF_NOVERBS)) <> 0 then
begin
Result := 0;
Exit;
end;
if not InsertMenuW(Menu, indexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst, PWideChar(MENU_VERB)) then
begin
Result := E_FAIL;
Exit;
end;
if FFiles.Count > 0 then
begin
GetCursorPos(Pt);
FThumbForm := TfrmThumbnail.Create(nil);
TfrmThumbnail(FThumbForm).ShowAt(FFiles[0], Pt);
end;
Result := 1;
end;
function TPicaViewExt.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
var
FileList: TStringList;
FilePath, CurrentPath, Ext: string;
SearchRec: TSearchRec;
StartIndex: Integer;
begin
if Assigned(FThumbForm) then
begin
FThumbForm.Release;
FThumbForm := nil;
end;
if FFiles.Count = 0 then
begin
Result := S_OK;
Exit;
end;
FilePath := FFiles[0];
Ext := LowerCase(ExtractFileExt(FilePath));
if (Ext <> '.jpg') and (Ext <> '.jpeg') and (Ext <> '.png') and (Ext <> '.bmp') and (Ext <> '.gif') and (Ext <> '.tiff') then
begin
Result := S_OK;
Exit;
end;
FileList := TStringList.Create;
try
CurrentPath := IncludeTrailingPathDelimiter(ExtractFilePath(FilePath));
StartIndex := -1;
if FindFirst(CurrentPath + '*.*', faAnyFile, SearchRec) = 0 then
begin
repeat
if (SearchRec.Attr and faDirectory) <> 0 then
Continue;
Ext := LowerCase(ExtractFileExt(SearchRec.Name));
if (Ext = '.jpg') or (Ext = '.jpeg') or (Ext = '.png') or (Ext = '.bmp') or (Ext = '.gif') or (Ext = '.tiff') then
begin
if SameText(CurrentPath + SearchRec.Name, FilePath) then
StartIndex := FileList.Count;
FileList.Add(CurrentPath + SearchRec.Name);
end;
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
if (FileList.Count > 0) and (StartIndex >= 0) then
begin
with TfrmImageViewer.Create(nil) do
ShowImage(FileList, StartIndex);
end;
finally
FileList.Free;
end;
Result := S_OK;
end;
function TPicaViewExt.GetCommandString(idCmd: UINT_PTR; uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
begin
if (idCmd = 0) and (uType = GCS_HELPTEXT) then
begin
lstrcpynA(pszName, '用PicaView快速查看图像文件', cchMax);
Result := S_OK;
end
else
Result := E_NOTIMPL;
end;
{ TPicaViewFactory }
procedure TPicaViewFactory.UpdateRegistry(Register: Boolean);
var
Reg: TRegistry;
ClsIdStr: string;
begin
inherited;
ClsIdStr := GUIDToString(CLSID_PicaView);
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CLASSES_ROOT;
if Register then
begin
if Reg.OpenKey(SHELLEX_KEY, True) then
begin
Reg.WriteString('', ClsIdStr);
Reg.CloseKey;
end;
end
else
begin
if Reg.KeyExists(SHELLEX_KEY) then
Reg.DeleteKey(SHELLEX_KEY);
end;
finally
Reg.Free;
end;
end;
initialization
TPicaViewFactory.Create(ComServer, TPicaViewExt, CLSID_PicaView, 'PicaView', 'PicaView Shell Extension', ciMultiInstance, tmApartment);
end.
由于焦点问题,只能点击菜单项【用PicaView查看 】 这样就能全屏,前后浏览 ,ECS退出了!
ImageViewerForm.pas
[Delphi] 纯文本查看复制代码
unit ImageViewerForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.ExtCtrls, Vcl.StdCtrls, System.Math;
type
TfrmImageViewer = class(TForm)
imgMain: TImage;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure FormDblClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
procedure imgMainMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure WMSysCmd(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
private
FFileList: TStringList;
FCurrentIndex: Integer;
FZoomFactor: Double;
FOriginalPicture: TPicture;
FFullscreen: Boolean;
FSavedBorderStyle: TFormBorderStyle;
FSavedWindowState: TWindowState;
FSavedBounds: TRect;
procedure LoadImage;
procedure UpdateCaption;
procedure MoveToPrevious;
procedure MoveToNext;
procedure ApplyZoom;
procedure ToggleFullscreen;
procedure ExitFullscreen;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ShowImage(const AFileList: TStringList; AStartIndex: Integer);
end;
implementation
{$R *.dfm}
constructor TfrmImageViewer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FFileList := TStringList.Create;
FCurrentIndex := 0;
FZoomFactor := 1.0;
FOriginalPicture := TPicture.Create;
FFullscreen := False;
end;
destructor TfrmImageViewer.Destroy;
begin
FOriginalPicture.Free;
FFileList.Free;
inherited Destroy;
end;
procedure TfrmImageViewer.FormCreate(Sender: TObject);
begin
KeyPreview := True;
imgMain.Align := alClient;
imgMain.OnMouseDown := imgMainMouseDown;
PopupMode := pmExplicit;
FormStyle := fsStayOnTop;
end;
procedure TfrmImageViewer.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
procedure TfrmImageViewer.WMSysCmd(var Msg: TWMSysCommand);
begin
if Msg.CmdType = SC_CLOSE then
Free
else
inherited;
end;
procedure TfrmImageViewer.ShowImage(const AFileList: TStringList; AStartIndex: Integer);
begin
FFileList.Assign(AFileList);
FCurrentIndex := AStartIndex;
LoadImage;
ShowWindow(Handle, SW_SHOW);
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
BringToFront;
SetForegroundWindow(Handle);
end;
procedure TfrmImageViewer.UpdateCaption;
begin
if (FCurrentIndex >= 0) and (FCurrentIndex < FFileList.Count) then
Caption := Format('%s (%d/%d)', [ExtractFileName(FFileList[FCurrentIndex]), FCurrentIndex + 1, FFileList.Count])
else
Caption := '图片查看器';
end;
procedure TfrmImageViewer.LoadImage;
begin
if (FCurrentIndex >= 0) and (FCurrentIndex < FFileList.Count) then
begin
try
imgMain.Picture.LoadFromFile(FFileList[FCurrentIndex]);
FOriginalPicture.Assign(imgMain.Picture);
FZoomFactor := 1.0;
UpdateCaption;
except
on E: Exception do
;
end;
end;
end;
procedure TfrmImageViewer.ApplyZoom;
var
bmp: TBitmap;
w, h: Integer;
begin
if FOriginalPicture.Graphic = nil then Exit;
FZoomFactor := EnsureRange(FZoomFactor, 0.05, 20.0);
w := Max(1, Round(FOriginalPicture.Width * FZoomFactor));
h := Max(1, Round(FOriginalPicture.Height * FZoomFactor));
bmp := TBitmap.Create;
try
bmp.SetSize(w, h);
bmp.Canvas.StretchDraw(Rect(0, 0, w, h), FOriginalPicture.Graphic);
imgMain.Picture.Assign(bmp);
finally
bmp.Free;
end;
end;
procedure TfrmImageViewer.ToggleFullscreen;
begin
if FFullscreen then
ExitFullscreen
else
begin
FSavedBorderStyle := BorderStyle;
FSavedWindowState := WindowState;
FSavedBounds := BoundsRect;
BorderStyle := bsNone;
WindowState := wsMaximized;
FFullscreen := True;
end;
end;
procedure TfrmImageViewer.ExitFullscreen;
begin
if not FFullscreen then Exit;
BorderStyle := FSavedBorderStyle;
WindowState := FSavedWindowState;
BoundsRect := FSavedBounds;
FFullscreen := False;
end;
procedure TfrmImageViewer.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
VK_ESCAPE:
begin
if FFullscreen then
ExitFullscreen
else
Free;
end;
VK_PRIOR, VK_LEFT:
begin
if FCurrentIndex > 0 then
begin
Dec(FCurrentIndex);
LoadImage;
end;
end;
VK_NEXT, VK_RIGHT, VK_SPACE:
begin
if FCurrentIndex < FFileList.Count - 1 then
begin
Inc(FCurrentIndex);
LoadImage;
end;
end;
end;
end;
procedure TfrmImageViewer.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
ToggleFullscreen;
end;
procedure TfrmImageViewer.imgMainMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
ToggleFullscreen;
end;
procedure TfrmImageViewer.MoveToPrevious;
begin
if FCurrentIndex > 0 then
begin
Dec(FCurrentIndex);
LoadImage;
end;
end;
procedure TfrmImageViewer.MoveToNext;
begin
if FCurrentIndex < FFileList.Count - 1 then
begin
Inc(FCurrentIndex);
LoadImage;
end;
end;
procedure TfrmImageViewer.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
if ssCtrl in Shift then
begin
if WheelDelta > 0 then
FZoomFactor := FZoomFactor * 1.25
else
FZoomFactor := FZoomFactor * 0.8;
ApplyZoom;
end
else
begin
if WheelDelta > 0 then
MoveToPrevious
else
MoveToNext;
end;
Handled := True;
end;
procedure TfrmImageViewer.FormDblClick(Sender: TObject);
begin
if FFullscreen then
ExitFullscreen
else if FZoomFactor <> 1.0 then
begin
FZoomFactor := 1.0;
ApplyZoom;
end
else
ToggleFullscreen;
end;
end.
uThumbnailForm.pas
[Delphi] 纯文本查看复制代码
unit uThumbnailForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TfrmThumbnail = class(TForm)
private
FImageFile: string;
FImage: TImage;
FLblInfo: TLabel;
FLblBottom: TLabel;
FBtnOpen: TButton;
procedure BtnOpenClick(Sender: TObject);
procedure LoadThumbnail(const AFileName: string);
function GetFileSizeStr(const AFileName: string): string;
function GetBitDepthStr(Pic: TPicture): string;
procedure WMTimer(var Msg: TMessage); message WM_TIMER;
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WMKeyDown(var Msg: TWMKeyDown); message WM_KEYDOWN;
public
constructor Create(AOwner: TComponent); override;
procedure ShowAt(const AFileName: string; const ScreenPt: TPoint);
end;
const
TIMER_RAISE = 1;
TIMER_CHECK = 2;
implementation
uses
Vcl.Imaging.Jpeg, Vcl.Imaging.PngImage, Vcl.Imaging.GIFImg;
const
MAX_THUMB = 256;
RAISE_DELAY = 120;
CHECK_INTERVAL = 200;
BAR_HEIGHT = 22;
constructor TfrmThumbnail.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
FImage := TImage.Create(Self);
FImage.Parent := Self;
FImage.Align := alClient;
FImage.Center := True;
FImage.Stretch := True;
FImage.Proportional := True;
FLblInfo := TLabel.Create(Self);
FLblInfo.Parent := Self;
FLblInfo.Align := alTop;
FLblInfo.AutoSize := False;
FLblInfo.Height := BAR_HEIGHT;
FLblInfo.Color := clBlack;
FLblInfo.Font.Color := clLime;
FLblInfo.Font.Size := 9;
FLblInfo.Alignment := taCenter;
FLblInfo.Layout := tlCenter;
FLblInfo.Caption := '';
FLblBottom := TLabel.Create(Self);
FLblBottom.Parent := Self;
FLblBottom.Align := alBottom;
FLblBottom.AutoSize := False;
FLblBottom.Height := BAR_HEIGHT;
FLblBottom.Color := clBlack;
FLblBottom.Font.Color := clLime;
FLblBottom.Font.Size := 9;
FLblBottom.Alignment := taCenter;
FLblBottom.Layout := tlCenter;
FLblBottom.Caption := '';
FBtnOpen := TButton.Create(Self);
FBtnOpen.Parent := Self;
FBtnOpen.Caption := '关闭';
FBtnOpen.Align := alBottom;
FBtnOpen.OnClick := BtnOpenClick;
BorderStyle := bsNone;
BorderIcons := [];
FormStyle := fsStayOnTop;
Color := clBlack;
DoubleBuffered := True;
ClientWidth := MAX_THUMB + 8;
ClientHeight := MAX_THUMB + 8 + BAR_HEIGHT * 2 + FBtnOpen.Height;
end;
procedure TfrmThumbnail.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.WndParent := GetDesktopWindow;
Params.ExStyle := Params.ExStyle or WS_EX_TOOLWINDOW or WS_EX_NOACTIVATE or WS_EX_TOPMOST;
end;
procedure TfrmThumbnail.WMKeyDown(var Msg: TWMKeyDown);
begin
if Msg.CharCode = VK_ESCAPE then
Release
else
inherited;
end;
procedure TfrmThumbnail.WMTimer(var Msg: TMessage);
var
Pt: TPoint;
begin
case Msg.WParam of
TIMER_RAISE:
begin
KillTimer(Handle, TIMER_RAISE);
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW);
SetTimer(Handle, TIMER_CHECK, CHECK_INTERVAL, nil);
end;
TIMER_CHECK:
begin
if FindWindow('#32768', nil) <> 0 then
Exit;
GetCursorPos(Pt);
if (Pt.X >= Left) and (Pt.X < Left + Width) and (Pt.Y >= Top) and (Pt.Y < Top + Height) then
Exit;
KillTimer(Handle, TIMER_CHECK);
Release;
end;
end;
end;
function TfrmThumbnail.GetFileSizeStr(const AFileName: string): string;
var
F: file;
begin
AssignFile(F, AFileName);
Reset(F, 1);
try
if FileSize(F) < 1024 then
Result := Format('%d B', [FileSize(F)])
else if FileSize(F) < 1024 * 1024 then
Result := Format('%.1f KB', [FileSize(F) / 1024])
else
Result := Format('%.1f MB', [FileSize(F) / (1024 * 1024)]);
finally
CloseFile(F);
end;
end;
function TfrmThumbnail.GetBitDepthStr(Pic: TPicture): string;
var
Bmp: TBitmap;
begin
if Pic.Graphic is TJPEGImage then
Result := '24'
else if Pic.Graphic is TPNGImage then
Result := IntToStr(TPNGImage(Pic.Graphic).Header.BitDepth)
else if Pic.Graphic is TBitmap then
begin
case TBitmap(Pic.Graphic).PixelFormat of
pf1bit:
Result := '1';
pf4bit:
Result := '4';
pf8bit:
Result := '8';
pf15bit:
Result := '15';
pf16bit:
Result := '16';
pf24bit:
Result := '24';
pf32bit:
Result := '32';
pfCustom:
Result := '?';
else
Result := '?';
end;
end
else if Pic.Graphic is TGIFImage then
Result := '8'
else
Result := '?';
Result := Result + ' 位';
end;
procedure TfrmThumbnail.LoadThumbnail(const AFileName: string);
var
Pic: TPicture;
DimStr, SizeStr, ExtStr, DepthStr: string;
begin
if not FileExists(AFileName) then
Exit;
Pic := TPicture.Create;
try
Pic.LoadFromFile(AFileName);
FImageFile := AFileName;
FImage.Picture.Assign(Pic);
DimStr := Format('%d x %d', [Pic.Width, Pic.Height]);
SizeStr := GetFileSizeStr(AFileName);
FLblInfo.Caption := DimStr + ' ' + SizeStr;
DepthStr := GetBitDepthStr(Pic);
ExtStr := UpperCase(ExtractFileExt(AFileName));
FLblBottom.Caption := DepthStr + ' ' + ExtStr;
finally
Pic.Free;
end;
end;
procedure TfrmThumbnail.ShowAt(const AFileName: string; const ScreenPt: TPoint);
var
W, H: Integer;
begin
LoadThumbnail(AFileName);
W := Width;
H := Height;
Left := ScreenPt.X + 20;
Top := ScreenPt.Y - H div 4;
if Left + W > Screen.DesktopWidth then
Left := ScreenPt.X - W - 10;
if Top + H > Screen.DesktopHeight then
Top := Screen.DesktopHeight - H - 10;
if Top < 0 then
Top := 0;
ShowWindow(Handle, SW_SHOWNOACTIVATE);
SetTimer(Handle, TIMER_RAISE, RAISE_DELAY, nil);
end;
procedure TfrmThumbnail.BtnOpenClick(Sender: TObject);
begin
Release;
end;
end.