字符串在ComboBox.AddObject中被破坏。如何以正确的方式添加它们?

时间:2015-06-07 13:19:57

标签: delphi delphi-xe7

我正在向TComboBox添加带有对象(也是字符串)的字符串,但是在稍后尝试检索时会收到损坏的字符串。

这就是我添加它们的方式:

var
  i: Integer;
  sl: TStringList;
  c: Integer;
  s: PChar;
begin

  for i := 1 to tblCalls.FieldCount do
    if tblCalls.Fields[i - 1].Tag = 1 then
      ListBox1.Items.Append(tblCalls.Fields[i - 1].FieldName);

  sl := TStringList.Create;
  try
    LoadStyles(TStrings(sl));

    for c := 0 to sl.Count - 1 do
    begin

      s := PChar(sl.Values[sl.Names[c]]);
      ComboBox1.Items.AddObject(sl.Names[c], TObject(s));
    end;

  finally
    sl.Free;
  end;

end;

procedure LoadStyles(var AStylesList: TStrings);
var
  f, n: String;
  filelist: TStringDynArray;
begin
  f := ExtractFilePath(ParamStr(0)) + 'Styles';
  if (not DirectoryExists(f)) then
    Exit;

  filelist := TDirectory.GetFiles(f);

  for f in filelist do
  begin
    n := ChangeFileExt(ExtractFileName(f), EmptyStr);
    AStylesList.Add(n + '=' + f);
  end;
end;

..这就是我正在尝试检索字符串对象的地方:

procedure TfrmOptions.ComboBox1Change(Sender: TObject);
var
  si: TStyleInfo;
  i: Integer;
  s: String;
begin
  i := TComboBox(Sender).ItemIndex;
  s := PChar(TComboBox(Sender).Items.Objects[i]);

  Showmessage(s); // --> Mostly shows a corrupted string (gibberish chars)

  if (TStyleManager.IsValidStyle(s, si)) then
  begin
    if (not MatchStr(s, TStyleManager.StyleNames)) then
      TStyleManager.LoadFromFile(s);
    TStyleManager.TrySetStyle(si.Name);
  end;
end;

我怀疑它与我添加它们的方式有关。也许我需要在以下位置分配内存:

s := PChar(sl.Values[sl.Names[c]]);

不确定。看看StrNew,NewStr和StrAlloc的帮助,它说这些函数已被弃用。你能帮忙指出什么是错的吗?

2 个答案:

答案 0 :(得分:2)

没有什么可以保持弦乐活着。当你写:

s := PChar(sl.Values[sl.Names[c]]);

创建一个类型为string的隐式局部变量,用于保存sl.Values[sl.Names[c]]求值的任何内容。该局部变量超出范围,只要编译器知道,没有任何引用它,并且字符串对象被销毁。

事实上,情况甚至更糟。因为上面的赋值发生在循环中,所以只有一个隐式局部变量。每次循环时,您要求组合框记住的字符串都会被销毁。

您需要找到一种方法来延长字符串的生命周期。你可以这样做:

var
  StrPtr: ^string;
....
for c := 0 to sl.Count - 1 do
begin
  New(StrPtr);
  StrPtr^ := sl.Values[sl.Names[c]];
  ComboBox1.Items.AddObject(sl.Names[c], TObject(StrPtr));
end;

然后当你需要访问字符串时,你可以这样做:

var
  StrPtr: ^string;
....
TObject(StrPtr) := TComboBox(Sender).Items.Objects[i];
// do something with StrPtr^

当您清除组合框时,您还必须浏览每个项目并在指针上调用Dispose

话虽如此,不这样做也会容易得多。停止尝试将字符串强制转换为与每个项目关联的TObject数据。而是保留包含这些字符串的并行字符串列表。当您需要查找名称时,请在该列表中查找,而不是在组合框中查找。

答案 1 :(得分:0)

我知道这是一个老问题,但是我又遇到了这个问题,而不是使用单独的字符串列表,而是使用了一个带有字符串值的对象(我认为有人在注释中建议了它),如下所示:

使用字符串值将类型声明为TObject:

  TStringObject = class(TObject)
    StringValue : string;
  end;

然后在添加项目时,声明TStringObject的本地变量并为每个项目创建一个新实例:

var
  strObj : TStringObject
begin

    ...

    for c := 0 to sl.Count - 1 do
    begin
      strObj := TStringObject.Create;
      strObj.StringValue := sl.Values[sl.Names[c]];
      ComboBox1.Items.AddObject(sl.Names[c], strObj);
    end;

在检索值时:

s := TStringObject(TComboBox(Sender).Items.Objects[i]).StringValue;