最近在写一套系统,用了ADO,想增加一些功能,改写ADOQuery控件,再系统调试时发现内存泄漏,烦请各位前辈帮忙修改。
控件完整源码如下:
unit ADOMacQuery;interfaceuses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Db, ADODB, DBTables;type
  TADOMacQuery = class(TADOQuery)
  private
  FKeyField: string;
  FMacParams: TParams;
  FMacSQL: TStrings;
  procedure SetMacParams(Value: TParams);
  procedure SetMacSQL(Value: TStrings);
  procedure MacSQLChanged(Sender: TObject);
  protected
  procedure ExpandMacs; dynamic;
  procedure OpenCursor(InfoQuery: Boolean); override;
  public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
  function ParamByN(const Value: string): TParam;
  procedure ExecSQL;
  procedure ReQuery();
  published
  property SQL: Tstrings read FMacSQL write SetMacSQL;
  property Parameters: TParams read FMacParams write SetMacParams;
  property KeyField: string read FKeyField write FKeyField;
  end;procedure Register;implementationprocedure Register;
begin
  RegisterComponents('SelfControl', [TADOMacQuery]);
end;constructor TADOMacQuery.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);  FMacSQL := TStringList.Create;
  TStringList(FMacSQL).OnChange := MacSQLChanged;
  FMacParams := TParams.Create(self);
  ParamCheck := False;
end;destructor TADOMacQuery.Destroy;
begin
  FreeAndNil(FMacSQL);
  FreeAndNil(FMacParams);
  inherited Destroy;
  //FMacParams.Free;
end;procedure TADOMacQuery.SetMacParams(Value: TParams);
begin
  FMacParams.AssignValues(Value);
end;function TADOMacQuery.ParamByN(const Value: string): TParam;
begin
  Result := FMacParams.ParamByName(Value);
end;procedure TADOMacQuery.SetMacSQL(Value: TStrings);
begin
  if FMacSQL.Text = Value.Text then
  Exit; // Disconnect;
  FMacSQL.BeginUpdate;
  try
  FMacSQL.Assign(Value);
  finally
  FMacSQL.EndUpdate;
  end;
end;procedure TADOMacQuery.MacSQLChanged(Sender: TObject);
var
  List: TParams;
  i: integer;
begin
  if (csReading in ComponentState) then
  Exit;  List := TParams.Create(Self);
  try
  {FText :=}List.ParseSQL(FMacSQL.Text, True);
  List.AssignValues(FMacParams);
  for i := List.Count - 1 downto 0 do
  if List[i].DataType = ftUnknown then
  begin
  List[i].DataType := ftString;
  List[i].AsString := '';
  end;
  FMacParams.Clear;
  FMacParams.Assign(List);
  finally
  List.Free;
  end;
end;procedure TADOMacQuery.OpenCursor(InfoQuery: Boolean);
begin
  ExpandMacs;
  inherited OpenCursor(InfoQuery);{{*************地址指向这里***************}}
end;procedure TADOMacQuery.ExecSQL;
begin
  ExpandMacs;
  inherited ExecSQL;
end;procedure TADOMacQuery.ExpandMacs;
var
  i: integer;
  tsSQL, tsValue: string;
begin
  tsSQL := FMacSQL.GetText(); {*************地址指向这里***************}
  for i := FMacParams.Count - 1 downto 0 do
  begin
  case FMacParams[i].DataType of
  ftDate: tsValue := '''' + FormatDatetime('yyyy-mm-dd',
  FMacParams[i].AsDate) + '''';
  else
  tsValue := FMacParams[i].AsString;
  end;
  tsSQL := StringReplace(tsSQL, ':' + FMacParams[i].Name
  , tsValue, [rfReplaceAll, rfIgnoreCase]);
  end;
  inherited SQL.SetText(PChar(tsSQL));
end;procedure TADOMacQuery.ReQuery();
var
  SavePlace: TBookMark;
  KeyValue: Variant;
begin
  //浏览窗口刷新数据,并恢复记录指针
  SavePlace := nil;
  DisableControls;
  try
  if FKeyField = 'RecNo' then
  begin
  if not IsEmpty then
  SavePlace := GetBook;
  end
  else if FKeyField <> '' then
  KeyValue := FieldByName(FKeyField).Value;
  Close;
  Open;
  if FKeyField = 'RecNo' then
  begin
  if SavePlace <> nil then
  begin
  try
  if BookValid(SavePlace) then
  GotoBook(SavePlace);
  finally
  FreeBook(SavePlace);
  end;
  end;
  end
  else if FKeyField <> '' then
  Locate(FKeyField, KeyValue, []);
  finally
  EnableControls;
  end;
end;end.
泄漏情况:
一个内存块已泄露. 大小是: 36该内存块分配于线程 0x1714, 当时的堆栈跟踪(返回地址):  
402E4C  
40E45D  
40E499  
423AF9  
4C3FE3  
4C3FAE  
48E407  
48E252  
4C43BB  
44AD0A  
44108D  该内存块当前被用于一个属于以下类的对象: 未知

解决方案 »

  1.   


    procedure TADOMacQuery.MacSQLChanged(Sender: TObject);
    var
      List: TParams;
      i: integer;
    begin
      if (csReading in ComponentState) then
      Exit;  List := TParams.Create(Self);
      try
      {FText :=}List.ParseSQL(FMacSQL.Text, True);
      List.AssignValues(FMacParams);
      for i := List.Count - 1 downto 0 do
      if List[i].DataType = ftUnknown then
      begin
      List[i].DataType := ftString;
      List[i].AsString := '';
      end;
      FMacParams.Clear;
      FMacParams.Assign(List);         //这里改下  FMacParams.AssignValues(List);
      finally
      List.Free;
      end;
    end;
      

  2.   

    谢谢回复,问题好像不是出在哪里,刚才测试了下,问题还是存在
    跟踪下来好像是 tsSQL := FMacSQL.GetText(); 和
    inherited OpenCursor(InfoQuery);这两地方存在问题。想了很久还是没有头绪!
      

  3.   

    查了很多资料,原来是FMacSQL.GetText(),没有释放!
      

  4.   

    恩,源代码简单查看了下,TStrings.GetText()函数有分配内存,没有释放掉.function TStrings.GetText: PChar;
    begin
      Result := StrNew(PChar(GetTextStr));
    end;function StrNew(const Str: PAnsiChar): PAnsiChar;
    var
      Size: Cardinal;
    begin
      if Str = nil then Result := nil else
      begin
        Size := StrLen(Str) + 1;
        Result := StrMove(AnsiStrAlloc(Size), Str, Size);
        //AnsiStrAlloc(Size)有分配内存,用 StrDispose 释放
      end;
    end;
      

  5.   

    类似:function SearchEdit(EditControl: TCustomEdit; const SearchString: String;
          SearchOptions: TStringSearchOptions; FindFirst: Boolean = False): Boolean;
          var
          Buffer, P: PChar;
          Size: Word;
          begin
          Result := False;
          if (Length(SearchString) = 0) then Exit;
          Size := EditControl.GetTextLen;
          if (Size = 0) then Exit;
          Buffer := StrAlloc(Size + 1);
          try
          EditControl.GetTextBuf(Buffer, Size + 1);
          P := SearchBuf(Buffer, Size, EditControl.SelStart, EditControl.SelLength,
          SearchString, SearchOptions);
          if P <> nil then begin
          EditControl.SelStart := P - Buffer;
          EditControl.SelLength := Length(SearchString);
          Result := True;
          end;
          finally
          StrDispose(Buffer);
          end;
          end;