線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:4872
推到 Plurk!
推到 Facebook!

這樣寫會一直吃掉記憶體嗎?

答題得分者是:nod32
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#1 引用回覆 回覆 發表時間:2007-03-02 16:09:36 IP:60.248.xxx.xxx 訂閱
我固定每3分鐘執行一次下面這一段程式,我有觀察了它每3分鐘就會吃掉2千多k的memory
我要如何 將memory給釋放出來,有那位大大可以幫小弟解惑一下,謝謝

procedure TranSfer(OpenFileName: String);
var InputData: TStringList;
ReadRecord: TDataRecord; -->自定的record
i: Integer;
begin
InputData := TStringList.Create;
InputData.LoadFromFile(OpenFileName);
for i := 0 to (InputData.Count - 1) do
begin
if InputData.Strings[i] <> '' then begin
StrLCopy(ReadRecord.DataBuf,PChar(InputData.Strings[i]),SizeOf(ReadRecord.DataBuf)-1); --> 這一行好像會一直吃掉memory 並不會release出來
SaveToDataBase(ReadRecord);
end;
end;
InputData.Free;
end;
編輯記錄
taishyang 重新編輯於 2007-05-22 15:02:39, 註解 將文章分類成[問題]‧
zxjrainbow@21cn.com
一般會員


發表:0
回覆:3
積分:5
註冊:2002-08-11

發送簡訊給我
#2 引用回覆 回覆 發表時間:2007-03-18 17:29:08 IP:125.89.xxx.xxx 訂閱
你把buffer填上了,也不用?就放那里?总有个地方要使用的吧?
使用完后,删除它就行了。
不过你这种的使用方法有点问题啊。。
------
http://www.9ele.com
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#3 引用回覆 回覆 發表時間:2007-03-19 02:44:05 IP:211.74.xxx.xxx 訂閱
你有Free掉StringList,但其實Copy到Record的來源也是String,它也是資源之一,
在寫到DB之後把Record指向的記憶體也釋放掉吧
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
rotoyang
一般會員


發表:0
回覆:1
積分:0
註冊:2005-04-11

發送簡訊給我
#4 引用回覆 回覆 發表時間:2007-03-22 15:14:13 IP:211.72.xxx.xxx 訂閱
InputData.Free;  --> FreeAndNil(InputData);

這樣改, 看看記憶體的變化量.

P.S. 記憶體是每次都會長嗎? 沒有下來過??
------
----------------------------------------
勉強が一番大事だ。
知、力、行。
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#5 引用回覆 回覆 發表時間:2007-05-22 14:54:49 IP:60.248.xxx.xxx 訂閱

===================引 用 rotoyang 文 章===================
InputData.Free; --> FreeAndNil(InputData);

這樣改, 看看記憶體的變化量.

P.S. 記憶體是每次都會長嗎? 沒有下來過??

大大你好
原按你方法去釋放記憶體 有看到變化 會增加後會遞減
經過幾天測試 又回復原狀 記憶體不會釋放一直增加,我合理懷疑 我文章所說的那一行延伸出來的記憶體 未被釋放出

還有InputData.Free; --> FreeAndNil(InputData); 這有什麼差異嗎?
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#6 引用回覆 回覆 發表時間:2007-05-22 14:55:50 IP:60.248.xxx.xxx 訂閱

===================引 用 Coffee 文 章===================
你有Free掉StringList,但其實Copy到Record的來源也是String,它也是資源之一,
在寫到DB之後把Record指向的記憶體也釋放掉吧

大大請問要如何釋放呢?
nod32
初階會員


發表:3
回覆:29
積分:31
註冊:2007-05-21

發送簡訊給我
#7 引用回覆 回覆 發表時間:2007-05-22 16:15:46 IP:202.85.xxx.xxx 訂閱
建议: 为TDataRecord做一个指针类型。PDataRecord   

