4

I want to send a HTTP Post Request in Delphi 2010 using WinInet, but my script doesn't work ;/

It's my Delphi script:

uses WinInet;
procedure TForm1.Button1Click(Sender: TObject);
var
  hNet,hURL,hRequest: HINTERNET;
begin
  hNet := InternetOpen(PChar('User Agent'),INTERNET_OPEN_TYPE_PRECONFIG or INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hNet) then
  begin
  try
    hURL := InternetConnect(hNet,PChar('http://localhost/delphitest.php'),INTERNET_DEFAULT_HTTP_PORT,nil,nil,INTERNET_SERVICE_HTTP,0,DWORD(0));
    if(hURL<>nil) then
      hRequest := HttpOpenRequest(hURL, 'POST', PChar('test=test'),'HTTP/1.0',PChar(''), nil, INTERNET_FLAG_RELOAD or INTERNET_FLAG_PRAGMA_NOCACHE,0);
    if(hRequest<>nil) then
      HttpSendRequest(hRequest, nil, 0, nil, 0);
    InternetCloseHandle(hNet);
  except
      ShowMessage('error');
    end
  end;
end;

and my PHP script:

$data = $_POST['test'];
$file = "test.txt";
$fp = fopen($file, "a");
flock($fp, 2);
fwrite($fp, $data);
flock($fp, 3);
fclose($fp);
Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
noxwow
  • 113
  • 2
  • 2
  • 5

3 Answers3

16

Major problems:

  • The second parameter of InternetConnect should contain only the name of the server, not the entire URL of the server-side script.

  • The third parameter of HttpOpenRequest should be the file name (URL) of the script, not the POST data!

  • The actual POST data should be the forth parameter of HttpSendRequest.

Minor problems

  • INTERNET_OPEN_TYPE_PRECONFIG or INTERNET_OPEN_TYPE_PRECONFIG: It is sufficient with INTERNET_OPEN_TYPE_PRECONFIG.

  • DWORD(0) is overkill. 0 is enough.

Sample Code

I use the following code to POST data:

procedure WebPostData(const UserAgent: string; const Server: string; const Resource: string; const Data: AnsiString); overload;
var
  hInet: HINTERNET;
  hHTTP: HINTERNET;
  hReq: HINTERNET;
const
  accept: packed array[0..1] of LPWSTR = (PChar('*/*'), nil);
  header: string = 'Content-Type: application/x-www-form-urlencoded';
begin
  hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    hHTTP := InternetConnect(hInet, PChar(Server), INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 1);
    try
      hReq := HttpOpenRequest(hHTTP, PChar('POST'), PChar(Resource), nil, nil, @accept, 0, 1);
      try
        if not HttpSendRequest(hReq, PChar(header), length(header), PChar(Data), length(Data)) then
          raise Exception.Create('HttpOpenRequest failed. ' + SysErrorMessage(GetLastError));
      finally
        InternetCloseHandle(hReq);
      end;
    finally
      InternetCloseHandle(hHTTP);
    end;
  finally
    InternetCloseHandle(hInet);
  end;
end;

For instance:

WebPostData('My UserAgent', 'www.rejbrand.se', 'mydir/myscript.asp', 'value=5');

Update in response to answer by OP

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

This function thus only reads data, with no prior POST. However, the InternetReadFile function can also be used with a handle created by HttpOpenRequest, so it will work in your case also. You do know that the WinInet reference is MSDN, right? All Windows API functions are described in detail there, for instance InternetReadFile.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 1
    Thanks, I have merge these scripts and the script now looks like this: http://paste.org/pastebin/view/19370 Works as expected. Thanks again. – noxwow Jun 05 '10 at 10:20
  • @noxwow "Check" doesn't seem like a great name for a method. – Simon Hartcher Nov 04 '10 at 00:06
  • Im using turbo delphi and it says: [Pascal Fehler] uHTTP.pas(22): E2010 incompatible types: 'Char' und 'WideChar'.. How to fixx? – Hidden Jun 21 '13 at 07:58
  • In WebPostData, why you use AnsiString instead of String for the type of parameter Data? String is WideString. – alancc Apr 08 '21 at 06:01
  • Also why you use AnsiChar for Buffer, does that means the response text will be in ANSI, not Unicode? – alancc Apr 08 '21 at 06:10
  • @alancc: Before Delphi 2009, `string` was `AnsiString`; since Delphi 2009, `string` is `UnicodeString`. `WideString` is something else. But more to the point: this code reads only bytes from the WWW. It isn't aware of character encodings. Changing the string to `string` (=`UnicodeString`) would almost certainly make the code fail, because the WWW file is probably either ASCII or UTF-8 (so English letters are 1 byte wide), while `string` is two bytes per char, so the output would be garbage (mostly random Chinese characters). Using `AnsiString`, this will work correctly for WWW ASCII files. – Andreas Rejbrand Apr 08 '21 at 06:13
  • But you are right: this doesn't support non-ASCII characters. It only supports ASCII. To make it work with non-ASCII, you need to add more code to (1) determine the actual encoding of the WWW file and then (2) convert the bytes obtained from the WWW to a UTF-16LE `string`. – Andreas Rejbrand Apr 08 '21 at 06:15
  • (My response above is for reading, but roughly the same remarks apply to writing. If you want to write UTF-8 data, you need to convert the UTF-16LE `string` into UTF-8 explicitly. Remember that using WinInet is low level and that this A was written almost at the same time as Delphi became Unicode!) – Andreas Rejbrand Apr 08 '21 at 06:17
  • @AndreasRejbrand, Thank you very much. I create a function in Delhi XE3 based on yours. And now have made sure both the data sent and received are all in AnsiString or AnsiChar. – alancc Apr 08 '21 at 21:49
2
var
  BufferIn : INTERNET_BUFFERS;
  Buffer: array[0..1024] of Byte;
  FTmp: TSomeStream:
  FURL: string;
  ...
begin
... // Create FTmp, set FUrl, ...

  NetHandle := InternetOpen( 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3',
                          INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  UrlHandle := HttpOpenRequest(NetHandle, 'POST', PChar(FURL), nil, nil, nil, INTERNET_FLAG_NO_CACHE_WRITE, 0);

  ... // Check handles, etc
  try
    BufferIn.dwBufferTotal := FTmp.Size;

    if not HttpSendRequestEx(UrlHandle, @BufferIn, nil, 0, 0) then
      raise Exception.CreateFmt('Error on HttpSendRequestEx %d\n', [GetLastError()]);

    size := FTmp.Read(Buffer, 1024);
    InternetWriteFile(UrlHandle, @Buffer, size, BytesWritten);

    while (BytesWritten = size) and (BytesWritten > 0) do
    begin
      size := FTmp.Read(Buffer, 1024);
      InternetWriteFile(UrlHandle, @Buffer, size, BytesWritten);
    end;
  finally
    FTmp.Free;

    InternetCloseHandle(UrlHandle);
    InternetCloseHandle(NetHandle);
  end;
dmajkic
  • 3,448
  • 1
  • 18
  • 24
1

There is a library (called 'HTTPGet component for Delphi 32') on http://www.utilmind.com. It installs a visual control into your component palette. It does exactly what you want, so you may want to take a look.

Gabriel
  • 20,797
  • 27
  • 159
  • 293