Skip to content

Commit

Permalink
Skyline: Consolidate use of P/Invoke into new library (#3270)
Browse files Browse the repository at this point in the history
Introduce a new library for using P/Invoke. This makes Skyline's use of Win32 APIs more transparent and reduces code duplication.

APIs for production code are in CommonUtil under namespace pwiz.Common.SystemUtil.PInvoke. Test-only use of P/Invoke goes in TestRunnerLib under namespace TestRunnerLib.PInvoke. New code inspections check for P/Invoke use outside these locations (with limited exceptions).

New or modified uses of P/Invoke should follow conventions described in PInvokecommon and go in the correct namespace. Also update the expected values used during code inspection to check the size and shape of the P/Invoke APIs in CodeInspectionTest.

New project dependencies:
* Test depends on TestRunnerLib
* TestFunctional depends on TestRunnerLib
* TestRunnerLib depends on System.Windows.Forms
  • Loading branch information
ekoneil authored Jan 2, 2025
1 parent 23ff91c commit d5bb1c3
Show file tree
Hide file tree
Showing 59 changed files with 1,388 additions and 894 deletions.
42 changes: 11 additions & 31 deletions pwiz_tools/Shared/Common/Controls/AutoScrollTextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,24 @@
*/

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using pwiz.Common.SystemUtil.PInvoke;

namespace pwiz.Common.Controls
{
public class AutoScrollTextBox : TextBox
{
// Constants for extern calls to various scrollbar functions
//private const int SB_HORZ = 0x0;
private const int SB_VERT = 0x1;
//private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int WM_SETREDRAW = 11;
private const int SB_THUMBPOSITION = 4;
//private const int SB_BOTTOM = 7;
//private const int SB_OFFSET = 13;

[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
[DllImport("user32.dll")]
private static extern bool LockWindowUpdate(IntPtr hWndLock);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);

/// <summary>
/// Stop updating control (helps prevent flickering).
/// </summary>
public void SuspendDrawing()
{
SendMessage(Handle, WM_SETREDRAW, 0, IntPtr.Zero);
User32.SendMessage(Handle, User32.WinMessageType.WM_SETREDRAW, User32.False, IntPtr.Zero);
}

public void ResumeDrawing()
{
SendMessage(Handle, WM_SETREDRAW, 1, IntPtr.Zero);
User32.SendMessage(Handle, User32.WinMessageType.WM_SETREDRAW, User32.True, IntPtr.Zero);
Invalidate(true);
Update();
}
Expand All @@ -75,19 +52,22 @@ public void AppendLineWithAutoScroll(string line)
// Win32 magic to keep the textbox scrolling to the newest append to the textbox unless
// the user has moved the scrollbox up
sbOffset = (ClientSize.Height - SystemInformation.HorizontalScrollBarHeight) / Font.Height;
savedVpos = GetScrollPos(Handle, SB_VERT);
GetScrollRange(Handle, SB_VERT, out _, out VSmax);
savedVpos = this.GetScrollPos(Orientation.Vertical);
User32.GetScrollRange(Handle, Orientation.Vertical, out _, out VSmax);
if (savedVpos >= (VSmax - sbOffset - 1))
bottomFlag = true;
SuspendDrawing();
AppendText(line);
if (bottomFlag)
{
GetScrollRange(Handle, SB_VERT, out _, out VSmax);
User32.GetScrollRange(Handle, Orientation.Vertical, out _, out VSmax);
savedVpos = VSmax - sbOffset;
}
SetScrollPos(Handle, SB_VERT, savedVpos, true);
PostMessageA(Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
this.SetScrollPos(Orientation.Vertical, savedVpos);
User32.PostMessageA(Handle,
User32.WinMessageType.WM_VSCROLL,
User32.SB_THUMBPOSITION + 0x10000 * savedVpos,
0);
ResumeDrawing();
}
}
Expand Down
8 changes: 2 additions & 6 deletions pwiz_tools/Shared/Common/Controls/DataGridViewProgressBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using pwiz.Common.Collections;
using pwiz.Common.SystemUtil.PInvoke;


namespace CustomProgressCell
Expand Down Expand Up @@ -129,10 +129,6 @@ public DataGridViewProgressCell()
_animationStopTimer.Tick += (x, y) => { _animationStepTimer.Stop(); _animationStopTimer.Stop(); };
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr w, IntPtr l);
const uint PBM_SETSTATE = 0x0410; // 1040

enum ProgressBarState
{
Normal = 1, // green
Expand All @@ -153,7 +149,7 @@ protected override void Dispose(bool disposing)

private void SetProgressBarState(ProgressBarState state)
{
SendMessage(_progressBar.Handle, PBM_SETSTATE, (IntPtr)state, IntPtr.Zero);
User32.SendMessage(_progressBar.Handle, User32.WinMessageType.PBM_SETSTATE, (IntPtr)state, IntPtr.Zero);
}

protected override void OnDataGridViewChanged()
Expand Down
7 changes: 2 additions & 5 deletions pwiz_tools/Shared/CommonUtil/Collections/NaturalComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using pwiz.Common.SystemUtil.PInvoke;

namespace pwiz.Common.Collections
{
Expand All @@ -38,13 +38,10 @@ namespace pwiz.Common.Collections
/// </summary>
public class NaturalFilenameComparer
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int StrCmpLogicalW(string x, string y);

//Compare strings with natural sort
public static int Compare(string x, string y)
{
return StrCmpLogicalW(x, y);
return Shlwapi.StrCmpLogicalW(x, y);
}
}

Expand Down
10 changes: 9 additions & 1 deletion pwiz_tools/Shared/CommonUtil/CommonUtil.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,31 @@
<Compile Include="SystemUtil\Caching\Production.cs" />
<Compile Include="SystemUtil\Caching\Receiver.cs" />
<Compile Include="SystemUtil\Caching\Producer.cs" />
<Compile Include="SystemUtil\CenterWinDialog.cs" />
<Compile Include="SystemUtil\CommonFormEx.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="SystemUtil\CommonTextUtil.cs" />
<Compile Include="SystemUtil\PInvoke\Advapi32.cs" />
<Compile Include="SystemUtil\PInvoke\Gdi32.cs" />
<Compile Include="SystemUtil\PInvoke\Kernel32.cs" />
<Compile Include="SystemUtil\PInvoke\Shell32.cs" />
<Compile Include="SystemUtil\PInvoke\Shlwapi.cs" />
<Compile Include="SystemUtil\PInvoke\User32.cs" />
<Compile Include="SystemUtil\PInvoke\User32Extensions.cs" />
<Compile Include="SystemUtil\Messages.cs" />
<Compile Include="SystemUtil\FileSize.cs" />
<Compile Include="SystemUtil\FileSizeFormatProvider.cs" />
<Compile Include="SystemUtil\IFormView.cs" />
<Compile Include="SystemUtil\ItemDescription.cs" />
<Compile Include="SystemUtil\PInvoke\PInvokeCommon.cs" />
<Compile Include="SystemUtil\ProcessEx.cs" />
<Compile Include="SystemUtil\StreamEx.cs" />
<Compile Include="SystemUtil\StringSearch.cs" />
<Compile Include="Spectra\SpectrumMetadata.cs" />
<Compile Include="Spectra\SpectrumPrecursor.cs" />
<Compile Include="SystemUtil\SubstringFinder.cs" />
<Compile Include="Collections\UniqueList.cs" />
<Compile Include="SystemUtil\CenterWinDialog.cs" />
<Compile Include="SystemUtil\CommonException.cs" />
<Compile Include="SystemUtil\ProgressHandler.cs" />
<Compile Include="SystemUtil\MemoryBlockingCollection.cs" />
Expand Down
46 changes: 18 additions & 28 deletions pwiz_tools/Shared/CommonUtil/SystemUtil/CenterWinDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using pwiz.Common.SystemUtil.PInvoke;

// DO NOT DELETE without due diligence on how this is used across all ProteoWizard projects.
// As of 12/31/2024, it is not used in Skyline so VisualStudio will say it's unreferenced,
// but it is used in MSConvertGUI which is separate from the Skyline solution.
//
// CONSIDER: move to pwiz_tools/MSConvertGUI
namespace pwiz.Common.SystemUtil
{
public class CenterWinDialog : IDisposable
Expand All @@ -24,8 +29,8 @@ private void findDialog()
{
// Enumerate windows to find the message box
if (mTries < 0) return;
EnumThreadWndProc callback = checkWindow;
if (EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero))
User32.EnumThreadWindowsProc callback = checkWindow;
if (User32.EnumThreadWindows(Kernel32.GetCurrentThreadId(), callback, IntPtr.Zero))
{
if (++mTries < 10) mOwner.BeginInvoke(new MethodInvoker(findDialog));
}
Expand All @@ -34,38 +39,23 @@ private bool checkWindow(IntPtr hWnd, IntPtr lp)
{
// Checks if <hWnd> is a dialog
StringBuilder sb = new StringBuilder(260);
GetClassName(hWnd, sb, sb.Capacity);
User32.GetClassName(hWnd, sb, sb.Capacity);
if (sb.ToString() != @"#32770") return true;
// Got it
Rectangle frmRect = new Rectangle(mOwner.Location, mOwner.Size);
RECT dlgRect;
GetWindowRect(hWnd, out dlgRect);
MoveWindow(hWnd,
frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) / 2,
frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) / 2,
dlgRect.Right - dlgRect.Left,
dlgRect.Bottom - dlgRect.Top, true);

var dlgRect = new User32.RECT();
User32.GetWindowRect(hWnd, ref dlgRect);
User32.MoveWindow(hWnd,
frmRect.Left + (frmRect.Width - dlgRect.right + dlgRect.left) / 2,
frmRect.Top + (frmRect.Height - dlgRect.bottom + dlgRect.top) / 2,
dlgRect.right - dlgRect.left,
dlgRect.bottom - dlgRect.top, true);
return false;
}
public void Dispose()
{
mTries = -1;
}

// P/Invoke declarations
private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc);
[DllImport("user32.dll")]
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);