procedure TranSfer(OpenFileName: String);
var InputData: TStringList;
// ReadRecord: TDataRecord; -->自定的record
ReadRecord:PDataRecord;
i: Integer;
datalen:Integer;
begin
InputData := TStringList.Create;
InputData.LoadFromFile(OpenFileName);
New(ReadRecord);
Datalen: =sizeof(ReadRecord^.DataBuf)-1;
for i := 0 to (InputData.Count - 1) do
begin
if InputData.Strings [i] <>'' then
begin
//StrLCopy(ReadRecord.DataBuf,PChar(InputData.Strings [i]),SizeOf(ReadRecord.DataBuf)-1); --> 這一行好像會一直吃掉memory 並不會release出來
ZeroMemory(ReadRecord.DataBuf,datalen);
move(InputData.Strings[i][1],ReadRecord.DataBuf,datalen);
SaveToDataBase(ReadRecord);
end;
end;
freemem(readrecord);
InputData.Free;
end;

由于不知道你Databuf的类型,可能上面的程序不一定能正确运行,但做简单修改应当可以满足。
編輯記錄
nod32 重新編輯於 2007-05-22 16:16:22, 註解 無‧
nod32 重新編輯於 2007-05-22 16:20:24, 註解 無‧
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#8 引用回覆 回覆 發表時間:2007-05-23 11:03:23 IP:60.248.xxx.xxx 訂閱
謝謝大大
可是記憶體還是一直增加中
下為程式碼 請大大幫我看一下會是那一部份有問題 實在是找不出問題點(我個人在猜想是否在寫入資料庫那一段)
type
PDatarecord=^TDataRecord;
TDataRecord=record
case integer of
0: (
tTrande_Kind: Char;
tBrokerID: array[0..3] of Char;
tTraderName: array[0..9] of Char;
tOrderNumber0:Char;
tOrderNumber1:array[0..3] of Char;
tOrderKind: Char;
tInvestorID: array[0..6] of Char;
tTradeDate: array[0..6] of Char;
tTradeTime: array[0..7] of Char;
tStockID: array[0..5] of Char;
tBuySell: Char;
tPirce: array[0..6] of Char;
tQty: array[0..7] of Char;
tSType: Char;
tPBrokerID: array[0..3] of Char;
tOrderSeq: array[0..6] of Char;
tTradeSeq: array[0..6] of Char;
tRecEof: array[0..1] of Char;
);
1: (
DataBuf: array[0..84] of Char;
);
end;

procedure TranSfer(OpenFileName: String);
var InputData: TStringList;
// ReadRecord: TDataRecord;
ReadRecord:PDatarecord;
i, DataLen: Integer;
begin
InputData := TStringList.Create;
InputData.LoadFromFile(OpenFileName);
New(ReadRecord);
Datalen := SizeOf(ReadRecord^.DataBuf)-1;
for i := 0 to (InputData.Count - 1) do
begin
if InputData.Strings[i] <> '' then begin
// StrLCopy(ReadRecord.DataBuf,PChar(InputData.Strings[i]),SizeOf(ReadRecord.DataBuf)-1);
ZeroMemory(ReadRecord,Datalen);
Move(InputData.Strings[i][1],ReadRecord^.DataBuf,Datalen);
SaveToDataBase(ReadRecord);
// Dispose(pbyte);
// ZeroMemory(@ReadRecord,SizeOf(ReadRecord.DataBuf));
end;
end;
FreeMemory(ReadRecord);
FreeAndNil(InputData);
// InputData.Free;
end;

