[英]How can I open a File Explorer window from a Visual Studio app and set the position and size?
I have a VB.net application that compacts JPG files, renames them, and copies them from one location to another.我有一个 VB.net 应用程序,它可以压缩 JPG 文件、重命名它们并将它们从一个位置复制到另一个位置。 When the end-user uses the program, they'll open two file explorer windows to get the source and destination locations and drag them onto the text boxes.
当最终用户使用该程序时,他们将打开两个文件浏览器 windows 以获取源和目标位置并将它们拖到文本框上。
I have added code that opens two file explorers to set locations, but I would like one windows to be in the lower left size of the screen and the other to be in the lower right.我添加了打开两个文件资源管理器以设置位置的代码,但我希望一个 windows 位于屏幕的左下方,另一个位于屏幕的右下方。 Each would be sized to take up 1/4 of the screen.
每个都将占据屏幕的 1/4。
Most of what I have found is very old.我发现的大部分东西都很古老。 I've found people who said it can't be done and others who provide very old code that doesn't seem to play nice with Visual Studio 2019.
我发现有人说无法完成,其他人提供的代码非常陈旧,似乎无法与 Visual Studio 2019 配合使用。
Private Sub btnOpenExplorer_Click(sender As Object, e As EventArgs) Handles btnOpenExplorer.Click
Process.Start("explorer.exe", String.Format("/n, /e, {0}", "C:\Users\" & Environment.UserName & "\Box\Site Visit Photos"))
Process.Start("explorer.exe", String.Format("/n, /e, {0}", "P:\"))
End Sub
The above code works well.上面的代码运行良好。 I just need to add sizing and positioning.
我只需要添加尺寸和定位。
You can use MoveWindow()
in user32.dll
.您可以在
user32.dll
中使用MoveWindow()
。 The window handle can be obtained by proc.MainWindowHandle
where proc
is the process returned by Process.Start()
. window 句柄可以通过
proc.MainWindowHandle
获得,其中proc
是Process.Start()
返回的进程。
Additionaly this works well for me: https://www.codeproject.com/Tips/1057230/Windows-Resize-and-Move另外这对我很有效: https://www.codeproject.com/Tips/1057230/Windows-Resize-and-Move
The Problem问题
I think it's difficult to do it in the caller routine btnOpenExplorer_Click
since it will be too early to get a process object with all of its properties assigned.我认为在调用程序
btnOpenExplorer_Click
中很难做到这一点,因为现在获得一个分配了所有属性的进程 object 还为时过早。 Mostly, the ProcessMainWindowTitle and the Process.MainWindowHandle properties which are needed to solve this problem.大多数情况下,解决此问题所需的ProcessMainWindowTitle和Process.MainWindowHandle属性。 A workaround to do this is to make the caller starts the processes and a Timer to do the positioning and resizing by the SetWindowPos function.
一个解决方法是让调用者启动进程和一个计时器来通过SetWindowPos function 进行定位和调整大小。
Here's how I'd do it:这是我的做法:
The API Functions API 功能
<DllImport("user32.dll", EntryPoint:="SetWindowPos")>
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function IsIconic(hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll")>
Public Shared Function ShowWindow(hWnd As IntPtr, <MarshalAs(UnmanagedType.I4)> nCmdShow As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Class Level Constants & Variables Class 电平常数和变量
Private Const HWND_TOP As Integer = &H0
Private Const SW_SHOWNORMAL As Integer = &H1
Private dir1, dir2 As String
Private WithEvents Timer1 As New Timer With {.Interval = 250}
Process Finder进程查找器
Private Function GetExplorerProcess(title As String) As Process
Dim dirName As String = If(IO.Directory.Exists(title), New IO.DirectoryInfo(title).Name, title).ToLower
Return Process.GetProcesses.Where(
Function(a) a.ProcessName.ToLower.Equals("explorer") AndAlso
a.MainWindowTitle.ToLower.Equals(dirName)
).FirstOrDefault
End Function
The Caller呼叫者,召集者
Private Sub btnOpenExplorer_Click(sender As Object, e As EventArgs) Handles btnOpenExplorer.Click
Dim proc1 As Process = GetExplorerProcess(dir1)
If proc1 Is Nothing Then
Dim procInfo1 As New ProcessStartInfo With {
.FileName = "explorer.exe",
.Arguments = dir1,
.WindowStyle = ProcessWindowStyle.Normal
}
Process.Start(procInfo1)
End If
Dim proc2 As Process = GetExplorerProcess(dir2)
If proc2 Is Nothing Then
Dim procInfo2 As New ProcessStartInfo With {
.FileName = "explorer.exe",
.Arguments = dir2,
.WindowStyle = ProcessWindowStyle.Normal
}
Process.Start(procInfo2)
End If
Timer1.Start()
End Sub
The Timer - Activate the Windows, Set Both Size & Location计时器 - 激活 Windows,设置大小和位置
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim proc1 As Process = GetExplorerProcess(dir1)
Dim proc2 As Process = GetExplorerProcess(dir2)
If proc1 IsNot Nothing AndAlso proc2 IsNot Nothing Then
Timer1.Stop()
Dim R As Rectangle = Screen.PrimaryScreen.WorkingArea
Dim R1 As New Rectangle(R.X, R.Height - (R.Height / 3), R.Width / 2, R.Height / 4)
Dim R2 As New Rectangle(R1.Right, R1.Y, R1.Width, R1.Height)
Dim hWnd1 As IntPtr = proc1.MainWindowHandle
Dim hWnd2 As IntPtr = proc2.MainWindowHandle
'Restore the first window if its minimized.
If IsIconic(hWnd1) Then
ShowWindow(hWnd1, SW_SHOWNORMAL)
End If
'Set the size and location of the first window.
SetWindowPos(hWnd1, IntPtr.op_Explicit(HWND_TOP), R1.X, R1.Y, R1.Width, R1.Height, 0)
'Restore the second window if its minimized.
If IsIconic(hWnd2) Then
ShowWindow(hWnd2, SW_SHOWNORMAL)
End If
'Set the size and location of the second window.
SetWindowPos(hWnd2, IntPtr.op_Explicit(HWND_TOP), R2.X, R2.Y, R2.Width, R2.Height, 0)
End If
End Sub
After some googling here and there, I figured out a better approach (I think) through using the Microsoft Internet Controls - SHDocVw component.在到处搜索之后,我通过使用Microsoft Internet Controls - SHDocVw 组件找到了一种更好的方法(我认为)。
First, we need to add a reference to that COM component:首先,我们需要添加对 COM 组件的引用:
References
node and select Add Reference
.References
节点和 select Add Reference
。COM
tab.COM
选项卡。Microsoft Internet Controls
and hit OK.Microsoft Internet Controls
,然后单击确定。 We need from solution1 the APIs and the constants only, and a new function to get the IE window:我们只需要解决方案1 中的 API 和常量,以及一个新的 function 来获取 IE window:
Private Function GetIE(dir As String) As SHDocVw.InternetExplorer
Return (From ie In New SHDocVw.ShellWindows
Where New Uri(DirectCast(ie, SHDocVw.InternetExplorer).LocationURL).LocalPath.Equals(dir, StringComparison.OrdinalIgnoreCase)
Select DirectCast(ie, SHDocVw.InternetExplorer)).FirstOrDefault
End Function
And finally, the caller:最后,调用者:
Private Sub btnOpenExplorer_Click(sender As Object, e As EventArgs) Handles btnOpenExplorer.Click
Dim dir1 As String = "FirstPath"
Dim dir2 As String = "SecondPath"
Dim ie1, ie2 As SHDocVw.InternetExplorer
If Not IO.Path.GetPathRoot(dir1).Equals(dir1, StringComparison.OrdinalIgnoreCase) Then
dir1 = dir1.TrimEnd(IO.Path.DirectorySeparatorChar)
End If
If Not IO.Path.GetPathRoot(dir2).Equals(dir2, StringComparison.OrdinalIgnoreCase) Then
dir2 = dir2.TrimEnd(IO.Path.DirectorySeparatorChar)
End If
ie1 = GetIE(dir1)
ie2 = GetIE(dir2)
If ie1 Is Nothing OrElse ie2 Is Nothing Then
Process.Start(dir1)
Process.Start(dir2)
Threading.Thread.Sleep(1000)
End If
If ie1 Is Nothing Then ie1 = GetIE(dir1)
If ie2 Is Nothing Then ie2 = GetIE(dir2)
If ie1 IsNot Nothing AndAlso ie2 IsNot Nothing Then
Dim hWnd1 = IntPtr.op_Explicit(ie1.HWND)
Dim hWnd2 = IntPtr.op_Explicit(ie2.HWND)
Dim R As Rectangle = Screen.PrimaryScreen.WorkingArea
Dim R1 As New Rectangle(R.X, R.Height - (R.Height \ 3), R.Width \ 2, R.Height \ 4)
Dim R2 As New Rectangle(R1.Right, R1.Y, R1.Width, R1.Height)
SetWindowPos(hWnd1, IntPtr.op_Explicit(HWND_TOP), R2.X, R2.Y, R2.Width, R2.Height, 0)
SetWindowPos(hWnd2, IntPtr.op_Explicit(HWND_TOP), R1.X, R1.Y, R1.Width, R1.Height, 0)
If IsIconic(hWnd1) Then
ShowWindow(hWnd1, SW_SHOWNORMAL)
End If
If IsIconic(hWnd2) Then
ShowWindow(hWnd2, SW_SHOWNORMAL)
End If
End If
End Sub
Please note, this solution works also with drives (IE: c:\
, d:\
..etc.) while the first one doesn't.请注意,此解决方案也适用于驱动器(即:
c:\
, d:\
..etc.),而第一个不适用。
Here's a demo:这是一个演示:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.