Shell extension

unit uGetResList;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  Windows, ActiveX, Classes, Sysutils, Messages, ComObj, ShellAPI, ShlObj,
  Math, Graphics, JPEG, Registry;

type
  TGetResList = class(TComObject, IShellExtInit, IContextMenu, IContextMenu3)
  private
    FFileList: TStrings;
    FGraphic: TGraphic;
  protected
    //IShellExtInit
    function IShellExtInit.Initialize = SEInitialize;
    function SEInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
      hKeyProgID: HKEY): HResult; stdcall;
    //IContextMenu
    function QueryContextMenu(Menu: HMENU;
      indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;//before popup
    function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;//onclick
    function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
      pszName: LPSTR; cchMax: UINT): HResult; stdcall;//hint when move over
    //IContextMenu2
    function HandleMenuMsg(uMsg: UINT; WParam, LParam: Integer): HResult; stdcall;
    //IContextMenu3
    function HandleMenuMsg2(uMsg: UINT; wParam, lParam: Integer;
      var lpResult: Integer): HResult; stdcall;
  public
    procedure Initialize; override;
    destructor Destroy; override;
  end;

  TGetResListFactory = class(TComObjectFactory)
  public
    procedure UpdateRegistry(Register: Boolean); override;
  end;

const
  Class_GetResList: TGUID = '{AAE1817E-34EA-4892-B6A7-8D5738BA3074}';

  //menu type
  mfString    = MF_STRING or MF_BYPOSITION;
  mfOwnerDraw = MF_OWNERDRAW or MF_BYPOSITION;
  mfSpearator = MF_SEPARATOR or MF_BYPOSITION;

  //menu ID
  idCopyAnyWhere = 0;//copy(move)
  idRegister = 5;   //registerActiveX
  idUnregister = 6; //unregisterActiveX
  idImagePreview = 10;//preview picture
  idMenuRange = 90; //

resourcestring
  //menu item name
  sCopyAnyWhere = 'Copy any where...';
  sCopyAnyWhereTip = '可将选定的文件复制到任何路径下';
  sRegister = '注册...';
  sRegisterTip = '注册GetResList插件库';
  sUnregister = '取消注册...';
  sUnregisterTip = '取消注册GetResList插件库';
  sImagePriview = '预览图片文件';
  sImagePriviewTip = '预览图片文件';

function GetFileListFromDataObject(lpdobj: IDataObject; sl: TStrings): HResult;
function IsActiveLib(const FileName: string): Boolean;
procedure RegisterActiveLib(Wnd: HWND; const FileName: string);
procedure UnregisterActiveLib(Wnd: HWND; const FileName: string);
procedure ReportWin32Error(Wnd: HWND; const Prefix: string; dwError: DWORD);
function IsImageFile(const FileName: string): Boolean;
function ImageFromFile(const FileName: string): TGraphic;
function ExecuteFile(Wnd: HWND; const FileName: string): THandle;
procedure DrawGraphic(adc: HDC; rc: TRect; State: Integer; Graphic: TGraphic);
function ImageInfoToStr(Graphic: TGraphic): string;

function Make_HResult(sev, fac, code: Word): DWORD;
procedure DoCopyAnyWhere(Wnd: HWND; sl: TStrings);


implementation

uses ComServ;

//* 根据图片对象,得到图片相关的信息
function ImageInfoToStr(Graphic: TGraphic): string;
begin
  Result := Format('%d * %d', [Graphic.Width, Graphic.Height]);
  if Graphic is TIcon then
    Result := Result + ' 图标';
  if Graphic is TBitmap then
  begin
    case TBitmap(Graphic).PixelFormat of
      pfDevice: Result := Result + ' DDB';
      pf1bit: Result := Result + ' 2色';
      pf4bit: Result := Result + ' 16色';
      pf8bit: Result := Result + '256色';
      pf15bit, pf16bit: Result := Result + ' 16位色';
      pf24bit: Result := Result + ' 24位色';
      pf32bit: Result := Result + ' 32位色';
      pfCustom: Result := Result + ' 自定义色';
    end;
    Result := Result + '位图';
  end;
  if Graphic is TMetafile then
  begin
    Result := Result + Format('(%d*%d) 元文件', [TMetafile(Graphic).MMWidth div 100,
      TMetafile(Graphic).MMHeight div 100]);
  end;
  if Graphic is TJPEGImage then
  begin
    case TJPEGImage(Graphic).PixelFormat of
      jf24Bit: Result := Result + ' 24位色 JPEG';
      jf8Bit: Result := Result + ' 8位色 JPEG';
    end;
  end;
