Target Eye Source Code Revealed

<div dir=”rtl” style=”text-align: right;” trbidi=”on”>
<ul class=”download” dir=”ltr” style=”text-align: left;”>
<li><a href=”http://www.codeproject.com/KB/shell/TargetEye/TargetEye-AutoUpdate_Demonstration.zip” target=”_blank”>Download TargetEye-AutoUpdate Demonstration – 141.37 KB</a>&nbsp;</li>
</ul>
<h2 dir=”ltr” style=”text-align: left;”>
Introduction&nbsp;</h2>
<div dir=”ltr” style=”text-align: left;”>
Auto Update mechanism doesn’t have to be based on the actual version number (kept in the Version String), but can also base on the Last Modified Date Stamp
of the newer version compared to the old one. Target Eye Monitoring System, developed starting of 2000, had such mechanism.</div>
<h2 dir=”ltr” style=”text-align: left;”>
Background</h2>
<div dir=”ltr” style=”text-align: left;”>
<a href=”http://www.targeteye.biz/” title=”Target Eye by Michael Haephrati”>Target Eye Monitoring System</a>, which I have developed 12 years ago, was one of the
first surveillance and monitoring tools for capturing activity of remote computers. The following description is taken from the original Business Plan of this venture:</div>
<div class=”separator” style=”clear: both; text-align: center;”>
<a href=”http://1.bp.blogspot.com/-JVnE62eePZI/T7a_EdEUbMI/AAAAAAAAtag/rnP7spJHK7E/s1600/TE_BANNER.jpg” imageanchor=”1″ style=”margin-left: 1em; margin-right: 1em;”><img border=”0″ src=”http://1.bp.blogspot.com/-JVnE62eePZI/T7a_EdEUbMI/AAAAAAAAtag/rnP7spJHK7E/s1600/TE_BANNER.jpg” /></a></div>
<div align=”center” dir=”ltr”>
<br /></div>
<div align=”center” dir=”ltr”>
<strong>Target Eye Monitogin System&nbsp;</strong></div>
<div dir=”ltr” style=”text-align: left;”>
<em>Target Eye is a start-up company whose
mission is to develop integrated software solutions for real-time monitoring of
remote PCs, which are based on the company’s patent pending technologies
(60/204,084 and 60/203,832). Our major product, Target Eye Monitoring System,
is a software product that can continuously track, record, playback, analyze
and report any activity performed on one or multiple remote PCs, in a way which
is undetectable by their users. The software relies on a stream of rapidly
captured, compressed full-screen images and continuous keystroke capturing to
provide a comprehensive and accurate account of user activities, including
local activities which do not generate any network traffic. In this way, the
software can track and record activities, which are undetectable by systems
relying on network traffic analysis. A smart agent module running on the
monitored PCs uses a rule base to send alerts to the monitoring location(s) or
perform pre-defined local operations. Monitoring can be performed from multiple
locations. Major markets are law-enforcement.</em></div>
<div dir=”ltr” style=”text-align: center;”>
</div>
<div dir=”ltr” style=”text-align: left;”>
Target
Eye Monitoring System was developed with an auto update mechanism. This
mechanism allows smooth and silent (un-attendant) execution of the new
version instead of the current one. </div>
<div class=”separator” style=”clear: both; text-align: center;”>
<a href=”http://4.bp.blogspot.com/-S_NuZMgY-0o/T7a_MdnZysI/AAAAAAAAtas/tZbgjl3G4dc/s1600/target-eye-2000.jpg” imageanchor=”1″ style=”margin-left: 1em; margin-right: 1em;”><img border=”0″ height=”251″ src=”http://4.bp.blogspot.com/-S_NuZMgY-0o/T7a_MdnZysI/AAAAAAAAtas/tZbgjl3G4dc/s320/target-eye-2000.jpg” width=”320″ /></a></div>
<div dir=”ltr” style=”text-align: left;”>
<br /></div>
<div dir=”ltr” style=”text-align: center;”>
<strong>An historical image: The first version of Target Eye (Apr 2000)&nbsp;</strong>&nbsp;</div>
<div class=”separator” style=”clear: both; text-align: center;”>
<a href=”http://4.bp.blogspot.com/-tXqcxJoMGfc/T7a_RmqfcII/AAAAAAAAta0/8m_ffHA63CY/s1600/TargetEyeCompiler.jpg” imageanchor=”1″ style=”margin-left: 1em; margin-right: 1em;”><img border=”0″ height=”284″ src=”http://4.bp.blogspot.com/-tXqcxJoMGfc/T7a_RmqfcII/AAAAAAAAta0/8m_ffHA63CY/s320/TargetEyeCompiler.jpg” width=”320″ /></a></div>
<div dir=”ltr” style=”text-align: left;”>
<br /></div>
<div dir=”ltr” style=”text-align: left;”>
<strong style=”text-align: center;”><span class=”Apple-tab-span” style=”white-space: pre;”> </span>A more recent version (Target Eye 2007)</strong><span style=”text-align: center;”>&nbsp;</span>
</div>
<div dir=”ltr” style=”text-align: left;”>
<br /></div>
<div dir=”ltr” style=”text-align: left;”>
This article focuses only in one aspect of this product, which is the Auto Update mechanism.&nbsp;&nbsp;</div>
<h2 dir=”ltr” style=”text-align: left;”>
Creating and Marking Incremental
Versions </h2>
<div dir=”ltr” style=”text-align: left;”>
In
order to be able to determine a newer version over the current one, there must
be a mechanism to mark the various versions, to increment the version number,
and to examine the version of a given executable.</div>
<div dir=”ltr” style=”text-align: left;”>
The
most common way of doing so is using the “Version String” resource,
and to use a prebuild automated tool to promote this string each time the application
is built. </div>
<div dir=”ltr” style=”text-align: left;”>
I
used another method which is based on the list modification date of the executable
file. This method has its pros and cons, but can be useful for most cases,
since it will allow your end users to get any version of your application that
was built after the one that is currently installed. </div>
<div dir=”ltr” style=”text-align: left;”>
Before I explain, it is important
to mention 2 global variables which are used to build at start:</div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”>strRegularLocation &nbsp;&nbsp;</pre>
<div dir=”ltr” style=”text-align: left;”>
The location and full path of the
application when it runs normally (i.e. c:\program files\your app name\your
app.exe), </div>
<div dir=”ltr” style=”text-align: left;”>
and… </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”>strTemporaryLocation</pre>
<div dir=”ltr” style=”text-align: left;”>
Another full path to be used for the
temporary version downloaded when there is an update. </div>
<div dir=”ltr” style=”text-align: left;”>
The reason for doing so it because since the
application downloads it’s new version, the new version can’t be downloaded to
its current location, and can’t replace itself because while a file is used (or
an application is running) the file is locked and can’t be moved, deleted or
renamed. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
char strRegularLocation[256];</div>
<div>
char strTemporaryLocation[256];</div>
</pre>
<div dir=”ltr” style=”text-align: left;”>
Filling
<code>strRegularLocation</code> and <code>strTemporaryLocation</code> with real values</div>
<div dir=”ltr” style=”text-align: left;”>
<code>strRegularLocation</code>
is taken simply from <code>__argv[0]</code>, provided that we are sure that we aren’t
running already from the temporary location. We ensure that by using a
parameter named “INSTALL” which will be explained later.&nbsp;</div>
<div dir=”ltr” style=”text-align: left;”>
<code>strTemporaryLocaiton</code>
is built using a common folder and a temporary name combined. We use
<code>GetSpecialFolder()</code> to find the path name of this folder in any computer running
the application. </div>
<h2 dir=”ltr” style=”text-align: left;”>
Getting
the date stamp of current version&nbsp;</h2>
<div dir=”ltr” style=”text-align: left;”>
To
do so, <code>TEGetVersion()</code>, the first building block, is used and returns a <code>CString</code> containing the last modification date of a given file.&nbsp;</div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
//</div>
<div>
TEGetVersion returns the last modification date / time of a given file</div>
CString
TEGetVersion (CString FileName)&nbsp;
{&nbsp;&nbsp;
<div>
Time1;
if( CFile::Ge</div>
CFileStatus status1;
CTime
tStatus( FileName, status1) )
{
<div>
return (Time1.Format(“%d%m%M%S”)</div>
Time1 = status1.m_mtime;&nbsp;

);
}&nbsp;
// Failed&nbsp;
return ((CString)”");
}
<div>
</div>
//</pre>
<div dir=”ltr” style=”text-align: left;”>
When my application starts, I store the date / time stamp of it somewhere. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
TE_Options.Version=TEGetVersion((CString)__argv[0]);</div>
<div>
// here we keep the date/time stamp of the current version</div>
</pre>
<div dir=”ltr” style=”text-align: left;”>
Now we need to get the date / time stamp of the file online, preferably, without having to download it first, so we only download when we need to update to a newer version. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
HINTERNET FileHandle=NULL;&nbsp;</div>
<div>
ND_DATA ftpFileData;&nbsp;
//find </div>
WIN32_F
Ithe file on the ftp server&nbsp;

<div>
ndle, FTP_NEWVERSION, &amp;ftpFileData,
INTERNET_FLAG_RELOAD, 0 );
if( NULL </div>
FileHandle= FtpFindFirstFile( m_ftpH
a!= FileHandle )
{

// get the write time of the ftp file

<div>
FileTimeToSystemTime( &amp;ftpFileData.ftL</div>
SYSTEMTIME ftpFileWriteTime, stUTC1;

FILETIME ftp;

astWriteTime, &amp;stUTC1 );

SystemTimeToTzSpecificLocalTime( NULL, &amp;stUTC1, &amp;ftpFileWriteTime );
<div>
</div>
}</pre>
<div dir=”ltr” style=”text-align: left;”>
We need to define how old should be the current
version in order to update it. Again, this approach can be very useful in some
cases and less useful in other. For example, if your application involves a
database, you might be interested to ensure that the database is always most
recent and never older than 3 days. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
#define UPDATEEVERY 60*24*7 // 7 days</div>
<div>
#define APP_EXE_NAME “TargetEyeTest.exe”</div>
#define FTP_NEWVERSION “NewVersion.exe”
<div>
\”</div>
#define APP_REGULAR_FOLDER “\\TargetEye
\</pre>
<div dir=”ltr” style=”text-align: left;”>
The next step is to compare the date / time
stamp of each file. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
CFileStatus</div>
statusOld;
<div>
ld;
if( CFile:</div>
CTime Time
<div>
O:GetStatus( FileHandle, statusOld ) )</div>
{
CTime ct,OldTime;
<div>
d.m_mtime;
hFindFile.GetLastWr</div>
OldTime=statusO
liteTime(ct);
LONG Diff;
<div>
Y %H:%M %Z”);
oldver=OldTime.FormatGmt</div>
ver=ct.FormatGmt(“%d %m
%(“%d %m %Y %H:%M %Z”);
<div>
-OldTime)).GetTotalMinutes();
hFindFile.Close();
</div>
Diff = ((CTimeSpan)(c
tif (Diff&gt;UPDATEEVERY || resultSpecific)
{
<div>
</div>
// download the newer version
}
<div>
</div>
}&nbsp;</pre>
<h2 dir=”ltr” style=”text-align: left;”>
Downloading the new version </h2>
<div dir=”ltr” style=”text-align: left;”>
Downloading the newer version is performed using <code>TE_DownladLoad()</code> which is listed here. We make several attempts in case there is a temporary block or communication problem. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
#define FTPRETRIES 5 // number of retries</div>
<div>
BOOL TE_DownloadLoad(char *FtpFileName,char *LocalFileName)</div>
{
int DoTry=FTPRETRIES;&nbsp;
int result;&nbsp;
<div>
t = MyConnection</div>
TryAgain:;&nbsp;
try&nbsp;
{
resu
l.m_FtpConn-&gt;GetFile(FtpFileName, LocalFileName, FALSE);
}
<div>
4];&nbsp;
pEx-&gt;GetErrorMessage(sz</div>
catch (CInternetException* pEx)
{
TCHAR sz[10
2,1024);
WriteToLog("Error %s\n", sz);
<div>
6 - TE_Load",MB_OK);
pEx-&gt;Delete();&nbsp;
}
if (!resul</div>
if(TE_DEBUG) MessageBox(NULL,sz,"Error
t)
{
if(DoTry-- &gt;0) goto TryAgain;
return(FALSE);
}
else
{
<div>
</div>
return (TRUE);
}
<div>
</div>
}</pre>
<div dir="ltr" style="text-align: left;">
Now we are ready to switch between the currently running version (the
old one) with the newer one.&nbsp;</div>
<h2 dir="ltr" style="text-align: left;">
Executing the newer version&nbsp;</h2>
<pre dir="ltr" lang="C++" style="text-align: left;"><div>
BOOL ExecuteNewVersion(char *ExeName,char *Param)</div>
{
STARTUPINFO sinfo;
<div>
pinfo;
ZeroMemory(&amp;sinfo, s</div>
PROCESS_INFORMATION
izeof(sinfo));
sinfo.cb = sizeof(sinfo);
<div>
info.dwFlags=STARTF_USESHOWWINDOW ;
</div>
sinfo.lpDesktop= "WinSta0\\Default";

ssinfo.wShowWindow=SW_SHOW;
<div>
(char*)(LPCTSTR)((CString)(ExeName)+(CString)"
"+(CString)(Param)</div>
if(!CreateProcess(NULL
,), NULL, NULL,FALSE,NORMAL_PRIORITY_CLASS |
CREATE_NEW_CONSOLE, NULL, NULL, &amp;sinfo, &amp;pinfo))
<div>
);
</div>
{
char s[256];
sprintf(s,”Can’t execute program: %s params %s”,ExeName,Para
m // ERROR LOG
TELog.LogError(“Execute New Version”,s,0);
return FALSE;
}
<div>
</div>
else
{
return TRUE;
}
<div>
</div>
}&nbsp;</pre>
<div dir=”ltr” style=”text-align: left;”>
So if we put all the code together we get:</div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
CFileStatus statusOld;</div>
CTime TimeOld;
<div>
atus( FileHandle, statusOld ) )
{
CTime c</div>
if(CFile::GetS
tt,OldTime;
OldTime=statusOld.m_mtime;
<div>
NG Diff;
ver=ct.FormatGmt(“%d %</div>
hFindFile.GetLastWriteTime(ct);
L
Om %Y %H:%M %Z”);
oldver=OldTime.FormatGmt(“%d %m %Y %H:%M %Z”);
<div>
e.Close();
if (Diff&gt;UPDATEEVERY || resultSp</div>
Diff = ((CTimeSpan)(ct-OldTime)).GetTotalMinutes();
hFindFi
lecific)
{
// downloading the newer version
<div>
MPPLACE))
{
// We have successfully downloade</div>
if(TE_DownLoad((resultGeneric)?NEWEXESTR:NEWEXE,T
Ed the newer version
if(ExecuteNewVersion(TEMPPLACE,”INSTALL”))
{
<div>
rent version can now quit
}
</div>
// We have successfully executed the
// newer version. Cu
r else
// Failed to execute new version
}
else
{
<div>
</div>
TELog.LogError(“New Ftp version found”,”Can’t download”,0);
}
}
<div>
</div>
}</pre>
<h2 dir=”ltr” style=”text-align: left;”>
The TE_Init() function&nbsp;</h2>
<div dir=”ltr” style=”text-align: left;”>
TE_Init() is used to determine the parameters
used when application was executed (Unlike the full version of Target Eye
Monitoring System, which is much more complex, in our example, there is one
optional parameter – “INSTALL”). </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
if(__argc&gt;1)&nbsp;</div>
{
<div>
if(strcmp(__argv[1],”INSTALL”)==0)
</div>
{
<div>
// TE_FirstTime(); -&gt; here you can place code you wish to
</div>
//be executed only during the first run&nbsp;
}
}
else
<div>
xists at the tem</div>
// No parameters
{
// Delete a temporary version if
eporary location
<div>
</div>
}</pre>
<h2 dir=”ltr” style=”text-align: left;”>
Replacing old with new&nbsp;</h2>
<div dir=”ltr” style=”text-align: left;”>
In order to quit in a normal fashion, without missing anything we wish
to do before quitting, the main even loop contains a check for the value of
NeedToQuit, which would normally be FALSE. </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”>BOOL NeedToQuit=FALSE;&nbsp;</pre>
<div dir=”ltr” style=”text-align: left;”>
When NeedToQuit becomes TRUE, the application will perform any routine
required before quitting (for example, saving unsaved work). For example:</div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
if(NeedToQuit)</div>
{
<div>
if(TE_DEBUG)
</div>
<div>
MessageBox(NULL,”Terminating Targe Eye”,</div>
“Target Eye Monitoring System”,NULL);
<div>
</div>
return FALSE;
<div>
</div>
}&nbsp;</pre>
<div dir=”ltr” style=”text-align: left;”>
Further, the application expects to be executed either with the
“INSTALL” parameter as part of the command line, or without it. The
following scheme illustrates the flow of an installation of a newer version to
a temporary location (the Desktop folder, in our example), up to the moment the
temporary file used for it is deleted. This requires several stages:</div>
<div class=”Caption” dir=”ltr” style=”text-align: left;”>
The Target Eye Cycle</div>
<table class=”ArticleTable” dir=”ltr” style=”text-align: left;”>

<thead>
<tr>

<td valign=”top” width=”94″><div align=”center”>
Stage</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
Ran
with Parameter</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
Ran
from location</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
Description</div>
</td>
</tr>
</thead>

 
<tbody>
<tr>

<td valign=”top” width=”94″><div align=”center”>
1&nbsp;</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
None</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
Regular</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
The
current (old) version is running before the newer version is available</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
2</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
The
newer version checks if there is a temporary copy of itself at the temporary
location, but there isn’t any</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
3</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
A
newer version is found</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
4</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
Newer
version is downloaded to a temporary location</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
5</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
Newer
version runs from the temporary location with the “INSTALL”
parameter. </div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
6</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
Current
version quits</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
7</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
INSTALL</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
Temporary</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
The
newer application ran from the temporary location copies itself to the
regular location, replacing the old version which quitted (5)</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
8</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
The
newer version runs the copy located in the regular location and quits.</div>
</td>
</tr>
<tr>

<td valign=”top” width=”94″><div align=”center”>
9</div>
</td>

 

<td valign=”top” width=”130″><div align=”center”>
None</div>
</td>

 

<td valign=”top” width=”157″><div align=”center”>
Regular</div>
</td>

 

<td valign=”top” width=”187″><div align=”center”>
The
newer version checks if there is a temporary copy of itself at the temporary
location, and deletes it.</div>
</td>
</tr>
</tbody>

</table>
<div class=”separator” style=”clear: both; text-align: center;”>
<a href=”http://1.bp.blogspot.com/-hg5TxpOnyGc/T7a_c8SEPhI/AAAAAAAAta8/paVrsd1TjGg/s1600/ROUTING.png” imageanchor=”1″ style=”margin-left: 1em; margin-right: 1em;”><img border=”0″ height=”239″ src=”http://1.bp.blogspot.com/-hg5TxpOnyGc/T7a_c8SEPhI/AAAAAAAAta8/paVrsd1TjGg/s320/ROUTING.png” width=”320″ /></a></div>
<div dir=”ltr” style=”text-align: left;”>
<br /></div>
<h2 dir=”ltr” style=”text-align: left;”>
Choosing an FTP server for this demo</h2>
<div dir=”ltr” style=”text-align: left;”>
In order to use the source code that attached to this article, there are
predefined settings of a public Secured FTP server available to the public by <a href=”http://www.chilkatsoft.com/”>Chilkat Software, Inc.</a></div>
<div dir=”ltr” style=”text-align: left;”>
The details of this server are:</div>
<div dir=”ltr” style=”text-align: left;”>
Secure FTP Server
Details</div>
<div align=”left” dir=”ltr”>
<table border=”0″ cellpadding=”0″ class=” “>

<tbody>
<tr>

<td>Type</td>

 

<td>FileZilla</td>
</tr>
<tr>

<td>Address</td>

 

<td><a href=”ftp://ftp.secureftp-test.com/”>ftp.secureftp-test.com</a> </td>
</tr>
<tr>

<td>Login</td>

 

<td>Test</td>
</tr>
<tr>

<td>Password</td>

 

<td>Test</td>
</tr>
</tbody>

</table>
</div>
<div dir=”ltr” style=”text-align: left;”>
There is a file there named hamlet.xml which can be used for testing a
remote file date stamp. </div>
<h2 dir=”ltr” style=”text-align: left;”>
Internationalization</h2>
<div dir=”ltr” style=”text-align: left;”>
To comply with scenarios in which there
are users worldwide, and yet we wish to release a version to be available at
the same moment to all of them regardless of their local time, we use </div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”>FormatGmt&nbsp;</pre>
<div dir=”ltr” style=”text-align: left;”>
GMT is an absolute time reference and doesn’t
change regardless of the season or the location. FormatGmt is used like that:</div>
<pre dir=”ltr” lang=”C++” style=”text-align: left;”><div>
CTime t( 1999, 3, 19, 22, 15, 0 );</div>
// 10:15 PM March 19, 1999
<div>
%B %d, %Y” );
ATLASSERT( s == “Friday, M</div>
CString s = t.Format( “%A,
<div>
arch 19, 1999″ );</div>
</pre>
<h2 dir=”ltr” style=”text-align: left;”>
Limiting to a single instance</h2>
<div dir=”ltr” style=”text-align: left;”>
The mechanism described in this article can only work if we limit our application to run only once at any given moment.
To do so, several methods can be used, such as CreateMutex().</div>
<div dir=”ltr” style=”text-align: left;”>
See <a href=”http://support.microsoft.com/kb/243953″>http://support.microsoft.com/kb/243953</a></div>
<div dir=”ltr” style=”text-align: left;”>
Target Eye Monitoring System uses a different and a bit “brutal” approach, which will be explain in details over another article. Basically, Target Eye Monitoring System searches
for other instances currently running in memory, and when found, does one of the two following options:</div>
<ol dir=”ltr” style=”text-align: left;”>
<li>If the instance found is newer, the current running instance quits.</li>
<li>If the instance found is older, the current running instance kills it.</li>
</ol>
<div dir=”ltr” style=”text-align: left;”>
To explain, let’s consider the following
scenario. An end user had his current copy of software updated to a newer one.
A day after, this end user runs the original CD of the software. The version
that resides on the original CD, will search for new updates at the FTP server,
which is unnecessary. Further, if the application runs constantly (like a
monitoring application should), then probably when the CD version is ran, there
is also another newer version already running. To address such scenario and other
scenarios, we should take the necessary measures to ensure that an application
will always be up to date. </div>
<div dir=”ltr” style=”text-align: left;”>
<br /></div>
<div dir=”ltr” style=”text-align: left;”>
Published at <a href=”http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=5956881″ target=”_blank”>www.codeproject.com&nbsp;</a></div>
</div>

Searching for a reliable Hardware ID

Download GetMacAddress_by_Michael_Haephrati.zip – 132.06 KB

Download GetHDSerialNumber_by_Michael_Haephrati.zip – 147 KB

Introduction

Many desktop applications developers need to uniquely identify the computer in which their software is running on.

Such identification must produce a unique data element which will be different per each computer and will be reproduced the same ID, on any given computer.

The WMI set of classes

Windows provides a set of classes that can be used for most of hardware enumeration and identification tasks, which is named WMI or Windows Management Instrumentation.

These are extensions to the Windows Driver Model (WDM).

WMI provides per instrumented components static information and dynamic notification about any changes. Most of programming languages can be used to manage, locally and remotely, computers and servers, enumerating their instrumented components and to be alerted as for changes that occour.

During my research I came to the conclusion that if speed and reliability is important, it is better to access the hardware via Win32 API and not to use WMI. I have experienced many delays and in some occasions, WMI failed to detect an element such as the CPU ID.

This article focuses in the direct approach for obtaining this data without using WMI.

Obtaining a unique CPU ID

The solution that seems to be the best choice is to sample the CPU unique identification number (or CPU ID). However, there are several problems that makes it impossible to relay on reading the CPU ID.

To begin with, most CPUs with the exception of the old Pentium 3, don’t have a unique CPU Serial Number. Intel has removed this feature for privacy reasons.

It is still possible to generate a unique ID from the motherboard as a whole. That certainly works but the huge number of different types of motherboards and manufacturers makes it next to impossible to generate a unique ID that will cover all of them.

In fact, a French company named CPU ID, focuses in this field and spends a lot of resources in getting to learn each type of motherboard and CPU, in order to cover them all.

The following screenshot shows the details that can be collected per each machine.

Their SDK can be downloaded here, and can be used both as a static library (per special request) or a DLL with any application developed. The bad news are that even the guys from CPUID, say it is impossible to generate a unique Hardware ID based on the CPU or the motherboard of a given machine.

 

Mac Address based Hardware ID

The next choice for obtaining such unique ID would be sampling the MAC address. To begin with, what is the “MAC address”? whll, it stands for “Media Access Control“. The Mac address is 48 bits long (6 bytes). The GetMACAddress code sample explains how to obtain the Mac address.

However, there is one problem with this approach: Mac address can be easily changed into a new one…

Hard Drive Serial Number

It seems that the only reliable solution for obtaining a machine ID would be using the serial number of the main Hard Drive.

The second example, GetHDSerialNumber, shows how to obtain this ID. From my experience, this approach is the best one and the most reliable one for generating a unique machine based Hardware ID.

Was published at www.codeproject.com

Target Eye Revealed #1 – Target Eye’s Unique AutoUpdate Mechanism

By Michael Haephrati

How Target Eye’s Auto updating mechanism allows a silent update of an application from identifying newer versions, downloading them and running them instead of the old one

Introduction

Auto Update mechanism doesn’t have to be based on the actual version number (kept in the Version String), but can also base on the Last Modified Date Stamp

of the newer version compared to the old one. Target Eye Monitoring System, developed starting of 2000, had such mechanism.

Background

Target Eye Monitoring System, which I have developed 12 years ago, was one of the

first surveillance and monitoring tools for capturing activity of remote computers. The following description is taken from the original Business Plan of this venture:

Target Eye Monitogin System

Target Eye is a start-up company whose

mission is to develop integrated software solutions for real-time monitoring of

remote PCs, which are based on the company’s patent pending technologies

(60/204,084 and 60/203,832). Our major product, Target Eye Monitoring System,

is a software product that can continuously track, record, playback, analyze

and report any activity performed on one or multiple remote PCs, in a way which

is undetectable by their users. The software relies on a stream of rapidly

captured, compressed full-screen images and continuous keystroke capturing to

provide a comprehensive and accurate account of user activities, including

local activities which do not generate any network traffic. In this way, the

software can track and record activities, which are undetectable by systems

relying on network traffic analysis. A smart agent module running on the

monitored PCs uses a rule base to send alerts to the monitoring location(s) or

perform pre-defined local operations. Monitoring can be performed from multiple

locations. Major markets are law-enforcement.

Target

Eye Monitoring System was developed with an auto update mechanism. This

mechanism allows smooth and silent (un-attendant) execution of the new

version instead of the current one.

This article focuses only in one aspect of this product, which is the Auto Update mechanism.

Creating and Marking Incremental Versions

In order to be able to determine a newer version over the current one, there must

be a mechanism to mark the various versions, to increment the version number,

and to examine the version of a given executable.

The most common way of doing so is using the “Version String” resource, and to use a prebuild automated tool to promote this string each time the application is built. I used another method which is based on the list modification date of the executable

file. This method has its pros and cons, but can be useful for most cases, since it will allow your end users to get any version of your application that was built after the one that is currently installed.

Before I explain, it is important to mention 2 global variables which are used to build at start:

strRegularLocation

The location and full path of the

application when it runs normally (i.e. c:\program files\your app name\your app.exe),

and…

strTemporaryLocation

Another full path to be used for the

temporary version downloaded when there is an update.

The reason for doing so it because since the application downloads it’s new version, the new version can’t be downloaded to

its current location, and can’t replace itself because while a file is used (or an application is running) the file is locked and can’t be moved, deleted or renamed.

char strRegularLocation[256];

char strTemporaryLocation[256];

Filling

strRegularLocation and strTemporaryLocation with real values

strRegularLocation

is taken simply from __argv[0], provided that we are sure that we aren’t

running already from the temporary location. We ensure that by using a

parameter named “INSTALL” which will be explained later.

strTemporaryLocaiton

is built using a common folder and a temporary name combined. We use

GetSpecialFolder() to find the path name of this folder in any computer running

the application.

Getting

the date stamp of current version

To

do so, TEGetVersion(), the first building block, is used and returns a CString containing the last modification date of a given file.

//TEGetVersion returns the last modification date / time of a given file

CString

TEGetVersion (CString FileName) 

{  

         CFileStatus status1;

         CTime Time1;

         if( CFile::GetStatus( FileName, status1) )

         {        

                 Time1 = status1.m_mtime; 

                 return (Time1.Format("%d%m%M%S"));

         } 

         // Failed          return ((CString)"");

}

//

When my application starts, I store the date / time stamp of it somewhere.

TE_Options.Version=TEGetVersion((CString)__argv[0]);

// here we keep the date/time stamp of the current version

Now we need to get the date / time stamp of the file online, preferably, without having to download it first, so we only download when we need to update to a newer version.

HINTERNET FileHandle=NULL; 

WIN32_FIND_DATA ftpFileData; 

//find the file on the ftp server        

FileHandle= FtpFindFirstFile( m_ftpHandle, FTP_NEWVERSION, &ftpFileData,

INTERNET_FLAG_RELOAD, 0 );

if( NULL != FileHandle )

{

    // get the write time of the ftp file        

    SYSTEMTIME ftpFileWriteTime, stUTC1;

    FILETIME ftp;

    FileTimeToSystemTime( &ftpFileData.ftLastWriteTime, &stUTC1 );

    SystemTimeToTzSpecificLocalTime( NULL, &stUTC1, &ftpFileWriteTime );

}

We need to define how old should be the current

version in order to update it. Again, this approach can be very useful in some

cases and less useful in other. For example, if your application involves a

database, you might be interested to ensure that the database is always most

recent and never older than 3 days.

#define UPDATEEVERY  60*24*7 // 7 days #define APP_EXE_NAME "TargetEyeTest.exe"

#define FTP_NEWVERSION "NewVersion.exe"

#define APP_REGULAR_FOLDER "\\TargetEye\\"

The next step is to compare the date / time

stamp of each file.

CFileStatus

statusOld;

CTime TimeOld;

if( CFile::GetStatus( FileHandle, statusOld ) )

{

	CTime ct,OldTime;

    	OldTime=statusOld.m_mtime;

	hFindFile.GetLastWriteTime(ct);

	LONG Diff;

	ver=ct.FormatGmt("%d %m %Y %H:%M %Z");

	oldver=OldTime.FormatGmt("%d %m %Y %H:%M %Z");

 	Diff = ((CTimeSpan)(ct-OldTime)).GetTotalMinutes();

 	hFindFile.Close();

 	if (Diff>UPDATEEVERY || resultSpecific)

 	{

        // download the newer version  	}

}

Downloading the new version

Downloading the newer version is performed using TE_DownladLoad() which is listed here. We make several attempts in case there is a temporary block or communication problem.

#define FTPRETRIES 5 // number of retries BOOL TE_DownloadLoad(char *FtpFileName,char *LocalFileName)

{

     int DoTry=FTPRETRIES; 

     int result; 

     TryAgain:; 

     try 

     {

         result = MyConnection.m_FtpConn->GetFile(FtpFileName, LocalFileName, FALSE);

     }

     catch (CInternetException* pEx)

     {

        TCHAR sz[1024]; 

        pEx->GetErrorMessage(sz,1024);

        WriteToLog("Error %s\n", sz);

        if(TE_DEBUG) MessageBox(NULL,sz,"Error 6 - TE_Load",MB_OK);

        pEx->Delete(); 

     }

     if (!result)

     {

         if(DoTry-- >0) goto TryAgain;

         return(FALSE);

     }

     else     

     {

           return (TRUE);

     }

}

Now we are ready to switch between the currently running version (the

old one) with the newer one.

Executing the newer version

BOOL ExecuteNewVersion(char *ExeName,char *Param)

{

     STARTUPINFO sinfo;

     PROCESS_INFORMATION  pinfo;

     ZeroMemory(&sinfo, sizeof(sinfo));

     sinfo.cb = sizeof(sinfo); 

     sinfo.lpDesktop= "WinSta0\\Default";

     sinfo.dwFlags=STARTF_USESHOWWINDOW ;

     sinfo.wShowWindow=SW_SHOW;

     if(!CreateProcess(NULL,(char*)(LPCTSTR)((CString)(ExeName)+(CString)" "+(CString)(Param)), NULL, NULL,FALSE,NORMAL_PRIORITY_CLASS |

         CREATE_NEW_CONSOLE, NULL, NULL, &sinfo, &pinfo))

     {

           char s[256];

           sprintf(s,"Can't execute program: %s params %s",ExeName,Param);

           // ERROR LOG            TELog.LogError("Execute New Version",s,0);

           return FALSE;

     }

     else

     {                                  

           return TRUE;

     }

}

So if we put all the code together we get:

CFileStatus statusOld;

CTime TimeOld;

if(CFile::GetStatus( FileHandle, statusOld ) )

{

     CTime ct,OldTime;

     OldTime=statusOld.m_mtime;

     hFindFile.GetLastWriteTime(ct);

     LONG Diff;

     ver=ct.FormatGmt("%d %m %Y %H:%M %Z");

     oldver=OldTime.FormatGmt("%d %m %Y %H:%M %Z");

     Diff = ((CTimeSpan)(ct-OldTime)).GetTotalMinutes();

     hFindFile.Close();        

     if (Diff>UPDATEEVERY || resultSpecific)

     { 

         // downloading the newer version          if(TE_DownLoad((resultGeneric)?NEWEXESTR:NEWEXE,TEMPPLACE))

         {

              // We have successfully downloaded the newer version               if(ExecuteNewVersion(TEMPPLACE,"INSTALL"))

              {

                  // We have successfully executed the                   // newer version. Current version can now quit               }

              else

              // Failed to execute new version           }

         else

         {

            TELog.LogError("New Ftp version found","Can't download",0);

         }

     }

}

The TE_Init() function

TE_Init() is used to determine the parameters

used when application was executed (Unlike the full version of Target Eye

Monitoring System, which is much more complex, in our example, there is one

optional parameter – “INSTALL”).

if(__argc>1) 

{

     if(strcmp(__argv[1],"INSTALL")==0)

     {

        // TE_FirstTime(); -> here you can place code you wish to         //be executed only during the first run      }

}

else

// No parameters {

     // Delete a temporary version if exists at the temporary location }

Replacing old with new

In order to quit in a normal fashion, without missing anything we wish

to do before quitting, the main even loop contains a check for the value of

NeedToQuit, which would normally be FALSE.

BOOL NeedToQuit=FALSE;

When NeedToQuit becomes TRUE, the application will perform any routine

required before quitting (for example, saving unsaved work). For example:

if(NeedToQuit)

{

     if(TE_DEBUG)

         MessageBox(NULL,"Terminating Targe Eye",

           "Target Eye Monitoring System",NULL);

        return FALSE;

}

Further, the application expects to be executed either with the

“INSTALL” parameter as part of the command line, or without it. The

following scheme illustrates the flow of an installation of a newer version to

a temporary location (the Desktop folder, in our example), up to the moment the

temporary file used for it is deleted. This requires several stages:

The Target Eye Cycle

Stage

Ran

with Parameter

Ran

from location

Description

1

None

Regular

The

current (old) version is running before the newer version is available

2

The

newer version checks if there is a temporary copy of itself at the temporary

location, but there isn’t any

3

A

newer version is found

4

Newer

version is downloaded to a temporary location

5

Newer

version runs from the temporary location with the “INSTALL”

parameter.

6

Current

version quits

7

INSTALL

Temporary

The

newer application ran from the temporary location copies itself to the

regular location, replacing the old version which quitted (5)

8

The

newer version runs the copy located in the regular location and quits.

9

None

Regular

The

newer version checks if there is a temporary copy of itself at the temporary

location, and deletes it.

Choosing an FTP server for this demo

In order to use the source code that attached to this article, there are

predefined settings of a public Secured FTP server available to the public by Chilkat Software, Inc.

The details of this server are:

Secure FTP Server

Details

Type FileZilla
Address ftp.secureftp-test.com
Login Test
Password Test

There is a file there named hamlet.xml which can be used for testing a

remote file date stamp.

Internationalization

To comply with scenarios in which there

are users worldwide, and yet we wish to release a version to be available at

the same moment to all of them regardless of their local time, we use

FormatGmt

GMT is an absolute time reference and doesn’t

change regardless of the season or the location. FormatGmt is used like that:

CTime t( 1999, 3, 19, 22, 15, 0 );

// 10:15 PM March 19, 1999 CString s = t.Format( "%A, %B %d, %Y" );

ATLASSERT( s == "Friday, March 19, 1999" );

Limiting to a single instance

The mechanism described in this article can only work if we limit our application to run only once at any given moment.

To do so, several methods can be used, such as CreateMutex().

See http://support.microsoft.com/kb/243953

Target Eye Monitoring System uses a different and a bit “brutal” approach, which will be explain in details over another article. Basically, Target Eye Monitoring System searches

for other instances currently running in memory, and when found, does one of the two following options:

  1. If the instance found is newer, the current running instance quits.
  2. If the instance found is older, the current running instance kills it.

To explain, let’s consider the following

scenario. An end user had his current copy of software updated to a newer one.

A day after, this end user runs the original CD of the software. The version

that resides on the original CD, will search for new updates at the FTP server,

which is unnecessary. Further, if the application runs constantly (like a

monitoring application should), then probably when the CD version is ran, there

is also another newer version already running. To address such scenario and other

scenarios, we should take the necessary measures to ensure that an application

will always be up to date.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Michael Haephrati

 

CEO

Michael Haephrati

Israel Israel

Member

Follow on Twitter Follow on Twitter

An inventor and an expert specilizes in software development and information security, who has built a unique perspective which combines technology and the end user experience.

Mr. Haephrati has founded Target Eye on 2000. The Target Eye Monitoring software was developed since then. Before inventing Target Eye, worked on many ventures starting from HarmonySoft, designing the first Graphical Multi-lingual word processor for Amiga computer. Other ventures included: Data Cleansing (as part of the DataTune system which was implemented in:the Standards Institute of Israel, The Israeli Export Institute, Bezeq Call, Microsoft Israel, Del Technologies and Elite), developed GIS systems, Credit Scoring computerized systems. Managed a great number of software and IS projects for: Telecom, New Zealand (1994-1995), Apple, Silicon Valley (1995-1996), Israeli Police, OCR for traffic tickets pilot and license plate number identification for road cameras (1995, as part of TIS, Project Manager). During 1998-2000 has developed a credit scoring system based on geographical statistical data, participating VISA CAL, Isracard, Bank Leumi and Bank Discount (Target Scoring, being the VP Business Development of a large Israeli institute).

Member of the London Institute of Directors (since 2001)

Member of the International Association of Financial Crimes Investigators (since 1998).

Member of IACTI – THE INTERNATIONAL ASSOCIATION OF COUNTERTERRORISM INVESTIGATORS. (since 2000).

 

Rate this:   Poor  

 

Excellent


How Rashumon was developed

When I have devleoped Rashumon, there was no built in support for multi-lingual / bi-directional text and I had to develop such from scratch

Background

During 1989 to 1994 I have developed Rashumon, the first multi-lingual graphic word processor for the Amiga. Rashumon

brought some unique features:

  • Multiple Selection of text (selecting non continious parts of text simmultaneously)
  • Table generator

  • Multiple key maps support (up to 5 simultaneously)
  • Search and replace includes color, style and font filters

    Rashumon - Search / Replace

  • Multi-lingual string gadgets to be used for creating and renaming files, drawers, etc.
  • Imports and exports multilingual ASCII files from and to PC and MAC
  • Ultra fast screen updating and scrolling
  • IFF graphics support (import and export).
  • Direct access to each one of the 256 characters per each font.

Using the code

The code samples used in this article are taken from Rashumon source code, as can be examined by any C++ compiler, even though they were created for Amiga Aztec C compiler.

Points of Interest

Now days we tend to forget some of the complexeity which was part of coding 20 years ago, and today is built in as part of any Operating System SDK, among them: bi directional editing, text editing in general, scorlling text and word wrapping.

Developing a multi-lingual graphic word for the Amiga, back in 1989, required in fact, writing parts of what would be today, part of an Operating System, but was missing back then.

Developing Rashumon

The Amiga was and still is a great computer, with great capabilities, especially when it comes to sound and video. However, basic elements such as a File Browse dialog box was missing, not to mention any support for right to left languages.

Today, all operating systems contain the core elements required to support multi-lingual text editing. The text is stored by the order it was typed, and displayed backward, when it comes to a right to left language. This makes it simple and easy to edit and manipulate it, since the storage reflects to the logical order of the text flow. Back then, there was a need to develop such building block, and it was have made my word processor too slow, had I displayed the text differently than the way it is stored.

Instead, I wanted to develop a word wrapping engine of my own. Word Wrapping is the mechanism that allows breaking lines without breaking words. Unlike the old type writer, where you get to the end of the line, and sometime break a word at the middle, word processors are able to move the last typed word to the next line, in case there is not enough room for it in the current line.

That becomes even more complex when you have to deal with proportional fonts, where each character has its own width, and when you allow combining several fonts. All of the above was not part of a high level API, but required calculating the predicted length of a given text, in pixels, taking into calculation each character width based on the character and the font, attribute (bold, italics) and size used, along with the margins selected.

So even if we deal only one direction (left to right) of editing, it was still complicated to develop from scratch.

To being with, I wrote the routine to calculate a length of a given line:

int LLen=(mode==GRAPHICS)?ln->Len:(ln->CharLen=strlen(ln->Txt))*8;

As you can see, there is a simple scenario where “mode” isn’t “GRAPHICS”, then the length is calculated based on the number of characters multiplied by 8 (which is the length of each character when mono spaced font is used).

When it comes to editing bi directional text using proportional and multiple fonts, it is more complex even to insert a single character:

// This is a rountine for adding a single character, taken from Rashumon source code

static put_char(wrap,chr)

BOOL wrap;

UBYTE chr;

{

	UBYTE c[2];

	BOOL update=FALSE;

	c[1]='';

	c[0]=chr;

	if(ENGLISH_H)

	// Left to right text 	{

		if(chr>='a' && chr<='z' && map[Header->format >> 11]<2) chr=ucase(chr);

		if(Header->Insert || !HCL->Txt[CR])

		{

			if(!wrap && HCL->Len+font_width(HCL,CR)>HCL->MaxLen) return();

			char_insert(HCL,c[0],CR);

			HCL->CharLen++;

			CR++;

			HCL->Len+=font_width(HCL,CR-1);   

// Here we add the additional size to the overall line //size in pixels 
HCL->CursLen+=font_width(HCL,CR-1);

		}

		/* OVERWRITE IN ENGLISH */

		{

// Now we treat Overwrite mode 
HCL->Txt[CR]=c[0];

		HCL->Format[CR]=Header->format;

		if(c[0]==9) 

		{

			SetFlag(HCL->Format[CR],TAB);

			HCL->Txt[CR]=SPACE_CHR;

			SetFlag(MD,TABS);

		}

		CR++;

		calc_charlen(HCL);

		calc_all(HCL);

		Clear(HCL);

	}

}

else

// Hebrew ( or Right to Left) mode {

	if(!Header->Insert && CR)

	{

		CR--;

		HCL->CursLen-=font_width(HCL,CR+1);

		HCL->Txt[CR]=c[0];

		HCL->Format[CR]=Header->format;

		if(c[0]==9) 

		{

			SetFlag(HCL->Format[CR],TAB);

			HCL->Txt[CR]=SPACE_CHR;

			SetFlag(MD,TABS);

		}

		calc_all(HCL);

		Clear(HCL);

	}

	else

	{

		if(!wrap && HCL->Len+font_width(HCL,CR)>HCL->MaxLen) return();

		char_insert(HCL,c[0],CR);

		HCL->Len+=font_width(HCL,CR);

		HCL->CharLen++;

	}

}

if(HCL->Mode & TABS) calc_all(HCL);

if(c[0]!=SPACE_CHR && fonts[Header->format >> 11]->tf_YSize>LH(HCL)) 

{

	HCL->LineHeight=Header->LineHeight=fonts[Header->format >> 11]->tf_YSize;

	HCL->BaseLine=Header->BaseLine=fonts[Header->format >> 11]->tf_YSize-fonts[Header->format >> 11]->tf_Baseline;

	update=TRUE;

}

else

if(c[0]==SPACE_CHR && HCL->prev && !(HCL->Mode & PAR) && wrap)

{

	WrapLine(HCL->prev,!(update));

}

if(HCL->Len<=HCL->MaxLen && !(update))

{

	showtext(HCL);

	SetCursor();

}

else

if(wrap)

	FixLine(HCL,!update);

if(update)

	update_lh(HCL,TRUE);

}

The next step was to perform word wrap to bi-directional lines. As I have explained, the lines were displayed as they were stored in memory. The text

“abcאבג

was stored exactly like it looks. Rashumon used double byte characters, meaning that each character was stored using 2 bytes. That was before

UNICODE was invented, so the first byte was sufficient to store any character in any language supported. At that time, ASCII characters were in two forms, one form used the values from 0 to 127 and the expanded form uses values 0 to 255. I used the expanded form, and had to decide where to place the right to left languages.

There was no standard for right to left languages. IBM used places 128 to 154, but I found it problematic and chose the places starting of 224, which seems to be the right choice today, as it is identical to how right to left languages are represented today using Double Byte encoding.So if I open a floppy disk image from 1989 (.ADF file), all Rashumon Hebrew documents appear in the correct encoding.

As for the 2nd byte, it was used to store the character color (3 types, meaning up to 8 colors), font attributes (BoldItalics and underline, or any combination between the 3), language (right to left or left to right) and the font, by pointing to an index of this font within a local list created from the entire list of fonts used per each document.

/* Line structure */

#define COLOR_BIT_1 1           /* 1 */

#define COLOR_BIT_2 2           /* 2 */

#define COLOR_BIT_3 4           /* 3 */

#define UNDL 8                  /* 4 */

#define BOLD 16                 /* 5 */

#define ITAL 32                 /* 6 */

#define SELECT 64               /* 7 */

#define LANG 128                /* 8 */

#define TAB 256                 /* 9 */

#define UNUSED_1 1024           /* 10 */

#define UNUSED_2 2048           /* 11 */

#define FONT_BIT_1 4096         /* 12 */

#define FONT_BIT_2 8192         /* 13 */

#define FONT_BIT_3 16384        /* 14 */

#define FONT_BIT_4 32768        /* 15 */

#define FONT_BIT_5 65536        /* 16 */

Key Mapping and Encoding

Key Mapping was used as an array of all characters per the places starting from “1″ and to the end of the array.

Here is another part taken from Rashumon source code, where the keymaps are

defined:

/* HEBREW AND ENGLISH MAPS */

unsigned char regmap[] = ";1234567890-=\/'-˜€ˆ...\"[] 123(tm)ƒ‚‹'‰‡ŒŠ", 456 †'„Ž-š•. .789 "
unsigned char engmap[] = "`1234567890-=\qwertyuiop[] 123asdfghjkl;' 456zxcvbnm,./ .789 "; 
unsigned char shiftmap[] = "~!@#$%^&*()_+|0QWERTYUIOP{} 123ASDFGHJKL:\" 456 ZXCVBNM<>?.789 "; 
unsigned char shiftrus[] = "~!@#$%^&*()_+|0°¶₪±³¸´¨(r)¯{} 123 ²£¥¦§(c)׫:\" 456 ¹·¢µ¡­¬<>? .789 "; 
unsigned char rusmap[] = "`1234567890-=\׀ײִׁ׃״װָ־ֿ[] 123ְֳֵֶַֹֺֻׂ;'  456 ׳ֲױֱּֽ,./ .789 "; 

As you can see, “regmap” is the Hebrew encoding, “engmap” is for the Latin text, “shiftmap” is for the characters typed with SHIFT button, and there was also a keymap for Russian (and later on, one for Arabic as well, thanks to John Hajjer, from Chicago, who spent a lot of time to help me release an Arabic version).

Switching between the two directions was made using a unique ruller with two versions: left to right and right to left:

Pressing the arrow, changed the direction of editing.

Scrolling Text

Even obvious things such as scrolling had to be invented back then. That includes determining how many lines of text to display, based on the current window size (Amiga windows had the ability to be resized by the end user, as well as maximized and minimized), displaying a scroll bar, and calculating the size of the scroll bar’s gauge which should be proportional to the possible movement and the available scroll.

scroll(ln,lines)

struct Line *ln;

int

lines;

{ 

       register SHORT distance,

                     top=TOP,

                     bot=BOT;

#if

DEBUG

       printf("BEFORE: top=%ld (%ld <> TOP=%ld) ",

                     Header->top->num,

                     Header->top->y,TOP+Header->shift);

       printf("bottom=%ld (%ld <> BOT=%ld)\n",

                     Header->bottom->num,

                     Header->bottom->y+LH(Header->bottom),BOT+Header->shift);

#endif

       if(lines>0)

       {

              distance=Header->bottom->next->y+LH(Header->bottom->next)-Header->shift-Header->Height;

              Header->shift+=distance;

              while(Header->top->y<Header->shift) 

                     Header->top=Header->top->next;

              Header->bottom=Header->bottom->next;

       }

       else

       {

              distance=-(Header->shift-Header->top->prev->y);

              Header->shift+=distance;

              Header->top=Header->top->prev;

              while(Header->bottom->y+LH(Header->bottom)>Header->Height+Header->shift)

                     Header->bottom=Header->bottom->prev;

       }

       if(distance<100)

              ScrollRaster(rp,0,distance,0,TOP,640,BOT);

       else

              calc_top_bottom(TRUE,0,0);

#if

DEBUG

       printf("AFTER: top=%ld (%ld <> TOP=%ld) ",

                     Header->top->num,

                     Header->top->y,TOP+Header->shift);

       printf("bottom=%ld (%ld <> BOT=%ld)\n",

                     Header->bottom->num,

                     Header->bottom->y+LH(Header->bottom),BOT+Header->shift);

#endif

}

Word Wrapping Bi-Directional Text

But now let’s go back to the bi-directional text word wrap. Basically, the algorithm, developed by me along with Tamer Even –Zohar, and her husband Nimrod, was based on examining a given line and if it is longer than the size between the two margins (calculating the line width in pixels, taking into calculation each character, based on it’s independent attributes), we should remove the last word from it, and then check again the new length, and so on, until the line is within the width of the margins. The first question to ask is where is the “last” word? If it is a right to left paragraph, the last word will appear first, in the buffer.

In such case, I used the following function, which in fact measured the size (in pixels) of the first word in a given buffer. The following routines are based on mono space font, which is complicated enough…

/* returns the len of the first word in s */

#define BLNK(c)      ((c)==' '

|| (c)=='\n')

first_wordlen(s,margin,blnks1,blnks2)

char

*s; 

int

margin, *blnks1, *blnks2;

{

    register

    int i, j;

    for (i=margin; BLNK(s[i]) && s[i]; i++);

             *blnks1 = i;

       for (; !(BLNK(s[i])) && s[i]; i++);

       for (j=i; BLNK(s[j]) && s[i]; j++);

       *blnks2 = j-i;

       return(i);

}

If the line is a left to right one, a different function was used:

last_wordlen(s,blnks1,blnks2,maxlen)

char

*s;

int

*blnks1, *blnks2, maxlen;

{

    register int i, j;

        if (!strlen(s)) return(0);

    for (i=strlen(s)-1; BLNK(s[i]) && i; i--);

    if (i==0) return(0); 

    *blnks1 = (strlen(s)-(i+1));

    for (i=min(maxlen,strlen(s)-1); BLNK(s[i]) && i; i--);

    for (; !(BLNK(s[i])) && i; i--);

    for (j=i; BLNK(s[j]) && j; j--);

     i++; 

       *blnks2 = i - j;

       return(strlen(s)- i);

}

Of course we do not only remove the last word from a line, but also place back the first word of the next line, when there is space available (for example, if the first word in the current line is deleted, and space becomes available), so another building block would be placing the next word (from the beginning of the next line) back to the end of the current line.

/* copies first word of length len & trailing blanks blnks fron s2 to s1 */

copy_first(s1,s2,len,blnks)

char

*s1,*s2;

int

len,blnks;

{ 

       append(s2,s1,strlen(s1)+len+(blnks? 1 : 0));

       delete1(s2,0,len+(blnks ?1 : 0));

}

In Rashumon, the paragraph direction of text was automatically calculated by examining the encoding of each character in each line, and determining which direction is dominant. While brainstorming with Tamar Even Zohar and her husband Nimrod, we came to

know that even space ” ” character can have a direction, and we had to decide if we wish to have Hebrew space character in addition to the Latin one. Well, this requirement became a “must”, as it is needed to word wrap paragraphs with multiple languages combined.

For example:

This is an example of a paragraph with opposed direction languages. זוהי דוגמה לפיסקה עם שילוב של שתי שפות עם כיוונים מנוגדים”

The following clip demonstrate how bi-directional text is edited by Rashumon.

http://youtu.be/QNsqbp7mNOA  

Now, if you make change the margins, which word will “jump” to the next line, or “jump” back to the current line? The only way to determine that, is by knowing the direction of each character (either right to left or left to right), including special characters such as tabs, spaces, commas, etc. Rashumon can still be downloaded from Aminet at this link.

An Article about Rashumon (UK)

Further reading

Rashumon web site

HarmonySoft web site

My Blog (hebrew)

My Blog (English)

My Coding Blog

Inventing the AmigaHASP

During 1989, while developing Rashumon, I found myself looking for a copy protection solution for Amiga software but there wasn’t any. The Amiga had a very non standard Parallel port, which made it impossible to just adopt a copy protection dongle from other platforms, and there was a need to develop a completely new system.

Hardware based copy protection systems are based on a hardware device which interact with the computer using the port assigned to it. Back then (1989), the USB wasn’t invented yet, and the Parallel port was used. The Parallel port was the port used for printers for many years, and therefore any dongle would need to have “pass through” connector allowing the printer to be connected to it, instead to the port directly.

Hardware based copy protection requires interaction with the device through most of the connector pins, being able to read each pin’s value, and to change each pin’s value from 0 to 1 and vice versa. The Amiga didn’t provide any API to do so, which made me look for undocumented features, and code directly to the hardware instead of using any existing SDK.

With the help of Shimon Groper, the founder of Eliashim, I have made many attempts to create a dongle compatible with the unique and undocumented hardware. I used to go from Tel-Aviv to Haifa, and after several hours, leave with a box, covered with many wires in all colors, which was supposed to be the prototype… Eventually I found the way to implement the first Amiga based copy protection dongle and instead of buying bulk dongles from Aladdin Knowledge Systems, I have accepted the kind offer of Yankee Margalit, their founder and CEO, to by the Amiga product from my small software house (HarmonySoft), and after a short period of negotiation, Aladdin paid me $12,000 and my product became the AmigaHASP.

An Italian magazine published an article about the AmigaHASP:

article-about-amigahasp12.jpg?w=762

article-about-amigahasp21.jpg?w=758

 article-about-amigahasp31.jpg

article-about-amigahasp41.jpg

As part of the deal, I have trained the technical people from Aladdin about programming the Amiga, and even gave them Amiga books and magazines. We have announced the new product, and if Commodore wouldn’t have gone out of business, short after, the AmigaHASP would have probably been useful for many Amiga based software houses. I can tell from my own experience, that it was used for Rashumon, the multi lingual graphic word processor I have developed.

 

11/11/11 11:11

I wanted to write or do something on the 11/11/11 at 11:11 but had a meeting, so I created a small application and signed it at 16:16 instead…

I am using VeriSign Code Signing certificate and the date / time are taken from http://timestamp.verisign.com/scripts/timstamp.dll, which showed 16:19…

I recently have been using Visual Basic 6.0 for such 3 minutes applications, as I think VB6 is the Swiss Army Knife for such tasks. MSDN subscribers can any Microsoft product, including discontinued ones (say, Windows 3.11) , can be downloaded from their web site.

Using GoogleDocs programatically

This article explains how to develop a simple deskptop application that will be use the free and the paid GoogleDocs service of storing documents and files online, allowing the end user to drag and drop files and entire folders and by doing so, backing up the selected files to a pre-defined GoogleDocs account.

Google Docs allows users create and upload documents and files, work on them and store them online. The service is free of charge however, there are limitations which don’t exist when the paid service is used.

Google Docs Account Types

As described here, the free account type has his limitations when it comes to types of files allowed to be uploaded. These limitations applies to programatically usage of the service, as opposed to using the online interface (docs.google.com).

To lift some of the limitations, the end user should hold a Premier Account. Users of the free account can only upload  HTML, PDF, Word, Excel, PowerPoint, OpenOffice, text and images.

The Implementation

As a base, we have developed a class library named SmartHTTP.

We have defined:

#define HTTP_POST_VERB                      L"POST"
#define HTTP_CONTENT_LENGTH                 L"Content-Length"
#define HTTP_CONTENT_TYPE                   L"Content-Type"
#define HTTP_MIME_BINARY                    L"application/octet-stream"
#define HTTP_MIME_FORM_URL_ENCODED          L"application/x-www-form-urlencoded"
Connecting to the GoogleDocs server is done by using:
  BOOL Connect(LPCTSTR szServerName, INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTP_PORT);
and the call:
if (!m_internet.Connect(GOOGLE_LOGIN_SERVER, INTERNET_DEFAULT_HTTPS_PORT))
    SetWinInetError(m_internet.GetError());
  else
  {
'... here we upload the documents, etc.

GOOGLE_LOGIN_SERVER is L”www.google.com”

Basically my applicaiton is a container, to which, files and entire folders can be dragged and dropped,
causing all files within them, including files inside subfolders, to be sent to the GoogleDocs server.

When the application is started, the container appears.

Closing it, will send it to the IconTray.

Then, files and folders can be dragged, which will place them in queue for being uploaded to the server, with the exceptions that are set by Google, per your account type.

For example: .jpg files which are restricted by Google Docs…

To sum things up, the combination of what GoogleDocs has to offer and our application creates a simple, intuitive and easy to use backup system.

Automatic Recovery

15 years ago, I used to work as a manager of a team of coders. I managed a department that focused on providing information about households in various geographical areas. In most cases there was a need to program quickly applications and solutions to provide or to calculate certain data, which in many cases required long processing. Such application would normally run for several days, processing millions of records, and provide the outcome at the end of the processing.

Since computers tend to crash, restart or in many cases for many reasons someone stops the application, or even a bug causes the application to terminate, I found out that a recovery mechanism would be great.

Such mechanism would make sure that when the application is started, it first checks if it was ran before, and if so, allows it to continue from the point it was stopped. For example, if the application has processed record 144,356 and then crashed, it will continue from this record instead of starting all over.

Implementing such task is quite simple. You develop a mechanism to store enough information to allow such recovery, but not too much information to slow down the processing speed. You use this mechanism frequently enough to log as closely as possible, the last task performed before any abnormal termination, and yet, not too frequently, again, to avoid slowing down the processing speed.

Using the Registry would be your best choice for such implementation. You need to determine which pieces of information are required to be able to recover and continue from where the application stopped last time. That could be the sequential record number, and the project name, instead of storing the data processed itself. You should decide which value represents a normal completion of the task. For example, if the application runs over N number of records, you store either the current value of N, or say -1 when the task is completed. Then, when your application starts, you check this Registry key. The key might be empty; indicating the application is ran for the first time, in such case you might wish to place a “Welcome message” to the end user, explaining the automatic recovery feature. If the value is -1, you do nothing, since that indicates that the application has terminated normally. In any other case, you assume that the value read, reflects to the last record processed, so you can continue from that record.

The building block of our mechanism are:

Reading a value

Dim LastProcessedRecord As Integer
LastProcessedRecord = My.Computer.Registry.GetValue("HKEY_CURRENT_USER\Software\
MyApp", "LastProcessedRecord", -1)

Another approach would be calling GetSetting, which uses an .ini file instead of the Registry, but the result would be the same.

LastProcessedRecord  = GetSetting (App.EXEName, "RecoveryMechanism",
 "LastProcessedRecord, -1)

Writing a value

Dim LastProcessedRecord As Integer
LastProcessedRecord = CurrentProcessedRecord
My.Computer.Registry.SetValue "HKEY_CURRENT_USER\Software\MyApp", "LastProcessedRecord",

or

LastProcessedRecord = CurrentProcessedRecord
SaveSetting App.EXEName, "RecoveryMechanism", "LastProcessedRecord, LastProcessedRecord

Now, the flow would be: