15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stddef.h> 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h> 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <mmsystem.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/event_recorder.h" 106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/files/file_util.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A note about time. 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For perfect playback of events, you'd like a very accurate timer 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// so that events are played back at exactly the same time that 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// they were recorded. However, windows has a clock which is only 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// granular to ~15ms. We see more consistent event playback when 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// using a higher resolution timer. To do this, we use the 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// timeGetTime API instead of the default GetTickCount() API. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EventRecorder* EventRecorder::current_ = NULL; 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam, 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LPARAM lParam) { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(EventRecorder::current()); 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam); 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam, 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LPARAM lParam) { 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(EventRecorder::current()); 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EventRecorder::~EventRecorder() { 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Try to assert early if the caller deletes the recorder 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // while it is still in use. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!journal_hook_); 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!is_recording_ && !is_playing_); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EventRecorder::StartRecording(const FilePath& filename) { 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (journal_hook_ != NULL) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (is_recording_ || is_playing_) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Open the recording file. 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!file_); 52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) file_ = OpenFile(filename, "wb+"); 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!file_) { 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder could not open log file"; 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the faster clock, if possible. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::timeBeginPeriod(1); 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the recording hook. JOURNALRECORD can only be used as a global hook. 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc, 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetModuleHandle(NULL), 0); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!journal_hook_) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder Record Hook failed"; 66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) CloseFile(file_); 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_recording_ = true; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void EventRecorder::StopRecording() { 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (is_recording_) { 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(journal_hook_ != NULL); 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::UnhookWindowsHookEx(journal_hook_)) { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder Unhook failed"; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Nothing else we can really do here. 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::timeEndPeriod(1); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(file_ != NULL); 87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) CloseFile(file_); 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_ = NULL; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) journal_hook_ = NULL; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_recording_ = false; 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EventRecorder::StartPlayback(const FilePath& filename) { 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (journal_hook_ != NULL) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (is_recording_ || is_playing_) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Open the recording file. 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!file_); 103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) file_ = OpenFile(filename, "rb"); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!file_) { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder Playback could not open log file"; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Read the first event from the record. 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) { 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder Playback has no records!"; 111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) CloseFile(file_); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the faster clock, if possible. 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::timeBeginPeriod(1); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Playback time is tricky. When playing back, we read a series of events, 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // each with timeouts. Simply subtracting the delta between two timers will 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // lead to fast playback (about 2x speed). The API has two events, one 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // which advances to the next event (HC_SKIP), and another that requests the 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // event (HC_GETNEXT). The same event will be requested multiple times. 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Each time the event is requested, we must calculate the new delay. 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // To do this, we track the start time of the playback, and constantly 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // re-compute the delay. I mention this only because I saw two examples 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // of how to use this code on the net, and both were broken :-) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) playback_start_time_ = timeGetTime(); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) playback_first_msg_time_ = playback_msg_.time; 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the hook. JOURNALPLAYBACK can only be used as a global hook. 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc, 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetModuleHandle(NULL), 0); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!journal_hook_) { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder Playback Hook failed"; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_playing_ = true; 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void EventRecorder::StopPlayback() { 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (is_playing_) { 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(journal_hook_ != NULL); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::UnhookWindowsHookEx(journal_hook_)) { 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(ERROR) << "EventRecorder Unhook failed"; 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Nothing else we can really do here. 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(file_ != NULL); 153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) CloseFile(file_); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_ = NULL; 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::timeEndPeriod(1); 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) journal_hook_ = NULL; 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_playing_ = false; 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Windows callback hook for the recorder. 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) { 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool recording_enabled = true; 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EVENTMSG* msg_ptr = NULL; 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The API says we have to do this. 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (nCode < 0) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check for the break key being pressed and stop recording. 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (::GetKeyState(VK_CANCEL) & 0x8000) { 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StopRecording(); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam); 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The Journal Recorder must stop recording events when system modal 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // dialogs are present. (see msdn link above) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (nCode) { 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_SYSMODALON: 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) recording_enabled = false; 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_SYSMODALOFF: 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) recording_enabled = true; 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (nCode == HC_ACTION && recording_enabled) { 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Aha - we have an event to record. 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg_ptr = reinterpret_cast<EVENTMSG*>(lParam); 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg_ptr->time = timeGetTime(); 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_); 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fflush(file_); 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return CallNextHookEx(journal_hook_, nCode, wParam, lParam); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Windows callback for the playback mode. 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam, 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LPARAM lParam) { 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool playback_enabled = true; 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int delay = 0; 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (nCode) { 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A system modal dialog box is being displayed. Stop playing back 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // messages. 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_SYSMODALON: 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) playback_enabled = false; 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A system modal dialog box is destroyed. We can start playing back 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // messages again. 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_SYSMODALOFF: 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) playback_enabled = true; 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Prepare to copy the next mouse or keyboard event to playback. 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_SKIP: 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!playback_enabled) 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Read the next event from the record. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this->StopPlayback(); 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Copy the mouse or keyboard event to the EVENTMSG structure in lParam. 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_GETNEXT: 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!playback_enabled) 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memcpy(reinterpret_cast<void*>(lParam), &playback_msg_, 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sizeof(playback_msg_)); 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The return value is the amount of time (in milliseconds) to wait 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // before playing back the next message in the playback queue. Each 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // time this is called, we recalculate the delay relative to our current 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // wall clock. 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delay = (playback_msg_.time - playback_first_msg_time_) - 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (timeGetTime() - playback_start_time_); 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (delay < 0) 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delay = 0; 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delay; 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // indicating that the message is not removed from the message queue after 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // PeekMessage processing. 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case HC_NOREMOVE: 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return CallNextHookEx(journal_hook_, nCode, wParam, lParam); 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 259