end;

//* 画图像
procedure DrawGraphic(adc: HDC; rc: TRect; State: Integer; Graphic: TGraphic);
var
  rcImage, rcText, rcStretch: TRect;
  Canvas: TCanvas;
  nSaveDC: Integer;
  x, y: Integer;
  xScale, yScale, Scale: Double;
  xStretch, yStretch: Integer;
begin
  rcImage.Left := rc.Left + 10;
  rcImage.Right := rc.Right - 10;
  rcImage.Top := rc.Top + 10;
  rcImage.Bottom := rc.Bottom - 30;

  rcText.Left := rc.Left + 10;
  rcText.Right := rc.Right - 10;
  rcText.Top := rc.Bottom - 20;
  rcText.Bottom := rc.Bottom;

  Canvas := TCanvas.Create;
  nSaveDC := 0;
  try
    nSaveDC := SaveDC(adc);
    Canvas.Handle := adc;

    if not Assigned(Graphic) then
    begin
      Canvas.Rectangle(rcImage);
      Canvas.MoveTo(rcImage.Left, rcImage.Top);
      Canvas.LineTo(rcImage.Right, rcImage.Bottom);
      Canvas.MoveTo(rcImage.Right, rcImage.Top);
      Canvas.LineTo(rcImage.Left, rcImage.Bottom);
      DrawText(Canvas.Handle, '未知图像', -1, rcImage, DT_SINGLELINE or DT_CENTER or DT_VCENTER);
    end
    else
    begin
      if (Graphic.Width < rcImage.Right - rcImage.Left)
        and (Graphic.Height < rcImage.Bottom - rcImage.Top) then
      begin
        x := rcImage.Left + (rcImage.Right - rcImage.Left - Graphic.Width) div 2;
        y := rcImage.Top + (rcImage.Bottom - rcImage.Top - Graphic.Height) div 2;
        Canvas.Draw(x, y, Graphic);
      end
      else
      begin
        xScale := Graphic.Width / (rcImage.Right - rcImage.Left);
        yScale := Graphic.Height / (rcImage.Bottom - rcImage.Top);
        Scale := Max(xScale, yScale);
        xStretch := Trunc(Graphic.Width / Scale);
        yStretch := Trunc(Graphic.Height / Scale);
        x := rcImage.Left + (rcImage.Right - rcImage.Left - xStretch) div 2;
        y := rcImage.Top + (rcImage.Bottom - rcImage.Top - yStretch) div 2;
        rcStretch := Rect(x, y, x + xStretch, y + yStretch);
        Canvas.StretchDraw(rcStretch, Graphic);
      end;
      Windows.FillRect(Canvas.Handle, rcText, GetSysColorBrush(COLOR_MENU));
      SetTextColor(Canvas.Handle, GetSysColor(COLOR_MENUTEXT));
      SetBkColor(Canvas.Handle, GetSysColor(COLOR_MENU));
      DrawText(Canvas.Handle, PChar(ImageInfoToStr(Graphic)), -1, rcText,
        DT_SINGLELINE or DT_CENTER or DT_VCENTER);
    end;
  finally
    Canvas.Handle := 0;
    FreeAndNil(Canvas);
    RestoreDC(adc, nSaveDC);
  end;
end;

//* 打开文件
function ExecuteFile(Wnd: HWND; const FileName: string): THandle;
var
  Path: string;
begin
  Path := ExtractFilePath(FileName);
  Result := ShellExecute(Wnd, 'open', PChar(FileName), nil, PChar(Path), SW_SHOW);
end;

//* 图片从文件载入(其实也是判断文件是否是真正的图片文件。如果是,则能正常载入)
function ImageFromFile(const FileName: string): TGraphic;
var
  Ext: string;
begin
  Ext := UpperCase(ExtractFileExt(FileName));
  Result := nil;
  if not IsImageFile(FileName) then
    Exit;
  try
    if (Ext = '.ICO') then
      Result := TIcon.Create
    else if Ext = '.BMP' then
      Result := TBitmap.Create
    else if (Ext = '.EMF') or (Ext = '.WMF') then
      Result := TMetafile.Create
    else if (Ext = '.JPG') or (Ext = '.JPEG') then
      Result := TJPEGImage.Create;
    Result.LoadFromFile(FileName);
  except
    if Assigned(Result) then
      FreeAndNil(Result);
  end;
