开发者

dynamic arrays and wininet in delphi?

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

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开发者_StackOverflow中文版 now information that can be bigger or smaller than 5000; worst yet, in InternetReadFile can return a variable size in the 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.

1) I still don't know the size of indexdata that the server will return so I cannot properly set it to, say, 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.

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.

Code is appreciated

thanks!


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.

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.

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

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

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.

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:

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')


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

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.

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.


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.

  • Use dynamic arrays and SetLength()
  • Use dynamically allocated buffers using GetMem() and FreeMem(), very much alike you would do in 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;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