动态数组和wininet在德尔福?

时间:2022-05-09 17:35:56

I'm using WinInet to connect and retrieve information from one of our server. I'm using the following:

我正在使用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);

The two array of bytes were enough up until now. Things changed. The server can return now information that can be bigger or smaller than 5000; worst yet, in InternetReadFile can return a variable size in the infoBuffer.

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

So i tried declaring the indexdata and infobuffer as array of byte and then using SetLength to set its length, but 2 things happened.

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

1) I still don't know the size of indexdata that the server will return so I cannot properly set it to, say, 100000.

1)我仍然不知道服务器返回的indexdata的大小,所以我不能正确地设置为100000。

2) I cannot use (as it is now) CopyMemory passing Low(indexdata) to copy indexdata to a simple string variable so I can use the data.

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

How do I handle this in Delphi? I can do it in C but I can't seem to be able to do it properly in Delphi.

我如何在德尔福处理这个?我可以用C表示,但是我不能用Delphi表示。

Code is appreciated

代码是欣赏

thanks!

谢谢!

3 个解决方案

#1


3  

You do know that char is a Unicode character since Delphi 2009, but an ANSI character prior to Delphi 2009. In the same way, in Delphi 2009 a string is a UnicodeString, not an AnsiString.

您确实知道char是自Delphi 2009以来的Unicode字符,但是在Delphi 2009之前是ANSI字符。同样地,在Delphi 2009中,字符串是单行字符串,而不是AnsiString。

So, when you write SetLength(text, Reserved) you do set the number of characters in text to Reserved. But the number of bytes will be 2*Reserved.

因此,当您写入SetLength(文本,保留)时,您确实将文本中的字符数设置为保留。但是字节数是2*预留的。

In other words, in Delphi 2009+, one char is not one byte, but two bytes.

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

You can get back the old behaviour by replacing all char with AnsiChar and all string with AnsiString.

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

Update

Since you did not post your entire code, I cannot really say what the problem is. Nevertheless, you might find it interesting to read my example usage of InternetReadFile in Delphi. See my answer to this question. It is a fully-working example of how to read a text file from the Internet using Delphi and InternetReadFile.

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

For your convinience, I paste my code below as well:

为了方便您,我也将代码粘贴在下面:

To read data from the Internet, use InternetReadFile function. I use the following code to read a small (one-line) text file from the 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;

Sample usage:

示例用法:

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

#2


1  

Instead of CopyMemory, try using the Move routine, like so:

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

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

(Yes, without the @ symbols.) That should solve problem #2. As for problem #1, that's between you and the server. You need to have some way to know what the upper bound is on the size that it will return, and make your buffer at least that large. You should either be able to find this in the documentation, or call another API first that will give you the size of the incoming data.

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

Also put some check on there so that if it returns something larger than your buffer, it immediately raises an exception. If your buffer is overrun, you should assume that your program is under attack and react accordingly.

还可以在那里进行一些检查,这样如果它返回比您的缓冲区更大的东西,它会立即引发异常。如果您的缓冲区溢出,您应该假定您的程序受到攻击,并作出相应的反应。

#3


1  

Windows functions that return a buffer of unknown size, usually return the require size of the buffer if a too small buffer is passed. This way you do not need to size in advance a buffer large enough to hold the largest data - pass a small buffer (even a zero length one), and the function will return how large the buffer should be. Then you can size your buffer and pass it again the the function to read data - check for example HttpQueryInfo() explanation in MSDN. To handle buffer of an unknown size in Delphi, you have two ways.

Windows函数返回一个未知大小的缓冲区,如果传递的缓冲区太小,通常返回需要的缓冲区大小。这样,您不需要预先设置足够大的缓冲区来容纳最大的数据—传递一个小缓冲区(即使是一个零长度的缓冲区),并且函数将返回缓冲区的大小。然后,您可以对缓冲区进行大小调整,并再次通过它来读取数据—检查MSDN中的HttpQueryInfo()解释。要在Delphi中处理未知大小的缓冲区,有两种方法。

  • Use dynamic arrays and SetLength()
  • 使用动态数组和SetLength()
  • Use dynamically allocated buffers using GetMem() and FreeMem(), very much alike you would do in C.
  • 使用GetMem()和FreeMem()使用动态分配的缓冲区,在C中非常类似。

For example:

例如:

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;

#1


3  

You do know that char is a Unicode character since Delphi 2009, but an ANSI character prior to Delphi 2009. In the same way, in Delphi 2009 a string is a UnicodeString, not an AnsiString.

您确实知道char是自Delphi 2009以来的Unicode字符,但是在Delphi 2009之前是ANSI字符。同样地,在Delphi 2009中,字符串是单行字符串,而不是AnsiString。

So, when you write SetLength(text, Reserved) you do set the number of characters in text to Reserved. But the number of bytes will be 2*Reserved.

因此,当您写入SetLength(文本,保留)时,您确实将文本中的字符数设置为保留。但是字节数是2*预留的。

In other words, in Delphi 2009+, one char is not one byte, but two bytes.

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

You can get back the old behaviour by replacing all char with AnsiChar and all string with AnsiString.

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

Update

Since you did not post your entire code, I cannot really say what the problem is. Nevertheless, you might find it interesting to read my example usage of InternetReadFile in Delphi. See my answer to this question. It is a fully-working example of how to read a text file from the Internet using Delphi and InternetReadFile.

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

For your convinience, I paste my code below as well:

为了方便您,我也将代码粘贴在下面:

To read data from the Internet, use InternetReadFile function. I use the following code to read a small (one-line) text file from the 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;

Sample usage:

示例用法:

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

#2


1  

Instead of CopyMemory, try using the Move routine, like so:

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

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

(Yes, without the @ symbols.) That should solve problem #2. As for problem #1, that's between you and the server. You need to have some way to know what the upper bound is on the size that it will return, and make your buffer at least that large. You should either be able to find this in the documentation, or call another API first that will give you the size of the incoming data.

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

Also put some check on there so that if it returns something larger than your buffer, it immediately raises an exception. If your buffer is overrun, you should assume that your program is under attack and react accordingly.

还可以在那里进行一些检查,这样如果它返回比您的缓冲区更大的东西,它会立即引发异常。如果您的缓冲区溢出,您应该假定您的程序受到攻击,并作出相应的反应。

#3


1  

Windows functions that return a buffer of unknown size, usually return the require size of the buffer if a too small buffer is passed. This way you do not need to size in advance a buffer large enough to hold the largest data - pass a small buffer (even a zero length one), and the function will return how large the buffer should be. Then you can size your buffer and pass it again the the function to read data - check for example HttpQueryInfo() explanation in MSDN. To handle buffer of an unknown size in Delphi, you have two ways.

Windows函数返回一个未知大小的缓冲区,如果传递的缓冲区太小,通常返回需要的缓冲区大小。这样,您不需要预先设置足够大的缓冲区来容纳最大的数据—传递一个小缓冲区(即使是一个零长度的缓冲区),并且函数将返回缓冲区的大小。然后,您可以对缓冲区进行大小调整,并再次通过它来读取数据—检查MSDN中的HttpQueryInfo()解释。要在Delphi中处理未知大小的缓冲区,有两种方法。

  • Use dynamic arrays and SetLength()
  • 使用动态数组和SetLength()
  • Use dynamically allocated buffers using GetMem() and FreeMem(), very much alike you would do in C.
  • 使用GetMem()和FreeMem()使用动态分配的缓冲区,在C中非常类似。

For example:

例如:

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;