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:请注意缺少“@”并不是CopyMemory
和move
之间的唯一区别——前两个参数的顺序不同,(Destionation,源)与(源,目的地)!
谢谢。我看到了,在梅森写的cmets上很清楚。
Move
和 CopyMemory
在各个方面的行为都应该相同,因为后者是用前者实现的。如果通过使用一个而不是另一个来解决任何问题,那么问题比其中任何一个都更深。【参考方案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出错