做为一个共享软件开发者,软件发布出去以后,我们很关心用户使用软件的情况--到底有多少人在用我的软件?是否常用?什么时候下载的?什么时候开始用的?用了多久了?用户忠诚度如何?
这些数据如何获得呢?从软件下载站或者自己的网站,我们只能获得软件的下载次数以及网站的访问统计数据。更详细的数据需要我们的软件提供。
很多数据我们自己的软件当然能够获得,只是,软件什么时候向我们提交这些数据呢?很自然的,我想到了软件自动更新。我的软件提供自动更新功能,软件会定期访问我的服务器检测是否存在新版本的程序,于是可以在检测新版程序的同时,向服务器提交软件的一些我们所关心数据(当然,不能包含用户的隐私数据),这些数据可以包括:用户软件的当前版本,用户使用软件的次数以及软件的安装时间,用户唯一ID(为了跟踪某个用户的长期的使用情况,而为每个用户分配一个唯一的ID号)。
上面这些数据的获取是没有什么问题。但是我还想知道用户是从哪里下载的软件?用户从官方下载软件时,是从哪个网站连入软件官网的?用户下载软件的时候是什么时候?这些数据的获取就比较麻烦了--要实现这个功能,需要实现,每次下载软件时,下载的文件内容会不同(下载的文件要包含用户的下载时间,入站地址等信息)。我的想法是,使用php(或别的动态网页技术)来处理软件安装程序的下载请求,然后通过php(或别的动态网页技术)控制软件安装程序的下载内容,在文件最后,附加上下载时间,入站地址等信息。下面是我现实此功能的php程序的伪代码:
- clearstatcache();
- if (!file_exists($file))
- {
- //文件不存在
- die("file not existed!");
- }
- $len = filesize($file);
- $filename = basename($file);
- $ctype = "application/octet-stream";
- $externInfo = "";
- //额外信息起始位置标识
- $externInfoFlag = '****ExternInfo****-=Flystarsoft=-{F3A1A1AE-80CA-4bd8-B5E3-457E7DE9AB77}&1j_123ajJ*jdfaa83a:' . "/r/n";
- $downloadTime = time();
- $remote_addr = "0.0.0.0";
- if (isset($_SERVER["REMOTE_ADDR"]))
- {
- $remote_addr = $_SERVER["REMOTE_ADDR"];
- }
- $downloadId = md5("md5" . $this->m_downloadSite . $downloadTime . $remote_addr . rand() . rand());//生成唯一下载ID
- $externInfo = $externInfoFlag.
- "DownloadSite=" . $this->m_downloadSite . "/r/n" .
- "DownloadId=" . $downloadId . "/r/n" .
- "DownloadTime=" . $downloadTime . "/r/n/r/n/r/n";
- //说明:这面的DownloadSite是指软件的入站地址,用户从Google搜索到我的软件后,通过Google进入我们
- // 软件并下载了我的软件,这时所要附加的额外信息中DownloadSite将为“www.Google.com”。
- //在额外信息后面增加空白符,使得整个额外信息的长度为512字节
- $addSpaces = 512 - strlen($externInfo);
- for ($i = 0; $i < $addSpaces; $i++)
- {
- $externInfo = $externInfo . " ";
- }
- $len = $len + strlen($externInfo);
- @ob_end_clean();
- @set_time_limit(0);
- //输出文件下载的HTTP头
- header("Cache-Control: public, must-revalidate");
- header('Cache-Control: pre-check=0, post-check=0, max-age=0');
- header("Pragma: no-cache");
- header("Expires: 0");
- header("Content-Description: File Transfer");
- header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
- header("Content-Type: " . $ctype);
- header("Content-Length: ".(string)$len);
- header('Content-Disposition: attachment; filename="'.$filename.'"');
- header("Content-Transfer-Encoding: binary/n");
- //发送文件内容
- @readfile($file);
- //发送附加额外信息
- @print($externInfo);
这样,所有从官网上下载的软件安装程序,都会在其后而增加我们所需要的附加信息。那么,我们如何获取到这些信息的?要取得这些信息,只能在安装程序运行时获取,大家都知道InnoSetup可以提供DLL按扩展其功能,而我的软件本来就是用InnoSetup来制作安装程序的,所以我自然的就想到了,利用InnoSetup调用DLL,读取当前运行的安装程序文件的最后一段数据,并通过额外信息起位位置标识来读取下载安装程序时在其最后增加的额外信息,然后再将这些信息保存下后,以后每个进行软件更新检测时,就可以附加这些信息。大家还可以交流一下,看还能提供哪些有用信息。有了这些信息后,就可以更好的得到软件用户使用这款软件的情况了。
下面是由InnoSetup调用的,读取保存在安装程序中额外信息的C++伪代码:
- //取当前进程的父进程
- //注:对InnoSetup而言,装载DLL的父进程的程序才是原来的安装程序(Setup.exe),
- // 当前进程是从安装程序中解压出来的一个安装过程控制程序
- DWORD parentProcessId = GetParentProcessId(GetCurrentProcessId);
- //取当前程序的父进程(即安装程序)的可执行程序文件名
- CCharString setupExecFilename = GetProcessModuleFilename(parentProcessId);
- FILE *fp = fopen(setupExecFilename, "rb");
- if (fp == NULL)
- {
- return FALSE;
- }
- //提取文件的最后512字节
- const int LAST_BYTES = 512
- if (fseek(fp, -LAST_BYTES, SEEK_END) != 0)
- {
- //提取失败
- fclose (fp);
- return FALSE;
- }
- CMyString lastInformats;
- if (fread(lastInformats.GetBufferAsLength(LAST_BYTES), 1, LAST_BYTES, fp)
- != LAST_BYTES)
- {
- //提取失败
- fclose(fp);
- return FALSE;
- }
- lastInformats.ReleaseBuffer(LAST_BYTES);
- fclose(fp);
- fp = NULL;
- //确定额外信息的位置
- int infoPos = lastInformats.Find(LOCATE_FLAG);
- if (infoPos <= 0)
- {
- //无额外信息(认为获取到空的额外信息)
- return TRUE;
- }
- //分离额外信息
- CMyString externInfo = lastInformats.Mid(infoPos + strlen(LOCATE_FLAG));
- downloadSite = ReadExternInfo(externInfo, "DownloadSite", "");
- downloadId = ReadExternInfo(externInfo, "DownloadId", "");
- downloadTime = TimetToString((time_t)strtoul(GetResultValue(externInfo, "DownloadTime", "0"), NULL, 10));
- //写入注册表
- WriteToRegistry("downloadSite", downloadSite);
- WriteToRegistry("downloadId", downloadId);
- WriteToRegistry("downloadTime", downloadTime);
目前,我已经在我的软件中使用上述方法跟踪软件的下载和使用请求,目前感觉效果还比较理想。当然,这里面还有一个问题,就是有些下载站会从我的软件官方上下载程序,然后放到他们的网站上给用户下载,这样,所有这些用户的下载时间、下载站点都将完全相同,不过好在有一个唯一下载ID,根据这个标识可以很容易的分辨这些用户,在进行数据统计时,直接忽略掉这些数据即可。
目前,我使用这个方法时间也不长,等过段时间再和大家交流一下这种方法的使用效果吧。 :D