You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Inno Setup卸载模式调用SHFileOperation移至回收站失败求助

问题:Inno Setup卸载模式下调用SHFileOperation移至回收站失败

我在安装程序中尝试通过SHFileOperation实现将文件夹移至回收站的功能,该函数在安装模式下运行正常,但卸载模式下无法工作。Inno Setup 5和6版本分别抛出不同异常:

Inno Setup 5报错:

Runtime Error (at 2:67): Internal error: An attempt was made to access WizardForm before it has been created.

Inno Setup 6报错:

Runtime error (at 2:82): Could not call proc.

相关代码如下:

; -- Example1.iss --
; Demonstrates copying 3 files and creating an icon.

; SEE THE DOCUMENTATION FOR DETAILS ON CREATING .ISS SCRIPT FILES!

[Setup]
AppName=My Program
AppVersion=1.5
WizardStyle=modern
DefaultDirName={pf}\My Program
DefaultGroupName=My Program
UninstallDisplayIcon={app}\MyProg.exe
Compression=lzma2
SolidCompression=yes

[Files]
Source: "MyProg.exe"; DestDir: "{app}"
Source: "MyProg.chm"; DestDir: "{app}"

[Icons]
Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"

[Code]

type
  TSHFileOpStruct = record
    hwnd: HWND;
    wFunc: UINT;
    pFrom: string;
    pTo: string;
    fFlags: Word;
    fAnyOperationsAborted: BOOL; 
    hNameMappings: HWND;
    lpszProgressTitle: string;
  end; 

const
  FO_COPY            = $0002;
  FO_DELETE          = $0003;
  FOF_SILENT         = $0004;
  FOF_NOCONFIRMATION = $0010;
  FOF_NOCONFIRMMKDIR = $0020;
  FOF_ALLOWUNDO      = $0040;
  FOF_NOERRORUI      = $0400;
  FOF_NO_UI          = $0434; //(FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR);

function SHFileOperation(lpFileOp: TSHFileOpStruct): Integer;
external 'SHFileOperationW@shell32.dll stdcall';

function MoveToTrash(path: string) : Boolean;
var
  FileOp: TSHFileOpStruct;
  FromPath: string;
begin
  FromPath := path + #0;
  FileOp.hwnd := WizardForm.Handle;
  FileOp.wFunc := FO_DELETE;
  FileOp.pFrom := FromPath;
  FileOp.fFlags := $0474; //(FOF_ALLOWUNDO | OF_SILENT);
  Result := SHFileOperation(FileOp) = 0;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  res : Integer;
begin
  if CurStep=ssInstall then begin
    MoveToTrash('c:\9'); // WORKS FINE HERE
  end;
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  res : Integer;
begin
  if CurUninstallStep=usUninstall then begin
    MoveToTrash('c:\9'); // GETTING WEIRD ERROR HERE
  end;
end;

问题原因与解决办法

这不是Inno Setup的Bug,你的代码存在两处关键问题:

1. 窗口句柄在卸载模式下的错误引用

卸载模式下,WizardForm并非始终存在(比如静默卸载时完全没有交互界面),直接访问WizardForm.Handle会触发Inno Setup 5中的报错。正确做法是区分安装/卸载模式,选择对应的窗口句柄;如果是静默运行,直接传入0(SHFileOperation允许hwnd参数为0)。

2. SHFileOperation的字符串格式错误

SHFileOperationW要求pFrom参数是双空终止的字符串(末尾需添加两个#0),你当前仅添加了一个,导致函数调用失败,对应Inno Setup 6中的"Could not call proc"错误。另外代码中fFlags的硬编码数值存在笔误(将FOF_SILENT写成OF_SILENT),虽然数值正确,但建议使用常量组合提升代码可读性。

修正后的代码

替换原Code段为以下内容:

[Code]

type
  TSHFileOpStruct = record
    hwnd: HWND;
    wFunc: UINT;
    pFrom: string;
    pTo: string;
    fFlags: Word;
    fAnyOperationsAborted: BOOL; 
    hNameMappings: HWND;
    lpszProgressTitle: string;
  end; 

const
  FO_COPY            = $0002;
  FO_DELETE          = $0003;
  FOF_SILENT         = $0004;
  FOF_NOCONFIRMATION = $0010;
  FOF_NOCONFIRMMKDIR = $0020;
  FOF_ALLOWUNDO      = $0040;
  FOF_NOERRORUI      = $0400;
  FOF_NO_UI          = $0434; //(FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR);

function SHFileOperation(lpFileOp: TSHFileOpStruct): Integer;
external 'SHFileOperationW@shell32.dll stdcall';

function MoveToTrash(path: string) : Boolean;
var
  FileOp: TSHFileOpStruct;
  FromPath: string;
  Handle: HWND;
begin
  // 处理双空终止字符串
  FromPath := path + #0#0;
  
  // 确定合适的窗口句柄
  Handle := 0;
  if IsUninstaller then
  begin
    // 卸载模式下优先用卸载进度窗口句柄,不存在则传0
    if UninstallProgressForm <> nil then
      Handle := UninstallProgressForm.Handle;
  end
  else
  begin
    // 安装模式下用向导窗口句柄
    if WizardForm <> nil then
      Handle := WizardForm.Handle;
  end;

  FillChar(FileOp, SizeOf(FileOp), 0);
  FileOp.hwnd := Handle;
  FileOp.wFunc := FO_DELETE;
  FileOp.pFrom := FromPath;
  // 用常量组合替代硬编码,修正笔误
  FileOp.fFlags := FOF_ALLOWUNDO or FOF_SILENT or FOF_NOERRORUI or FOF_NOCONFIRMATION;
  Result := SHFileOperation(FileOp) = 0;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep=ssInstall then begin
    MoveToTrash('c:\9');
  end;
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep=usUninstall then begin
    MoveToTrash('c:\9');
  end;
end;

关键修正点说明

  • 增加IsUninstaller判断,区分安装/卸载模式,选择对应窗口句柄
  • 将FromPath改为path + #0#0,满足SHFileOperationW的字符串格式要求
  • 用FillChar初始化结构体,避免未初始化的内存导致异常
  • 用常量组合定义fFlags,修正原笔误并提升代码可读性

内容的提问来源于stack exchange,提问作者Alexander Dyagilev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 22:25:15