这是这几天里我同时解决的另一个问题。
我用的是VC2003,在非托菅的本机代码中调用webservice时,很有一些令人糊涂的地方,在这里记下来,以后也方便自己回忆。
方法:
1.增加webservice引用。
2.自动生成代理类。
上面简单。但当看着所生成的代理类时,多年没碰过VC代码的我哭了……VC的代理类是下面这样的:
template <typename TClient = CSoapSocketClientT<> >
}
模板套模板。。。
嘛意思?
意思是:代理类在实例化时,要指定一个网络客户端工具类。默认这个类是CSoapSocketClientT<>, 这也是一个模板类。
{ 。。。}
看,默认的默认类是ZEvtSyncSocket,这是个啥类我实在没有心情去研究,看名字是一个Sock通讯类。不要不要!
那么调用webservice方法仲么办? 要这样:
proxy->SetUrl("http://xxx.com/Webservice1.asmx");
proxy->HelloWork();
这里我没有用VC神经乎乎地推荐的那个 CSoapSocketClientT<ZEvtSyncSocket>, 而是用了CSoapWininetClient类做为工具类(atlsoap.h)。
注意上面的代码,有一个SetUrl的方法调用,这个方法你一定在代理类中找不到,但是编译运行又都正常。奇怪不?
请您这时候再回去注意一下CWebService1T的定义,CSoapWininetClient被置换为TClient泛型参数,而TClient又被放在了类定义的冒号后面,啥意思?
就是说你代入的这个模板参数类型将是代理类的父类呀!简直把人绕晕了!
也就是说,我写CWebService1T<CSoapWininetClient> proxy 时,CSoapWininetClient将是CWebService1T<>的基类!CSoapWininetClient有SetUrl, SetProxy等方法,所以proxy 也就可以调用这些方法了。
因为 CSoapWininetClient是用了Wininet.dll库的api进行通信的,这个库的特点就是支持http,而且和ie的设置是相通的,因此,用了这个工具类,你的proxy对象就可以透过代理服务器调用webserivce了。
一切问题都解决了?没有。
我这两天遇上了几个做难死人的问题——一个客户的网络需要用代理服务器上网,而且是需要输入口令的。
本来这个问题也不是问题,只要ie能上网,在代理服务器要口令时,输入口令并选一下保存密码的复选框,这个用户凭据就会记录在系统中, CSoapWininetClient就可以自动取用, 很多客户的网络环境都是这样的,我们的客户端程序运行得很好。
但是不幸的是,这个客户的机器被设置了策略,就算是在ie里选了保存密码的复选框,密码也不能保存下来,每次打开ie时,都需要重新输入口令。
既然系统里没有用户凭据,CSoapWininetClient也就没有办法从ie得到用户名与口令。
我研究了CSoapWininetClient这个类,可恨的是这个类没有任何事件、callback、委托等扩展渠道让我在合适地地方切进去,设置那些代理啦、用户名啦口令啦什么的。在代理类调用hellowork时,就调用CSoapWininetClient的第一个方法就是SendRequest方法,在这个方法里,它打开对话,建立连接,Post数据、得到回应的流,一气呵成了!中间没有用户的机会加入代码。
我发愁了老半天,决定从 CSoapWininetClient派生一个子类来用,想试试能不能通过重载一下某些方法来介入自己的代码。这叫什么模式来着?装饰类?
结果发现这个类里很多粉重要的方法、成员都是private的,没法引用。
真没有办法了?我暴怒了!我和他拼了!我把 CSoapWininetClient类的源代码全拷贝出来,在项目里建了一个新类,叫CFuckedSoapWininetClient, 然后把CSoapWininetClient类的所有内容都复制了进去,现在,你是我的啦!哇哈哈!
再把调用Webservice的代码改一下,把参数类名改为Fuck过的新类。
proxy->SetUrl("http://xxx.com/Webservice1.asmx");
proxy->HelloWork();
编译,通过。运行,通过!
现在,通信类是我的了,任我哪啥了!想加代理服务器支持,那是易如翻掌了。
有关 代理服务器支持 功能的实现,请参考另一篇文章 http://www.cnblogs.com/haoxiaobo/archive/2012/01/09/2317559.html。