我有一些代码使用P / Invoke来启动进程并捕获标准输出。 (我们为什么使用P / Invoke代替System.Diagnostics.Process
这样做的故事很漫长而且令人费解;足以说它是一个要求。)它已经在重负荷下生产了将近一年,并且测试运动它总是过去。
今天早上虽然我进行了测试,但他们失败了。我不能确定我上次在今天早上(2014年5月15日)之前进行测试,但我相信这是2014年4月24日。测试通过了,但今天早上失败了。我收到了“PInvokeStackImbalance”错误消息,所以我做了一些研究,最终意识到extern
方法(在这个例子中CreatePipe
)使用的结构之一的签名是不正确的。我改变了,测试又开始了。
我很高兴找到了修复程序,但我很关心部署。 为什么结构的签名会发生变化?我没有升级我的操作系统或其他任何东西 - 我在4/24运行Windows 7 x64,而且我现在仍在运行它。 (部署环境是Windows Server 2012.)从那时起,我已经安装(并卸载)了一些应用程序,但它们是轻量级的第三方工具,而不是Microsoft或系统组件。我假设Windows Update修复程序负责,但我无法弄清楚哪一个。
要清楚,在我自己的代码中,我改变的是:
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
到此:
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES
{
public int nLength = 12;
public IntPtr lpSecurityDescriptor = IntPtr.Zero;
public bool bInheritHandle;
}
我需要确保在部署到生产环境时,为使代码在我的机器上运行所做的更改不会破坏应用程序。有谁知道如何确定需要更改的内容以及如何确定生产环境是否需要更改?
修改
以下是打开标准输出管道的代码:
private PipeInfo CreatePipe()
{
PipeInfo pipeInfo = new PipeInfo();
SafeFileHandle safeFileHandle = null;
try
{
Native.SECURITY_ATTRIBUTES pipeAttributes = new Native.SECURITY_ATTRIBUTES();
pipeAttributes.bInheritHandle = true;
if (!Native.CreatePipe(out safeFileHandle, out pipeInfo.ChildHandle, pipeAttributes, 0) || safeFileHandle.IsInvalid || pipeInfo.ChildHandle.IsInvalid)
{
throw new Win32Exception();
}
if (!Native.DuplicateHandle(new HandleRef(this, Native.GetCurrentProcess()), safeFileHandle, new HandleRef(this, Native.GetCurrentProcess()), out pipeInfo.ParentHandle, 0, false, 2))
{
throw new Win32Exception();
}
}
finally
{
if (safeFileHandle != null && !safeFileHandle.IsInvalid)
{
safeFileHandle.Close();
}
}
return pipeInfo;
}
我不能完全赞同这个代码,我在很大程度上将其从.NET Reference Source
中解除了只是要明确时间表:
CreatePipe
SECURITY_ATTRIBUTES
代码
SECURITY_ATTRIBUTES
的第二个版本,错误消失答案 0 :(得分:0)
我们在x64上遇到了此问题,而该帖子是我们搜索的最佳结果。就像我们从C#来源获得的解决方案那样,我们将魔术12用于nLength:https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES {
#if !SILVERLIGHT
// We don't support ACL's on Silverlight nor on CoreSystem builds in our API's.
// But, we need P/Invokes to occasionally take these as parameters. We can pass null.
public int nLength = 12;
public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
public bool bInheritHandle = false;
#endif // !SILVERLIGHT
}
事实证明,CreatePipe需要来自docs的指针:
lpPipeAttributes
指向SECURITY_ATTRIBUTES结构的指针,该结构确定返回的句柄是否可以由子进程继承。如果lpPipeAttributes为NULL,则不能继承该句柄。
此stackoverflow post中详细介绍了解决方案。它适用于x86和x64。下面的代码基于该堆栈溢出文章和进程源(使用DWORD = System.UInt32;在顶部)。
internal static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
IntPtr lpPipeAttributes, int nSize);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, SafeHandle hSourceHandle,
IntPtr hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess,
bool bInheritHandle, int dwOptions);
[StructLayout(LayoutKind.Sequential)]
public struct PIPE_SECURITY_ATTRIBUTES
{
public DWORD nLength;
public IntPtr lpSecurityDescriptor;
[MarshalAs(UnmanagedType.Bool)]
public bool bInheritHandle;
}
public static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
{
PIPE_SECURITY_ATTRIBUTES lpPipeAttributes = new PIPE_SECURITY_ATTRIBUTES();
lpPipeAttributes.nLength = (DWORD)Marshal.SizeOf(lpPipeAttributes);
lpPipeAttributes.bInheritHandle = true;
lpPipeAttributes.lpSecurityDescriptor = IntPtr.Zero;
IntPtr attr = Marshal.AllocHGlobal(Marshal.SizeOf(lpPipeAttributes));
Marshal.StructureToPtr(lpPipeAttributes, attr, true);
SafeFileHandle hWritePipe = null;
try
{
if (parentInputs)
CreatePipeWithSecurityAttributes(out childHandle, out hWritePipe, attr, 0);
else
CreatePipeWithSecurityAttributes(out hWritePipe, out childHandle, attr, 0);
if (!DuplicateHandle(GetCurrentProcess(), hWritePipe, GetCurrentProcess(), out parentHandle, 0, false, 2))
throw new Exception();
}
finally
{
if ((hWritePipe != null) && !hWritePipe.IsInvalid)
{
hWritePipe.Close();
}
}
}
public static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
IntPtr lpPipeAttributes, int nSize)
{
hReadPipe = null;
if ((!CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize) || hReadPipe.IsInvalid) || hWritePipe.IsInvalid)
throw new Exception();
}
}