“Delphi中的SOAP客户端”;对于请求的操作,句柄处于错误状态”;

“Delphi中的SOAP客户端”;对于请求的操作,句柄处于错误状态”;,delphi,soap,delphi-xe,Delphi,Soap,Delphi Xe,我已经在VisualStudio中构建了世界上最愚蠢、最简单的SOAP服务器,只需点击大约3次。VisualStudio2010中的确切步骤是:首先创建一个新项目作为web应用程序,然后添加一个web服务类型的新项。(有关图片,请参阅接受的答案。)soap服务器服务Service1有一个简单的方法GetData: clientService1.pas中的一个片段,使用WSDL导入器创建 IService1 = interface(IInvokable) ['{967498E8-4F67-

我已经在VisualStudio中构建了世界上最愚蠢、最简单的SOAP服务器,只需点击大约3次。VisualStudio2010中的确切步骤是:首先创建一个新项目作为web应用程序,然后添加一个web服务类型的新项。(有关图片,请参阅接受的答案。)soap服务器服务Service1有一个简单的方法GetData:

clientService1.pas中的一个片段,使用WSDL导入器创建

  IService1 = interface(IInvokable)
  ['{967498E8-4F67-AAA5-A38F-F74D8C7E346A}']
    function  GetData(const value: Integer): string; stdcall;
    function  GetDataUsingDataContract(const composite: CompositeType2): CompositeType2; stdcall;
  end;
当我尝试运行此方法时,如下所示:

procedure TForm3.Button1Click(Sender: TObject);
var
 rio : THTTPRIO;
 sv:IService1;
 addr : string;
 data : string;
begin
    //addr := '....'; // url from visual studio 2010 live debug instance.
    rio := THTTPRIO.Create(nil);
    sv := GetIService1( true, addr, rio );
    try
        data := sv.GetData(  0);

        Button1.Caption := data;

    finally
        sv := nil;

        rio.Free;
    end;
 end;
我得到的错误是:

ESOAPHTTPException: 
 The handle is in the wrong state for the requested operation -    
 URL:http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/ -      
 SOAPAction:http://tempuri.org/IService1/GetData'.
当我将上面的URL粘贴到web浏览器中时,URL工作正常,因此通常的答案是Delphi中的SOAP代码不会注意到HTTP失败,这似乎不太可能。相反,看起来我要么(a)在WinInet中遇到了破坏(已知在某些windows版本中会发生),要么(b)做错了什么


在我看来,安装了VisualStudio和delphi的任何人都应该能够尝试让VisualStudio中的虚拟starter Soap服务器与delphi中的Soap客户机对话,而无需任何努力。但我想不出最简单的事情。

有一次,Embarcadero的工作人员布鲁诺·巴比特(Bruneau Babet)讨论了一段对话中的错误,这段对话早就从Embarcadero论坛上删除了

布鲁诺说:

你好,

我发布了SOAPHTTPTrans.pas的补丁版本,其中包含一个补丁 关于本期:

[论坛链接被编辑,不管怎么说都不起作用了,帖子不见了]

您仍然可以按照C++Builder中的描述重写该事件 所指章节;或者,简单得多,至少对于Delphi用户来说,简单得多 将更新的SOAPHTTPTrans.pas添加到应用程序的项目中。让我们知道 如果这对你不起作用

干杯

布鲁诺

您可以从以下内容或其他内容中获得原始论坛格式的修复和注释,这样您就不必从周围的文本中提取文件

Warren Update 2016:有人告诉我,试图在Delphi XE上使用该修复程序,但该修复程序在Delphi XE中不适用。如能对bitbucket中的代码进行任何进一步更新,以解决剩余的bug,我们将不胜感激。

我在2018年11月使用Delphi Tokyo 10.2.3遇到了一个问题:对于请求的操作问题,句柄处于错误状态,然后查看了下面的代码补丁

该代码非常旧,测试代码不再工作(SOAP服务不可用)。此外,从布鲁诺的代码中还不清楚他到底修补了什么

将该源代码与我的Delphi版本中的源代码进行比较,似乎这是
HandleWinInetError
过程(“此处的补丁”)中所需的(两个)修改:

函数THTTPReqResp.HandleWinInetError(LastError:DWord;
请求:HINTERNET;
RaiseError:布尔):DWord;
函数CallInternetErrorDlg:DWord;
变量
P:指针;
开始
结果:=InternetErrorDlg(GetDesktopWindow(),请求,LastError,
标记\u错误\u用户界面\u过滤器\u查找\u错误或
标志\u错误\u用户界面\u标志\u更改\u选项或
标志\u错误\u用户界面\u标志\u生成\u数据,P);
{再次选择客户端证书发送请求后,
注意:InternetErrorDlg在使用调用时始终返回错误\u SUCCESS
错误\u INTERNET\u客户端\u需要身份验证\u证书\u}
如果LastError=ERROR\U INTERNET\U CLIENT\U AUTH\U CERT\U需要
结果:=错误\u INTERNET\u强制\u重试;
结束;
常数
{我们的WinInet当前缺少}
互联网选项客户端证书上下文=84;
变量
Flags,FlagsLen,DWCert,DWCertLen:DWord;
ClientCertInfo:IClientCertInfo;
CertSerialNum:字符串;
{$IFDEF客户端\证书\支持}
hStore:HCERTSTORE;
CertContext:PCERT_CONTEXT;
{$ENDIF}
开始
{分派到自定义处理程序(如果有)}
如果已分配(FOnWinInetError),则
结果:=FOnWinInetError(LastError,请求)
其他的
开始
结果:=错误\u INTERNET\u强制\u重试;
{谨慎地处理无效的\u CA}
如果(LastError=ERROR\u INTERNET\u INVALID\u CA)和(soIgnoreInvalidCerts in InvokeOptions),则
开始
FlagsLen:=SizeOf(标志);
InternetQueryOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
标志:=标志或安全标志\u忽略\u未知\u CA;
InternetSetOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
结束
否则,如果(LastError=ERROR\u INTERNET\u SEC\u CERT\u REV\u失败)和(调用选项中的soIgnoreInvalidCerts),则
开始
FlagsLen:=SizeOf(标志);
InternetQueryOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
标志:=标志或安全标志忽略撤销;
InternetSetOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
结束
{$IFDEF客户端\证书\支持}
else if(LastError=ERROR\u INTERNET\u CLIENT\u AUTH\u CERT\u需要)和
支持(Self、IClientCertInfo、ClientCertInfo)和
(ClientCertInfo.GetCertSerialNumber“”)然后
开始
CertSerialNum:=ClientCertInfo.GetCertSerialNumber();
hStore:=ClientCertInfo.GetCertStore();
如果hStore=nil,则
开始
hStore:=CertOpenSystemStore(0,PChar('MY'));
ClientCertInfo.SetCertStore(hStore);
结束;
CertContext:=FindCertWithSerialNumber(hStore,CertSerialNum);
如果为零,则
开始
ClientCertInfo.SetCertContext(CertContext);
InternetSetOption(请求、INTERNET选项、客户端证书上下文、,
CertContext,SizeOf(CERT_CONTEXT));
结束
其他的
开始
如果RaiseError,则RaiseCheck(LastError);//在这里打补丁
结果:=CallInternetErrorDlg;
结束;
结束
{$ENDIF}
否则(最后)
function THTTPReqResp.HandleWinInetError(LastError: DWord; 
                                         Request: HINTERNET;
                                         RaiseError: Boolean): DWord;

  function CallInternetErrorDlg: DWord;
  var
    P: Pointer;
  begin
    Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
                               FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
                               FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
                               FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);

    { After selecting client certificate send request again,
      Note: InternetErrorDlg always returns ERROR_SUCCESS when called with
            ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED }
    if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then
      Result := ERROR_INTERNET_FORCE_RETRY;
  end;

