【问题标题】:Is it safe to assume InternetCloseHandle() won't fail, thereby allowing cleaner code?假设 InternetCloseHandle() 不会失败是否安全,从而允许更清晰的代码?
【发布时间】:2011-09-28 20:24:06
【问题描述】:

这是一个使用 WinINet 执行 HTTP 请求并返回获取的字符串或引发异常的例程:

function Request(const pConnection: HINTERNET; const localpath: string): string;
var Buffer: packed Array[1..5000] of Char; BytesRead: Cardinal; pRequest: HINTERNET;     sent: boolean;
begin
Result := '';
pRequest := HTTPOpenRequest(pConnection, 'GET', pchar(localpath), nil, nil, nil, 0, 0);
if pRequest <> nil then
  begin
  sent := HTTPSendRequest(pRequest, nil, 0, nil, 0);
  if sent then
    while InternetReadFile(pRequest, @Buffer, SizeOf(Buffer)-1 {leave room for terminator}, BytesRead) do
      begin
      Buffer[BytesRead+1] := #0;
      Result := Result + buffer;
      end;
  InternetCloseHandle(pRequest);
  if not sent then RaiseLastOSerror; // HTTPSendRequest failed
  end
else RaiseLastOSerror; // HTTPOpenRequest failed
end;

如果 InternetCloseHandle(pRequest) 可能失败,即使 pRequest 已成功分配,GetLastError() 将返回 InternetCloseHandle() 的错误代码,而不是 HTTPSendRequest()。解决这个问题需要如下代码:

function Request(const pConnection: HINTERNET; const localpath: string): string;
var Buffer: packed Array[1..5000] of Char; BytesRead: Cardinal; pRequest: HINTERNET;
begin
Result := '';
pRequest := HTTPOpenRequest(pConnection, 'GET', pchar(localpath), nil, nil, nil, 0, 0);
if pRequest <> nil then
  begin
  if HTTPSendRequest(pRequest, nil, 0, nil, 0) then
    while InternetReadFile(pRequest, @Buffer, SizeOf(Buffer)-1 {leave room for terminator}, BytesRead) do
      begin
      Buffer[BytesRead+1] := #0;
      Result := Result + buffer;
      end
  else
    begin
    InternetCloseHandle(pRequest);
    RaiseLastOSerror; // HTTPSendRequest failed
    end;
  InternetCloseHandle(pRequest);
  end
else RaiseLastOSerror; // HTTPOpenRequest failed
end;

但乍一看,这似乎更丑陋,更令人困惑。

假设 InternetCloseHandle() 不会失败是否安全,从而允许使用更简单的代码?

【问题讨论】:

    标签: delphi wininet


    【解决方案1】:

    如果句柄创建成功,您需要确保它是“关闭”的。也就是说,你应该这样做

    hReq := HTTPOpenRequest(...);
    if hReq <> 0 then
      try      
        // Do stuff
      finally
        InternetCloseHandle(hReq);
      end;
    

    【讨论】:

    • 不回答问题。代码已经关闭了句柄。问题是在引发所需异常之前是否会失败。
    • @mike:不,你错了。如果HTTPSendRequestInternetReadFile 引发异常怎么办?然后执行将停止而不调用InternetCloseHandle。这就是 Delphi 中 try..finally.. 构造的全部要点!
    • @mike:为什么不尽快做 Win32Check 或 RaiseLastOsError(两者都依赖于 GetLastError)?如果你喜欢你应该这样做,也就是说,如果你喜欢我在回答中建议的那样,即使发生异常,InternetCloseHandle 也会运行。
    • HTTPSendRequest 和 InternetReadFile 不会产生异常,它们返回 false 或 nil 表示失败是不是真的?
    • @mike,根据the documentation,该函数有一个返回值显示失败,如果发生这种情况,请您致电GetLastError。你有它,这从字面上回答了你的问题。但几乎所有 API 调用都是如此,艺术在于处理这些错误。
    【解决方案2】:

    首先这种代码没有帮助:

    raise Exception.Create(IntToStr(GetLastError))
    

    改用这个:

    RaiseLastOsError; // This raises an exception with the description of the error
    

    由于您在代码中使用了异常,如何调用这样的函数,以便在无法关闭句柄时引发异常?

    Win32Check(InternetCloseHandle(H))
    

    【讨论】:

    • 最好打电话给Win32Check(InternetCloseHandle(H))
    • 不回答问题。我不在乎是否可以关闭句柄,我想引发 Send 异常,而不会从尝试关闭句柄时获取错误代码。
    • @mike:为什么不尽快做Win32CheckRaiseLastOsError(两者都依赖GetLastError)?如果你喜欢你应该这样做,也就是说,如果你喜欢我在回答中建议的那样,即使发生异常,InternetCloseHandle 也会运行。
    【解决方案3】:

    查看 SOAPHTTPTrans,有很多对 InternetCloseHandle 的调用。尽管许多出现在 except 块中,但没有一个是由一个保护的。所以看起来 Codegear(我的是 D2006)假设它不会失败。

    【讨论】:

    • 不,这意味着 InternetCloseHandle 不会引发异常。
    【解决方案4】:

    我认为你的做法是错误的。您应该简单地检查每个 API 调用的错误,并在遇到异常时立即引发异常。这样,您将获得适合产生异常的错误的错误消息。您根本不能指望继续调用其他 API 函数,然后为一段时间前发生的错误引发异常。

    我认为你想要一些类似的东西:

    Result := '';
    pRequest := HTTPOpenRequest(pConnection, 'GET', pchar(localpath), nil, nil, nil, 0, 0);
    if pRequest=nil then
      RaiseLastOSerror;
    try
      CheckWin32Error(HTTPSendRequest(pRequest, nil, 0, nil, 0));
      while InternetReadFile(pRequest, @Buffer, SizeOf(Buffer)-1, BytesRead) do begin
        Buffer[BytesRead+1] := #0;
        Result := Result + buffer;
      end;
      if GetLastError<>0 then
        RaiseLastOSerror;
    finally
      CheckWin32Error(InternetCloseHandle(pRequest));
    end;
    

    请注意,您没有对InternetReadFile 进行任何错误检查。我已经试着为你写了。

    【讨论】:

    • 我假设你的意思是 Win32check。
    • 尽管 InternetCloseHandle 是否会失败仍未得到解答,但此代码是最佳解决方案。
    • @mike 文档指出它可能会失败。我认为你必须假设它可以。也就是说,引发错误是否有任何好处尚无定论,但我也怀疑它永远不会发生。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-13
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-12
    相关资源
    最近更新 更多