Delphi 如何将指向句柄列表的指针传递给UpdateProctThreadAttribute函数

Delphi 如何将指向句柄列表的指针传递给UpdateProctThreadAttribute函数,delphi,winapi,vcl,kernel32,Delphi,Winapi,Vcl,Kernel32,我有一个生成多个CreateProcess线程的应用程序,我正在成功地将stdout和stderr输出重定向到每个线程的文本文件 然而,我发现了一个特性,即stdout/strderr句柄被所有这样的线程继承,而不仅仅是我希望它们被继承的线程。因此,我开始了一段旅程,使用InitializeProctThreadAttributeList、UpdateProctThreadAttribute函数和扩展的_STARTUPINFO_PRESENT以及CreateProcess函数中的STARTUPI

我有一个生成多个CreateProcess线程的应用程序,我正在成功地将stdout和stderr输出重定向到每个线程的文本文件

然而,我发现了一个特性,即stdout/strderr句柄被所有这样的线程继承,而不仅仅是我希望它们被继承的线程。因此,我开始了一段旅程,使用InitializeProctThreadAttributeList、UpdateProctThreadAttribute函数和扩展的_STARTUPINFO_PRESENT以及CreateProcess函数中的STARTUPINFOEX结构来解决这个问题,但我被卡住了

如果我在UpdateProcThreadAttribute过程中使用PROC_THREAD_ATTRIBUTE_HANDLE_LIST作为属性参数,它希望lpValue参数是指向要由子进程继承的句柄列表的指针

对于列表,我尝试使用

TList<Cardinal>
并且还创建了一个基数数组,但无法获得任何一种编译方法

问题:如何创建和填充这样的列表

其次,它使用的是kernel32.dll中的函数和过程,但它们也存在于Windows单元中。我使用的是Delphi 10.3,尽管定义不同:

例如,InitializeProcThreadAttributeList nil,1,0,vAListSize;由于nil参数,我不会使用Windows单元进行编译,因为实际变量和形式变量参数的类型必须相同,但我使用kernel32中的参数时没有此类问题

问题:我应该使用这些函数/过程的哪个版本


谢谢。

如果有用的话,下面是我实现所有这些的代码:

type
  TStartupInfoEx = record
    StartupInfo: TStartupInfo;
    lpAttributeList: Pointer;
  end;

const
  PROC_THREAD_ATTRIBUTE_HANDLE_LIST = $00020002;

function InitializeProcThreadAttributeList(
  lpAttributeList: Pointer;
  dwAttributeCount: DWORD;
  dwFlags: DWORD;
  var lpSize: SIZE_T
): BOOL; stdcall; external kernel32;

function UpdateProcThreadAttribute(
  lpAttributeList: Pointer;
  dwFlags: DWORD;
  Attribute: DWORD_PTR;
  lpValue: Pointer;
  cbSize: SIZE_T;
  lpPreviousValue: PPointer;
  lpReturnSize: PSIZE_T
): BOOL; stdcall; external kernel32;

function DeleteProcThreadAttributeList(
  lpAttributeList: Pointer
): BOOL; stdcall; external kernel32;

function CreateProcessWithInheritedHandles(
  lpApplicationName: LPCWSTR;
  lpCommandLine: LPWSTR;
  lpProcessAttributes,
  lpThreadAttributes: PSecurityAttributes;
  const Handles: array of THandle;
  dwCreationFlags: DWORD;
  lpEnvironment: Pointer;
  lpCurrentDirectory: LPCWSTR;
  const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation
): Boolean;
var
  i: Integer;
  StartupInfoEx: TStartupInfoEx;
  size: SIZE_T;
