SetLayeredWindowAttributes 上的无效参数错误
Invalid parameter error on SetLayeredWindowAttributes
为了便于调试,我在我的 WPF 应用程序中添加了一个控制台 window,我可以在其中监视应用程序中所有正在进行的事件。我还添加了一些不错的方法,可以自动将 window 移动到我的辅助屏幕,将其最大化并将其不透明度设置为 90%。
使用调试配置,它工作得很好并且符合预期。但是,一旦我使用发布配置,我就会发现一些奇怪的异常。使用此 P/Invoke 实现:
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
public static void SetOpacity(byte alpha)
{
IntPtr handle = GetConsoleWindow();
if (handle == IntPtr.Zero) return;
Logging.Trace("Found console handle.");
Logging.Trace($"Setting console opacity to: {alpha}.");
if (!SetLayeredWindowAttributes(handle, 0, alpha, 0x2))
{
int error = Marshal.GetLastWin32Error();
Logging.Trace($"Return value: {error}");
if (error != 0)
throw new Win32Exception(error);
}
Logging.Trace("Set LW attributes.");
}
运行 这没有日志记录我返回“87:参数不正确”。当我添加日志记录时,我得到(很奇怪)“183:当该文件已经存在时无法创建文件。”
我真的不明白为什么会这样。以及为什么日志记录会影响结果。以及为什么这可以使用调试配置。
经过大量研究后,我发现 SetLayeredWindowAttributes method. I can't explain why this does work perfectly fine when building in debug configuration but to make it work with release configuration you need to first enhance the window style with SetWindowLongPtr (you will also need GetWindowLongPtr) 的内容更多。 请注意,您应该使用后缀为 'Ptr' 的方法,因为只有这些方法适用于 x86 和 x64 Windows 版本.
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLongPtr(HandleRef hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
private const int GWL_EXSTYLE = -20;
private const int WS_EX_LAYERED = 0x00080000;
public static void SetOpacity(byte alpha)
{
if (alpha == byte.MaxValue)
{
MakeOpaque();
return;
}
IntPtr handle = GetConsoleWindow();
if (handle == IntPtr.Zero) return;
int error;
if (IntPtr.Size == 4) // x86
{
int winFlags = GetWindowLongPtr32(handle, GWL_EXSTYLE).ToInt32();
winFlags |= WS_EX_LAYERED;
if (SetWindowLongPtr(new HandleRef(Core.IDE, handle), GWL_EXSTYLE, winFlags) == 0)
{
error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
else // x64
{
long winFlags = GetWindowLongPtr64(handle, GWL_EXSTYLE).ToInt64();
winFlags |= WS_EX_LAYERED;
if (SetWindowLongPtr64(new HandleRef(Core.IDE, handle), GWL_EXSTYLE, new IntPtr(winFlags)).ToInt64() == 0)
{
error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
if (!SetLayeredWindowAttributes(handle, 0, alpha, 0x2))
{
error = Marshal.GetLastWin32Error();
Logging.Trace($"Return value: {error}");
if (error != 0)
throw new Win32Exception(error);
}
}
有了这个,两种架构上的一切都完美无缺。您可以在 MSDN 上找到简短的代码,您应该可以轻松地将其转换为 C#。
SetLayeredWindowAttributes()
仅适用于具有 WS_EX_LAYERED
属性的 windows。控制台 window 可能没有那种样式,这可以解释您看到的错误 87。所以你必须事先用 SetWindowLongPtr()
设置样式,例如:
[DllImport("user32.dll", EntryPoint="GetWindowLong", SetLastError=true)]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint="GetWindowLongPtr", SetLastError=true)]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint="SetWindowLong", SetLastError=true)]
private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint="SetWindowLongPtr", SetLastError=true)]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError=true)]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
[DllImport("kernel32.dll")]
static extern void SetLastError(uint dwErrorCode);
private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size == 8)
return GetWindowLongPtr64(hWnd, nIndex);
else
return GetWindowLongPtr32(hWnd, nIndex);
}
private static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
if (IntPtr.Size == 8)
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
else
return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
}
private const int GWL_EXSTYLE = -20;
private const int WS_EX_LAYERED = 0x00080000;
public static void SetOpacity(byte alpha)
{
if (alpha == byte.MaxValue)
{
MakeOpaque();
return;
}
IntPtr handle = GetConsoleWindow();
if (handle == IntPtr.Zero)
return;
Logging.Trace("Found console handle.");
int error;
SetLastError(0);
int winFlags = (int) GetWindowLongPtr(handle, GWL_EXSTYLE);
if (winFlags == 0)
{
error = Marshal.GetLastWin32Error();
if (error != 0)
{
Logging.Trace($"GetWindowLongPtr error: {error}");
throw new Win32Exception(error);
}
}
if ((winFlags & WS_EX_LAYERED) == 0)
{
Logging.Trace($"Setting console layered style.");
winFlags |= WS_EX_LAYERED;
SetLastError(0);
if (SetWindowLongPtr(handle, GWL_EXSTYLE, new IntPtr(winFlags)) == 0)
{
error = Marshal.GetLastWin32Error();
if (error != 0)
{
Logging.Trace($"SetWindowLongPtr error: {error}");
throw new Win32Exception(error);
}
}
}
Logging.Trace($"Setting console opacity to: {alpha}.");
if (!SetLayeredWindowAttributes(handle, 0, alpha, 0x2))
{
error = Marshal.GetLastWin32Error();
Logging.Trace($"SetLayeredWindowAttributes error: {error}");
throw new Win32Exception(error);
}
Logging.Trace("Set LW attributes.");
}
为了便于调试,我在我的 WPF 应用程序中添加了一个控制台 window,我可以在其中监视应用程序中所有正在进行的事件。我还添加了一些不错的方法,可以自动将 window 移动到我的辅助屏幕,将其最大化并将其不透明度设置为 90%。
使用调试配置,它工作得很好并且符合预期。但是,一旦我使用发布配置,我就会发现一些奇怪的异常。使用此 P/Invoke 实现:
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
public static void SetOpacity(byte alpha)
{
IntPtr handle = GetConsoleWindow();
if (handle == IntPtr.Zero) return;
Logging.Trace("Found console handle.");
Logging.Trace($"Setting console opacity to: {alpha}.");
if (!SetLayeredWindowAttributes(handle, 0, alpha, 0x2))
{
int error = Marshal.GetLastWin32Error();
Logging.Trace($"Return value: {error}");
if (error != 0)
throw new Win32Exception(error);
}
Logging.Trace("Set LW attributes.");
}
运行 这没有日志记录我返回“87:参数不正确”。当我添加日志记录时,我得到(很奇怪)“183:当该文件已经存在时无法创建文件。”
我真的不明白为什么会这样。以及为什么日志记录会影响结果。以及为什么这可以使用调试配置。
经过大量研究后,我发现 SetLayeredWindowAttributes method. I can't explain why this does work perfectly fine when building in debug configuration but to make it work with release configuration you need to first enhance the window style with SetWindowLongPtr (you will also need GetWindowLongPtr) 的内容更多。 请注意,您应该使用后缀为 'Ptr' 的方法,因为只有这些方法适用于 x86 和 x64 Windows 版本.
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLongPtr(HandleRef hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
private const int GWL_EXSTYLE = -20;
private const int WS_EX_LAYERED = 0x00080000;
public static void SetOpacity(byte alpha)
{
if (alpha == byte.MaxValue)
{
MakeOpaque();
return;
}
IntPtr handle = GetConsoleWindow();
if (handle == IntPtr.Zero) return;
int error;
if (IntPtr.Size == 4) // x86
{
int winFlags = GetWindowLongPtr32(handle, GWL_EXSTYLE).ToInt32();
winFlags |= WS_EX_LAYERED;
if (SetWindowLongPtr(new HandleRef(Core.IDE, handle), GWL_EXSTYLE, winFlags) == 0)
{
error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
else // x64
{
long winFlags = GetWindowLongPtr64(handle, GWL_EXSTYLE).ToInt64();
winFlags |= WS_EX_LAYERED;
if (SetWindowLongPtr64(new HandleRef(Core.IDE, handle), GWL_EXSTYLE, new IntPtr(winFlags)).ToInt64() == 0)
{
error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
if (!SetLayeredWindowAttributes(handle, 0, alpha, 0x2))
{
error = Marshal.GetLastWin32Error();
Logging.Trace($"Return value: {error}");
if (error != 0)
throw new Win32Exception(error);
}
}
有了这个,两种架构上的一切都完美无缺。您可以在 MSDN 上找到简短的代码,您应该可以轻松地将其转换为 C#。
SetLayeredWindowAttributes()
仅适用于具有 WS_EX_LAYERED
属性的 windows。控制台 window 可能没有那种样式,这可以解释您看到的错误 87。所以你必须事先用 SetWindowLongPtr()
设置样式,例如:
[DllImport("user32.dll", EntryPoint="GetWindowLong", SetLastError=true)]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint="GetWindowLongPtr", SetLastError=true)]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint="SetWindowLong", SetLastError=true)]
private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint="SetWindowLongPtr", SetLastError=true)]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError=true)]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
[DllImport("kernel32.dll")]
static extern void SetLastError(uint dwErrorCode);
private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size == 8)
return GetWindowLongPtr64(hWnd, nIndex);
else
return GetWindowLongPtr32(hWnd, nIndex);
}
private static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
if (IntPtr.Size == 8)
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
else
return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
}
private const int GWL_EXSTYLE = -20;
private const int WS_EX_LAYERED = 0x00080000;
public static void SetOpacity(byte alpha)
{
if (alpha == byte.MaxValue)
{
MakeOpaque();
return;
}
IntPtr handle = GetConsoleWindow();
if (handle == IntPtr.Zero)
return;
Logging.Trace("Found console handle.");
int error;
SetLastError(0);
int winFlags = (int) GetWindowLongPtr(handle, GWL_EXSTYLE);
if (winFlags == 0)
{
error = Marshal.GetLastWin32Error();
if (error != 0)
{
Logging.Trace($"GetWindowLongPtr error: {error}");
throw new Win32Exception(error);
}
}
if ((winFlags & WS_EX_LAYERED) == 0)
{
Logging.Trace($"Setting console layered style.");
winFlags |= WS_EX_LAYERED;
SetLastError(0);
if (SetWindowLongPtr(handle, GWL_EXSTYLE, new IntPtr(winFlags)) == 0)
{
error = Marshal.GetLastWin32Error();
if (error != 0)
{
Logging.Trace($"SetWindowLongPtr error: {error}");
throw new Win32Exception(error);
}
}
}
Logging.Trace($"Setting console opacity to: {alpha}.");
if (!SetLayeredWindowAttributes(handle, 0, alpha, 0x2))
{
error = Marshal.GetLastWin32Error();
Logging.Trace($"SetLayeredWindowAttributes error: {error}");
throw new Win32Exception(error);
}
Logging.Trace("Set LW attributes.");
}