带有 lpVerb = L"runas" 和 lpFile = L"cmd" 的 ShellExecuteExW 函数成功但失败

问题描述 投票:0回答:2

我对一个类进行了单元测试,该类旨在以提升的权限运行 shell 命令。一个简单的测试用例是将测试文件复制到

C:\Program Files
。该类调用 C 函数
ShellExecuteExW
函数,并在
lpVerb = L"runas"
参数中设置
lpFile = L"cmd"
SHELLEXECUTEINFOAW
。调用成功,没有任何错误,UAC 提升提示中的详细信息都是正确的,但是当我在程序文件中查找测试文件时,它不在那里。

我使用的是 Eiffel 编译器,它生成看起来神秘的 MSVC 编译的 C 代码,但这就是如果用 C 手动编写的代码的样子。请注意,在实际代码中,

%TEMP%
已经展开了。

#include <windows.h>
#include <Shellapi.h>

int main() {
    SHELLEXECUTEINFOAW shExecInfo = { 0 };

    // Set the structure size
    shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOAW);

    // Specify the operation to perform
    shExecInfo.lpVerb = L"runas"; // "runas" elevates the privileges

    // Specify the file or folder to be copied
    shExecInfo.lpFile = L"cmd";

    // Specify the parameters (in this case, the copy command)
    shExecInfo.lpParameters = L"/C /U copy data\\txt\\file.txt \"C:\\Program Files\" 2> %TEMP%\\copy-errors.txt";

    // Specify the working directory
    shExecInfo.lpDirectory = NULL;

    // Set the show property
    shExecInfo.nShow = SW_HIDE;

    // Execute the operation
    if (ShellExecuteExW(&shExecInfo) == FALSE) {
        // Handle error
        DWORD dwError = GetLastError();
        if (dwError == ERROR_CANCELLED) {
            // The user refused the UAC prompt
            printf("User refused the UAC prompt.\n");
        } else {
            // Other error
            printf("Error: %lu\n", dwError);
        }
        return 1;
    }

    return 0;
}

我运行单元测试,得到预期的提升提示,并且命令的所有详细信息都是正确的。

UAC提示

单击“确定”后,命令返回且没有错误,但测试文件未按预期复制到

C:\Program Files
。如果我使用管理员权限手动打开 shell 并复制/粘贴完全相同的命令,则会成功并且文件实际上被复制。所以我很困惑为什么这在 C 调用中不起作用。

Eiffel 单元测试调用

ShellExecuteExW

test_admin_level_execution
   -- OS_COMMAND_TEST_SET.test_admin_level_execution
   note
      testing: "[
         covers/{EL_WINDOWS_ADMIN_SHELL_COMMAND}.execute,
         covers/{EL_WINDOWS_ADMIN_SHELL_COMMAND}.set_command_and_parameters
      ]"
      status: "[
         Commented out in `make_named'. Failing on Windows
      ]"
   local
      command: EL_OS_COMMAND_I; destination_path: FILE_PATH
   do
      if Executable.Is_work_bench then
         create {EL_COPY_FILE_COMMAND_IMP} command.make ("data/txt/file.txt", Directory.applications)
         command.administrator.enable
         command.execute
         assert ("successful", not command.has_error)
         destination_path := Directory.applications + "file.txt"
         if destination_path.exists then
            create {EL_DELETE_FILE_COMMAND_IMP} command.make (destination_path)
            command.administrator.enable
            command.execute
            assert ("File deleted", not destination_path.exists)
         else
            failed ("File exists")
         end
      end
   end

测试在检查文件是否已复制时失败。我还手动检查了文件资源管理器。

替代方法

为了找到解决方法,我也在尝试替代功能:(但结果是相同的)

    HINSTANCE ShellExecute (
        _In_opt_ HWND    hwnd,
        _In_opt_ LPCTSTR lpOperation,
        _In_     LPCTSTR lpFile,
        _In_opt_ LPCTSTR lpParameters,
        _In_opt_ LPCTSTR lpDirectory,
        _In_     INT     nShowCmd
    );

环境

适用于 x64 的 Microsoft (R) C/C++ 优化编译器版本 16.00.40219.01

操作系统:Windows 7

winapi elevated-privileges runas shellexecuteex
2个回答
1
投票

问题是,显然当 cmd 启动提升时,它不支持当前目录,而是使用 system32 文件夹:https://stackoverflow.com/a/38034535/179634

因此,您需要在参数中指定

copy
命令的完整路径,或者使用
cd
命令更改命令行中的当前目录。


0
投票

正如 Luke 指出的,当

cmd
启动时,它不会遵循当前目录,而是使用 system32 文件夹。卢克提供的解决方案非常有帮助,但并没有完全解决问题,正如我将解释的那样。我尝试使用这个命令部分序列:

调试器视图

当我手动检查输出文件夹时,我可以看到

file.txt
,但单元测试仍然失败。为什么会这样?事实证明
ShellExecute
是异步运行的,所以我需要某种方法来等待该过程完成。我想出的解决方案如下。 (请注意任何带有“c_”前缀的内容都是来自 Eiffel 的 C 调用)

class
   EL_WINDOWS_SHELL_COMMAND

inherit
   EL_ALLOCATED_C_OBJECT
      rename
         make_default as make
      redefine
         make
      end

feature -- Basic operations

   execute
      local
         n: INTEGER
      do
         c_set_n_show (self_ptr, show_type)
         if c_shell_execute (self_ptr) then
         -- make asynchronous process appear to be synchronous
            n := c_wait_for_single_object (process_handle)
            if c_close_handle (process_handle) then
               do_nothing
            end
            is_successful := n >= 0
         end
      end

feature {NONE} -- Implementation

   process_handle: NATURAL
      do
         Result := c_process (self_ptr)
      end

end

Class

EL_WINDOWS_SHELL_COMMAND
是 C 结构的包装
SHELLEXECUTEINFOW

自我提醒

我需要要求 Stackoverflow 改进 Eiffel 的语法突出显示

© www.soinside.com 2019 - 2024. All rights reserved.