我有一台有 2 个显示器的 VacationCalendar PC。我还有两个 Excel 电子表格,一个左一个,一个右一个。我想打开右侧的电子表格,并在脚本运行时将其移至右侧屏幕。
Excel 确实会记住它的最后一个窗口位置,但根据我的测试,它不会对单个电子表格执行此操作,它始终会在打开软件的最后一个显示器上打开电子表格。
这是我当前的批处理脚本:
@echo off
start excel.exe "C:\Users\vacationcalendar\Desktop\VacationCalendar_RIGHT.xlsx"
timeout /t 5 /nobreak >nul
powershell -Command "(new-object -ComObject WScript.Shell).SendKeys('{ESC}')"
timeout /t 1 /nobreak >nul
powershell -Command "(new-object -ComObject WScript.Shell).SendKeys('{#}{SHIFT}{RIGHT}')"
timeout /t 15 /nobreak >nul
start excel.exe "C:\Users\vacationcalendar\Desktop\VacationCalendar_LEFT.xlsx"
它打开正确的文件,然后等待 5 秒,然后发送 ESC 按键。这样做是因为当 Excel 文件打开时,一个单元格会突出显示。但我不相信它起作用,因为当它发送 {#}{SHIFT}{RIGHT} 击键时,它会将“#”符号放入突出显示的单元格中。
{#}{SHIFT}{RIGHT} 按键应该代表 WINKEY + SHIFT + 向右键,将窗口移动到右侧显示器。
我可以做得更好/学习如何让它发挥作用?
我尝试过的VBA宏:
Sub OpenAndPositionWorkbooks()
Dim ExcelApp As Object
Dim Workbook1 As Object
Dim Workbook2 As Object
' Create a new instance of Excel
Set ExcelApp = CreateObject("Excel.Application")
ExcelApp.Visible = True
' Open the first workbook and position it on the left monitor
Set Workbook1 = ExcelApp.Workbooks.Open("C:\Users\whull\Desktop\VacationCalendar_LEFT.xlsx")
Workbook1.Windows(1).WindowState = xlMaximized
Workbook1.Windows(1).Left = 0
' Open the second workbook and position it on the right monitor
Set Workbook2 = ExcelApp.Workbooks.Open("C:\Users\whull\Desktop\VacationCalendar_RIGHT.xlsx")
Workbook2.Windows(1).WindowState = xlMaximized
Workbook2.Windows(1).Left = Screen.Width \ Screen.TwipsPerPixelX
' Release objects
Set Workbook1 = Nothing
Set Workbook2 = Nothing
Set ExcelApp = Nothing
End Sub
试试这个代码。这会将 Excel 文件移动到右侧监视器。我已经尝试过并且有效。我已经对代码进行了注释,因此您应该不会在理解它时遇到问题。
重要提示:由于我们使用的是VBA代码,因此您的文件需要保存为
.xlsm
而不是.xlsx
在ThisWorkbook代码模块中。
Option Explicit
Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, _
ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, _
ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const SWP_NOSIZE As Long = &H1
Private Const SWP_NOACTIVATE As Long = &H10
Private Const SWP_NOZORDER As Long = &H4
Private Sub Workbook_Open()
Dim leftPos As Long
Dim appHwnd As Long
'~~> Get the left position of the second monitor
leftPos = GetSecondMonitorLeft()
If leftPos = -1 Then
MsgBox "No second monitor detected.", vbInformation
Else
'~~> Get the handle of the Excel application window
appHwnd = Application.hwnd
'~~> This is important because you can't move a maximized window
Application.WindowState = xlNormal
'~~> Move the application window to the second monitor
SetWindowPos appHwnd, 0, leftPos, 0, 0, 0, _
SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOACTIVATE
'~~> Maximize the application window
Application.WindowState = xlMaximized
End If
End Sub
在普通模块中
Option Explicit
Private Declare PtrSafe Function GetSystemMetrics32 Lib "user32" Alias _
"GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function EnumDisplayMonitors Lib "user32" (ByVal hdc As LongPtr, _
ByVal lprcClip As LongPtr, ByVal lpfnEnum As LongPtr, ByVal dwData As LongPtr) As Boolean
Private Declare PtrSafe Function GetMonitorInfo Lib "user32.dll" Alias _
"GetMonitorInfoA" (ByVal hMonitor As LongPtr, ByRef lpmi As Any) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias _
"RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type monitorInfo
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
End Type
Private Const SM_CMONITORS As Long = 80
'~~> This function gets the .Left of the 2nd monitor
Public Function GetSecondMonitorLeft() As Long
Dim monitorCount As Integer
Dim monitorInfo As monitorInfo
Dim hdc As LongPtr
Dim monCount As Long
monitorInfo.cbSize = Len(monitorInfo)
hdc = 0
'~~> This will get the number of monitors
monitorCount = GetSystemMetrics32(SM_CMONITORS)
'~~> Check if there are at least 2 monitors
If monitorCount >= 2 Then
'~~> Get the information of the second monitor
EnumDisplayMonitors 0, ByVal 0, AddressOf MonitorEnumProc, VarPtr(monitorInfo)
monCount = monitorInfo.rcMonitor.Left
Else
'~~> If there is only 1 monitor, return -1
monCount = -1
End If
GetSecondMonitorLeft = monCount
End Function
Private Function MonitorEnumProc(ByVal hMonitor As LongPtr, ByVal hdcMonitor As LongPtr, _
ByVal lprcMonitor As LongPtr, ByVal dwData As LongPtr) As Long
Dim monitorInfo As monitorInfo
monitorInfo.cbSize = Len(monitorInfo)
GetMonitorInfo hMonitor, monitorInfo
'~~> Here we copy the monitor info to the provided structure
CopyMemory ByVal dwData, monitorInfo, Len(monitorInfo)
'~~> Next enumeration
MonitorEnumProc = 1
End Function
示例文件:
您可以从这里下载示例文件进行测试。