rainmeter-studio/Plugins/PluginPing/Ping.cpp

322 lines
8.0 KiB
C++
Raw Normal View History

2009-02-10 18:37:48 +00:00
/*
Copyright (C) 2005 Kimmo Pekkola
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <windows.h>
#include <math.h>
#include <string>
#include <map>
#include <vector>
#include <time.h>
#include <Ipexport.h>
#include <Windns.h>
#include <stdlib.h>
2011-02-03 18:09:24 +00:00
#include "../../Library/Export.h" // Rainmeter's exported functions
2009-02-10 18:37:48 +00:00
2011-02-03 18:09:24 +00:00
#include "../../Library/DisableThreadLibraryCalls.h" // contains DllMain entry point
2009-02-10 18:37:48 +00:00
/* The exported functions */
extern "C"
{
__declspec( dllexport ) UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id);
__declspec( dllexport ) void Finalize(HMODULE instance, UINT id);
__declspec( dllexport ) double Update2(UINT id);
__declspec( dllexport ) UINT GetPluginVersion();
__declspec( dllexport ) LPCTSTR GetPluginAuthor();
}
struct pingData
{
IPAddr destAddr;
DWORD timeout;
double timeoutValue;
DWORD updateRate;
DWORD updateCounter;
HANDLE threadHandle;
double value;
};
typedef struct tagIPINFO
{
u_char nTtl; // Time To Live
u_char nTos; // Type Of Service
u_char nIPFlags; // IP flags
u_char nOptSize; // Size of options data
u_char FAR* pOptions; // Options data buffer
} IPINFO;
typedef IPINFO* PIPINFO;
static std::map<UINT, pingData*> g_Values;
2011-03-29 19:21:57 +00:00
static CRITICAL_SECTION g_CriticalSection;
2009-02-10 18:37:48 +00:00
static bool g_Initialized = false;
static HINSTANCE g_ICMPInstance = NULL;
typedef HANDLE (WINAPI *IcmpCreateFile)(VOID);
typedef BOOL (WINAPI *IcmpCloseHandle)(HANDLE);
typedef DWORD (WINAPI *IcmpSendEcho)(HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD);
typedef DWORD (WINAPI *IcmpSendEcho2)(HANDLE, HANDLE, FARPROC, PVOID, IPAddr, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD);
static IcmpCreateFile g_IcmpCreateFile = NULL;
static IcmpCloseHandle g_IcmpCloseHandle = NULL;
static IcmpSendEcho g_IcmpSendEcho = NULL;
static IcmpSendEcho2 g_IcmpSendEcho2 = NULL;
std::string ConvertToAscii(LPCTSTR str)
{
std::string szAscii;
if (str && *str)
2009-02-10 18:37:48 +00:00
{
int strLen = (int)wcslen(str);
int bufLen = WideCharToMultiByte(CP_ACP, 0, str, strLen, NULL, 0, NULL, NULL);
if (bufLen > 0)
{
szAscii.resize(bufLen);
WideCharToMultiByte(CP_ACP, 0, str, strLen, &szAscii[0], bufLen, NULL, NULL);
}
2009-02-10 18:37:48 +00:00
}
return szAscii;
}
/*
This function is called when the measure is initialized.
2011-03-29 19:21:57 +00:00
The function must return the maximum value that can be measured.
2009-02-10 18:37:48 +00:00
The return value can also be 0, which means that Rainmeter will
track the maximum value automatically. The parameters for this
function are:
instance The instance of this DLL
iniFile The name of the ini-file (usually Rainmeter.ini)
section The name of the section in the ini-file for this measure
id The identifier for the measure. This is used to identify the measures that use the same plugin.
*/
UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{
bool valid = false;
pingData* pData = new pingData;
if (!g_Initialized)
{
InitializeCriticalSection(&g_CriticalSection);
g_ICMPInstance = LoadLibrary(L"ICMP.DLL");
if (g_ICMPInstance)
{
g_IcmpCreateFile = (IcmpCreateFile)GetProcAddress(g_ICMPInstance, "IcmpCreateFile");
g_IcmpCloseHandle = (IcmpCloseHandle)GetProcAddress(g_ICMPInstance, "IcmpCloseHandle");
g_IcmpSendEcho = (IcmpSendEcho)GetProcAddress(g_ICMPInstance,"IcmpSendEcho");
g_IcmpSendEcho2 = (IcmpSendEcho2)GetProcAddress(g_ICMPInstance,"IcmpSendEcho2");
}
2011-03-29 19:21:57 +00:00
2009-02-10 18:37:48 +00:00
g_Initialized = true;
}
memset(pData, 0, sizeof(pingData));
/* Read our own settings from the ini-file */
LPCTSTR data = ReadConfigString(section, L"DestAddress", NULL);
if (data)
{
std::string szData = ConvertToAscii(data);
pData->destAddr = inet_addr(szData.c_str());
if (pData->destAddr == INADDR_NONE)
{
WSADATA wsaData;
2011-03-29 19:21:57 +00:00
if (WSAStartup(0x0101, &wsaData) == 0)
2009-02-10 18:37:48 +00:00
{
LPHOSTENT pHost;
pHost = gethostbyname(szData.c_str());
if (pHost)
{
pData->destAddr = *(DWORD*)pHost->h_addr;
}
else
{
LSLog(LOG_WARNING, NULL, L"PingPlugin.dll: Unable to get host by name");
2009-02-10 18:37:48 +00:00
}
WSACleanup();
}
else
{
LSLog(LOG_WARNING, NULL, L"PingPlugin.dll: Unable to start WSA");
2009-02-10 18:37:48 +00:00
}
}
valid = true;
}
data = ReadConfigString(section, L"UpdateRate", L"32");
2009-02-10 18:37:48 +00:00
if (data)
{
pData->updateRate = _wtoi(data);
}
data = ReadConfigString(section, L"Timeout", L"30000");
if (data)
{
pData->timeout = _wtoi(data);
}
data = ReadConfigString(section, L"TimeoutValue", L"30000");
if (data)
{
pData->timeoutValue = wcstod(data, NULL);
}
2011-03-29 19:21:57 +00:00
2009-02-10 18:37:48 +00:00
if (valid)
{
g_Values[id] = pData;
}
return pData->timeout;
}
DWORD WINAPI NetworkThreadProc(LPVOID pParam)
{
if (g_IcmpCreateFile && g_IcmpCloseHandle && g_IcmpSendEcho)
{
pingData* pData = (pingData*)pParam;
const DWORD replySize = sizeof(ICMP_ECHO_REPLY) + 32;
BYTE* reply = new BYTE[replySize];
2009-02-10 18:37:48 +00:00
HANDLE hIcmpFile;
hIcmpFile = g_IcmpCreateFile();
if (hIcmpFile != INVALID_HANDLE_VALUE)
{
DWORD res = g_IcmpSendEcho(
hIcmpFile,
pData->destAddr,
2011-03-29 19:21:57 +00:00
NULL,
2009-02-10 18:37:48 +00:00
0,
NULL,
2011-03-29 19:21:57 +00:00
reply,
replySize,
2009-02-10 18:37:48 +00:00
pData->timeout);
g_IcmpCloseHandle(hIcmpFile);
}
EnterCriticalSection(&g_CriticalSection);
ICMP_ECHO_REPLY* pReply = (ICMP_ECHO_REPLY*)reply;
if (pReply->Status == IP_REQ_TIMED_OUT)
{
pData->value = pData->timeoutValue;
}
else
{
pData->value = pReply->RoundTripTime;
}
delete [] reply;
2009-02-10 18:37:48 +00:00
CloseHandle(pData->threadHandle);
pData->threadHandle = 0;
LeaveCriticalSection(&g_CriticalSection);
}
2011-03-29 19:21:57 +00:00
return 0; // thread completed successfully
2009-02-10 18:37:48 +00:00
}
/*
This function is called when new value should be measured.
The function returns the new value.
*/
double Update2(UINT id)
{
double value = 0.0;
std::map<UINT, pingData*>::iterator i = g_Values.find(id);
if (i != g_Values.end())
{
pingData* pData = (*i).second;
EnterCriticalSection(&g_CriticalSection);
value = pData->value;
LeaveCriticalSection(&g_CriticalSection);
if (pData->threadHandle == NULL)
{
if (pData->updateCounter == 0)
{
// Launch a new thread to fetch the web data
DWORD id;
pData->threadHandle = CreateThread(NULL, 0, NetworkThreadProc, pData, 0, &id);
}
pData->updateCounter++;
if (pData->updateCounter >= pData->updateRate)
{
pData->updateCounter = 0;
}
}
}
return value;
}
/*
If the measure needs to free resources before quitting.
The plugin can export Finalize function, which is called
when Rainmeter quits (or refreshes).
*/
void Finalize(HMODULE instance, UINT id)
{
std::map<UINT, pingData*>::iterator i1 = g_Values.find(id);
if (i1 != g_Values.end())
{
EnterCriticalSection(&g_CriticalSection);
if ((*i1).second->threadHandle)
{
// Should really wait until the thread finishes instead terminating it...
TerminateThread((*i1).second->threadHandle, 0);
}
LeaveCriticalSection(&g_CriticalSection);
delete (*i1).second;
g_Values.erase(i1);
}
// Last instance deletes the critical section
2011-03-29 19:21:57 +00:00
if (g_Values.empty())
2009-02-10 18:37:48 +00:00
{
if (g_ICMPInstance)
{
FreeLibrary(g_ICMPInstance);
g_ICMPInstance = NULL;
}
if (g_Initialized)
{
DeleteCriticalSection(&g_CriticalSection);
g_Initialized = false;
}
}
}
UINT GetPluginVersion()
{
return 1002;
}
LPCTSTR GetPluginAuthor()
{
return L"Rainy (rainy@iki.fi)";
}