const
  { Missing from our WinInet currently }
  INTERNET_OPTION_CLIENT_CERT_CONTEXT = 84;

var
  Flags, FlagsLen, DWCert, DWCertLen: DWord;
  ClientCertInfo: IClientCertInfo;
  CertSerialNum: string;
{$IFDEF CLIENT_CERTIFICATE_SUPPORT}
  hStore: HCERTSTORE;
  CertContext: PCERT_CONTEXT;
{$ENDIF}
begin
  { Dispatch to custom handler, if there's one }
  if Assigned(FOnWinInetError) then
    Result := FOnWinInetError(LastError, Request)
  else
  begin
    Result := ERROR_INTERNET_FORCE_RETRY;
    { Handle INVALID_CA discreetly }
    if (LastError = ERROR_INTERNET_INVALID_CA) and (soIgnoreInvalidCerts in InvokeOptions) then
    begin
      FlagsLen := SizeOf(Flags);
      InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
      Flags := Flags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
      InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
    end
    else if (LastError = ERROR_INTERNET_SEC_CERT_REV_FAILED) and (soIgnoreInvalidCerts in InvokeOptions) then
    begin
      FlagsLen := SizeOf(Flags);
      InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
      Flags := Flags or SECURITY_FLAG_IGNORE_REVOCATION;
      InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
    end
{$IFDEF CLIENT_CERTIFICATE_SUPPORT}
    else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and
             Supports(Self, IClientCertInfo, ClientCertInfo) and
             (ClientCertInfo.GetCertSerialNumber <> '') then
    begin
      CertSerialNum := ClientCertInfo.GetCertSerialNumber();
      hStore := ClientCertInfo.GetCertStore();
      if hStore = nil then
      begin
        hStore := CertOpenSystemStore(0, PChar('MY'));
        ClientCertInfo.SetCertStore(hStore);
      end;
      CertContext := FindCertWithSerialNumber(hStore, CertSerialNum);
      if CertContext <> nil then
      begin
        ClientCertInfo.SetCertContext(CertContext);
        InternetSetOption(Request, INTERNET_OPTION_CLIENT_CERT_CONTEXT,
                          CertContext, SizeOf(CERT_CONTEXT));
      end
      else
      begin
        if RaiseError then RaiseCheck(LastError);  // PATCH HERE
        Result := CallInternetErrorDlg;
      end;
    end
{$ENDIF}
    else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and (soPickFirstClientCertificate in InvokeOptions) then
    begin
      { This instructs WinInet to pick the first (a random?) client cerficate }
      DWCertLen := SizeOf(DWCert);
      DWCert := 0;
      InternetSetOption(Request, INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT,
                        Pointer(@DWCert), DWCertLen);
    end
    else
    begin
      if RaiseError then RaiseCheck(LastError);  // PATCH HERE
      Result := CallInternetErrorDlg;
    end;
  end;
end;
Uses SOAP.SOAPHTTPTrans;

const Request2 =
'<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ndf="http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">' +
'   <soapenv:Header/>' +
'   <soapenv:Body>' +
'      <ndf:NDFDgenByDay soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' +
'         <latitude xsi:type="xsd:decimal">38.9936</latitude>' +
'         <longitude xsi:type="xsd:decimal">-77.0224</longitude>' +
'         <startDate xsi:type="xsd:date">%tomorrow%</startDate>' +
'         <numDays xsi:type="xsd:integer">5</numDays>' +
'         <Unit xsi:type="dwml:unitType" xmlns:dwml="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">e</Unit>' +
'         <format xsi:type="dwml:formatType" xmlns:dwml="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">12 hourly</format>' +
'      </ndf:NDFDgenByDay>' +
'   </soapenv:Body>' +
'</soapenv:Envelope>';

const URL2= 'https://graphical.weather.gov:443/xml/SOAP_server/ndfdXMLserver.php';

procedure TFrmHandleWinINetError.Button1Click(Sender: TObject);
var
  RR: THTTPReqResp;
  Response: TMemoryStream;
  U8: UTF8String;
begin
  RR := THTTPReqResp.Create(nil);
  try
    try
      RR.URL := URL2;
      RR.UseUTF8InHeader := True;
      RR.SoapAction := 'NDFDgenByDay';
      Response := TMemoryStream.Create;
      RR.Execute(Request2, Response);
      SetLength(U8, Response.Size);
      Response.Position := 0;
      Response.Read(U8[1], Length(U8));
      ShowMessage(String(U8));
      except
        on E:Exception do ShowMessage('ERROR CAUGHT: ' + e.message);
      end;
    finally
      Response.Free;
      RR.Free;
    end;
  end;
end;