本人最近正在進(jìn)行呼叫中心的座席端和服務(wù)器軟件開發(fā),座席端登錄部分是進(jìn)行提取主機(jī)信息,然后使用SOCKET提交給遠(yuǎn)程服務(wù)器,服務(wù)器再在后臺數(shù)據(jù)庫中進(jìn)行查找信息,進(jìn)行對比,看是否允許座席端主機(jī)登錄,因?yàn)槲业碾娔X中無法安裝SQLSERVER,所以 ,后臺數(shù)據(jù)庫暫時使用access小型數(shù)據(jù)庫進(jìn)行代替,到后期可以改為SQLSERVER數(shù)據(jù)庫,,我在后臺數(shù)據(jù)庫中使用了兩個表,使用WORKMARK字段將其相關(guān)聯(lián),如果在第一個表中查找到信息以后馬上會進(jìn)入第二個表中進(jìn)行查找,如果第一個表中的信息符合,就返回個客戶第二個表中的信息,因?yàn)樽酥鳈C(jī)有200多臺,為了防止幾臺主機(jī)同時進(jìn)行驗(yàn)證信息,引起沖突,我在服務(wù)器端建立多線程,利用線程進(jìn)行后臺的查詢,以及查找結(jié)果的回傳,編寫成功后進(jìn)行測試,發(fā)現(xiàn)當(dāng)一個主機(jī)連接后可以測試成功,但是,當(dāng)?shù)诙€或者以后的線程產(chǎn)生時,數(shù)據(jù)集在第一個表中可以查到正確的信息,但是進(jìn)入第二個表中時,會發(fā)現(xiàn)按照正確的查找條件進(jìn)行查找時,查到的信息都為NIL,而且經(jīng)過跟蹤,發(fā)現(xiàn),其實(shí)ADO已經(jīng)找到了正確的信息,而且已經(jīng)發(fā)送成功了,但是卻無法接收到,當(dāng)時我在論壇中發(fā)表了一個帖子尋求幫助,原貼內(nèi)容:http://expert.csdn.net/Expert/topic/1420/1420051.xml?temp=.3060114
有些朋友說可能ACCESS數(shù)據(jù)庫不提供多線程訪問,但是我聽說很多的論壇中好像都使用了ACCESS進(jìn)行后臺線程的查找的。翻看MSDN中關(guān)于COM的說明:發(fā)現(xiàn)我的線程中竟然缺少最重要的兩個函數(shù)過程
CoInitialize( nil );凡是訪問com對象都應(yīng)該使用。線程的關(guān)閉時應(yīng)該使用CoUnInitialize;否則ADO肯定出錯,我相信肯定有不少的朋友經(jīng)歷過這種問題的:
我的線程代碼如下:
unit Unitquerythread;
interface
uses
windows,SysUtils,Classes, Dialogs,ADODB,db, NMMSG, ActiveX{必須要有};
type
QueryThread = class(TThread)
PRivate
FComputerInfor,Fipaddressinfo: string; //傳遞的信息
FAdoQuery,FAdoQUserInfor:TadoQuery; //動態(tài)生成的表一,表2
Fadoconnection:Tadoconnection; //動態(tài)生成的連接
Fnmmsg,FNMretureMSG:TNMMSG;//用來發(fā)送返回信息的組件
filestream:tfilestream;
procedure ExtractInfor(TransInfo:string{自定義的從該提交信息中分離信息的過程});
procedure ShellexeQuery;
Destructor Destroy;
protected
procedure Execute; override;
public
constructor create(ComputerInfor:string;{傳輸來的計算機(jī)信息}IPaddressinfo:string{傳輸來的IP地址信息};nmmsg,NMretureMSG:TNMMSG);
end;
var
ExtractDomainUser:string; //提取出來的域用戶名
ExtractComputername:string;//提取出來的計算機(jī)名稱
ExtractDomainname:string;//提取出來的域名
strlist:Tstringlist;
IPlist:Tstringlist;//保存查找到的本工作組的ip地址信息
const defeat='defeat'; //驗(yàn)證失敗,必須注銷客戶機(jī),在數(shù)據(jù)庫中重新進(jìn)行寫入
implementation
uses Unitsendthread;
constructor QueryThread.create(ComputerInfor, IPaddressinfo: string;nmmsg,NMretureMSG:TNMMSG);
begin
CoInitialize( nil );//使用com對象必須要初始化
inherited create(false);
FreeOnTerminate:=true;
FComputerInfor:=ComputerInfor;
FIPaddressinfo:=IPaddressinfo;
IPlist:=Tstringlist.create;//創(chuàng)造ip列表
FAdoQuery:=TAdoQuery.Create(nil); //動態(tài)生成表一
FAdoQUserInfor:=TadoQuery.Create(nil);//動態(tài)生成表2
Fadoconnection:=Tadoconnection.Create(nil);//動態(tài)生成的連接
Fadoconnection.LoginPrompt :=false;
Fadoconnection.KeepConnection :=true;
Fadoquery.Connection:=Fadoconnection;
FAdoQUserInfor.Connection :=Fadoconnection;
Fadoconnection.ConnectionString :='Provider=Microsoft.Jet.OLEDB.4.0;Data Source=PersonInformation.mdb;Mode=Read;Persist Security Info=False';
Fnmmsg:=nmmsg;
FNMretureMSG:=NMretureMSG;
strlist:=Tstringlist.Create;
end;
destructor QueryThread.Destroy;
begin
FAdoQuery.FREE;
FAdoQUserInfor.FREE;
Fadoconnection.Free;
strlist.Free;
iplist.free;
CoUnInitialize;//必須使用
inherited destroy;
end;
procedure QueryThread.Execute;
begin
try
begin
extractinfor(FComputerInfor); //進(jìn)行信息提取
Synchronize(ShellexeQuery);//
end;
except
self.Terminate;
end;
end;
procedure QueryThread.ExtractInfor(TransInfo: string);//改過程進(jìn)行提取用#進(jìn)行分割//的串里的各個字符串
integer;
j:array [1..2]of integer;//用來保存分割符的位置
H,L,M,N:integer;//進(jìn)行組合字符串時的循環(huán)參數(shù)
begin
全局變量,首先清空
ExtractDomainUser:='';
ExtractDomainname:='';
ExtractComputername:='';
H:=1;
//=================================
try
begin
for i := 1 to length(TransInfo) do
if TransInfo[i]='#' then //找到了
begin
j[H]:=i;
inc(H);
continue; //跳出循環(huán)
end;
BEGIN
for L:=1 to j[1]-1 do
ExtractDomainUser:=ExtractDomainUser+TransInfo[L]; //提取的登陸域用戶名
://showmessage(extractdomainuser);
for M:=j[1]+1 to j[2]-1 do
ExtractDomainname:=ExtractDomainname+TransInfo[M];//提取的域控制器名
//showmessage(extractdomainname);
for N:=j[2]+1 to length(trim(TransInfo)) do
ExtractComputername:=ExtractComputername+Transinfo[N];//提取的計算機(jī)名
//showmessage(extractcomputername);
END;
end;//進(jìn)行異常處理
Except
// messagebox(0,'信息提取出現(xiàn)錯誤!','提示信息',mb_iconinformation);
end;
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure QueryThread.ShellexeQuery;
var
mark:string; //保存查找到的工號信息
SkillInfor:string;
RetureInformation:string; //返回個客戶端的信息
WorkGroupUserInfor:Tstringlist;
filename:string;
begin
try
begin
FAdoQuery.Close;
Fadoquery.SQL.Clear;
FAdoQuery.SQL.Add ('SELECT * From LOGON WHERE DomainUser='+'"'+ExtractDomainUser+'"'+'and DomainName='+'"'+ExtractDomainname+'"'+'and ComputerName='+'"'+ExtractComputername+'"'+'and IPaddress='+'"'+FIPaddressinfo+'"');
FAdoQuery.Prepared :=true;
FAdoQuery.active:=true;
file://=====================================================================
if (FAdoQuery.fieldbyname('WorkMark').asstring ='') or (FAdoQuery.fieldbyname('Privilege').asstring='') then //沒有查到工號信息
begin
Fnmmsg.Disconnect;
Fnmmsg.Host:=FIPaddressinfo;
Fnmmsg.Port :=6711;
Fnmmsg.FromName :='a';
Fnmmsg.PostIt(defeat); //沒有找到權(quán)限和工號則發(fā)送錯誤消息
FAdoQuery.active:=false;
end
else //查詢到了工號,和權(quán)限信息 ,從階連表中得到所有信息
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
begin
strlist.Clear;
strlist.Add(FAdoQuery.fieldbyname('WorkMark').asstring);//把工號寫入
mark:=FAdoQuery.fieldbyname('WorkMark').asstring; //將工號寫入mark臨時變量
FAdoQuery.close; //關(guān)閉第一個數(shù)據(jù)集
//===========進(jìn)入第二個表進(jìn)行查找===============================================
with FAdoQUserInfor do
begin
if active =true then close; //關(guān)閉數(shù)據(jù)集
sql.Clear;
sql.Add('Select * From WORKER Where WorkMark='+'"'+mark+'"'); //該部分可能要求具體化內(nèi)容
Prepared :=true;
open;
//連接第二個線程時這里出現(xiàn)問題
if (fieldbyname('Phone').asstring='')or (fieldbyname('Name').asstring='')or (fieldbyname('SkillLevel').asstring='')then
begin
showmessage('nil');
Fnmmsg.Disconnect;
Fnmmsg.Host:= FIPaddressinfo;
Fnmmsg.Port :=6711;
Fnmmsg.FromName :='b';
Fnmmsg.PostIt(defeat); //發(fā)送錯誤消息
close;
end
else //如果找到了電話號碼
begin
strlist.add(fieldbyname('Name').asstring); //寫入姓名
SkillInfor:=fieldbyname('SkillGroup').asstring;
strlist.add(fieldbyname('SkillGroup').asstring+'業(yè)務(wù)處理='+fieldbyname('SkillLevel').asstring); //上海市業(yè)務(wù)處理=1
strlist.Add(fieldbyname('Phone').asstring); //寫入電話號碼
RetureInformation :=strlist.Strings[0]{工號}+'#'+strlist.Strings[1]{姓名}+'#'+strlist.Strings[2]+'#'{技能信息}+strlist.strings[3]{電話號碼};
//edit; //煩會給客戶端的信息
// fieldbyname('IsLogon').asstring:='1'; //登錄成功,寫入成功標(biāo)志
// Post;
FNMretureMSG.Disconnect;
FNMretureMSG.Host :=FIPaddressinfo;
FNMretureMSG.Port:=6722; //回傳信息遠(yuǎn)程端口為6722;
FNMretureMSG.PostIt(RetureInformation); //返回成功的信息
/·······························
{進(jìn)行查詢工作組信息,然后傳送信息}
if active =true then close;
sql.clear;
SQL.Add('SELECT * FROM WORKER WHERE SkillGroup='+'"'+SkillInfor+'"');
open;
WorkGroupUserInfor:=Tstringlist.create;
while not eof do
begin
WorkGroupUserInfor.Add(fieldByName('Name').asstring ); {index0 name}
WorkGroupUserInfor.Add(fieldByName('WorkMark').asstring ); {index1 workmark}
WorkGroupUserInfor.Add(fieldByName('Phone').asstring ); {index2 Phone}
WorkGroupUserInfor.Add(fieldByName('SkillGroup').asstring +'業(yè)務(wù)處理='+fieldbyname('SkillLevel').asstring);{index3 SkillGroup}
IPlist.Add(fieldbyname('IPaddress').asstring); ://所有的ip地址保留到內(nèi)存中
//showmessage(fieldbyname('IPaddress').asstring);
iplist.SaveToFile (fieldByName('SkillGroup').asstring+'IPlst.INI');
next;
end;
WorkGroupUserInfor.SaveToFile(fieldByName('SkillGroup').asstring+'.ini');
close;
end;
end;
end;
end;
except //防止查詢失敗
begin
FAdoQuery.close;
FAdoQUserInfor.close;
end;
end;
end;
end.
注意:使用這兩個函數(shù)應(yīng)該加上activex.pas單元,否則,無法運(yùn)行。
我個人認(rèn)為,如果開發(fā)基于ADO的多線程操作,最好在每個線程中放置一個連接,而數(shù)據(jù)集和數(shù)據(jù)連接都使用動態(tài)生成,當(dāng)然你必須要注意必須要保證線程能過正常的釋放,否則你的電腦很快就快死機(jī)了,呵呵,當(dāng)然還有第二種方法,你可以生成一個數(shù)據(jù)模塊,靜態(tài)放置數(shù)據(jù)集和ADOCONNECTION組件,然后使該數(shù)據(jù)模塊不在程序開始時生成,而可以在線程中動態(tài)的生成,這種方法也可以,有興趣的朋友可以試一下二者之間的執(zhí)行效率。
而對于ADO,我認(rèn)為在開始情況下,如果不初始函數(shù)和后來的釋放函數(shù),卻不能報錯,應(yīng)該是一個BUG吧。
新聞熱點(diǎn)
疑難解答