Comment créer une fenêtre de rappel Outlook au-dessus des autres fenêtres?
Après avoir cherché en ligne pendant un long moment; Je n'ai pas pu trouver de réponse satisfaisante à cette question.
Utilisation de Windows 7 et Microsoft Outlook 2007+; lorsqu'un rappel clignote, il ne donne plus de boîte modale pour attirer votre attention. Au travail, lorsque l'installation de plug-ins supplémentaires peut être problématique (droits d'administrateur) et lors de l'utilisation d'un système silencieux, les demandes de réunion sont souvent ignorées.
Existe-t-il un moyen plus simple d'implémenter cela que d'utiliser des plugins/applications tiers?
* Pour la dernière macro, voir la mise à jour 3 *
Après avoir cherché pendant un moment, j'ai trouvé une réponse partielle sur un site Web qui semblait me donner la majorité de la solution; https://superuser.com/questions/251963/how-to-make-Outlook-calendar -reminders-restez-en-haut-dans-windows-7
Cependant, comme indiqué dans les commentaires, le premier rappel n'a pas pu être affiché; tandis que d'autres rappels ont ensuite fait. sur la base du code, j'ai supposé que c'était parce que la fenêtre n'avait pas été détectée jusqu'à ce qu'elle soit instanciée
Pour contourner cela, j'ai cherché à utiliser une minuterie pour vérifier périodiquement si la fenêtre était présente et si elle l'était, puis placez-la au premier plan . Prenez le code du site Web suivant; Outlook VBA - Exécuter un code toutes les demi-heures
Ensuite, la fusion des deux solutions a donné une solution de travail à ce problème.
Depuis le centre de confiance, j'ai activé l'utilisation de macros, puis en ouvrant l'éditeur Visual Basic à partir d'Outlook (alt + F11), j'ai ajouté le code suivant au module 'ThisOutlookSession'.
Private Sub Application_Startup()
Call ActivateTimer(5) 'Set timer to go off every 5 seconds
End Sub
Private Sub Application_Quit()
If TimerID <> 0 Then Call DeactivateTimer 'Turn off timer upon quitting
End Sub
Puis ajouté un module et ajouté le code suivant
Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent _
As Long, ByVal uElapse As Long, ByVal lpTimerfunc As Long) As Long
Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent _
As Long) As Long
Private Declare Function FindWindowA Lib "user32" (ByVal lpClassName _
As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal _
hWndInsertAfter As Long, 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 = &H1
Private Const SWP_NOMOVE = &H2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE
Private Const HWND_TOPMOST = -1
Public TimerID As Long 'Need a timer ID to eventually turn off the timer.
' If the timer ID <> 0 then the timer is running
Public Sub ActivateTimer(ByVal nSeconds As Long)
nSeconds = nSeconds * 1000
'The SetTimer call accepts milliseconds, so convert from seconds
If TimerID <> 0 Then Call DeactivateTimer
'Check to see if timer is running before call to SetTimer
TimerID = SetTimer(0, 0, nSeconds, AddressOf TriggerTimer)
If TimerID = 0 Then MsgBox "The timer failed to activate."
End Sub
Public Sub DeactivateTimer()
Dim lSuccess As Long
lSuccess = KillTimer(0, TimerID)
If lSuccess = 0 Then
MsgBox "The timer failed to deactivate."
Else
TimerID = 0
End If
End Sub
Public Sub TriggerTimer(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal idevent As Long, ByVal Systime As Long)
Call EventMacro
End Sub
Public Sub EventMacro()
Dim ReminderWindowHWnd As Variant
On Error Resume Next
ReminderWindowHWnd = FindWindowA(vbNullString, "1 Reminder")
If ReminderWindowHWnd <> 0 Then SetWindowPos ReminderWindowHWnd, _
HWND_TOPMOST, 0, 0, 0, 0, FLAGS
ReminderWindowHWnd = Nothing
End Sub
Alors c'est tout; toutes les 5 secondes, le minuteur vérifie si une fenêtre avec une légende "1 Rappel" existe puis le place en haut de l'écran ...
UPDATE _ (12 février 2015): après avoir utilisé cela pendant un moment, j'ai trouvé un ennui réel avec le fait que le déclenchement du minuteur supprime le focus du courant la fenêtre. C'est un gros problème que vous écrivez un e-mail.
En tant que tel, j'ai mis à niveau le code de sorte que le minuteur ne s'exécute que toutes les 60 secondes. Dès qu'il trouve le premier rappel actif, le minuteur est arrêté et la fonction d'événement secondaire est ensuite utilisée pour activer le changement de focus de la fenêtre.
UPDATE 2 _ _ (4 septembre 2015): Ayant migré vers Outlook 2013 - ce code a cessé de fonctionner pour moi. Je l'ai maintenant mis à jour avec une autre fonction (FindReminderWindow) qui recherche une gamme de légendes de rappel contextuelles. Cela fonctionne maintenant pour moi en 2013 et devrait fonctionner pour les versions inférieures à 2013.
La fonction FindReminderWindow prend une valeur qui correspond au nombre d'itérations à parcourir pour trouver la fenêtre. Si vous avez régulièrement un plus grand nombre de rappels que 10 popup, vous pouvez augmenter ce nombre dans la sous-...
Code mis à jour ci-dessous: Ajoutez le code suivant au module 'ThisOutlookSession'
Private Sub Application_Startup()
Call ActivateTimer(60) 'Set timer to go off every 60 seconds
End Sub
Private Sub Application_Quit()
If TimerID <> 0 Then Call DeactivateTimer 'Turn off timer upon quitting
End Sub
Private Sub Application_Reminder(ByVal Item As Object)
Call EventMacro
End Sub
Ensuite, le code du module mis à jour ...
Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent _
As Long, ByVal uElapse As Long, ByVal lpTimerfunc As Long) As Long
Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent _
As Long) As Long
Private Declare Function FindWindowA Lib "user32" (ByVal lpClassName _
As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal _
hWndInsertAfter As Long, 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 = &H1
Private Const SWP_NOMOVE = &H2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE
Private Const HWND_TOPMOST = -1
Public TimerID As Long 'Need a timer ID to eventually turn off the timer.
' If the timer ID <> 0 then the timer is running
Public Sub ActivateTimer(ByVal nSeconds As Long)
nSeconds = nSeconds * 1000
'The SetTimer call accepts milliseconds, so convert from seconds
If TimerID <> 0 Then Call DeactivateTimer
'Check to see if timer is running before call to SetTimer
TimerID = SetTimer(0, 0, nSeconds, AddressOf TriggerTimer)
If TimerID = 0 Then MsgBox "The timer failed to activate."
End Sub
Public Sub DeactivateTimer()
Dim lSuccess As Long
lSuccess = KillTimer(0, TimerID)
If lSuccess = 0 Then
MsgBox "The timer failed to deactivate."
Else
TimerID = 0
End If
End Sub
Public Sub TriggerTimer(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal idevent As Long, ByVal Systime As Long)
Call EventMacro
End Sub
Public Sub EventMacro()
Dim ReminderWindowHWnd As Variant
On Error Resume Next
ReminderWindowHWnd = FindReminderWindow(10)
If ReminderWindowHWnd <> 0 Then
SetWindowPos ReminderWindowHWnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS
If TimerID <> 0 Then Call DeactivateTimer
End If
ReminderWindowHWnd = Nothing
End Sub
Private Function FindReminderWindow(iUB As Integer) As Variant
Dim i As Integer: i = 1
FindReminderWindow = FindWindowA(vbNullString, "1 Reminder")
Do While i < iUB And FindReminderWindow = 0
FindReminderWindow = FindWindowA(vbNullString, i & " Reminder(s)")
i = i + 1
Loop
End Function
UPDATE 3 _ (8 août 2016): Après avoir repensé mon approche et basé sur l'observation, j'ai modifié le code pour avoir un impact minimal sur le travail alors qu'Outlook était ouvrir; Je constaterais que le chronomètre prenait toujours moins de place dans les courriels que je rédigeais et que d’autres problèmes liés à la perte de concentration de Windows pourraient être liés.
Au lieu de cela - j'ai supposé que la fenêtre des rappels une fois instanciée était simplement cachée et non détruite lorsque les rappels étaient affichés; en tant que tel, je garde maintenant un descripteur global de la fenêtre, je n'ai donc qu'à regarder une fois les titres de la fenêtre, puis à vérifier si la fenêtre des rappels est visible avant de la rendre modale.
En outre, la minuterie est désormais utilisée uniquement lorsque la fenêtre de rappel est déclenchée, puis désactivée une fois la fonction exécutée. j'espère que nous arrêterons toute macro intrusive en cours d'exécution pendant la journée de travail.
Voyez lequel travaille pour vous, je suppose ...
Code mis à jour ci-dessous: Ajoutez le code suivant au module 'ThisOutlookSession'
Private WithEvents MyReminders As Outlook.Reminders
Private Sub Application_Startup()
On Error Resume Next
Set MyReminders = Outlook.Application.Reminders
End Sub
Private Sub MyReminders_ReminderFire(ByVal ReminderObject As Reminder)
On Error Resume Next
Call ActivateTimer(1)
End Sub
Ensuite, le code du module mis à jour ...
Option Explicit
Private Declare Function SetTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerfunc As Long) As Long
Private Declare Function KillTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Function IsWindowVisible Lib "User32" (ByVal hWnd As Long) As Long
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName _
As String, ByVal lpWindowName As String) As Long
Private Declare Function ShowWindow Lib "User32" (ByVal hWnd As Long, ByVal nCmdSHow As Long) As Long
Private Declare Function SetWindowPos Lib "User32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
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 = &H1
Private Const SWP_NOMOVE = &H2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE
Private Const HWND_TOPMOST = -1
Public TimerID As Long 'Need a timer ID to turn off the timer. If the timer ID <> 0 then the timer is running
Public hRemWnd As Long 'Store the handle of the reminder window
Public Sub ActivateTimer(ByVal Seconds As Long) 'The SetTimer call accepts milliseconds
On Error Resume Next
If TimerID <> 0 Then Call DeactivateTimer 'Check to see if timer is running before call to SetTimer
If TimerID = 0 Then TimerID = SetTimer(0, 0, Seconds * 1000, AddressOf TriggerEvent)
End Sub
Public Sub DeactivateTimer()
On Error Resume Next
Dim Success As Long: Success = KillTimer(0, TimerID)
If Success <> 0 Then TimerID = 0
End Sub
Public Sub TriggerEvent(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idevent As Long, ByVal Systime As Long)
Call EventFunction
End Sub
Public Function EventFunction()
On Error Resume Next
If TimerID <> 0 Then Call DeactivateTimer
If hRemWnd = 0 Then hRemWnd = FindReminderWindow(100)
If IsWindowVisible(hRemWnd) Then
ShowWindow hRemWnd, 1 ' Activate Window
SetWindowPos hRemWnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS ' Set Modal
End If
End Function
Public Function FindReminderWindow(iUB As Integer) As Long
On Error Resume Next
Dim i As Integer: i = 1
FindReminderWindow = FindWindow(vbNullString, "1 Reminder")
Do While i < iUB And FindReminderWindow = 0
FindReminderWindow = FindWindow(vbNullString, i & " Reminder(s)")
i = i + 1
Loop
If FindReminderWindow <> 0 Then ShowWindow FindReminderWindow, 1
End Function
À l'aide d'AutoHotKey, vous pouvez définir la fenêtre sur Toujours visible sans dérober le focus de la fenêtre en cours. (Testé avec WIn10/Outlook 2013)
TrayTip Script, Looking for Reminder window to put on top, , 16
SetTitleMatchMode 2 ; windows contains
loop {
WinWait, Reminder(s),
WinSet, AlwaysOnTop, on, Reminder(s)
WinRestore, Reminder(s)
TrayTip Outlook Reminder, You have an Outlook reminder open, , 16
WinWaitClose, Reminder(s), ,30
}
J'ai trouvé un programme gratuit appelé PinMe! ça fera exactement ce que je veux. Lorsque votre rappel Outlook apparaît, cliquez avec le bouton droit sur PinMe! dans la barre d'état système et sélectionnez la fenêtre Rappel. Cela placera une icône de verrou à côté de la fenêtre. Allez-y, rejetez ou répétez votre rappel. La prochaine fois que le rappel apparaîtra, il devrait apparaître devant toutes les autres fenêtres. Cela fonctionnera indépendamment de Outlook au premier plan ou réduit.
J'ai Office 2013 et Windows 8.1 Pro. De nombreuses macros que j'ai trouvées ne traitaient pas la nature variable du titre placé par Outlook dans la boîte de dialogue Rappel. Lorsque vous avez 1 rappel, le titre est "1 Rappel (s)" etc. J'ai créé une application Windows Forms simple dans VB.NET, que je charge au démarrage et que je garde réduite dans la barre d'état système. Il y a un minuteur 60 ajouté au formulaire qui déclenche le code actif. Lorsqu'il y a plus de 0 rappels, la boîte de dialogue sera placée en haut et déplacée à 0,0.
Voici le code:
Imports System.Runtime.InteropServices
Imports System.Text
Module Module1
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function FindWindowEx(ByVal parentHandle As IntPtr, ByVal childAfter As IntPtr, ByVal lclassName As String, ByVal windowTitle As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public 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 Integer) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function
End Module
Public Class Form1
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim titleString As String = ""
Dim nullHandle As New IntPtr
Dim windowHandle As New IntPtr
Dim titleLength As Long
Try
Do
Dim sb As New StringBuilder
sb.Capacity = 512
Dim prevHandle As IntPtr = windowHandle
windowHandle = FindWindowEx(nullHandle, prevHandle, "#32770", vbNullString)
If windowHandle <> 0 And windowHandle <> nullHandle Then
titleLength = GetWindowText(windowHandle, sb, 256)
If titleLength > 0 Then
titleString = sb.ToString
Dim stringPos As Integer = InStr(titleString, "Reminde", CompareMethod.Text)
If stringPos Then
Dim reminderCount As Integer = Val(Mid(titleString, 1, 2))
If reminderCount > 0 Then
Dim baseWindow As IntPtr = -1 '-1 is the topmost position
SetWindowPos(windowHandle, baseWindow, 0, 0, 100, 100, &H41)
End If
Exit Sub
End If
End If
Else
Exit Sub
End If
Loop
Catch ex As Exception
MsgBox(ex.Message.ToString)
End Try
End Sub
Private Sub ToolStripMenuItem1_Click(sender As Object, e As EventArgs) Handles ToolStripMenuItem1.Click
Me.Close()
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Me.Hide()
End Sub
End Class
Après avoir été inspiré par la réponse d'Eric Labashosky , j'ai poussé son concept un peu plus loin et j'ai créé l'application NotifyWhenMicrosoftOutlookReminderWindowIsOpen , que vous pouvez télécharger gratuitement. Il s'agit d'un petit exécutable capable de garantir que la fenêtre Rappels Outlook apparaît au-dessus des autres fenêtres, ainsi que d'autres moyens facultatifs d'avertir l'utilisateur que la fenêtre est ouverte.
Cela devrait fonctionner dans différentes versions d'Outlook, même si je ne l'avais testé que sous Outlook 2013.
Étant donné que je ne peux pas le tester dans une version anglaise localisée, vous devrez peut-être personnaliser les lignes de code liées à la recherche dans la fenêtre des rappels, même si, dans ma réponse, j'ai modifié les lignes de code associées afin de trouver la fenêtre dans la version anglaise localisée.
Faites-moi savoir si la macro fonctionne dans votre version anglaise de Outlook.
L'utilisateur est libre de réduire ou de fermer la fenêtre de rappel dans laquelle, lorsqu'un rappel nouveau ou existant est déclenché, la fenêtre de rappel est en haut et n'est pas activée.
Le titre de la fenêtre des rappels sera toujours mis à jour, reflétant le nombre réel de rappels visibles même sans l'activer.
Dans tous les cas, la fenêtre des rappels ne volera jamais le focus sauf, évidemment, si la fenêtre de premier plan est la fenêtre des rappels, c'est-à-dire sauf si l'utilisateur a délibérément sélectionné la fenêtre des rappels.
Cette macro, outre le fait de placer la fenêtre de rappel en premier, sélectionnera également le rappel le plus récent dans la fenêtre de rappel elle-même. Vous pouvez personnaliser ce comportement. Veuillez lire le code afin de pouvoir le faire.
La macro fait également clignoter la fenêtre des rappels lors de l'affichage de la fenêtre pour la première fois et chaque fois qu'un rappel nouveau ou existant se déclenche à nouveau.
Vous pouvez personnaliser le nombre de fois où la fenêtre clignote ou tout autre paramètre associé. Vous devez savoir comment procéder.
Collez les lignes de code suivantes dans le module de classe 'ThisOutlookSession':
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, _
ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function FlashWindowEx Lib "user32" (FWInfo As FLASHWINFO) As Boolean
Private Const FLASHW_STOP = 0
Private Const FLASHW_CAPTION = 1
Private Const FLASHW_TRAY = 2
Private Const FLASHW_ALL = FLASHW_CAPTION Or FLASHW_TRAY
Private Const FLASHW_TIMER = 4
Private Const FLASHW_TIMERNOFG = 12
Private Type FLASHWINFO
cbSize As Long
hwnd As Long
dwFlags As Long
uCount As Long
dwTimeout As Long
End Type
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2
Private Const HWND_TOP = 0
Private Const HWND_BOTTOM = 1
Private Const SWP_NOSIZE = 1
Private Const SWP_NOMOVE = 2
Private Const SWP_NOACTIVATE = 16
Private Const SWP_DRAWFRAME = 32
Private Const SWP_NOOWNERZORDER = 512
Private Const SWP_NOZORDER = 4
Private Const SWP_SHOWWINDOW = 64
Private Existing_reminders_window As Boolean
Private WithEvents Rmds As Reminders
Public Reminders_window As Long
Private Sub Application_Reminder(ByVal Item As Object)
If Existing_reminders_window = False Then
Set Rmds = Application.Reminders
'In order to create the reminders window
ActiveExplorer.CommandBars.ExecuteMso ("ShowRemindersWindow")
Reminders_window = FindWindow("#32770", "0 Reminder(s)")
If Reminders_window = 0 Then
Reminders_window = FindWindow("#32770", "0 Reminder")
If Reminders_window = 0 Then
Reminders_window = FindWindow("#32770", "0 Reminder ")
End If
End If
'To prevent stealing focus in case Outlook was in the foreground
ShowWindow Reminders_window, 0
SetWindowPos Reminders_window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOACTIVATE
Existing_reminders_window = True
End If
End Sub
Private Sub Rmds_BeforeReminderShow(Cancel As Boolean)
Dim FWInfo As FLASHWINFO
If Existing_reminders_window = True Then
Cancel = True
With FWInfo
.cbSize = 20
.hwnd = Reminders_window
.dwFlags = FLASHW_CAPTION
.uCount = 4
.dwTimeout = 0
End With
'In case the reminders window was not the highest topmost. This will not work on Windows 10 if the task manager window is topmost, the task manager and some other system windows have special z position
SetWindowPos Reminders_window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOACTIVATE
ShowWindow Reminders_window, 4
Select_specific_reminder
FlashWindowEx FWInfo
End If
End Sub
Collez les lignes de code suivantes dans un module standard nouveau ou existant:
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Const WM_CHAR = &H102
Private Const VK_HOME = &H24
Private Const VK_END = &H23
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Public Sub Select_specific_reminder()
Dim Retval As Long
Retval = EnumChildWindows(ThisOutlookSession.Reminders_window, AddressOf EnumChildProc, 0)
End Sub
Private Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim Nome_classe As String
Nome_classe = Space$(256)
GetClassName hwnd, Nome_classe, 256
If InStr(Nome_classe, "SysListView32") Then
'You can customize the next code line in order to select a specific reminder
SendMessage hwnd, WM_KEYDOWN, VK_HOME, ByVal 0&
End If
EnumChildProc = 1
End Function
Cette dernière fonctionnalité est intégrée dans les dernières versions d’Outlook. Vous trouverez la même réponse dans https://superuser.com/a/1327856/913992