/* 'AutoShutdown'-Plugin for Miranda IM Copyright 2004-2007 H. Herkenrath This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program (Shutdown-License.txt); if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" /************************* Stat Switch ********************************/ #define Li2Double(x) ((double)((x).HighPart)*4.294967296E9+(double)((x).LowPart)) static BOOL WinNT_PerfStatsSwitch(TCHAR *pszServiceName,BOOL fDisable) { HKEY hKeyServices,hKeyService,hKeyPerf; DWORD dwData,dwDataSize; BOOL fSwitched=FALSE; /* Win2000+ */ if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("System\\CurrentControlSet\\Services"),0,KEY_QUERY_VALUE|KEY_SET_VALUE,&hKeyServices)) { if(!RegOpenKeyEx(hKeyServices,pszServiceName,0,KEY_QUERY_VALUE|KEY_SET_VALUE,&hKeyService)) { if(!RegOpenKeyEx(hKeyService,_T("Performance"),0,KEY_QUERY_VALUE|KEY_SET_VALUE,&hKeyPerf)) { dwDataSize=sizeof(DWORD); if(!RegQueryValueEx(hKeyPerf,_T("Disable Performance Counters"),NULL,NULL,(BYTE*)&dwData,&dwDataSize)) if((dwData!=0)!=fDisable) fSwitched=!RegSetValueEx(hKeyPerf,_T("Disable Performance Counters"),0,REG_DWORD,(BYTE*)&fDisable,dwDataSize); RegCloseKey(hKeyPerf); } RegCloseKey(hKeyService); } RegCloseKey(hKeyServices); } return fSwitched; } #if !defined(_UNICODE) static void Win9x_PerfStatsSwitch(HKEY hKey,char *pszAction,char *pszName) { DWORD dwData,dwSize; dwSize=sizeof(dwData); if(!RegOpenKeyExA(hKey,pszAction,0,KEY_QUERY_VALUE,&hKey)) { /* a simple query does the trick (data and size must not be NULL!) */ RegQueryValueExA(hKey,pszName,NULL,NULL,(BYTE*)&dwData,&dwSize); RegCloseKey(hKey); } } #endif /************************* Poll Thread ********************************/ struct CpuUsageThreadParams { DWORD dwDelayMillis; CPUUSAGEAVAILPROC pfnDataAvailProc; LPARAM lParam; HANDLE hFirstEvent; DWORD *pidThread; }; static BOOL CallBackAndWait(struct CpuUsageThreadParams *param,BYTE nCpuUsage) { if(param->hFirstEvent!=NULL) { /* return value for PollCpuUsage() */ *param->pidThread=GetCurrentThreadId(); SetEvent(param->hFirstEvent); param->hFirstEvent=NULL; /* lower priority after first call */ SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_IDLE); } if(!param->pfnDataAvailProc(nCpuUsage,param->lParam)) return FALSE; SleepEx(param->dwDelayMillis,TRUE); return !Miranda_Terminated(); } #if !defined(_UNICODE) static void Win9x_PollThread(struct CpuUsageThreadParams *param) { HKEY hKeyStats,hKeyData; DWORD dwBufferSize,dwData; if(!RegOpenKeyExA(HKEY_DYN_DATA,"PerfStats",0,KEY_QUERY_VALUE,&hKeyStats)) { /* start query */ /* not needed for kernel * Win9x_PerfStatsSwitch(hKeyStats,"StartSrv","KERNEL"); */ Win9x_PerfStatsSwitch(hKeyStats,"StartStat","KERNEL\\CPUUsage"); /* retrieve cpu usage */ if(!RegOpenKeyExA(hKeyStats,"StatData",0,KEY_QUERY_VALUE,&hKeyData)) { dwBufferSize=sizeof(dwData); while(!RegQueryValueExA(hKeyData,"KERNEL\\CPUUsage",NULL,NULL,(BYTE*)&dwData,&dwBufferSize)) { dwBufferSize=sizeof(dwData); if(!CallBackAndWait(param,(BYTE)dwData)) break; } RegCloseKey(hKeyData); } /* stop query */ Win9x_PerfStatsSwitch(hKeyStats,"StopStat","KERNEL\\CPUUsage"); /* not needed for kernel * Win9x_PerfStatsSwitch(hKeyStats,"StopSrv","KERNEL"); */ RegCloseKey(hKeyStats); } /* return error for PollCpuUsage() if never succeeded */ if(param->hFirstEvent!=NULL) SetEvent(param->hFirstEvent); mir_free(param); } #endif /* !_UNICODE */ static void WinNT_PollThread(struct CpuUsageThreadParams *param) { DWORD dwBufferSize=0,dwCount; BYTE *pBuffer=NULL; PERF_DATA_BLOCK *pPerfData=NULL; LONG res,lCount; PERF_OBJECT_TYPE *pPerfObj; PERF_COUNTER_DEFINITION *pPerfCounter; PERF_INSTANCE_DEFINITION *pPerfInstance; PERF_COUNTER_BLOCK *pPerfCounterBlock; DWORD dwObjectId,dwCounterId; WCHAR wszValueName[11],*pwszInstanceName; BYTE nCpuUsage; BOOL fSwitched,fFound,fIsFirst=FALSE; LARGE_INTEGER liPrevCounterValue={0},liCurrentCounterValue={0},liPrevPerfTime100nSec={0}; /* init */ if(IsWinVer2000Plus()) { /* Win2000+: */ dwObjectId=238; /*'Processor' object */ dwCounterId=6; /* '% processor time' counter */ pwszInstanceName=L"_Total"; /* '_Total' instance */ } else { /* WinNT4: */ dwObjectId=2; /* 'System' object */ dwCounterId=240; /* '% Total processor time' counter */ pwszInstanceName=NULL; } _itow(dwObjectId,wszValueName,10); fSwitched=WinNT_PerfStatsSwitch(_T("PerfOS"),FALSE); /* poll */ for(;;) { /* retrieve data for given object */ res=RegQueryValueExW(HKEY_PERFORMANCE_DATA,wszValueName,NULL,NULL,(BYTE*)pPerfData,&dwBufferSize); while(!pBuffer || res==ERROR_MORE_DATA) { pBuffer=(BYTE*)mir_realloc(pPerfData,dwBufferSize+=256); if(!pBuffer) break; pPerfData=(PERF_DATA_BLOCK*)pBuffer; res=RegQueryValueExW(HKEY_PERFORMANCE_DATA,wszValueName,NULL,NULL,pBuffer,&dwBufferSize); } if(res!=ERROR_SUCCESS) break; /* find object in data */ fFound=FALSE; /* first object */ pPerfObj=(PERF_OBJECT_TYPE*)((BYTE*)pPerfData+pPerfData->HeaderLength); for(dwCount=0;dwCountNumObjectTypes;++dwCount) { if(pPerfObj->ObjectNameTitleIndex==dwObjectId) { /* find counter in object data */ /* first counter */ pPerfCounter=(PERF_COUNTER_DEFINITION*)((BYTE*)pPerfObj+pPerfObj->HeaderLength); for(dwCount=0;dwCount<(pPerfObj->NumCounters);++dwCount) { if(pPerfCounter->CounterNameTitleIndex==dwCounterId) { /* find instance in counter data */ if(pPerfObj->NumInstances==PERF_NO_INSTANCES) { pPerfCounterBlock=(PERF_COUNTER_BLOCK*)((BYTE*)pPerfObj+pPerfObj->DefinitionLength); liCurrentCounterValue=*(LARGE_INTEGER*)((BYTE*)pPerfCounterBlock+pPerfCounter->CounterOffset); fFound=TRUE; } else { /* first instance */ pPerfInstance=(PERF_INSTANCE_DEFINITION*)((BYTE*)pPerfObj+pPerfObj->DefinitionLength); for(lCount=0;lCount<(pPerfObj->NumInstances);++lCount) { pPerfCounterBlock=(PERF_COUNTER_BLOCK*)((BYTE*)pPerfInstance+pPerfInstance->ByteLength); if(!lstrcmpiW(pwszInstanceName,(WCHAR*)((BYTE*)pPerfInstance+pPerfInstance->NameOffset)) || !pwszInstanceName) { liCurrentCounterValue=*(LARGE_INTEGER*)((BYTE*)pPerfCounterBlock+pPerfCounter->CounterOffset); fFound=TRUE; break; } /* next instance */ pPerfInstance=(PPERF_INSTANCE_DEFINITION)((BYTE*)pPerfCounterBlock+pPerfCounterBlock->ByteLength); } } break; } /* next counter */ pPerfCounter=(PERF_COUNTER_DEFINITION*)((BYTE*)pPerfCounter+pPerfCounter->ByteLength); } break; } /* next object */ pPerfObj=(PERF_OBJECT_TYPE*)((BYTE*)pPerfObj+pPerfObj->TotalByteLength); } if(!fFound) break; /* calc val from data, we need two samplings * counter type: PERF_100NSEC_TIMER_INV * calc: time base=100Ns, value=100*(1-(data_diff)/(100NsTime_diff)) */ if(!fIsFirst) { nCpuUsage=(BYTE)((1.0-(Li2Double(liCurrentCounterValue)-Li2Double(liPrevCounterValue))/(Li2Double(pPerfData->PerfTime100nSec)-Li2Double(liPrevPerfTime100nSec)))*100.0+0.5); if(!CallBackAndWait(param,nCpuUsage)) break; } else fIsFirst=FALSE; /* store current sampling for next */ CopyMemory(&liPrevCounterValue,&liCurrentCounterValue,sizeof(LARGE_INTEGER)); CopyMemory(&liPrevPerfTime100nSec,&pPerfData->PerfTime100nSec,sizeof(LARGE_INTEGER)); } /* uninit */ if(pPerfData) mir_free(pPerfData); if(fSwitched) WinNT_PerfStatsSwitch(_T("PerfOS"),TRUE); /* return error for PollCpuUsage() if never succeeded */ if(param->hFirstEvent!=NULL) SetEvent(param->hFirstEvent); mir_free(param); } /************************* Start Thread *******************************/ // returns poll thread id on success DWORD PollCpuUsage(CPUUSAGEAVAILPROC pfnDataAvailProc,LPARAM lParam,DWORD dwDelayMillis) { struct CpuUsageThreadParams *param; DWORD idThread=0; HANDLE hFirstEvent; /* init params */ param=(struct CpuUsageThreadParams*)mir_alloc(sizeof(struct CpuUsageThreadParams)); if(param==NULL) return FALSE; param->dwDelayMillis=dwDelayMillis; param->pfnDataAvailProc=pfnDataAvailProc; param->lParam=lParam; param->pidThread=&idThread; param->hFirstEvent=hFirstEvent=CreateEvent(NULL,FALSE,FALSE,NULL); if(hFirstEvent==NULL) { mir_free(param); return 0; } /* start thread */ #if defined(_UNICODE) if(mir_forkthread(WinNT_PollThread,param)!=-1) #else if(mir_forkthread(IsWinVerNT()?WinNT_PollThread:Win9x_PollThread,param)!=-1) #endif WaitForSingleObject(hFirstEvent,INFINITE); /* wait for first success */ else mir_free(param); /* thread not started */ CloseHandle(hFirstEvent); return idThread; }