翻译《The Old New Thing》- How can I add an environment variable to a process launched via ...

103 篇文章 0 订阅
53 篇文章 1 订阅

How can I add an environment variable to a process launched via Shell­Execute­Ex or IContext­Menu? - The Old New Thing (microsoft.com)icon-default.png?t=N7T8https://devblogs.microsoft.com/oldnewthing/20240131-00/?p=109342

Raymond Chen 2024年01月31日


如何通过 ShellExecuteEx 或 IContextMenu 向启动的进程添加环境变量?

  ShellExecuteEx 函数和 IContextMenu 接口为调用者提供了多个地方来自定义执行过程,允许调用传递一个“站点”,ShellExecuteEx/IContextMenu 将在执行过程的各个点访问它。

        今天我们将通过 ShellExecuteExIContextMenu 启动的进程来演示添加环境变量的技术。

ICreatingProcess 接口由 ShellExecuteExIContextMenu 使用,允许调用者自定义进程的创建方式。通过查询站点 SID_ExecuteCreatingProcess 并请求 ICreatingProcess 来获取扩展点。如果产生了一个,系统将调用 OnCreating 方法,并通过一个对象来定制创建过程。

        今天使用的 C++ COM 库是(随机选择)C++/WinRT。

struct AddEnvironmentVariableSite :
    winrt::implements<AddEnvironmentVariableSite,
        ::IServiceProvider,
        ::ICreatingProcess>
{
    IFACEMETHOD(QueryService)
        (REFGUID service, REFIID riid, void** ppv)
    {
        if (service == SID_ExecuteCreatingProcess)
        {
            return this->QueryInterface(riid, ppv);
        }
        else
        {
            *ppv = nullptr;
            return E_NOTIMPL;
        }
    }

    IFACEMETHOD(OnCreating)(ICreateProcessInputs* inputs)
    {
        return inputs->SetEnvironmentVariable(
            L"EXTRAVARIABLE", L"Bonus");
    }
};

        这个站点通过响应 SID_ExecuteCreatingProcess 并返回它自己来实现 ICreatingProcess,它的 OnCreating 方法设置了一个名为 EXTRAVARIABLE 的环境变量,值为 Bonus。由于这是我们唯一做的事情,我们可以直接将 SetEnvironmentVariable() 的结果作为我们自己的返回值。如果你打算添加多个环境变量,你应该检查每个调用 SetEnvironmentVariable() 的返回值。

        通常,你的自定义站点会是一个所谓的“站点链”的一部分,并且会将任何未处理的服务转发到你自己的站点,以便外部站点可以响应。

        以下是一个示例,展示如何结合使用 ShellExecuteEx 和特殊的环境变量站点:

BOOL Sample() {
    SHELLEXECUTEINFO sei{ sizeof(sei) };
    sei.lpFile = LR"(C:\\Windows\\system32\\charmap.exe)";
    sei.nShow = SW_SHOWNORMAL;
    auto site = winrt::make_self<AddEnvironmentVariableSite>();
    sei.hInstApp = reinterpret_cast<HINSTANCE>(site.get());
    sei.fMask = SEE_MASK_FLAG_HINST_IS_SITE;
    return ShellExecuteEx(&sei);
}

        要传递站点给 ShellExecuteEx,我们将其放在 hInstApp 成员中,并设置 SEE_MASK_FLAG_HINST_IS_SITE 标志,这样系统就知道在 hInstApp 中查找站点。

        对于上下文菜单,我们显式地将自定义站点设置为上下文菜单的站点。以下是在托管 IContextMenu 时使用自定义站点的示例:

void OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos) {
    IContextMenu *pcm;
    if (SUCCEEDED(GetUIObjectOfFile(hwnd, L"C:\\Windows\\clock.avi",
                                   IID_IContextMenu, (void**)&pcm))) {
        HMENU hmenu = CreatePopupMenu();
        if (hmenu) {
            if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0,
                                               SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,
                                               CMF_NORMAL))) {
                CMINVOKECOMMANDINFO info = { 0 };
                info.cbSize = sizeof(info);
                info.hwnd = hwnd;
                info.lpVerb = "play";
                auto site = winrt::make_self<AddEnvironmentVariableSite>();
                IUnknown_SetSite(pcm, site.get());
                pcm->InvokeCommand(&info);
                IUnknown_SetSite(pcm, nullptr);
            }
            DestroyMenu(hmenu);
        }
        pcm->Release();
    }
}

我忽略了 winrt::make_self 可能会抛出异常,这在上述示例代码中可能导致内存泄漏的事实。

        这是一种相当笨拙的传递站点方式,但添加站点的能力是后来才加入到现有结构中的,所以我们不得不将它隐藏在一个其他情况下未使用的输入成员中。

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0x0007

可不可奖励我吃只毛嘴鸡 馋😋

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值