C++解析Foxmail本地时间存储文件recvdate.ind

写给C++开发人员,研究过程当然是曲折的,自己不懂delphi导致花了近4天才研究出来,还是要多了解其他语言,他山之石可以攻玉。

Foxmail7.0往后本地邮件数据不再是类似eml格式了,要获取数据只能自己逆向。

在软件安装路径下有用户数据文件夹,里面可以找到时间文件如C:\Foxmail 7.2\Storage\xxx@163.com\Indexes\recvdate.ind。

用UE打开后取出一封邮件的ascii信息来,如下:

00000020h: A1 C2 3F AE 00 00 00 01 00 0B 3C E0 03 55 74 60 ; ÷??.....<?Ut`

PS:这个的实际时间是 2017-05-31 15:32:12

分析一下,“A1 C2 3F AE ”是标识头,“ 00 00 00 01”是邮件编号,“00 0B 3C E0 03 55 74 60”当然就是时间信息了,但是坑来了,我一开始觉得“00 0B 3C E0 03 55 74 60”按4位分开明显是一个FILETIME的高位低位,百度一下还真的有这样的写法,于是各种组合,FileTimeToSystemTime函数结果都不对。

后面怀疑是是double类型的OLD时间,但是计算结果的年份一直不对,又没进展了。

百度得知Foxmail是用delphi开发的,直到看了delphi中TDateTime的基本知识http://blog.sina.com.cn/s/blog_56592a9f010008ku.html

才知道“在delphi里TDateTime类型本质上是Double类型的,其中整数位用于表达从1899年12月30日到现在所已经过去的天数”,也就是说“ OLD 转为 字符串日期” 中间要减去“1899年12月30日”并不是常见的1900-0-0的间隔。

上面说错了不过可以参考一下,其实并不是直接减去这么简单,是要把delphi的Double日期转为windows的Double日期,详情百度“delphi里TDateTime转FILETIME”。

简单提一下我的转法:

将d整数部分+1然后拿date里面的年-1899,小数部分自己计算。

	QString qtstr = maptable->data43.toHex().toUpper();
	long qtlong = qtstr.toLong(0, 16);//年月日
	char chBuff[128];
	sprintf(chBuff, "%d.%d", qtlong, 0);//计算方式不同不可用于计算时分秒
	double d = atof(chBuff);
	d += 1;//delphi里TDateTime转FILETIME 天数差1(1899-12-30 1990-0-0)
	_variant_t GetMaliTime;
	GetMaliTime.ChangeType(VT_DATE);
	GetMaliTime = d;
	_bstr_t Btime(GetMaliTime);
	std::string strData = Btime;//3916/05/30 00:00:00
	//处理年月日
	std::string strnyr[3];
	int n = 0;
	for (int s = 0; s < strData.size();s++)
	{
		if (strData[s]=='/'){
			n++;
		}
		else if (strData[s] == '\040'){
			break;
		}
		else{
			strnyr[n] += strData[s];
		}
	}
	int iyear = atoi(strnyr[0].c_str());//delphi里TDateTime转FILETIME 年要-1899
	iyear -= 1899;
	//计算时分秒
	qtstr = maptable->data44.toHex().toUpper();
	qtlong = qtstr.toLong(0, 16);
	DWORD haomiao = qtlong;//0x020808B3 
	DWORD miao = haomiao / 1000;
	DWORD real_haomiao = haomiao - miao * 1000;//毫秒
	DWORD fen = miao / 60;
	DWORD real_miao = miao - fen * 60;//秒
	DWORD shi = fen / 60;
	DWORD real_fen = fen - shi * 60;//分

	char chtime[32] = { 0 };
	sprintf(chtime, "%04d/%02s/%02s %02d:%02d:%02d", 
		iyear, strnyr[1].c_str(), strnyr[2].c_str(), shi, real_fen, real_miao);

 

using System.Net.Sockets; using System.Net; using System.Text; using System; using System.Diagnostics; namespace 上位机 { public partial class statetext1 : Form { public statetext1() { InitializeComponent(); // 正确地初始化 Timer runtimeTimer.Interval = 1000; runtimeTimer.Tick += (s, e) => yunx(); } void ipaddr(string data) { if (this.iptext.InvokeRequired) { this.iptext.Invoke(new Action<string>(ipaddr), data); } else { this.iptext.Text = data; } } void SL(string data) { if (this.SY_TEXT.InvokeRequired) { this.SY_TEXT.Invoke(new Action<string>(SL), data); } else { this.SY_TEXT.Text = data; } } void AX(string data) { if (this.AX_TEXT.InvokeRequired) { this.AX_TEXT.Invoke(new Action<string>(AX), data); } else { this.AX_TEXT.Text = data; } } void AY(string data) { if (this.AY_TEXT.InvokeRequired) { this.AY_TEXT.Invoke(new Action<string>(AY), data); } else { this.AY_TEXT.Text = data; } } void AZ(string data) { if (this.AZ_TEXT.InvokeRequired) { this.AZ_TEXT.Invoke(new Action<string>(AZ), data); } else { this.AZ_TEXT.Text = data; } } void GX(string data) { if (this.GX_TEXT.InvokeRequired) { this.GX_TEXT.Invoke(new Action<string>(GX), data); } else { this.GX_TEXT.Text = data; } } void GY(string data) { if (this.GY_TEXT.InvokeRequired) { this.GY_TEXT.Invoke(new Action<string>(GY), data); } else { this.GY_TEXT.Text = data; } } void GZ(string data) { if (this.GZ_TEXT.InvokeRequired) { this.GZ_TEXT.Invoke(new Action<string>(GZ), data); } else { this.GZ_TEXT.Text = data; } } void TEMP(string data) { if (this.TEMP_TEXT.InvokeRequired) { this.TEMP_TEXT.Invoke(new Action<string>(TEMP), data); } else { this.TEMP_TEXT.Text = data; } } void HUM(string data) { if (this.HUM_TEXT.InvokeRequired) { this.HUM_TEXT.Invoke(new Action<string>(HUM), data); } else { this.HUM_TEXT.Text = data; } } void LIGHT(string data) { if (this.LIGHT_TEXT.InvokeRequired) { this.LIGHT_TEXT.Invoke(new Action<string>(LIGHT), data); } else { this.LIGHT_TEXT.Text = data; } } void MQA(string data) { if (this.MQA_TEXT.InvokeRequired) { this.MQA_TEXT.Invoke(new Action<string>(MQA), data); } else { this.MQA_TEXT.Text = data; } } void MQB(string data) { if (this.MQB_TEXT.InvokeRequired) { this.MQB_TEXT.Invoke(new Action<string>(MQB), data); } else { this.MQB_TEXT.Text = data; } } void FLASH(string data) { if (this.FLASH_TEXT.InvokeRequired) { this.FLASH_TEXT.Invoke(new Action<string>(FLASH), data); } else { this.FLASH_TEXT.Text = data; } } void TIME(string data) { if (this.tim_text.InvokeRequired) { this.tim_text.Invoke(new Action<string>(TIME), data); } else { this.tim_text.Text = data; } } void Msg(string msg) { if (this.Msg_Text.InvokeRequired) { this.Msg_Text.Invoke(new Action<string>(Msg), msg); } else { this.Msg_Text.Text = this.Msg_Text.Text + DateTime.Now.ToString() + " --->" + msg + "\r\n"; this.Msg_Text.SelectionStart = this.Msg_Text.TextLength - 1; this.Msg_Text.ScrollToCaret(); } } int GetIntValue(string data, string startKey, string endKey) { int start = data.IndexOf(startKey); int end = data.IndexOf(endKey); if (start == -1 || end == -1 || end <= start) return 0; start += startKey.Length; string numStr = data.Substring(start, end - start); if (int.TryParse(numStr, out int result)) { return result; } return 0; } private System.Windows.Forms.Timer runtimeTimer = new System.Windows.Forms.Timer(); Stopwatch sw = Stopwatch.StartNew(); void date_dispose(string recvDate) { if (recvDate.StartsWith("ZETA")) { try { // 用正则表达式提取字段 int ax = GetIntValue(recvDate, "X", "AY"); int ay = GetIntValue(recvDate, "AY", "AZ"); int az = GetIntValue(recvDate, "AZ", "GX"); int gx = GetIntValue(recvDate, "GX", "GY"); int gy = GetIntValue(recvDate, "GY", "GZ"); int gz = GetIntValue(recvDate, "GZ", "MQA"); int mqa = GetIntValue(recvDate, "MQA", "MQB"); int mqb = GetIntValue(recvDate, "MQB", "TEMP"); int temp = GetIntValue(recvDate, "TEMP", "HUM"); int hum = GetIntValue(recvDate, "HUM", "LIGHT"); int light = GetIntValue(recvDate, "LIGHT", "FLASH"); string flashStr = recvDate.Substring(recvDate.IndexOf("FLASH") + 5); List<byte> flashData = new List<byte>(); if (flashStr.Length > 25) { flashStr = flashStr.Substring(0, 25); } for (int i = 0; i + 2 <= flashStr.Length && flashData.Count < 8; i += 2) { string byteStr = flashStr.Substring(i, 2); byte val = Convert.ToByte(byteStr, 16); flashData.Add(val); } this.AX(ax.ToString()); this.AX(ax.ToString()); this.AY(ay.ToString()); this.AZ(az.ToString()); this.GX(gx.ToString()); this.GY(gy.ToString()); this.GZ(gz.ToString()); this.TEMP(temp.ToString()); this.HUM(hum.ToString()); this.LIGHT(light.ToString()); this.MQA(mqa.ToString()); this.MQB(mqb.ToString()); this.FLASH(flashStr); } catch (Exception ex) { // 捕获解析异常 Console.WriteLine("数据解析异常:" + ex.Message); } } } // 获取本地IP地址的方法 private string GetLocalIPAddress() { var host = Dns.GetHostEntry(Dns.GetHostName()); foreach (var ip in host.AddressList) { // 过滤出IPv4地址 if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { return ip.ToString(); } } return string.Empty; // 没有找到IPv4地址时返回空字符串 } //创建SOCKET private TcpListener server; private CancellationTokenSource serverTokenSource; // 修改 StartServer 方法以支持多客户端连接 string dqip; string dqdk; string sendCMD; UdpClient udpServer; CancellationTokenSource udpTokenSource; private bool isConnected = false; private void StartUdpServer() { int portNumber = int.Parse(port.Text); udpServer = new UdpClient(portNumber); udpTokenSource = new CancellationTokenSource(); Msg("UDP 服务已启动,等待数据中..."); string localIP = GetLocalIPAddress(); if (!string.IsNullOrEmpty(localIP)) { this.ipaddr(localIP); } long totalBytesReceived = 0; object bytesLock = new object(); // 统计速率的定时任务 Task.Run(async () => { while (!udpTokenSource.Token.IsCancellationRequested) { long bytesPerSecond = 0; lock (bytesLock) { bytesPerSecond = totalBytesReceived; totalBytesReceived = 0; } string speedText; if (bytesPerSecond < 1024) speedText = $"{bytesPerSecond} B/s"; else speedText = $"{(bytesPerSecond / 1024.0):F2} KB/s"; this.SL(speedText); await Task.Delay(1000); } }, udpTokenSource.Token); // 接收数据任务 Task.Run(async () => { try { while (!udpTokenSource.Token.IsCancellationRequested) { UdpReceiveResult result = await udpServer.ReceiveAsync(); string requestData = Encoding.ASCII.GetString(result.Buffer); // 累计接收字节数 lock (bytesLock) { totalBytesReceived += result.Buffer.Length; } if (!string.IsNullOrEmpty(requestData.Trim())) { Msg($"收到来自 {result.RemoteEndPoint} 的数据: {requestData}"); date_dispose(requestData); if (!string.IsNullOrEmpty(sendCMD)) { byte[] responseBuffer = Encoding.ASCII.GetBytes(sendCMD); await udpServer.SendAsync(responseBuffer, responseBuffer.Length, result.RemoteEndPoint); sendCMD = string.Empty; } } } } catch (ObjectDisposedException) { Msg("UDP 服务已关闭。"); } catch (Exception ex) { Msg("UDP 接收时异常:" + ex.Message); } }, udpTokenSource.Token); } void yunx() { TimeSpan ts = sw.Elapsed; string timeStr = ts.ToString(@"hh\:mm\:ss"); // 格式:00:01:23 this.TIME(timeStr); } private void Disconnect() { isConnected = false; udpTokenSource?.Cancel(); udpTokenSource?.Dispose(); udpServer?.Close(); // 注意 UDP 使用 Close() udpServer?.Dispose(); } private void button1_Click(object sender, EventArgs e) { if (isConnected) { connect.Text = "开启"; Disconnect(); sw.Stop(); // 停止计时 runtimeTimer.Stop(); // 停止定时器 isConnected = false; } else { connect.Text = "关闭"; sendCMD = string.Empty; sw.Reset(); // 清零(可选) sw.Start(); // 开始计时 runtimeTimer.Start(); // 开始定时器 StartUdpServer(); isConnected = true; } } private void label13_Click(object sender, EventArgs e) { } private void label21_Click(object sender, EventArgs e) { } private void label22_Click(object sender, EventArgs e) { } private void MQB_TEXT_Click(object sender, EventArgs e) { } private void tim_text_Click(object sender, EventArgs e) { } } }
05-31
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值