begin
  Assert(Length(Handles)>0);

  StartupInfoEx.StartupInfo := lpStartupInfo;
  StartupInfoEx.StartupInfo.cb := SizeOf(StartupInfoEx);
  StartupInfoEx.lpAttributeList := nil;

  Win32Check(not InitializeProcThreadAttributeList(nil, 1, 0, size) and (GetLastError=ERROR_INSUFFICIENT_BUFFER));
  GetMem(StartupInfoEx.lpAttributeList, size);
  try
    Win32Check(InitializeProcThreadAttributeList(StartupInfoEx.lpAttributeList, 1, 0, size));
    try
      Win32Check(UpdateProcThreadAttribute(
        StartupInfoEx.lpAttributeList,
        0,
        PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
        @Handles[0],
        Length(Handles) * SizeOf(Handles[0]),
        nil,
        nil
      ));

      for i := 0 to High(Handles) do begin
        Win32Check(SetHandleInformation(Handles[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
      end;

      Result := CreateProcess(
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        True,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        StartupInfoEx.StartupInfo,
        lpProcessInformation
      );
    finally
      DeleteProcThreadAttributeList(StartupInfoEx.lpAttributeList);
    end;
  finally
    FreeMem(StartupInfoEx.lpAttributeList);
  end;
end;

从您的帖子中可以看出,在最新版本的Delphi中,Windows单元中似乎有一些InitializepRoctThreadAttributeList、UpdatepRoctThreadAttribute和DeleteProcThreadAttributeList的声明,但您的帖子暗示这些声明不正确。以上代码可以正常工作。

如果有用,下面是我的代码来实现所有这些:

type
  TStartupInfoEx = record
    StartupInfo: TStartupInfo;
    lpAttributeList: Pointer;
  end;

const
  PROC_THREAD_ATTRIBUTE_HANDLE_LIST = $00020002;

function InitializeProcThreadAttributeList(
  lpAttributeList: Pointer;
  dwAttributeCount: DWORD;
  dwFlags: DWORD;
  var lpSize: SIZE_T
): BOOL; stdcall; external kernel32;

function UpdateProcThreadAttribute(
  lpAttributeList: Pointer;
  dwFlags: DWORD;
  Attribute: DWORD_PTR;
  lpValue: Pointer;
  cbSize: SIZE_T;
  lpPreviousValue: PPointer;
  lpReturnSize: PSIZE_T
): BOOL; stdcall; external kernel32;

function DeleteProcThreadAttributeList(
  lpAttributeList: Pointer
): BOOL; stdcall; external kernel32;

function CreateProcessWithInheritedHandles(
  lpApplicationName: LPCWSTR;
  lpCommandLine: LPWSTR;
  lpProcessAttributes,
  lpThreadAttributes: PSecurityAttributes;
  const Handles: array of THandle;
  dwCreationFlags: DWORD;
  lpEnvironment: Pointer;
  lpCurrentDirectory: LPCWSTR;
  const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation
): Boolean;
var
  i: Integer;
  StartupInfoEx: TStartupInfoEx;
  size: SIZE_T;
begin
  Assert(Length(Handles)>0);

  StartupInfoEx.StartupInfo := lpStartupInfo;
  StartupInfoEx.StartupInfo.cb := SizeOf(StartupInfoEx);
  StartupInfoEx.lpAttributeList := nil;

  Win32Check(not InitializeProcThreadAttributeList(nil, 1, 0, size) and (GetLastError=ERROR_INSUFFICIENT_BUFFER));
  GetMem(StartupInfoEx.lpAttributeList, size);
  try
    Win32Check(InitializeProcThreadAttributeList(StartupInfoEx.lpAttributeList, 1, 0, size));
    try
      Win32Check(UpdateProcThreadAttribute(
        StartupInfoEx.lpAttributeList,
        0,
        PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
        @Handles[0],
        Length(Handles) * SizeOf(Handles[0]),
        nil,
        nil
      ));

      for i := 0 to High(Handles) do begin
        Win32Check(SetHandleInformation(Handles[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
      end;

      Result := CreateProcess(
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        True,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        StartupInfoEx.StartupInfo,
        lpProcessInformation
      );
    finally
      DeleteProcThreadAttributeList(StartupInfoEx.lpAttributeList);
    end;
  finally
    FreeMem(StartupInfoEx.lpAttributeList);
  end;
end;

从您的帖子中可以看出,在最新版本的Delphi中,Windows单元中似乎有一些InitializepRoctThreadAttributeList、UpdatepRoctThreadAttribute和DeleteProcThreadAttributeList的声明,但您的帖子暗示这些声明不正确。上面的代码工作正常。

您需要将指针传递到handles的数组句柄h[n]当函数需要THandle时,您为什么会考虑使用Cardinal?如果您不能将nil传递给InitializeProcThreadAttributeList,那么我怀疑您应该自己声明InitializeProcThreadAttributeList,并正确执行它。我就是这么做的,好问题!我到那里可能是因为定义上说的是pValue-System。Cardinal和我一直在想方设法让它工作,但都没有用!在UpdateProcThreadAttribute中使用@vMyHandles(其中vMyHandles是句柄数组)不会编译。我对使用指针有些困惑。不要使用IDE作为Windows API的参考。使用实际的Windows API参考。不要相信Emba的标题翻译。它们通常是错误的。@vMyHandles是指向数组地址的指针。这是一个间接的层次太远了。不管怎样,我的答案中已经包含了所有需要的内容。您需要将指针传递给handles的数组句柄h[n],当函数需要THandle时,您为什么会考虑使用Cardinal?如果您不能将nil传递给InitializeProcThreadAttributeList,那么我怀疑您应该自己声明InitializeProcThreadAttributeList,并正确执行它。我就是这么做的,好问题!我到那里可能是因为定义上说的是pValue-System。Cardinal和我一直在想方设法让它工作,但都没有用!在UpdateProcThreadAttribute中使用@vMyHandles(其中vMyHandles是句柄数组)不会编译。我对使用指针有些困惑。不要使用IDE作为Windows API的参考。使用实际的Windows API参考。不要相信Emba的标题翻译。它们通常是错误的。@vMyHandles是指向数组地址的指针。这是一个间接的层次太远了。不管怎样,从我的回答中你已经得到了你所需要的一切。