end;

//* 判断是否是图片文件
function IsImageFile(const FileName: string): Boolean;
var
  Ext: string;
begin
  Ext := UpperCase(ExtractFileExt(FileName));
  Result := (Ext = '.ICO') or (Ext = '.BMP') or (Ext = '.EMF') or (Ext = '.WMF')
    or (Ext = '.JPG') or (Ext = '.JPEG');
end;

//* 错误报告
procedure ReportWin32Error(Wnd: HWND; const Prefix: string; dwError: DWORD);
//var
//  szError: array[0..399] of char;
//  str: string;
begin
  OutputDebugString(PChar(Prefix));
//  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, dwError,
//    Make_LangID(LANG_NEUTRAL, SUBLANG_DEFAULT), szError, SizeOf(szError), nil);
//  str := Format('%s: %s', [Prefix, StrPas(szError)]);
//  MessageBox(Wnd, PChar(str), '错误', MB_ICONEXCLAMATION);
end;

//* 取消注册AcitveX库
procedure UnregisterActiveLib(Wnd: HWND; const FileName: string);
var
  hLib: THandle;
  fn: TDLLUnregisterServer;
  hr: HRESULT;
begin
  hLib := LoadLibrary(PChar(FileName));
  if hLib = 0 then
  begin
    ReportWin32Error(Wnd, '装载文件失败', GetLastError);
    Exit;
  end;
  try
    fn := TDLLUnregisterServer(GetProcAddress(hLib, 'DllUnregisterServer'));
    if not Assigned(fn) then
    begin
      MessageBox(Wnd, '定位函数入口点 DllUnregisterServer 失败', '错误', MB_ICONEXCLAMATION);
      Exit;
    end;
    hr := fn();
    if Failed(hr) then
    begin
      ReportWin32Error(Wnd, '取消注册动态库失败', hr);
      Exit;
    end;
    MessageBox(Wnd, '取消注册成功', '成功', MB_ICONINFORMATION);
  finally
    FreeLibrary(hLib);
  end;
end;

//* 注册ActiveX库
procedure RegisterActiveLib(Wnd: HWND; const FileName: string);
var
  hLib: THandle;
  fn: TDLLRegisterServer;
  hr: HRESULT;
begin
  hLib := LoadLibrary(PChar(FileName));
  if hLib = 0 then
  begin
    ReportWin32Error(Wnd, '装载文件失败', GetLastError);
    Exit;
  end;
  try
    fn := TDLLRegisterServer(GetProcAddress(hLib, 'DllRegisterServer'));
    if not Assigned(fn) then
    begin
      MessageBox(Wnd, '定位函数入口点 DllRegisterServer 失败', '错误', MB_ICONEXCLAMATION);
      Exit;
    end;
    hr := fn();
    if Failed(hr) then
    begin
      ReportWin32Error(Wnd, '注册动态库失败', hr);
      Exit;
    end;
    MessageBox(Wnd, '注册成功', '成功', MB_ICONINFORMATION);
  finally
    FreeLibrary(hLib);
  end;
end;

//* 检查指定的文件是否是ActiveX文件
function IsActiveLib(const FileName: string): Boolean;
var
  Ext: string;
  hLib: THandle;
begin
  Result := False;
  Ext := UpperCase(ExtractFileExt(FileName));
  if (Ext <> '.EXT') and (Ext <> '.DLL') and (Ext <> '.OCX') then
    Exit;
  hLib := LoadLibrary(PChar(FileName));
  if hLib = 0 then Exit;
  try
    Result := GetProcAddress(hLib, 'DllRegisterServer') <> nil;
  finally
    FreeLibrary(hLib);
  end;
end;

procedure DoCopyAnyWhere(Wnd: HWND; sl: TStrings);
begin
  //some code here.
end;

function Make_HResult(sev, fac, code: Word): DWORD;
begin
  Result := (sev shl 31) or (fac shl 16) or code;
end;

function GetFileListFromDataObject(lpdobj: IDataObject; sl: TStrings): HResult;
var
  fe: FormatEtc;
  sm: StgMedium;
  i, iFileCount: Integer;
  FileName: array[0..MAX_PATH - 1] of char;
