2010-07-12 4 views
2

J'utilise WinInet pour connecter et extraire des informations de l'un de nos serveurs. J'utilise les éléments suivants:tableaux dynamiques et wininet en Delphi?

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

Les deux tableau d'octets étaient assez jusqu'à présent. Les choses ont changé. Le serveur peut renvoyer maintenant des informations pouvant être plus grandes ou plus petites que 5000; pire encore, dans InternetReadFile peut renvoyer une taille variable dans le infoBuffer.

J'ai donc essayé de déclarer l'indexdata et l'infobuffer comme un tableau d'octets, puis d'utiliser SetLength pour définir sa longueur, mais 2 choses se sont produites.

1) Je ne sais pas encore la taille de INDEXDATA que le serveur retourne donc je ne peux pas définir correctement à, disons, 100000.

2) Je ne peux pas utiliser (comme il est maintenant) passant CopyMemory Faible (indexdata) pour copier des données d'index vers une simple variable de chaîne afin que je puisse utiliser les données.

Comment gérer cela dans Delphi? Je peux le faire en C mais je n'arrive pas à le faire correctement en Delphi.

Le code est apprécié

merci!

+1

'Reserved' est un nom de variable plutôt mauvais. Qu'en est-il de 'lpdwNumberOfBytesRead'? –

+1

... ou même le "InputSize" un peu plus lisible? : P –

+0

@andreas sûr, je vais le changer – Jessica

Répondre

3

Vous ne savez que char est un caractère Unicode depuis Delphi 2009, mais un caractère ANSI avant Delphi 2009. De la même manière, en Delphi 2009, un string est un UnicodeString, pas AnsiString. Par conséquent, lorsque vous écrivez SetLength(text, Reserved), vous définissez le nombre de caractères text à Reserved. Mais le nombre d'octets sera 2*Reserved. En d'autres termes, dans Delphi 2009+, un char n'est pas un byte, mais deux octets.

Vous pouvez retrouver l'ancien comportement en remplaçant tous les char avec AnsiChar et tout string avec AnsiString.

Mise à jour

Puisque vous ne publiez pas votre code entier, je ne peux pas vraiment dire quel est le problème. Néanmoins, vous pourriez trouver intéressant de lire mon exemple d'utilisation de InternetReadFile dans Delphi. Voir ma réponse au this question. C'est un exemple complet de la façon de lire un fichier texte à partir d'Internet en utilisant Delphi et InternetReadFile.

Pour votre convenance, je coller mon code ci-dessous ainsi:

Pour lire les données de l'Internet, utilisez la fonction InternetReadFile.J'utilise le code suivant pour lire un petit fichier texte (une ligne) de l'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; 

Exemple d'utilisation:

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

J'utilise delphi 7, mais merci pour l'explication – Jessica

+1

@ Jessica, vous devez implémenter la suggestion d'Andreas immédiatement. Vous pouvez changer votre déclaration en AnsiString et ne rien casser, et si vous passez à un Delphi plus récent, vous n'aurez pas à le retravailler. c'est-à-dire qu'il n'y a aucun inconvénient à déclarer AnsiString/AnsiChar dès maintenant. –

+0

ok, merci. cela ne répond toujours pas à ma question. – Jessica

1

Au lieu de CopyMemory, essayez d'utiliser la routine Move, comme suit:

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

(Oui, sans les symboles @.) Cela devrait résoudre le problème # 2. En ce qui concerne le problème n ° 1, c'est entre vous et le serveur. Vous devez avoir un moyen de savoir quelle est la limite supérieure de la taille qu'elle renverra, et rendre votre tampon au moins aussi grand. Vous devriez soit être en mesure de trouver cela dans la documentation, soit appeler d'abord une autre API qui vous donnera la taille des données entrantes.

Mettez également un contrôle là-bas afin que si elle renvoie quelque chose de plus grand que votre tampon, il déclenche immédiatement une exception. Si votre buffer est surchargé, vous devez supposer que votre programme est attaqué et réagir en conséquence.

+0

le "move" échoue avec une exception: violation d'accès de l'adresse xxxxx .... read of address yyyyy – Jessica

+0

Avez-vous appelé SetLength en premier? Si vous avez appelé SetLength, et que vous n'utilisez pas @s, et que vous ne dépassez pas la taille de 'text', cela ne devrait pas arriver. –

+1

@Jessica: Veuillez noter que l'absence de "@" n'est pas la seule différence entre 'CopyMemory' et' move' - les deux premiers arguments sont dans un ordre différent, (Destionation, Source) versus (Source, Destination)! –

1

Les fonctions Windows qui retournent un tampon de taille inconnue retournent généralement la taille requise du tampon si un tampon trop petit est passé. De cette façon, vous n'avez pas besoin de dimensionner à l'avance un tampon suffisamment grand pour contenir les plus grandes données - passez un petit tampon (même un zéro), et la fonction retournera la taille du tampon. Ensuite, vous pouvez redimensionner votre tampon et le passer à nouveau la fonction pour lire les données - vérifiez par exemple l'explication HttpQueryInfo() dans MSDN. Pour gérer un tampon d'une taille inconnue dans Delphi, vous avez deux façons.

  • Utiliser des tableaux dynamiques et SetLength()
  • Utiliser des tampons alloués dynamiquement à l'aide GetMem() et FreeMem(), très semblables que vous le feriez dans C.

Par exemple:

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; 
Questions connexes