美萍网管大师是一款不错的网吧管理软件,但是其数据库却不是美萍惯用的Acess。这让需要从美萍存取数据变得异常的困难。
经笔者研究,网管大师主要有以下两个数据文件:scon.rec(保存用户上网消费记录),member81.cfg(记录用户信息)。其中scon.rec官方有提供数据记录格式。格式如下:
Type SconRecFile=Record
RecType:Integer; //此项记录的类型(0计时 1限时 2会员 3通宵)
RecDate:TDateTime; //此项记录的日期
BeginTime:TDateTime; //上机开始时间
EndTime:TDateTime; //上机结束时间
RecMinTime:Integer; //上机总用时(以分钟为单位)
ComputerNum:Integer; //此机机号
Price:Real; //费率
Money1:Real; //上机费用 负值代表费用转移了
Money2:Real; //附加费用 负值代表费用转移了
Money3:Real; //实收金额 负值代表费用转移了
Manager:String[16]; //管理者名字
Username:String[16]; //使用者名字
Memo:String[100]; //备注
End;
根据以上数据,用Delphi读取数据时错误,发现以上格式有误。通过UE查看scon.rec发现每个数据包是196byte,但是依据以上的数据结构计算出来会占用(Integer:4Byte,TDateTime:8Byte,Real:8Byte;String[16]:是短字串,共17byte,第一个byte保存字串长度,后面16byte保存字串内容,string[100]类似,以上数据结构总长度:203Byte。也许是版本原因造成的差异)。经过仔细分析,发现数据结构应该是这样的:
Type SconRecFile = packed Record
RecType:Integer; //此项记录的类型(0计时 1限时 2会员 3通宵)
RecDate:TDateTime; //此项记录的日期
BeginTime:TDateTime; //上机开始时间
EndTime:TDateTime; //上机结束时间
RecMinTime:Integer; //上机总用时(以分钟为单位)
ComputerNum:Integer; //此机机号
Price:real48; //费率
Money1:real48; //上机费用 负值代表费用转移了
Money2:real48; //附加费用 负值代表费用转移了
Money3:real48; //实收金额 负值代表费用转移了
Manager:String[16]; //管理者名字
Username:String[16]; //使用者名字
Memo:String[101]; //备注
end;
差异如以上红字部分所示。(real48占用6byte)
根据以上数据结构,用Delphi写如下的代码便可读取scon.rec中的数据出来。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Edit1: TEdit; Label1: TLabel; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; SconRecFile = packed Record RecType:Integer; //此项记录的类型 RecDate:TDateTime; //此项记录的日期 BeginTime:TDateTime; //上机开始时间 EndTime:TDateTime; //上机结束时间 RecMinTime:Integer; //上机总用时(以分钟为单位) ComputerNum:Integer; //此机机号 Price:real48; //费率 Money1:real48; //上机费用 负值代表费用转移了 Money2:real48; //附加费用 负值代表费用转移了 Money3:real48; //实收金额 负值代表费用转移了 Manager:String[16]; //管理者名字 Username:String[16]; //使用者名字 Memo:String[101]; //备注 end; var Form1: TForm1; F:file of SconRecFile; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var TestType:SconRecFile; i:LongInt; begin AssignFile(F,'scon.rec'); Reset(F); Memo1.Lines.Clear; while not eof(F) do begin Read(F,TestType); Memo1.Lines.Add('Filepos:'+inttostr(FilePos(f))); Memo1.Lines.Add('记录类型:'+IntToStr(TestType.RecType)); Memo1.Lines.Add('记录日期:'+dateToStr(TestType.RecDate)); Memo1.Lines.Add('上机开始时间:'+TimeToStr(TestType.BeginTime)); Memo1.Lines.Add('上机结束时间:'+TimeToStr(TestType.EndTime)); Memo1.Lines.Add('上机总用时:'+IntToStr(TestType.RecMinTime)+'分钟'); Memo1.Lines.Add('此机机号:'+IntToStr(TestType.ComputerNum)); Memo1.Lines.Add('费率:'+FormatFloat('0.00',TestType.Price)); Memo1.Lines.Add('上机费用:'+FormatFloat('0.00',TestType.Money1)); Memo1.Lines.Add('附加费用:'+FormatFloat('0.00',TestType.Money2)); Memo1.Lines.Add('实收金额:'+FormatFloat('0.00',TestType.Money3)); Memo1.Lines.Add('管理者名字:'+TestType.Manager); Memo1.Lines.Add('使用者名字:'+TestType.Username); Memo1.Lines.Add('备注:'+TestType.Memo); Memo1.Lines.Add('-------------------------------'); end; CloseFile(F); end; end.
可以依次数据格式做统计。
另外一个重要的数据表是用户信息。记录在member81.cfg文件中,官方没有公布此数据的格式,但是经过笔者用UE等工具仔细分析,还是发现了一些端倪。此文件也是无头文件,里面全部记录的都是data。其数据结构如下:
MemRecFile = packed Record
Usercode:string[20]; //用户名
Userpw:string[20]; //密码(长度猜测,不一定对)
other:array[0..29] of char; //其它信息1
osamt:single; //余额
sumamt:single; //累计金额
Memo:array [0..283] of char; //其它信息2
end;
因为时间所限,只将比较重要的信息(用户,密码,余额,总金额)的位置找出,其它信息相信也可轻松的找出来。
其中密码是加密的,其它信息估计都是是明文的。
根据以上结构,笔者写了以下的存取(读取/修改)此文件的代码:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } function getpassword(crystr:string):string; public { Public declarations } end; MemRecFile = packed Record Usercode:string[20]; //用户名 Userpw:string[20]; //密码 other:array[0..29] of char; //未知信息 osamt:single; //余额 sumamt:single; //累计金额 Memo:array [0..283] of char; //其他信息 end; var Form1: TForm1; F:file of MemRecFile; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var TestType:MemRecFile; begin AssignFile(F,'member81.cfg'); Reset(F); Seek(F,500); Memo1.Lines.Clear; while not eof(F) do begin Read(F,TestType); Memo1.Lines.Add('Filepos:'+inttostr(FilePos(f))); Memo1.Lines.Add('使用者:' +TestType.Usercode); Memo1.Lines.Add('密码:' +getpassword(TestType.Userpw)); Memo1.Lines.Add('当前余额:' +formatfloat('0.00',TestType.osamt)); Memo1.Lines.Add('累计金额:' +formatfloat('0.00',TestType.sumamt)); Memo1.Lines.Add('-------------------------------'); end; CloseFile(F); end; procedure TForm1.Button2Click(Sender: TObject); var TestType:MemRecFile; begin //示例:修改某一笔数据的金额(充值30元) AssignFile(F,'member81.cfg'); Reset(F); Seek(F,555); Read(F,TestType); Seek(F,555); TestType.osamt:=TestType.osamt + 30; TestType.sumamt:=TestType.sumamt + 30; Write(F,TestType); CloseFile(F); end; function TForm1.getpassword(crystr:string):string; var i:integer; mychr: char; begin result:=''; for i:=1 to length(crystr) do begin case crystr[1] of #32:mychr:='i'; '1':mychr:='x'; '2':mychr:='{'; '3':mychr:='z'; '4':mychr:='}'; '5':mychr:='|'; '6':mychr:=chr(127); '7':mychr:='~'; '8':mychr:='q'; '9':mychr:='p'; '0':mychr:='y'; 'a':mychr:='('; 'b':mychr:='+'; 'c':mychr:='*'; 'd':mychr:='-'; 'e':mychr:=','; 'f':mychr:='/'; 'g':mychr:='.'; 'h':mychr:='!'; 'i':mychr:=#32; 'j':mychr:='#'; 'k':mychr:='"'; 'l':mychr:='%'; 'm':mychr:='$'; 'n':mychr:=''''; 'o':mychr:='&'; 'p':mychr:='9'; 'q':mychr:='8'; 'r':mychr:=';'; 's':mychr:=':'; 't':mychr:='='; 'u':mychr:='<'; 'v':mychr:='?'; 'w':mychr:='>'; 'x':mychr:='1'; 'y':mychr:='0'; 'z':mychr:='3'; '`':mychr:=')'; '{':mychr:='2'; '}':mychr:='4'; '|':mychr:='5'; chr(127):mychr:='6'; '~':mychr:='7'; '(':mychr:='a'; '+':mychr:='b'; '*':mychr:='c'; '-':mychr:='d'; ',':mychr:='e'; '/':mychr:='f'; '!':mychr:='h'; '#':mychr:='j'; '"':mychr:='k'; '%':mychr:='l'; '$':mychr:='m'; '''':mychr:='n'; '&':mychr:='o'; ';':mychr:='r'; ':':mychr:='s'; '=':mychr:='t'; '<':mychr:='u'; '?':mychr:='v'; '>':mychr:='w'; ')':mychr:='`'; else mychr:='X' end; result := result +mychr; end; end; end.
以上仅抛转引玉,错误在所难免,仅供参考。