begin
  Assert(lpdobj <> nil);
  Assert(sl <> nil);
  sl.Clear;

  fe.cfFormat := CF_HDROP;
  fe.ptd := nil;
  fe.dwAspect := DVASPECT_CONTENT;
  fe.lindex := -1;
  fe.tymed := TYMED_HGLOBAL;

  sm.tymed := TYMED_HGLOBAL;

  Result := lpdobj.GetData(fe, sm);
  if (FAILED(Result)) then Exit;
  iFileCount := DragQueryFile(sm.hGlobal, $FFFFFFFF, nil, 0);
  if iFileCount <= 0 then
  begin
    ReleaseStgMedium(sm);
    Result := E_INVALIDARG;
    Exit;
  end;
  for i := 0 to iFileCount - 1 do
  begin
    DragQueryFile(sm.hGlobal, i, FileName, Sizeof(FileName));
    sl.Add(FileName);
  end;
  ReleaseStgMedium(sm);
  Result := S_OK;
end;

{ TGetResListFactory }

procedure TGetResListFactory.UpdateRegistry(Register: Boolean);
  procedure DeleteRegValue(const Path, ValueName: string; Root: DWORD = HKEY_CLASSES_ROOT);
  var
    reg: TRegistry;
  begin
    reg := TRegistry.Create;
    try
      reg.RootKey := Root;
      if reg.OpenKey(Path, False) then
      begin
        if reg.ValueExists(ValueName) then
          reg.DeleteValue(ValueName);
        reg.CloseKey;
      end;
    finally
      FreeAndNil(reg);
    end;
  end;
const
  RegPath = '*\shellex\ContextMenuHandlers\GetResList';
  ApprovedPath = 'Software\Microsoft\Windows\CurrentVersion\ShellExtensions\Approved';
var
  strGUID: string;
begin
  inherited;
  strGUID := GUIDToString(Class_GetResList);
  if Register then
  begin
    CreateRegKey(RegPath, '', strGUID);
    CreateRegKey(ApprovedPath, strGUID, 'GetResList的外壳扩展', HKEY_LOCAL_MACHINE);
  end
  else
  begin
    DeleteRegKey(RegPath);
    DeleteRegValue(ApprovedPath, strGUID, HKEY_LOCAL_MACHINE);
  end;
end;

{ TGetResList }

