正在看着AV(AreCont)SDK的时候,老大突然让我转到soap上,原因是刚进公司的我,第一项任务便是研究ONVIF(so many specifications),当然主要的还是看core specification,这是一个axis,bosch and sony 三大牛x整出来的标准规范,为开发网络视频设备软件开发商提供一个标准规范,但是至今为止仍只是少数公司加入了标准规范的行列,可能是他们不求上进吧 。
在core specification中最主要的还是提到了webservice,兼容RTSP,RTCP, RTP 实现实时流传输,采用vc++实现,所以公司还是考虑通过vc++调用webservice,所以老大便给了我这个任务,先熟悉一下。整了一天,才搞了个差不多,感觉很是纳闷,其实程序看起来很简单,但是细节往往能让人迷糊一大段时间。
其实看了阿瑞得文章后,基本上差不多,当然先装soap toolkit3.0是前提,不过中间还是遇到很多问题的,又通过不断的查阅才得以解决,其中总是发生中断的情况,这个主要是因为webservice得URL没有写正确或者没有搞清URL和Namespace Uri的原因吧。不多说了,还是先看我做的流程吧。
First step: New a Asp.net Web Service Application project.
The following is the content of Service1.asmx.cs:
using System; using System.Linq; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using System.Xml.Linq; namespace TestWebService { [SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)] [WebService(Namespace = "http://localhost/WebService/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 // [System.Web.Script.Services.ScriptService] public class Service : System.Web.Services.WebService { public Service() { //CODEGEN: 该调用是 ASP.NET Web 服务设计器所必需的 InitializeComponent(); } #region 组件设计器生成的代码 //Web 服务设计器所必需的 private System.ComponentModel.IContainer components = null; /**/ /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { } /**/ /// <summary> /// 清理所有正在使用的资源。 /// </summary> protected override void Dispose(bool disposing) { if (disposing && components != null) { components.Dispose(); } base.Dispose(disposing); } #endregion // WEB 服务示例 // HelloWorld() 示例服务返回字符串 Hello World // 若要生成,请取消注释下列行,然后保存并生成项目 // 若要测试此 Web 服务,请按 F5 键 [WebMethod] public int Hello(int strName, int strPwd) { int temp = strName * strPwd; return temp; } [WebMethod] public string HelloWorld(int strName, int strPwd) { return "我是shawn!"; } [WebMethod] public string GetStringName() { return "shawn,rock!!!"; } } }
Then F5 debug mode test the application.
Second step:
New a win32 console application:
New a WSWrapper class:
WSWrapper.h
#ifndef _WS_WRAPPER_H_ #define _WS_WRAPPER_H_ #import "msxml4.dll" #import "C:/Program Files/Common Files/MSSoap/Binaries/mssoap30.dll" / exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", / "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME") #include <string> #include <Windows.h> using namespace MSXML2; using namespace MSSOAPLib30; using std::string; typedef struct { char * sParamName; char * sParamValue; }WSPARAM; class WSWrapper { public: WSWrapper(); virtual ~WSWrapper(); bool InvokeSoap(string wsURL, string wsNameSpace, string wsMethod, WSPARAM *wsParam, int wsCount, char *sEmsg, char *sRmsg, bool wsXML); private: /* const char *_wsURL; const char * _wsNameSapce; const char *_wsMethodName; */ }; #endif WSWrapper.cpp #include"WSWrapper.h" #include<cstdlib> #include<cstdio> using namespace std; WSWrapper::WSWrapper() { } WSWrapper::~WSWrapper() { try { CoUninitialize(); } catch(...) { } } // 参数说明: // wsURL:WEBSERVICE的链接地址 // wsNameSpace:在页面中定义的命名空间,一般地使用:http://tempuri.org/ // wsMethod:要访问的WEBSERVICE 方法名 // wsParam: 要使用的参数,见WSPARAM定义, // wsCount: 参数总数 // sEmsg://要返回给调用函数的错误码率信息 // sRmsg: //要返回的捃执行结果信息 // wsXML:要返回的是XML值还是TEXT // 返回值: // 如果正常执行返回true,中间有问题就返回false; bool WSWrapper::InvokeSoap(string wsURL, string wsNameSpace, string wsMethod, WSPARAM *wsParam, int wsCount, char *sEmsg, char *sRmsg, bool wsXML) { HRESULT hr = CoInitialize(NULL);//初始化com环境 if(FAILED(hr)) { strcpy(sEmsg,"初始化COM失败"); printf("initialized com failed."); return false; } ISoapSerializerPtr Serializer; ISoapReaderPtr Reader; ISoapConnectorPtr Connector; //连接到WebService hr = Connector.CreateInstance(__uuidof(HttpConnector30)); if(FAILED(hr)) { //创建com对象出错,一般是因为没有安装com strcpy(sEmsg,"创建HttpConnector FAILED"); printf("create HttpConnector failed."); return false; } Connector->Property["EndPointURL"] = wsURL.c_str(); hr = Connector->Connect(); if (FAILED(hr)) { strcpy(sEmsg,"连接WEBSERVICE FAILED"); printf("connecting webservice failed."); return false; } Connector->Property["SoapAction"] = (wsNameSpace + wsMethod).c_str(); //开始创建webservice的请求Soap包 Connector->BeginMessage(); hr = Serializer.CreateInstance(__uuidof(SoapSerializer30)); if (FAILED(hr)) { strcpy(sEmsg,"CreateInstance Serializer FAILED"); printf("create instance Serializer failed."); return false; } Serializer->Init(_variant_t((IUnknown*)Connector->InputStream)); Serializer->StartEnvelope("SOAP", "http://schemas.xmlsoap.org/soap/envelope/", ""); Serializer->SoapAttribute("xsi", "", "http://www.w3.org/2001/XMLSchema-instance", "xmlns"); Serializer->SoapAttribute("xsd", "", "http://www.w3.org/2001/XMLSchema", "xmlns"); //Serializer->StartEnvelope("","",""); Serializer->StartBody(L"NONE"); Serializer->StartElement(wsMethod.c_str(), wsNameSpace.c_str(), "NONE", ""); for (int i = 0; i < wsCount; i++) { Serializer->StartElement(wsParam[i].sParamName,wsNameSpace.c_str(),"NONE",""); Serializer->WriteString(wsParam[i].sParamValue); Serializer->EndElement(); } Serializer->EndElement(); Serializer->EndBody(); Serializer->EndEnvelope(); try { Connector->EndMessage(); } catch (...) { strcpy(sEmsg,"提交SOAP消息出错"); printf("提交SOAP消息出错"); return false; } //解析返回的soap包 hr = Reader.CreateInstance(__uuidof(SoapReader30)); if(FAILED(hr)) { strcpy(sEmsg,"CreateInstance READER FAILED"); printf("create instance reader failed."); return false; } Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), ""); if (wsXML) { strcpy(sRmsg,(const char*)Reader->RpcResult->xml); } else strcpy(sRmsg,(const char*)Reader->RpcResult->text); return true; }
#include "WSWrapper.h" int main() { string url = "http://localhost:1451/Service1.asmx"; string uri = "http://localhost/WebService/"; string methodName = "HelloWorld"; WSPARAM *params = new WSPARAM[2]; params[0].sParamName = "strName"; params[0].sParamValue = "3"; params[1].sParamName = "strPwd"; params[1].sParamValue = "4"; char errorMessage[20]; char revMessage[100]; WSWrapper wsWrapper; int wsParaCount = 2; bool retValue = wsWrapper.InvokeSoap(url,uri,methodName,params,wsParaCount,errorMessage,revMessage,false); if (retValue) { printf("%s",revMessage); } getchar(); getchar(); return 0; }
Serializer->StartElement("strName", _wsNameSapce.c_str(), "NONE", ""); //Serializer->SoapAttribute("xsi:type", "", "xsd:string", ""); Serializer->WriteString("3"); Serializer->EndElement(); Serializer->StartElement("strPwd",_wsNameSapce.c_str(),"NONE",""); Serializer->WriteString("4"); Serializer->EndElement();
一开始的时候,并没有指定strName和strPwd的namespace,最好是跟method的namespace是一致的,就是_wsNameSapce.c_str()。还有一个就是注意当运行webservice的时候把TestApp.cpp中的url改成运行测试webservice的url路径。当然还遇到了其他很多方面的问题,欢迎有需要的人跟我交流。