procedure SaveToDataBase(SaveData: PDataRecord);
var SaveSQL, ChkSQL, UpdateSQL: String;
Qry: TADOQuery;
tmp: Integer;
const ChkPrice: array[0..6] of Char = '0000.00';
ChkQty:array[0..7] of Char = '00000000';
begin
Qry := TADOQuery.Create(DataCon);
Qry.Connection := DataCon;
SaveSQL := 'Insert Into DailyTrade Values(';
SaveSQL := SaveSQL '''' MyIpm.Ipm_Today '''';
// with SaveData do
// begin
if (SaveData.tTrande_Kind = '2') and (StrToInt(SaveData.tTradeSeq) > 0) then begin //成交申報成交序號加6000號
tmp := StrToInt(String(SaveData.tTradeSeq)) 6000;
StrPCopy(SaveData.tTradeSeq, DupeString('0',7-Length(Trim(IntToStr(tmp)))) Trim(IntToStr(tmp)));
end;
if (SaveData.tTraderName = 'xxxxxxx') and (SaveData.tPBrokerID = 'xxxx') then begin
Qry.SQL.Clear;
UpdateSQL := 'Update DailyTrade Set Dt_PInvestorID = ' SaveData.tInvestorID ' ';
UpdateSQL := UpdateSQL 'Where Dt_Tday = ''' MyIpm.Ipm_Today ''' ';
UpdateSQL := UpdateSQL 'and Dt_TradeSeq = ' SaveData.tTradeSeq;
Qry.SQL.Text := UpdateSQL;
Qry.ExecSQL;
end;
if (SaveData.tBrokerID <> 'xxxx') or (SaveData.tPirce = ChkPrice) or (SaveData.tQty = ChkQty) then Exit;
case SaveData.tBuySell of
'1': SaveData.tBuySell := 'B';
'2': SaveData.tBuySell := 'S';
else SaveData.tBuySell := ' ';
end;
Qry.Close;
Qry.SQL.Clear;
ChkSQL := 'Select * From DailyTrade Where Dt_Tday = ''' MyIpm.Ipm_Today ''' ';
ChkSQL := ChkSQL 'and Dt_TradeSeq = ' SaveData.tTradeSeq ' ';
ChkSQL := ChkSQL 'and Dt_Broker = ''' SaveData.tBrokerID ''' ';
ChkSQL := ChkSQL 'and Dt_InvestorID = ' SaveData.tInvestorID ' ';
ChkSQL := ChkSQL 'and Dt_StockID = ''' SaveData.tStockID ''' ';
ChkSQL := ChkSQL 'and Dt_BuySell = ''' SaveData.tBuySell ''' ';
ChkSQL := ChkSQL 'and Dt_Price = ' SaveData.tPirce;
Qry.SQL.Text := ChkSQL;
Qry.Open; ---> 個人合理懷疑 是否這一段 會增加記憶體大小呢
if Qry.Eof then begin
case SaveData.tTrande_Kind of
'1': SaveSQL := SaveSQL ',''點選成交''';
'2': SaveSQL := SaveSQL ',''成交申報''';
else SaveSQL := SaveSQL ',''''';
end;
SaveSQL := SaveSQL ',''' SaveData.tBrokerID '''';
SaveSQL := SaveSQL ',''' SaveData.tTraderName '''';
SaveSQL := SaveSQL ',' SaveData.tInvestorID;
SaveSQL := SaveSQL ',''' SaveData.tTradeTime '''';
SaveSQL := SaveSQL ',''' SaveData.tStockID '''';
SaveSQL := SaveSQL ',''' SaveData.tBuySell '''';
SaveSQL := SaveSQL ',' SaveData.tPirce;
SaveSQL := SaveSQL ',' SaveData.tQty;
case SaveData.tSType of
'1': SaveSQL := SaveSQL ',''餘額''';
'2': SaveSQL := SaveSQL ',''逐筆''';
'3': SaveSQL := SaveSQL ',''券商自行選擇''';
else SaveSQL := SaveSQL ',''''';
end;
SaveSQL := SaveSQL ',''' SaveData.tPBrokerID '''';
SaveSQL := SaveSQL ',' SaveData.tOrderSeq;
SaveSQL := SaveSQL ',' SaveData.tTradeSeq;
SaveSQL := SaveSQL ',0,0,0,0)';
Qry.SQL.Clear;
Qry.SQL.Text := SaveSQL;
try
Qry.ExecSQL;
except
Application.MessageBox(PChar(SaveSQL), '寫入資料錯誤', MB_ICONWARNING);
end;
end;
// end;
Qry.Close;
Qry.Free;
end;
nod32
初階會員


發表:3
回覆:29
積分:31
註冊:2007-05-21

發送簡訊給我
#9 引用回覆 回覆 發表時間:2007-05-23 11:30:46 IP:123.112.xxx.xxx 訂閱
由于没有测试用例,所以我也没办法跑你的程序,不过一般来说执行Query的时候内存是会增加的,但不会一直增加。
建议程序做以下修改:
1、SaveToDataBase过程中的查询与修改分成两个query来工作。
2、不知道你用的什么数据库,如果是MS SQL或是My SQL 的话,建议把保存数据以及计算的部分转移到存储过程里去做,你现在这个做法效率可能会比较低,而且也容易出错。

有关内存泄露问题,建议你可以先不调用
SaveToDataBase过程空跑几次TranSfer试试,因为ADO这个东西虽然用起来简单,但内部细节并不是非常清楚,内存的增加也有可能是ADO在做缓存或者其它的事情,因此先把这部分抛开。
另外在使用
PDataRecord组员的时候要加^符号比较好。 比如:SaveData.tBuySell 写成 SaveData^.tBuySell。

还有一点,现在既然已经申明了
TDataRecord的指针变量,所以你的TDataRecord申明可以改成:
TDataRecord=record
tTrande_Kind: Char;
tBrokerID: array[0..3] of Char;
tTraderName: array[0..9] of Char;
tOrderNumber0:Char;
tOrderNumber1:array[0..3] of Char;
tOrderKind: Char;
tInvestorID: array[0..6] of Char;
tTradeDate: array[0..6] of Char;
tTradeTime: array[0..7] of Char;
tStockID: array[0..5] of Char;
tBuySell: Char;
tPirce: array[0..6] of Char;
tQty: array[0..7] of Char;
tSType: Char;
tPBrokerID: array[0..3] of Char;
tOrderSeq: array[0..6] of Char;
tTradeSeq: array[0..6] of Char;
tRecEof: array[0..1] of Char;
end;

因为我们有一个指针类型,可以直接向里面填数据了。
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#10 引用回覆 回覆 發表時間:2007-05-23 13:32:14 IP:60.248.xxx.xxx 訂閱
謝謝大大回覆

===================引 用 nod32 文 章===================
由于没有测试用例,所以我也没办法跑你的程序,不过一般来说执行Query的时候内存是会增加的(確定了Qry.Open記憶體一直增加),但不会一直增加(因為我固定每一段時間就去讀檔是否有新資料)
建议程序做以下修改:
1、SaveToDataBase过程中的查询与修改分成两个query来工作(拆開來還是一樣 因為是ado的問題)
2、不知道你用的什么数据库,如果是MS SQL(yes)或是My SQL 的话,建议把保存数据以及计算的部分转移到存储过程里(Cilent/Server架構嗎)去做,你现在这个做法效率可能会比较低,而且也容易出错(是有要改但要有時間.....@@時間永遠不夠用)

有关内存泄露问题,建议你可以先不调用
SaveToDataBase过程空跑几次TranSfer试试,因为ADO这个东西虽然用起来简单,但内部细节并不是非常清楚,内存的增加也有可能是ADO在做缓存或者其它的事情,因此先把这部分抛开(目前可以確定ado每open一次就會增加記憶體,這方面不知道可參考什麼資料來修改,請指導)
另外在使用
PDataRecord组员的时候要加^符号比较好。 比如:SaveData.tBuySell 写成 SaveData^.tBuySell(了解 謝謝)

还有一点,现在既然已经申明了
TDataRecord的指针变量,所以你的TDataRecord申明可以改成:
TDataRecord=record
tTrande_Kind: Char;
tBrokerID: array[0..3] of Char;
tTraderName: array[0..9] of Char;
tOrderNumber0:Char;
tOrderNumber1:array[0..3] of Char;
tOrderKind: Char;
tInvestorID: array[0..6] of Char;
tTradeDate: array[0..6] of Char;
tTradeTime: array[0..7] of Char;
tStockID: array[0..5] of Char;
tBuySell: Char;
tPirce: array[0..6] of Char;
tQty: array[0..7] of Char;
tSType: Char;
tPBrokerID: array[0..3] of Char;
tOrderSeq: array[0..6] of Char;
tTradeSeq: array[0..6] of Char;
tRecEof: array[0..1] of Char;
end;

因为我们有一个指针类型,可以直接向里面填数据了。
這2個的數值好奇怪Datalen := SizeOf(ReadRecord^.DataBuf)-1; ----> Datalen = 84
Datalen := SizeOf(ReadRecord^)-1; -----> Datalen = 85
Stallion
版主


發表:52
回覆:1600
積分:1995
註冊:2004-09-15

發送簡訊給我
#11 引用回覆 回覆 發表時間:2007-05-23 22:06:51 IP:211.22.xxx.xxx 未訂閱
以New要來的記憶體,必須以Dispose來歸還,以GetMem要來的記憶體,要以FreeMem來歸還記憶體。
<textarea cols="60" rows="10" class="delphi" name="code"> procedure TranSfer(OpenFileName: String); var InputData: TStringList; // ReadRecord: TDataRecord; ReadRecord:PDatarecord; i, DataLen: Integer; begin InputData := TStringList.Create; InputData.LoadFromFile(OpenFileName); New(ReadRecord); //配置一個指向DataRecord指標的記憶體空間,不用時應該 Dispose(ReadRecord) Datalen := SizeOf(ReadRecord^.DataBuf)-1; for i := 0 to (InputData.Count - 1) do begin if InputData.Strings[i] <> '' then begin // StrLCopy(ReadRecord.DataBuf,PChar(InputData.Strings[i]),SizeOf(ReadRecord.DataBuf)-1); ZeroMemory(ReadRecord,Datalen); Move(InputData.Strings[i][1],ReadRecord^.DataBuf,Datalen); SaveToDataBase(ReadRecord); // Dispose(pbyte); // ZeroMemory(@ReadRecord,SizeOf(ReadRecord.DataBuf)); end; end; FreeMemory(ReadRecord); //這個函數我查過不在VCL也不在WINAPI,不知道是作啥的,但看起來是要釋放之前要來的記憶體。 FreeAndNil(InputData); // InputData.Free; end; </textarea>
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#12 引用回覆 回覆 發表時間:2007-05-24 08:35:13 IP:60.248.xxx.xxx 訂閱
謝謝 Stallion 大大回覆

目前如以單跑不寫入DB 在DEBUG追蹤狀態下 問題出在TADOQuery.Open 不知道為什麼ADO會一直增加Memory

===================引 用 Stallion 文 章===================
以New要來的記憶體,必須以Dispose來歸還,以GetMem要來的記憶體,要以FreeMem來歸還記憶體。
<textarea class="delphi" rows="10" cols="60" name="code"> procedure TranSfer(OpenFileName: String); var InputData: TStringList; // ReadRecord: TDataRecord; ReadRecord:PDatarecord; i, DataLen: Integer; begin InputData := TStringList.Create; InputData.LoadFromFile(OpenFileName); New(ReadRecord); //配置一個指向DataRecord指標的記憶體空間,不用時應該 Dispose(ReadRecord) Datalen := SizeOf(ReadRecord^.DataBuf)-1; for i := 0 to (InputData.Count - 1) do begin if InputData.Strings[i] <> '' then begin // StrLCopy(ReadRecord.DataBuf,PChar(InputData.Strings[i]),SizeOf(ReadRecord.DataBuf)-1); ZeroMemory(ReadRecord,Datalen); Move(InputData.Strings[i][1],ReadRecord^.DataBuf,Datalen); SaveToDataBase(ReadRecord); // Dispose(pbyte); // ZeroMemory(@ReadRecord,SizeOf(ReadRecord.DataBuf)); end; end; FreeMemory(ReadRecord); //這個函數我查過不在VCL也不在WINAPI,不知道是作啥的,但看起來是要釋放之前要來的記憶體。 FreeAndNil(InputData); // InputData.Free; end; </textarea>
corey
一般會員


發表:34
回覆:44
積分:14
註冊:2003-04-19

發送簡訊給我
#13 引用回覆 回覆 發表時間:2007-05-24 10:41:10 IP:60.248.xxx.xxx 訂閱
目前解決方案 如下程式碼 可維持Memory 不會持續增加
個人覺的好奇怪 在SaveToDataBase 也有正常釋放 和現在寫法 有什麼不同?
Qry := TADOQuery.Create(Nil);
UQry := TADOQuery.Create(Nil);
Qry.Connection := DataCon;
UQry.Connection := DataCon;
InputData := TStringList.Create;
InputData.LoadFromFile(OpenFileName);
New(ReadRecord);
Datalen := SizeOf(ReadRecord^.DataBuf)-1;
// Datalen := SizeOf(ReadRecord^)-1;
for i := 0 to (InputData.Count - 1) do
begin
if InputData.Strings[i] <> '' then begin
ZeroMemory(ReadRecord,Datalen);
Move(InputData.Strings[i][1],ReadRecord^.DataBuf,Datalen);
SaveToDataBase(ReadRecord,Qry,UQry);
end;
end;
Dispose(ReadRecord);
FreeAndNil(InputData);
FreeAndNil(Qry);
FreeAndNil(UQry);
編輯記錄
corey 重新編輯於 2007-05-24 10:44:53, 註解 無‧
corey 重新編輯於 2007-05-24 10:45:47, 註解 無‧
Mickey
版主


發表:77
回覆:1882
積分:1390
註冊:2002-12-11

發送簡訊給我
#14 引用回覆 回覆 發表時間:2007-05-24 15:43:49 IP:218.163.xxx.xxx 訂閱
你好:

個人覺得沒啥錯誤會造成 Memory Leakage...就算有...也不是你造成的...哈哈

據我所知 Memory 並不是 Free 就會"吐"出來...Free只是告訴 OS...這愧記憶體...可用

要 OS...重新整理 Memory 才會"吐"出來...至於何時 OS 才會重整...這...我也不知道

但是可以用騙的..."手動"讓 OS...重整 Memory...

偷一段 ddy 的東西...嘿嘿嘿

SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);

其細部原理請自己找找 KTop...我記得 ddy 大大說得很清楚.

另外, 個人習慣用 packed record 例如 :
TSHead = packed record
StreamType : Char;
Name : array[0..10] of char;
StreamSize : DWord;
end;
編輯記錄
Mickey 重新編輯於 2007-05-24 15:51:08, 註解 無‧
nod32
初階會員


發表:3
回覆:29
積分:31
註冊:2007-05-21

發送簡訊給我
#15 引用回覆 回覆 發表時間:2007-05-28 13:54:15 IP:123.112.xxx.xxx 訂閱
:) 不知道有什么区别,可能主要还是ADO内部的一些细节处理不同所致。另外建议每次处理完成后把ADOconnection也做一次Close试试,有可能也能把内存里的缓存清掉,但这并不一定很有效率。另外有些内存泄露检测工具可以用,不过我没用过,另外你可以反复运行100次,或1000次,再或者找台机器不停的运行看一下效果,因为有ADO的关系,那些内存虽然没有被释放,很可能是ADO的缓存,或是ADO自己处理的某些生存周期,因此短时间,很少的执行量的情况下很难测试出问题:D

不过解决问题就好,你下面的代码还要记得加上异常处理,要不一但出错,那些内存就释不出来了。
===================引 用 corey 文 章===================
目前解決方案 如下程式碼 可維持Memory 不會持續增加
個人覺的好奇怪 在SaveToDataBase 也有正常釋放 和現在寫法 有什麼不同?
Qry := TADOQuery.Create(Nil);
UQry := TADOQuery.Create(Nil);
Qry.Connection := DataCon;
UQry.Connection := DataCon;
InputData := TStringList.Create;
InputData.LoadFromFile(OpenFileName);
New(ReadRecord);
Datalen := SizeOf(ReadRecord^.DataBuf)-1;
// Datalen := SizeOf(ReadRecord^)-1;
for i := 0 to (InputData.Count - 1) do
begin
if InputData.Strings[i] <> '' then begin
ZeroMemory(ReadRecord,Datalen);
Move(InputData.Strings[i][1],ReadRecord^.DataBuf,Datalen);
SaveToDataBase(ReadRecord,Qry,UQry);
end;
end;
Dispose(ReadRecord);
FreeAndNil(InputData);
FreeAndNil(Qry);
FreeAndNil(UQry);
系統時間:2024-07-01 9:33:37
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!