delphi中的动态数组和wininet?

Posted

技术标签:

【中文标题】delphi中的动态数组和wininet?【英文标题】:dynamic arrays and wininet in delphi? 【发布时间】:2010-07-12 16:44:23 【问题描述】:

我正在使用 WinInet 从我们的一台服务器连接和检索信息。我正在使用以下内容:

indexdata: array of byte[1..5000] of byte;
infoBuffer: array [0..5000] of char;
BufferSize: DWORD;
reserved:   DWORD;
text: string;

BufferSize := Length(infoBuffer);
res := HttpQueryInfo(hHttpRequest, HTTP_QUERY_RAW_HEADERS_CRLF, @infoBuffer, BufferSize, Reserved);

Reserved := 0;
InternetReadFile(hHttpRequest, @indexdata, sizeof(indexdata), Reserved);

SetLength(text, Reserved);
CopyMemory(@text[1], @indexdata[1], Reserved);

到目前为止,这两个字节数组已经足够了。事情发生了变化。服务器现在可以返回大于或小于5000的信息;最糟糕的是,在 InternetReadFile 中可以在 infoBuffer 中返回可变大小。

所以我尝试将 indexdata 和 infobuffer 声明为字节数组,然后使用 SetLength 设置其长度,但发生了两件事。

1) 我仍然不知道服务器将返回的 indexdata 的大小,因此我无法将其正确设置为 100000。

2) 我不能使用(现在这样)CopyMemory 传递 Low(indexdata) 将 indexdata 复制到一个简单的字符串变量,所以我可以使用数据。

我如何在 Delphi 中处理这个问题?我可以在 C 中完成,但我似乎无法在 Delphi 中正确完成。

感谢代码

谢谢!

【问题讨论】:

Reserved 是一个相当糟糕的变量名。 lpdwNumberOfBytesRead呢? ...或者更易读的“InputSize”? :P @andreas 当然,我会改变它 “字节数组[1..5000]字节”是指“字节数组[1..5000]”? indexdata 有 5000 个元素; infoBuffer 有 5001 个元素。 【参考方案1】:

您确实知道 char 自 Delphi 2009 以来是 Unicode 字符,但在 Delphi 2009 之前是 ANSI 字符。同样,在 Delphi 2009 中 string 是 @987654324 @,不是AnsiString

因此,当您编写SetLength(text, Reserved) 时,您确实将text 中的字符数设置为Reserved。但是字节数将是2*Reserved

换句话说,在 Delphi 2009+ 中,一个 char 不是一个 byte,而是两个字节。

您可以通过将所有char 替换为AnsiChar 并将所有string 替换为AnsiString 来恢复旧行为。

更新

由于您没有发布整个代码,我无法真正说出问题所在。不过,您可能会发现阅读我在 Delphi 中使用 InternetReadFile 的示例会很有趣。请参阅我对this question 的回答。这是一个完整的示例,说明如何使用 Delphi 和 InternetReadFile 从 Internet 读取文本文件。

为方便起见,我也将我的代码粘贴在下面:

要从 Internet 读取数据,请使用 InternetReadFile 函数。我使用下面的代码从网上读取一个小的(单行)文本文件:

function WebGetData(const UserAgent: string; const Server: string; const Resource: string): string;
var
  hInet: HINTERNET;
  hURL: HINTERNET;
  Buffer: array[0..1023] of AnsiChar;
  i, BufferLen: cardinal;
begin
  result := '';
  hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    hURL := InternetOpenUrl(hInet, PChar('http://' + Server + Resource), nil, 0, 0, 0);
    try
      repeat
        InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
        if BufferLen = SizeOf(Buffer) then
          result := result + AnsiString(Buffer)
        else if BufferLen > 0 then
          for i := 0 to BufferLen - 1 do
            result := result + Buffer[i];
      until BufferLen = 0;
    finally
      InternetCloseHandle(hURL);
    end;
  finally
    InternetCloseHandle(hInet);
  end;
end;