destructor TGetResList.Destroy;
begin
  OutputDebugString('TGetResList::Destroy'#13#10);
  if Assigned(FGraphic) then
    FreeAndNil(FGraphic);
  FreeAndNil(FFileList);
  inherited;
end;

function TGetResList.GetCommandString(idCmd, uType: UINT;
  pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
var
  strTip: string;
  wstrTip: WideString;
begin
  strTip := '';
  Result := E_INVALIDARG;
  if (uType and GCS_HELPTEXT) <> GCS_HELPTEXT then Exit;
  case idCmd of
    idCopyAnyWhere: strTip := sCopyAnyWhereTip;
    idRegister: strTip := sRegisterTip;
    idUnregister: strTip := sUnregisterTip;
    idImagePreview: strTip := sImagePriviewTip;
  end;
  if strTip <> '' then
  begin
    if (uType and GCS_UNICODE) = 0 then
    begin//Ansi
      lstrcpynA(pszName, PChar(strTip), cchMax);
    end
    else
    begin//Unicode
      wstrTip := strTip;
      lstrcpynW(PWideChar(pszName), PWideChar(wstrTip), cchMax);
    end;
    Result := S_OK;
  end;
end;

function TGetResList.HandleMenuMsg(uMsg: UINT; WParam,
  LParam: Integer): HResult;
var
  Ret: Integer;
begin
  Ret := 0;
  Result := HandleMenuMsg2(uMsg, WParam, LParam, Ret);
end;

function TGetResList.HandleMenuMsg2(uMsg: UINT; wParam, lParam: Integer;
  var lpResult: Integer): HResult;
var
  pmis: PMeasureItemStruct;
  pdis: PDrawItemStruct;
begin
  Result := S_OK;
  case uMsg of
    WM_MEASUREITEM:
      begin
        pmis := PMeasureItemStruct(lParam);
        if not Assigned(FGraphic) then
        begin
          pmis.itemWidth := 120;
          pmis.itemHeight := 120;
          Exit;
        end;
        //如果图片小于120 * 120,则按实际显示,否则缩放到120*120
        if (FGraphic.Width <= 120) and (FGraphic.Height <= 120) then
        begin
          pmis.itemWidth := FGraphic.Width;
          pmis.itemHeight := FGraphic.Height;
        end;
      end;
    WM_DRAWITEM:
      begin
        pdis := PDrawItemStruct(lParam);
        DrawGraphic(pdis.hDC, pdis.rcItem, pdis.itemState, FGraphic);
      end;
  end;
end;

procedure TGetResList.Initialize;
begin
  OutputDebugString('TGetResList::Initialize'#13#10);
  inherited;
  FFileList := TStringList.Create;
  FGraphic := nil; 
end;

function TGetResList.InvokeCommand(
  var lpici: TCMInvokeCommandInfo): HResult;
begin
  Result := E_INVALIDARG;
  if HiWord(Integer(lpici.lpVerb)) <> 0 then Exit;
  case LoWord(Integer(lpici.lpVerb)) of
    idCopyAnyWhere: DoCopyAnyWhere(lpici.hwnd, FFileList);
    idRegister: RegisterActiveLib(lpici.hwnd, FFileList[0]);
    idUnregister: UnregisterActiveLib(lpici.hwnd, FFileList[0]);
    idImagePreview: ExecuteFile(lpici.hwnd, FFileList[0]);
  end;
  Result := NOERROR;
end;

function TGetResList.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
  idCmdLast, uFlags: UINT): HResult;
var
  Added: UINT;
  hbmReg, hbmUnreg: HBITMAP;
begin
  if (uFlags and CMF_DEFAULTONLY) = (CMF_DEFAULTONLY) then
  begin
    Result := Make_HResult(SEVERITY_SUCCESS, FACILITY_NULL, 0);
    Exit;
  end;
  Added := 0;
  //加入CopyAnyWhere菜单项
  InsertMenu(Menu, indexMenu, mfSpearator, 0, nil);
  InsertMenu(Menu, indexMenu, mfString, idCmdFirst + idCopyAnyWhere, PChar(sCopyAnyWhere));
  InsertMenu(Menu, indexMenu, mfSpearator, 0, nil);
  Inc(Added, 3);

  if FFileList.Count = 1 then
  begin//单一文件
    if IsActiveLib(FFileList[0]) then
    begin
      InsertMenu(Menu, indexMenu + Added, mfSpearator, 0, nil);
      InsertMenu(Menu, indexMenu + Added, mfString, idCmdFirst + idUnregister, PChar(sUnregister));
      InsertMenu(Menu, indexMenu + Added, mfString, idCmdFirst + idRegister, PChar(sRegister));
      InsertMenu(Menu, indexMenu + Added, mfSpearator, 0, nil);
      Inc(Added, 4);
      hbmReg := LoadImage(HInstance, MakeIntResource(101), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
      hbmUnreg := LoadImage(HInstance, MakeIntResource(102), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
      SetMenuItemBitmaps(Menu, idCmdFirst + idRegister, MF_BYCOMMAND, hbmReg, hbmReg);
      SetMenuItemBitmaps(Menu, idCmdFirst + idUnregister, MF_BYCOMMAND, hbmUnreg, hbmUnreg);
    end;
    if {IsImageFile(FFileList[0])} False then
    begin//图片文件
      FGraphic := ImageFromFile(FFileList[0]);
      if Assigned(FGraphic) then
      begin
        InsertMenu(Menu, indexMenu + Added, mfSpearator, 0, nil);
        InsertMenu(Menu, indexMenu + Added, mfOwnerDraw, idCmdFirst + idImagePreview, nil);
        InsertMenu(Menu, indexMenu + Added, mfSpearator, 0, nil);
        //Inc(Added, 3);
      end;
    end;
  end
  else
    Result := Make_HResult(SEVERITY_SUCCESS, FACILITY_NULL, idMenuRange);

  Result := Make_HResult(SEVERITY_SUCCESS, FACILITY_NULL, idMenuRange);
end;

function TGetResList.SEInitialize(pidlFolder: PItemIDList;
  lpdobj: IDataObject; hKeyProgID: HKEY): HResult;
begin
  Result := GetFileListFromDataObject(lpdobj, FFileList);
end;

initialization
  TComObjectFactory.Create(ComServer, TGetResList, Class_GetResList,
    'GetResList', 'Get Select File List Main Unit', ciMultiInstance, tmApartment);
end.

原文地址:https://www.cnblogs.com/tongy0/p/tongy0.html