你研究一下这个先: If code is HC_GETNEXT and the return value is greater than zero, the system sleeps for the number of milliseconds specified by the return value. When the system continues, it calls the hook procedure again with code set to HC_GETNEXT to retrieve the same message. The return value from this new call to JournalPlaybackProc should be zero; otherwise, the system will go back to sleep for the number of milliseconds specified by the return value, call JournalPlaybackProc again, and so on. The system will appear to be hung
不好意思,小弟E文水平實在....,MSDN那段話只能看個似懂非懂。 To retrieve the same message over and over, the hook procedure can be called several times with the code parameter set to HC_GETNEXT without an intervening call with code set to HC_SKIP. If code is HC_GETNEXT and the return value is greater than zero, the system sleeps for the number of milliseconds specified by the return value. When the system continues, it calls the hook procedure again with code set to HC_GETNEXT to retrieve the same message. The return value from this new call to JournalPlaybackProc should be zero; otherwise, the system will go back to sleep for the number of milliseconds specified by the return value, call JournalPlaybackProc again, and so on. The system will appear to be hung. 是說在返回值大于0的時候,下一次的HOOK調用還是會傳入HC_GETNEXT以 retrieve the same message,然后才返回0, 那我怎樣才能控制系統以HC_SKIP來call hook procedure 呢? 不好意思,請熊哥再給我講講。
你看上面我贴的代码里有个bWait的变量,控制是否返回TimeStamp或者0
那麼,熊哥你的意思是:如果返回大于0,則下一次hook調用還是傳入HC_GETNEXT, 若返回0的話,則下一次hook調用傳入HC_SKIP,可是在<<windows程序設計指南>>一書中jeffrey并沒有提到這一點,難道在95以后hook的機制有了變化嗎,可是jeffrey的程序在我的2000也跑得蠻好的,這又是為什麼呢? 下面是其代碼,我貼出來給你看看。 LRESULT CALLBACK JrnlPlybkHookFunc (int nCode, WPARAM wParam,LPARAM lParam) { BOOL fCallNextHookProc = FALSE; LRESULT lResult = 0; LPRECORDSTAT lpRecordStat=0; LPEVENTMSG lpEvent; lpRecordStat = (LPRECORDSTAT) GlobalLock(_hGlblEvents); switch (nCode) { case HC_SKIP: // Prepare to return the next event the next time the // hook code is HC_GETNEXT. If all events have been // played, stop playing. if (++(lpRecordStat->wNumEventsPlayed) == lpRecordStat->wNumEvents) Recorder(RM_STOPPLAY, 0, 0); break; case HC_GETNEXT: // Copy the current event to the EVENTMSG // structure pointed to by lParam. lpEvent = (LPEVENTMSG) &lpRecordStat[1]; *((LPEVENTMSG) lParam) = lpEvent[lpRecordStat->wNumEventsPlayed]; // Adjust 'time' by adding time that playback started, ((LPEVENTMSG) lParam)->time += lpRecordStat->dwStartTime; // Return the number of milliseconds Windows should wait // before processing the event. lResult = ((LPEVENTMSG) lParam)->time - GetTickCount(); // If the event occurred in the past, have Windows // process it now. if (lResult < 0) lResult = 0; break; case HC_SYSMODALOFF: // When the system-modal dialog box is removed, stop // playing the events and notify the application. _PrematureHalt = REC_SYSMODALON; Recorder(RM_STOPPLAY, 0, 0); fCallNextHookProc = TRUE; break; case HC_SYSMODALON: default: fCallNextHookProc = TRUE; break; } GlobalUnlock(_hGlblEvents); if (fCallNextHookProc) lResult = CallNextHookEx(_hHookJrnl, nCode, wParam, lParam); return(lResult); }
参考以下代码看看:BOOL bWait=true;
LRESULT CALLBACK JournalPlaybackProc(
int code, // hook code
WPARAM wParam, // undefined
LPARAM lParam // address of message being processed
)
{
char Buffer[1023];
long Time=0,TimeStamp=0;
if(pCurr!=NULL)
{
TimeStamp = pCurr->Msg .time - pPre->Msg.time;
if(TimeStamp == 0)
TimeStamp = 10;
sprintf(Buffer,"code %d ,Index %d TimeStamp %d",code,pCurr->Index,TimeStamp );
}
if(pCurr!=pTail&&pCurr!=NULL)
{
switch (code )
{
case HC_NOREMOVE:
break;
case HC_GETNEXT:
memcpy((void *)lParam,&pCurr->Msg ,sizeof(EVENTMSG));
if(bWait)
{
bWait=false;
return TimeStamp;
}
else
{
pWnd->SetWindowText (Buffer);
return 0;
}
break;
case HC_SKIP:
if(pCurr!=NULL)
{
bWait=true;
pPre = pCurr;
pCurr=pCurr->next;
}
break;
case HC_SYSMODALOFF:
RecCtrl = HC_GETNEXT;
break;
case HC_SYSMODALON:
RecCtrl = HC_SYSMODALON;
return 0;
default:
break;
}
}else
{
if(::UnhookWindowsHookEx (hPlay))
{
MessageBox(0,"PlayBackEnd","Hook",MB_OK);
FreeList();
Count=0;
}
}
return ::CallNextHookEx (hPlay,code,wParam,lParam);
}
以下是我的一段代碼,你幫忙看看問是在哪里:LRESULT CALLBACK JournalPlaybackProc(int code,WPARAM wParam,LPARAM lParam)
{
LPEVENTMSG lpEvent;
LRESULT lResult;
TRACE("------%d-------\n",code);
switch(code)
{
case HC_SKIP:
if(++m_Record->lStartNum==m_Record->lNum)
{
::UnhookWindowsHookEx(m_hook);
m_hook=NULL;
}
break;
case HC_GETNEXT:
lpEvent=(LPEVENTMSG)&m_Record[1];
lpEvent=&lpEvent[m_Record->lStartNum];
lpEvent->time+=m_Record->lStartTime;
*(LPEVENTMSG)lParam=*lpEvent;
//為了跟蹤數據是否正確而設
TRACE("hwnd: %X\n",lpEvent->hwnd);
TRACE("msg: %X\n",lpEvent->message);
TRACE("paramH: %X\n",lpEvent->paramH);
TRACE("paramL: %X\n",lpEvent->paramL);
TRACE("time: %X\n",lpEvent->time); lResult=lpEvent->time-GetTickCount();
if(lResult<0) lResult=0;
return lResult;
break;
}
return ::CallNextHookEx (m_hook,code,wParam,lParam);
}其中的TRACE是我為了跟蹤而加上去的,而TRACE("------%d-------\n",code);總是會show出 1(HC_GETNEXT),只是偶而出個 2(HC_SKIP) (將近有100個1才會有兩個2),因而導致journalplayback成了死循環,不得已我只好把在HC_SKIP的一段碼copy到HC_GETNEXT段中,這樣我在HC_GETNEXT中的TRACE顯示的數據都是正確的,HOOK也可以正常結束。但,問題就是,我為什麼會收不到 HC_SKIP。
If code is HC_GETNEXT and the return value is greater than zero, the system sleeps for the number of milliseconds specified by the return value. When the system continues, it calls the hook procedure again with code set to HC_GETNEXT to retrieve the same message. The return value from this new call to JournalPlaybackProc should be zero; otherwise, the system will go back to sleep for the number of milliseconds specified by the return value, call JournalPlaybackProc again, and so on. The system will appear to be hung
To retrieve the same message over and over, the hook procedure can be called several times with the code parameter set to HC_GETNEXT without an intervening call with code set to HC_SKIP. If code is HC_GETNEXT and the return value is greater than zero, the system sleeps for the number of milliseconds specified by the return value. When the system continues, it calls the hook procedure again with code set to HC_GETNEXT to retrieve the same message. The return value from this new call to JournalPlaybackProc should be zero; otherwise, the system will go back to sleep for the number of milliseconds specified by the return value, call JournalPlaybackProc again, and so on. The system will appear to be hung.
是說在返回值大于0的時候,下一次的HOOK調用還是會傳入HC_GETNEXT以 retrieve the same message,然后才返回0,
那我怎樣才能控制系統以HC_SKIP來call hook procedure 呢? 不好意思,請熊哥再給我講講。
LRESULT CALLBACK JrnlPlybkHookFunc (int nCode, WPARAM wParam,LPARAM lParam)
{
BOOL fCallNextHookProc = FALSE;
LRESULT lResult = 0;
LPRECORDSTAT lpRecordStat=0;
LPEVENTMSG lpEvent; lpRecordStat = (LPRECORDSTAT) GlobalLock(_hGlblEvents);
switch (nCode) { case HC_SKIP:
// Prepare to return the next event the next time the
// hook code is HC_GETNEXT. If all events have been
// played, stop playing.
if (++(lpRecordStat->wNumEventsPlayed) ==
lpRecordStat->wNumEvents)
Recorder(RM_STOPPLAY, 0, 0); break; case HC_GETNEXT:
// Copy the current event to the EVENTMSG
// structure pointed to by lParam.
lpEvent = (LPEVENTMSG) &lpRecordStat[1];
*((LPEVENTMSG) lParam) =
lpEvent[lpRecordStat->wNumEventsPlayed];
// Adjust 'time' by adding time that playback started,
((LPEVENTMSG) lParam)->time += lpRecordStat->dwStartTime; // Return the number of milliseconds Windows should wait
// before processing the event.
lResult = ((LPEVENTMSG) lParam)->time - GetTickCount(); // If the event occurred in the past, have Windows
// process it now.
if (lResult < 0) lResult = 0;
break; case HC_SYSMODALOFF:
// When the system-modal dialog box is removed, stop
// playing the events and notify the application.
_PrematureHalt = REC_SYSMODALON;
Recorder(RM_STOPPLAY, 0, 0);
fCallNextHookProc = TRUE;
break; case HC_SYSMODALON:
default:
fCallNextHookProc = TRUE;
break; }
GlobalUnlock(_hGlblEvents); if (fCallNextHookProc)
lResult = CallNextHookEx(_hHookJrnl, nCode, wParam, lParam); return(lResult);
}
他返回的lResult几乎不可能大于0,消息是根本不加延迟就回放的,飞快。
而如果要模拟记录的消息的实际发生速度,应该用当前消息发生的时间减去上一个消息的时间,
得到时间间隔,让系统进行延时,系统延时结束后,再用HC_GETNEXT进入回调函数,这时
返回0并且拷贝当前消息,系统则回放该消息并且接着用HC_SKIP进入回调函数。
应该是这样的流程。
是這樣的,我沒有把全部的代碼貼出來。jeffrey的思路是在開始recorder時記下開始時間,記錄完后,再將每個eventmsg中的time都減去開始時間而得到每個事件發生的相對時間值,然后在播放時先記下播放開始時間,然后再為每個eventmsg.time加上該播放開始時間,這樣就得出每個事件的實際播放時間值,再拿此值減去當前時間來作為返回值
然后每次回调函数被调用的时候,
TimeStamp = pCurr->Msg .time - pPre->Msg.time;
当前消息的时间减上一个消息的时间得到让系统等待的时间长度,就这么简单。
就是控制播放速度的值,也是以Tick为单位。不用GetTickCount.
有空你不妨试一下。