/* '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; } /************************* 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(); } static void WinNT_PollThread(void *vparam) { CpuUsageThreadParams *param = (CpuUsageThreadParams*)vparam; 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 */ dwObjectId=238; /*'Processor' object */ dwCounterId=6; /* '% processor time' counter */ pwszInstanceName=L"_Total"; /* '_Total' instance */ _itot_s(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))) { 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 ((int)mir_forkthread(WinNT_PollThread, param) != -1) WaitForSingleObject(hFirstEvent,INFINITE); /* wait for first success */ else mir_free(param); /* thread not started */ CloseHandle(hFirstEvent); return idThread; }