【问题标题】:Delphi 7: Access violation - TByteDynArray problemDelphi 7:访问冲突 - TByteDynArray 问题
【发布时间】:2010-07-01 12:28:18
【问题描述】:

我对 Delphi 还很陌生,必须编写一个 SOAP 客户端。导入 WSDL 会生成此代码(我显然无法更改,因为我显然必须遵守服务器端)

  DataPart            = class;             
  Message             = class;             
  eMessage            = class;             

  eventType = ( send, delete, etc );

  DataPart = class(TRemotable)
  private
    FhasData: Boolean;
    Fdata: TByteDynArray;
  published
    property hasData: Boolean read FhasData write FhasData;
    property data: TByteDynArray read Fdata write Fdata;
  end;
  Message = class(TRemotable)
  private
    FMessageID: Int64;
    Ftimestamp: TXSDateTime;
    Fevent: eventType;
    FmagicNumber: WideString;
    FDataPart: DataPart;
  published
    property MessageID: Int64 read FMessageID write FMessageID;
    property timestamp: TXSDateTime read Ftimestamp write Ftimestamp;
    property event: eventType read Fevent write Fevent;
    property magicNumber: WideString read FmagicNumber write FmagicNumber;
    property DataPart: DataPart read FDataPart write FDataPart;
  end;

  eMessage = class(TRemotable)
  private
    FencryptedMessage: TByteDynArray;
    Fdata: DataPart;
  published
    property encryptedMessage: TByteDynArray read FencryptedMessage write FencryptedMessage;
    property data: DataPart read Fdata write Fdata;
  end;

  MyApplicationPortType = interface(IInvokable)
  ['{99767D33-6B4A-7547-4DAC-0608095CAC70}']

    function  sendMessage(const encryptedMessage: TByteDynArray; const data: DataPart): WideString; stdcall;
  end;

谁能给我编写一个带有虚拟值的示例,该示例将调用 sendMessage() 并且不会导致访问冲突?我真的不知道如何处理 TByteDynArray


[编辑] 根据要求,这是我的代码,但是 - 免责声明 - 在发布之前我不得不对其进行大量修改(减少它),因此它可能无法编译。 sendMessage() 的两个参数都是非空的

  var theMessageArray: TByteDynArray;
      theResult : WideString;
      messageData : TByteDynArray;
      i : Integer;
begin
  theMessage.messageID := theMessage.messageID + 1;
  theMessage.timestamp := TXSDateTime.Create();
  theMessage.timestamp.AsDateTime := Now();
  theMessage.event := delete;
  theMessage.magicNumber  := 'magic # ' + IntToStr(theMessage.messageID);

  SetLength(messageData, 1);
  messageData[0] := 0;

  theMessage.dataPart.hasData := True;
  messageData := theMessage.dataPart.messageData;

  SetLength(messageData, $1000 * dataSize);

  for i := 0 to $1000 * dataSize - 1 do
        messageData[i] := i and $FF;

  theMessage.DataPart.messageData := messageData;

  theMessageArray := TByteDynArray(theMessage);
  theResult := (HTTPRIO1 as MyApplicationPortType).sendMessage(theMessageArray, theMessage.dataPart);

【问题讨论】:

  • 这只是一个界面。你在做什么导致访问冲突?
  • +1 提出一个好问题。有两种方法可以解决这个问题 - 向您展示我的代码并弄清楚它为什么不起作用,或者有人发布了一些有效的代码,我可以将它与我自己的进行比较。好的,我会发布我的代码,但重要的是(我认为)两个参数在调用时都是非空的
  • 你真的是在从胜利的口中抢夺失败,因为你使用了 TByteDynArray 而不仅仅是 TBytes(这实际上只是一个字符串)。
  • 什么版本的delphi?请更新(重新标记)。
  • AV 源很可能是这样的:theMessageArray := TByteDynArray(theMessage); 变量不是动态数组;类型转换不会使它成为一个。该分配将尝试增加假定数组的引用计数,它甚至可以运行而不会崩溃。之后,sendMessage 尝试使用该内存,就好像它是一个数组一样。它读取“长度”并尝试访问那么多字节。该长度不能准确反映那里有多少字节,或者引用计数修改可能会破坏内存。经验法则:如果你对动态数组进行类型转换,就会出现问题。

标签: delphi


【解决方案1】:

新想法:你在这个单元有范围检查吗?添加 {$R+}

如果你想使用动态数组类型,你必须在访问它之前在构造函数中明确设置它的长度,并且在复制/赋值时,你也必须非常小心。

不仅必须在访问每个 TByteDynArray 的元素之前调用 SetLength:

SetLength(Fdata, MyDesiredLengthWhichIsGreaterThanZero):

这里你也必须小心,我认为这会给你带来麻烦:

  property data: TByteDynArray read Fdata write Fdata;

您的自动生成器为您制作了该代码,如果您真的知道您想要一个动态数组,您显然可以将其发布。 (更新:最初我错了)。

正如 Rob 所指出的,TRemotable 不适用于索引属性,但适用于“字节数组”(TByteDynArray)属性,所以如果你做的一切正确,你不需要停止使用 TByteDynArray(我错了最初关于那个)。

如果是我从头开始编写,我会使用“字符串”类型,而不是像 TBytes。我想知道为什么它不使用 TBytes,但我知道您正在使用一些自动生成的 WSDL 生成器代码来实现 SOAP 客户端。因此,鉴于此,让您的代码不会崩溃应该是非常有可能的。

see also this Related question

我不知道如何编写 SOAP 客户端,但看起来您的代码做了一些不可靠的事情。看起来您需要修复动态数组处理,包括 Rob 向您指出的“呃,哦,你为什么要在这里进行投射”问题。但是,您似乎也不能随意更改类型,因为您似乎必须使用 TRemotable 机制已知并由其处理的类型。

至于您的要求,这应该可行:

  procedure TestMe( whatever:TWhatever );
  var 
    FData:TByteDynArray;
  begin
     SetLength(FData,2); 
     FData[0] := 10; 
     FData[1] := 20;
     sendMessage(FData, whatever);
  end;

【讨论】:

  • 谢谢,沃伦 (+1)。我刚刚发布了我的代码(删除了很​​多不相关的内容)。是的,我设置了长度。我可以编写 Get/Set() 但我不能通过直接访问属性来完成吗? (告诉你我是 Delphi 的新手)
  • 你没有设置 fdata 的长度。而且您认为 SetLength(SomethingElse,10),然后分配 fdata = somthingelse 会起作用,但它不会。
  • 我不认为您建议的修复与 TRemotable 兼容。需要在类中定义数组,以便 TRemotables 的使用者知道如何获取数组的全部内容。使用数组属性,这是不可能发生的(因为消费者不知道有效的索引值是什么)。
  • 如果您使用为您生成的自动生成代码,则必须在错误时进行更改。查看我喜欢的相关问题:stackoverflow.com/questions/3154879/…
  • 设置本地 messageData 变量的长度,然后将其分配给动态数组属性似乎是正确的做法。将一个动态数组分配给另一个是完全安全的。引用计数得到更新,因此数组保持活动状态,直到每个人都完成它。我认为这可能归结为确定sendMessage 函数期望 接收的字节数组。那么,Mawg,什么是 MyApplicationPortType,你应该给它什么样的加密消息?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多