关于Delphi:Delphi – 程序无法完成,但在“showmessage”之间工作正常。

Delphi - procedure fails to complete, but works fine with “showmessage” between.?

我不太确定如何问这个问题,因为我不知道它是否与执行时间,应用程序进程,消息程序或其他任何内容有关。

我(对我来说)奇怪的情况,程序无法运行并在运行时引发系统异常,而如果我在其间放置"showmessage",它完全无故障运行(我这样做,以便我可以很快看到会发生什么介于两者之间。我更喜欢这种方式而不是手表...)。

我不确定代码是否重要,但我会在下面给出:

1
2
3
4
5
6
7
8
9
10
procedure LoadSettings;
var SettingsBuffToLoad: TStringList;
begin
  SettingsBuffToLoad:=TStringList.Create;
  Encoding:=TEncoding.ANSI;
  SettingsBuffToLoad.LoadFromFile('bin/settings.txt', Encoding);
//  showmessage(settingsbufftoload.Strings[0]);
  SettingsBuffer:=Decode(SettingsBuffToLoad);
//  showmessage(settingsbuffer.Strings[0]);  //decode
end;

解码过程声明为外部,并从dll读取。

如果我只删除那些"/",以便它成为代码而不是注释,它就可以正常工作。但是,按照您现在的设置,它会引发异常,但在该过程已经完成之后。 (调试器的最后一个断点在"end;"处停止,然后继续它会引发异常而不是显示表单;此过程在FormCreate过程中被调用为最后一个。

有没有什么与时间有关,ShowMessage解决了,还是......? :/

更新:
解码函数,如下所述:

这是它的声明方式,正好在表单的实现和变量之上:

function Decode(Buff:TStringList):TStringList; STDCALL;外部'bin settings.txt';

这是在DLL中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Decode(Buff: TStringList): TStringList; export;
var
t, u, h: integer;
s: String;
begin
DecodeBuffer.Clear;
DecodeBuffer:=Buff;
  for h := 0 to DecodeBuffer.Count-1 do
  begin
    s := DecodeBuffer.Strings[h];
    t := Length(s);
    if t > 0 then
    begin
      for u := 0 to t-1 do
      begin
        s[u+1] := DecodeChar(s[u+1], (h mod 5) + 1);
      end;
      DecodeBuffer.Strings[h] := s;
    end;
  end;
Result:=DecodeBuffer;
end;

这个代码在Delphi的一个问题中进行了讨论,在字符串中更改了Chars - 错误理解的行为 - XE3,并使用了Remy的答案。 DecodeChar,我相信在这里根本不重要,或者是它?

此外,保存设置的功能也是如此,在FormClose事件中调用:

这是:

1
2
3
4
5
6
7
8
9
10
procedure TScribbles.SaveSettings;
var SettingsBuffToSave: TStringList;
begin
  SettingsBuffToSave:=TStringList.Create;
  Encoding := TEncoding.ANSI;
//  Showmessage(settingsbuffer.Strings[0]);
  SettingsBuffToSave:=Encode(SettingsBuffer);
//  Showmessage(settingsbufftosave.Strings[0]);
  SettingsBuffToSave.SaveToFile('bin/settings.txt', Encoding);
end;

第一个ShowMessage用作代码而不是注释,它可以工作,而在上面写的注释函数中,它以与Decode相同的方式调用外部异常。
是否有可能,当它已经调用函数Encode时,尚未创建SettingsBuffToSave,或者是什么?
那时,SettingsBuffer存在并被填充,所以它引发错误似乎很奇怪,只需将ShowMessage放在那里就会消失。

(函数编码基本上是解码的镜像,所以代码在这里并不重要......)


这段代码在很多层面都非常危险。以不安全的方式跨DLL边界使用对象。跨函数调用的对象指针管理不善。你需要重新设计。尝试以下作为开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
procedure Decode(Buff: PChar; BuffLen: Integer; ListIndex: Integer); stdcall; export;
var
  u: integer;
begin
  for u := 0 to BuffLen-1 do
  begin
    Buff^ := DecodeChar(Buff^, (ListIndex mod 5) + 1);
    Inc(Buff);
  end;
end;

procedure Encode(Buff: PChar; BuffLen: Integer; ListIndex: Integer); stdcall; export;
var
  u: integer;
begin
  for u := 0 to BuffLen-1 do
  begin
    Buff^ := EncodeChar(Buff^, (ListIndex mod 5) + 1);
    Inc(Buff);
  end;
end;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
procedure Decode(Buff: PChar; BuffLen: Integer; ListIndex: Integer); stdcall; external '...';

procedure Encode(Buff: PChar; BuffLen: Integer; ListIndex: Integer); stdcall; external '...';

procedure LoadSettings;
var
  h: Integer;
begin
  SettingsBuffer := TStringList.Create;
  SettingsBuffer.LoadFromFile('bin/settings.txt', TEncoding.ANSI);
  for h := 0 to SettingsBuff.Count-1 do
  begin
    Decode(PChar(SettingsBuff[h]), Length(SettingsBuff[h]), h);
  end;
end;

procedure TScribbles.SaveSettings;
var
  h: Integer;
begin
  for h := 0 to SettingsBuff.Count-1 do
  begin
    Encode(PChar(SettingsBuff[h]), Length(SettingsBuff[h]), h);
  end;
  SettingsBuff.SaveToFile('bin/setpb95enc.dll', TEncoding.ANSI);
end;


这里显而易见的问题是代码存在于DLL中。很可能你没有安排DLL来共享它的主机堆。并且Delphi类不能跨DLL边界传递。

如果要在模块之间共享Delphi类,则必须使用包。当然,另一种选择是将所有代码放在同一个模块中。那就是删除DLL,并编译可执行文件中的所有内容。最后一个选项是为DLL使用有效的互操作类型。

当然,实际错误可能还有其他原因。代码闻起来很糟糕。例如,这是什么:

1
DecodeBuffer:=Buff;

DecodeBuffer是全局变量吗?如果是这样,那么在物体被摧毁后引用它是合理的。并不是说我能看到任何被破坏的证据。不希望看起来粗鲁,你的代码看起来可能有多个问题。作为紧急事项,您需要:

  • 处理上面描述的DLL问题。
  • 删除全局变量。
  • 修复终身问题。停止泄漏。
  • 启用范围检查以查找缓冲区溢出。
  • 在调试模式下添加FastMM以尝试捕获堆损坏。

  • 我想我知道这里发生了什么:我认为你的筹码被粉碎了。

    此外,我更怀疑实际原因是使用未初始化变量的Decode过程。您的ShowMessage语句(如果我是正确的话,它将是第一个重要的)更改堆栈中的内容,从而更改未初始化的变量。

    如果我是对的,这将有一些heisenbug属性 - 你做的任何事情,以找出正在发生的事情将改变未初始化的变量的价值。

    要尝试的一件事:声明一个大的局部变量(想法是用尽堆栈空间)并确保编译器不会丢弃它。这将把事情转移到记忆中,从而可能化解爆炸。如果它有效,它对于正在发生的事情是非常确定的。