[System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "UnassignedField.Compiler")]
private struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
}
}
}
51 changes: 0 additions & 51 deletions pwiz_tools/Shared/CommonUtil/SystemUtil/FormUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using pwiz.Common.Collections;

Expand Down Expand Up @@ -197,39 +196,6 @@ public static void RemoveTabPage(TabPage tabPage, ToolTip toolTipControl)
RemoveTabPage(tabPage, ImmutableList.Singleton(toolTipControl));
}

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static void SetForegroundWindow(this Control control)
{
SetForegroundWindow(control.Handle);
}

[DllImport("user32.dll")]
private static extern bool HideCaret(IntPtr hWnd);

public static void HideCaret(this Control control)
{
HideCaret(control.Handle);
}

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

public static void HideCaret(this ComboBox comboBox)
{
var handleEdit = FindWindowEx(comboBox.Handle, IntPtr.Zero, "Edit", null);
if (handleEdit != IntPtr.Zero)
HideCaret(handleEdit); }

[DllImport("user32.dll", SetLastError = true)]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

public static void SetScrollPos(this Control control, Orientation sd, int pos)
{
SetScrollPos(control.Handle, (int)sd, pos, true);
}

public static Control GetFocus(this Control control)
{
if (control.Focused)
Expand All @@ -238,22 +204,5 @@ public static Control GetFocus(this Control control)
from Control childControl in control.Controls
select GetFocus(childControl)).FirstOrDefault(focus => focus != null);
}

private const int SWP_NOMOVE = 0x0002;
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOACTIVATE = 0x0010;
private const int SWP_SHOWWINDOW = 0x0040;

private static readonly IntPtr HWND_TOP = new IntPtr(0);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

public static void BringWindowToSameLevelWithoutActivating(this Form targetWindow, IntPtr referenceWindowHandle)
{
// Use SetWindowPos to adjust z-order without activating
SetWindowPos(targetWindow.Handle, referenceWindowHandle, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
}
}
Loading

0 comments on commit d5bb1c3

Please sign in to comment.