示例用法:

WebGetData('My UserAgent', 'www.rejbrand.se', '/MyDir/update/ver.txt')

【讨论】:

@Jessica,你应该立即执行 Andreas 的建议。您可以将您的声明更改为 AnsiString 而不会破坏任何内容,然后如果/当您移动到较新的 Delphi 时,则不必重新工作。即,现在声明为 AnsiString/AnsiChar 没有缺点。 知道了,谢谢!不,我可以看到我的代码失败的地方:我试图一次性使用 InternetReadFile,而不是读取夹头并将它们保存在字符串变量中。 iirc 在 Delphi 2009 中,字符串是 UnicodeString,而不是 WideString @mjustin:是的,我总是犯这个错误。【参考方案2】:

尝试使用 Move 例程,而不是 CopyMemory,如下所示:

Move(indexdata[1], text[1], reserved);

(是的,没有@ 符号。)这应该可以解决问题#2。至于问题#1,那是您和服务器之间的问题。您需要有一些方法来知道它将返回的大小的上限是多少,并使您的缓冲区至少有那么大。您应该能够在文档中找到它,或者首先调用另一个 API 来为您提供传入数据的大小。

还在那里进行一些检查,以便如果它返回大于缓冲区的内容,它会立即引发异常。如果你的缓冲区溢出,你应该假设你的程序受到攻击并做出相应的反应。

【讨论】:

“移动”失败并出现异常:地址 xxxxx 的访问冲突....读取地址 yyyyy 您是否先调用了 SetLength?如果你调用了 SetLength,并且你没有使用 @s,并且你没有超过 text 的大小,那么这不应该发生。 @Jessica:请注意缺少“@”并不是CopyMemorymove 之间的唯一区别——前两个参数的顺序不同,(Destionation,源)与(源,目的地)! 谢谢。我看到了,在梅森写的cmets上很清楚。 MoveCopyMemory 在各个方面的行为都应该相同,因为后者是用前者实现的。如果通过使用一个而不是另一个来解决任何问题,那么问题比其中任何一个都更深。【参考方案3】:

返回未知大小的缓冲区的 Windows 函数,如果传递的缓冲区太小,通常会返回所需的缓冲区大小。这样,您不需要预先设置足够大的缓冲区来容纳最大的数据 - 传递一个小缓冲区(即使是零长度的缓冲区),该函数将返回缓冲区应该有多大。然后您可以调整缓冲区的大小并再次将其传递给读取数据的函数 - 检查例如 MSDN 中的 HttpQueryInfo() 说明。 在 Delphi 中处理未知大小的缓冲区,有两种方法。

使用动态数组和 SetLength() 使用 GetMem() 和 FreeMem() 使用动态分配的缓冲区,这与您在 C 中所做的非常相似。

例如:

var
  Buf: Pointer;
  BufSize: DWORD; 
  Rez: DWORD;
  ...
const
  InitialBufSize = 1024;

...
BufSize := InitialBufSize;
GetMem(Buf, BufSize);
try
  if not HttpQueryInfo(hRequest, dwInfoLevel, Buf, BufSize, lpdwIndex) then
  begin
    Rez := GetLastError;
    if Rez = ERROR_INSUFFICIENT_BUFFER then
    begin
      FreeMem(Buf, InitialBufSize);
      GetMem((Buf, BufSize);
      if not HttpQueryInfo(hRequest, dwInfoLevel, Buf, BufSize, lpdwIndex) then
        Rez := GetLastError
    end;
 ...
finally
  FreeMem(Buf, BufSize);
end;

【讨论】:

以上是关于delphi中的动态数组和wininet?的主要内容,如果未能解决你的问题,请参考以下文章

delphi中的adoquery动态连接adoConnection出错

delphi 如何对动态数组求和(INT型),求最大值和最小值

delphi 动态添加系统菜单

delphi中枚举类型和子界,数组,集合的详解以及类型说明

delphi 图像数组怎么创建和使用

delphi数组之菜鸟篇