Are you looking for a tool that periodically pings a list of hosts? Here is a toolset that will reschedule a ping utility. This ping utility pings a list of hosts and will create notifications if one or most hosts failed to answer.
I have done this toolset with three separate applications. The scheduler and the notification tool are written in C/C++ win32 API cause this API provided the best access to all the possibilities of the used functions. The user notification API is only supported with basic functionality by CF2. The CEUserNotification
API is not supported by CF2 at all.
Although OpenNetCF provides a C# interface to the used APIs, I did not like to include all the unneeded stuff. On the other hand, the scheduler is fast and small written in C/C++. With C#, I had problems with the notification, especially for removing existing notifications and why should the main tool reside in memory just for showing the notification.
The ping tool is written in C# targeting Compact Framework 2. C# was the easiest to implement the GUI.
All source is available through one Visual Studio 2008 solution. Yes, you can mix C/C++ and CF2 within one solution.
The code of the scheduler called PingAlertScheduler
is based on my code How to run an application periodically. The code first clears all existing schedules of themselves and then creates a new schedule for xx minutes in the future. If the scheduler is run from the schedule event, it will get a special arg transmitted “PingAlert
”.
...
TCHAR szExtApp[] = L"\\Windows\\PingAlert.exe";
TCHAR szExtArg[] = L"pingsweep";
TCHAR szScheduleArg[] = L"PingAlert";
...
if (_wcsicmp(szScheduleArg, lpCmdLine)==0)
{
nclog(L"PingAlertScheduler: processing CmdLine=%s...\n", szScheduleArg);
nclog(L"PingAlertScheduler: ...will wakeup again after reschedule...\n");
if ( !FAILED(ScheduleRunApp(lpFileName, szScheduleArg)) )
{
nclog(L"PingAlertScheduler: starting target app %s...\n", szExtApp);
PROCESS_INFORMATION pi;
if( CreateProcess(szExtApp,szExtArg,
NULL,NULL,NULL, 0, NULL,NULL,NULL,
&pi)!=0)
{
nclog(L"PingAlertScheduler: CreateProcess for '%s' OK\n", szExtApp);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
nclog(L"PingAlertScheduler: CreateProcess for '%s' FAILED: %u\n",
szExtApp, GetLastError());
goto MainExit;
}
else{
MessageBox(NULL, L"error in ScheduleRunApp", lpFileName,
MB_TOPMOST | MB_SETFOREGROUND);
nclog(L"PingAlertScheduler: error in ScheduleRunApp\n");
iRet=-2;
goto MainExit;
}
}
This code also calls the reschedule to run itself again in the specified time interval.
static HRESULT ScheduleRunApp(
LPCTSTR szExeName,
LPCTSTR szArgs)
{
SYSTEMTIME t;
memset(&t, 0, sizeof(SYSTEMTIME));
GetLocalTime(&t);
if ( (t.wYear == 2003) && (t.wMonth == 3) && (t.wDay == 21) )
{
nclog(L"PingAlertScheduler: # no next run schedule as date is 21.03.2003!\n");
return NOERROR;
}
HRESULT hr = S_OK;
HANDLE hNotify = NULL;
CE_NOTIFICATION_TRIGGER notifTrigger;
memset(&notifTrigger, 0, sizeof(CE_NOTIFICATION_TRIGGER));
notifTrigger.dwSize = sizeof(CE_NOTIFICATION_TRIGGER);
SYSTEMTIME st = {0};
GetLocalTime(&st);
st = AddDiff(&st, iScheduleInterval);
wsprintf(str, L"Next run at: %02i.%02i.%02i %02i:%02i:%02i\n",
st.wDay, st.wMonth , st.wYear,
st.wHour , st.wMinute , st.wSecond );
nclog(L"PingAlertScheduler: %s\n", str);
notifTrigger.dwType = CNT_TIME;
notifTrigger.stStartTime = st;
notifTrigger.lpszApplication = (LPTSTR)szExeName;
notifTrigger.lpszArguments = (LPTSTR)szArgs;
hNotify = CeSetUserNotificationEx(0, &notifTrigger, NULL);
if (!hNotify) {
hr = E_FAIL;
nclog(L"PingAlertScheduler: CeSetUserNotificationEx FAILED...\n");
} else {
CloseHandle(hNotify);
nclog(L"PingAlertScheduler: CeSetUserNotificationEx succeeded...\n");
}
return hr;
}
When PingAlertScheduler.exe is started with “PingAlert
”, it will also launch the second application of the toolset called PingAlert
. The Compact Framework application PingAlert
can be started normally to manage the hosts list and it can be started with the argument “pingsweep
” and then will automatically start to ping all hosts and then exits itself. If there were ping errors, PingAlert
will create an HTML file with a report and then start the notification application of this toolset, which is called PingAlertToast
.
How do we handle program arguments in compact framework. See program.cs:
[MTAThread]
static void Main(String[] args)
{
Application.Run(new Form1(args));
}
and then I change the form constructor code to receive the args string
list:
public Form1(String[] args)
{
InitializeComponent();
paSettings = new PingAlertSettings();
readSettings();
String myArg="";
if (args.Length == 1)
{
myArg = args[0];
if (myArg.Equals("pingsweep", StringComparison.OrdinalIgnoreCase))
bRunAndQuit = true;
}
At the end of pinging the host list, another boolean signals a timer tick event handler, that it is time to quit the app.
private void exitTimer1_Tick(object sender, EventArgs e)
{
if(bQuitIsOK){
exitTimer1.Enabled = false;
this.Close();
}
}
At the end of the host pings, the third app, PingAlertToast
will be started, if there was at least one error:
...
if (bte.qData._iCount == numberOfQueuedData)
{
closeHTML();
try
{
System.IO.TextWriter streamWriter =
new System.IO.StreamWriter(szHtmlFile);
streamWriter.WriteLine(sbHTML);
streamWriter.Flush();
streamWriter.Close();
}
catch (Exception x)
{
addLog("Exception in write HTML: " + x.Message);
}
showNotification();
bQuitIsOK = true;
}
...
Ah, what is this qData
? To have the GUI still responsive during the ping process, the PingAlert
uses a queue to add hosts to ping. The background process reads this queue and pings one by the other as long as there are hosts in the queue.
bgThread2.cs
...
if (_theQueue.Count > 0)
{
lock (_theQueue.SyncRoot)
{
_qData = (queueData)_theQueue.Dequeue();
}
try
{
_qData.IP = System.Net.Dns.Resolve(_qData.sHost).AddressList[0];
iPreplies = myPing.PingQdata(ref _qData);
}
...
To be able to have some extra information with the hosts, I use a class called QueueData
which will hold the hosts to ping, the number of ping retries, etc. You can add hosts using their DNS name or their IP address.
How can you disable the schedule? As you know, PingAlertScheduler
can be launched with different args. If it is launched without args, it will create a new schedule and terminates itself. If it is launched with “PingAlert
”, it will reschedule itself and launch PingAlert
with the arg “pingsweep
”. If it is launched with the argument “clear
”, it will clear all existing schedules to PingAlertScheduler
.
So, if you want to stop the scheduling, you have to launch PingAlertScheduler
with the argument “clear
”. To do so, you can simply use the button “Clear Schedule” in the main PingAlert
application.
If you need to start the schedule, you use the button “Schedule
”. It will simply launch PingAlertSchedule
without an argument.
Now to the last of the trio, PingAlertToast
. It is written in C/C++ and creates a user notification. You will get a nice symbol in the taskbar and if you click (tap) it, it will show the notification. This is only a reminder notification signaling that some ping failed.
If you don't like to keep the notification, just tap (Dismiss). To only hide it, tap on (Hide). To get the notification back on screen, tap the small symbol in the taskbar.
Within the notification text is a link which should open your internet browser on the device and that shows the report.
The notification API is very strong and enables you to configure nearly everything of the notification. As the API is not fully available in Compact Framework, I did code PingAlertToast
in C/C++. Here is the main part creating the notification:
int showNotification(TCHAR* szText){
LRESULT lRes=0;
SHNOTIFICATIONDATA sn = g_shNotificationData;
sn.cbStruct = sizeof(sn);
sn.dwID = g_NotificationID;
sn.npPriority = SHNP_INFORM;
sn.csDuration = -1;
sn.hicon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_SAMPLEICON));
sn.clsid = guidNotifyApp;
sn.grfFlags = 0;
sn.pszTitle = TEXT("PingAlert");
sn.pszHTML = TEXT("<html><body>
There was at least one error. Please click
<a href=\"file:///Windows\\PingAlertReport.html\">
PingAlert Report</a> for more details.</body></html>");
sn.rgskn[0].pszTitle = TEXT("Dismiss");
sn.rgskn[0].skc.wpCmd = g_dwNotificationCmdID1;
sn.rgskn[0].skc.grfFlags = NOTIF_SOFTKEY_FLAGS_STAYOPEN;
sn.rgskn[1].pszTitle = TEXT("Hide");
sn.rgskn[1].skc.wpCmd = g_dwNotificationCmdID2;
sn.rgskn[1].skc.grfFlags = NOTIF_SOFTKEY_FLAGS_HIDE;
sn.hwndSink=g_hWnd;
lRes = SHNotificationAdd(&sn);
return lRes;
}
Take a deeper look at PingAlertToast.cpp to see more details on handling menu taps, etc.
Not to forget, the settings are persistent in the registry at HKLM\Software\PingAlert.
REGEDIT4
[HKEY_LOCAL_MACHINE\Software\PingAlert]
"Log2File"=dword:00000000
"TimeInterval"=dword:0000000F
"Hosts"=hex(7):\
67,6F,6F,67,6C,65,2E,63,6F,6D,00,31,39,32,2E,31,36,38,2E,31,32,38,2E,35,\
00,73,6D,61,72,74,00,00
Log2File
specifies if you like to have a log file for PingAlert
’s action in \PingAlert.Log.txt. TimeInterval
specifies the interval between reschedules. - Hosts is a
Multi_SZ
registry key holding the list of hosts to ping.
Code at code.google.com
Executable set (copy all into \Windows dir on the device): DOWNLOAD: PingAlert Executables Set - PingAlertScheduler
, PingAlert
and PingAlertToast
(Hits: 1, size: 24.02 kB)
Installable CAB file (will place a link in your program folder): DOWNLOAD: PingAlert Installer CAB - (Hits: 1, size: 24.44 kB)
Bookmark It