WCF for .NET CF快速入门

摘要
本文可以看作是Chris Tacke的文章An Introduction to WCF for Device Developers的翻译,文中代码由于笔者调试需要稍作了修改,局部做了进一步的说明,并将部分链接更新。
Keyword
Windows Mobile, WCF, .NET CF, Services, Xml Serialization

这是一篇我推荐给很多朋友看过的文章。文章很短,也没有涉及太多WCF原理性的东西,但是作为WCF Mobile开发入门,这却是个很好的向导,为了方便阅读,有些地方我做了额外的说明。
Enjoy it~

1 开发环境与工具

1. Visual Studio 2008 (Orcas) RTM.
2. Power Toys for .NET Compact Framework 3.5
3. 一台 支持.NET Compact Framework3.5Windows Mobile 5.0/6.0 的模拟器或者设备.
(Chris Tacke在他的设备上调试成功,在模拟器上由于某些不可知的因素失败了。笔者在模拟器上调试成功)
4. Windows XP 或者 Vista的操作系统
(Chris TackeXP下调试的,笔者则是在Vista)

2 PC
端的WCF服务程序

当然,这只是一个很简单的模型,就是通过WCF使移动设备与PC进行信息交互。
首先,我们需要在PC机上构建一个服务,这个服务实现的功能很简单,就是进行两个整数的加法,并返回结果。
第一步,打开你的VS创建一个控制台程序,并添加引用:System.ServiceModel
然后,添加一个新的类(事实上是一个接口,它用于定义我们的服务所暴露出来的契约),我们把这个类命名为IMyProcess,其内容如下:

using System.ServiceModel;
namespace Freesc.WCF.Demo
{
        [ServiceContract(Namespace 
= http://Freesc.WCF.Demo)]
       public interface IMyProcess
        
{
            [OperationContract]
            
int Add(int a, int b);
        }

}

接下来,我们需要一个该借口的具体实现来作为我们的服务。为我们的工程另添加一个类,叫做MyProcessService,也就只包含一个Add方法。其代码如下:

using System;

namespace Freesc.WCF.Demo
{
 
public class MyProcessService:IMyProcess
    
{
      
public int Add(int a, int b)
      
{
          Console.WriteLine(
string.Format(
          
"Received 'Add({0}, {1})' returning {2}", a, b, a + b));
          
return a + b;
      }

    }

}

好,服务的内容已经写好了,我们需要来看看这个PC端的Server是怎么工作的了。首先,请将你的Program.cs替换为如下代码:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Net;

namespace Freesc.WCF.Demo
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
          
//获得服务器IP
            string hostIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[2].ToString();
#if CLIENT_DISCOVERY_BUILD
          Uri address 
= new Uri(string.Format("http://localhost:8000/calculator", hostIP));
#else
          Uri address 
= new Uri(string.Format("http://{0}:8000/calculator", hostIP));
#endif
            ServiceHost serviceHost 
= new ServiceHost(typeof(MyProcessService), address);
            
try
            
{
                
// 添加一个服务节点
                serviceHost.AddServiceEndpoint(
                    
typeof(IMyProcess),
                    
new BasicHttpBinding(),
                    
"Calculator");
#if !CLIENT_DISCOVERY_BUILD
                
// 允许运行时的元数据交互(使用NetCfSvcUtil工具)
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled 
= true;
                serviceHost.Description.Behaviors.Add(smb);
#endif
                serviceHost.Open();
                Console.WriteLine(
"MyProcessService is running at " + address.ToString());
                Console.WriteLine(
"Press <ENTER> to terminate");
                Console.ReadLine();
                
// 把服务状态置为关闭
                serviceHost.Close();
            }

            
catch (CommunicationException ce)
            
{
                Console.WriteLine(
"An exception occured: {0}", ce.Message);
                serviceHost.Abort();
            }

        }

    }

}


这段程序要稍稍复杂一点,首先,你也许会注意到这里用了一些预编译指令。使用这些预编译指令是因为我们要在稍后的一个工具中访问这个运行的服务并为我们生成一些代码。如果你暂时还比较晕的话,别急,稍后会说到这个工具的。

这里的CLIENT_DISCOVERY_BUILD只是一个条件编译的符号,您暂且可以无视之。其实这里只是为了我们编译代码的时候版本切换比较方便。

某些情况下我们希望把前面编译的A版本的代码暂存起来,以免在CS文件中该来改来改去,这时只需要在Debug 的配置项目下做一些修改,比如这里我们加上了Discover这一项,用记事本打开工程文件,可以看到这一段:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Discovery|AnyCPU' ">
    
<DebugSymbols>true</DebugSymbols>
    
<OutputPath>bin"Discovery"</OutputPath>
    
<DefineConstants>TRACE;DEBUG;CLIENT_DISCOVERY_BUILD</DefineConstants>
    
<DebugType>full</DebugType>
    
<PlatformTarget>AnyCPU</PlatformTarget>
    
<ErrorReport>prompt</ErrorReport>
 
</PropertyGroup>

好,那这一段主程序代码到底有什么作用呢?首先,我们需要为我们的服务创建一个Uri

要创建这个Uri首先要知道本机的IP。这里如果你直接Copy Chris Tacke的代码也许不能运行(特别是Vista用户),可能会抛出一个这样的异常:

这个时候你需要检查你的IPAddressAddressFamily,或者直接CMD下察看ipconfig,如果是IPV6的话(如我机器上的AddressList[0]就是iPV6的格式),则会抛除上述异常,iPv4则一切OK

接下来,我们创建了一个服务主机(ServiceHost)和一个服务节点(endpoint),请注意,这里我们用的协议绑定方式是BasicHttpBinding,在精简版的WCF framework中默认只有两种绑定绑定方式(另一种是WindowsMobileMailBinding,我在今后的文章中会介绍)。然后我们给服务起了个名字叫做“Calculator”。

接下来,可以看到我们先前提到过的条件编译符号,在我们开启Discovery的配置环境下,我们的工具(下文会提到)可以与该程序集的元数据进行交互用来生成一些代码,不过上面的代码里是处于关掉的模式。

好了,现在可以来试着编译这段代码了,编译的时候请确保在Discover的模式下。

如果一切OK,你将会看到如下的画面:

注意:如果是Vista用户,如果开启了UAC,你可能需要以管理员的身份运行编译好的可执行程序,否则可能会有某些权限问题。

然后,我们在浏览器中键入这里显示的地址,可以看到如下画面

如果你的确看到上述页面,很好,我们的工作已经完成一半了。

3. NetCFSvcUtil.exe

下面我们需要借助一个小工具---netcfSvcUtil来帮助我们生成一些代码,这个方法适用于那些初次进行WCF for Mobile编程的人,或者是象我这样比较懒,比较喜欢使用小工具的程序员。

打开你的命令行工具,你的Power Toy默认安装在类似这样的目录下:
C:"Program Files"Microsoft.NET"SDK"CompactFramework"v3.5"bin.

在之前的服务运行的时候,在该目录下运行如下的命令:

netcfSvcUtil.exe /language:cs http://localhost:8000/calculator

你可以看到如图的输出:

此时你会发现在该目录下生成了两个
CS文件,这里分别是MyProcessService.csCFClientBase.cs
在接下来的步骤中,我们会用到这两个文件。

4. CF端的应用程序

接下来,我们新建一个Smart Device 的窗体应用程序,工程命名为DeviceClient。将前面生成的那两个文件Copy到该工程中来,然后,我们得添加如下一些引用(主要是那两个由工具生成的cs文件需要)

- System.ServiceModel

- System.Runtime.Serialization

这里的代码如下:

      public Form1()
        
{
            InitializeComponent();
            
try
            
{
//在TextBox中显示本机IP
                tbMyIP.Text = Dns.GetHostEntry(
                Dns.GetHostName()).AddressList[
0].ToString();
            }

            
catch (Exception ex)
            
{
                MessageBox.Show(
"No NIC found?");
            }

        }

       
private void menuCalculate_Click(object sender, EventArgs e)
        
{
            
int a = 0;
            
int b = 0;
            
try
            
{
//接受文本框的输入,并存入整形变量a,b中
                a = int.Parse(tbFirst.Text);
                b 
= int.Parse(tbSecond.Text);
            }

            
catch
            
{
                
//如果转换失败(比如填入了无效的数字)则直接返回
                return;
            }

            SMC.Binding binding 
= MyProcessClient.CreateDefaultBinding();
            
string remoteAddress = MyProcessClient.EndpointAddress.Uri.ToString();
            remoteAddress 
= remoteAddress.Replace("localhost", “192.168.1.101”);
            EndpointAddress endpoint 
= new EndpointAddress(remoteAddress);
            MyProcessClient client 
= new MyProcessClient(binding, endpoint);
            
try
            
{
                lbResult.Text 
= client.Add(a, b).ToString();
            }

            
catch (Exception ex)
            
{
                MessageBox.Show(ex.Message);
            }

        }

代码很短,不过很重要的一点是我们没有使用DeviceClient的默认构造器,我们把RemoteAddress中的”localhost”替换成了服务所在主机的IP(你可以在MyProcessClient类的找到相应的静态字段)

点击menuCalculate我们看到以下的结果:
PC机上

设备上


下载完整代码

总结

从上面的例子我们看到,在Windows Mobile上构建一个WCF的应用程序并不是太难,代码量也不大,只是我们缺少相应的文档资源, 也许本文能帮你节约一点上手的时间。

希望这篇短文能对大家有所帮助,尽管它并没有对某些细节做清楚的阐述,但是相信您已经能够按照文中的做法,在Windows Mobile上轻松构建出你自己的WCF程序,那么本文的目的也就达到了。

Regards
©Freesc Huang
  黄季冬<fox23>@HUST

posted on 2008-03-18 04:03 Freesc Huang 阅读(4840) 评论(16) 编辑 收藏

评论

#1楼 2008-03-18 08:53 somesongs      

不错不错,收了。  回复 引用 查看   

#2楼 2008-03-18 17:34 蓝星空      

fox23老兄的博客已经被我的手机订阅了。呵呵

用手机看老兄的博客,学mobile开发,真是一件开心的事情呀!
 回复 引用 查看   

#3楼[楼主] 2008-03-18 18:17 fox23      

@somesongs
@蓝星空
感谢支持 欢迎常来 :)
 回复 引用 查看   

#4楼 2008-03-18 21:35 黎波      

支持:)  回复 引用 查看   

#5楼[楼主] 2008-03-18 22:21 fox23      

@黎波
常来玩 多交流 :-)
 回复 引用 查看   

#6楼 2008-03-19 00:31 fish.sophia[未注册用户]

收藏了~
多写点~
 回复 引用   

#7楼[楼主] 2008-03-19 10:33 fox23      

@fish.sophia
感谢支持,我会尽量多写的.最近实在比较忙.
 回复 引用 查看   

#8楼 2008-03-20 10:11 下风      

学习下!
你好,问个简单问题用C#怎么屏蔽ppc下的输入图标?
 回复 引用 查看   

#9楼[楼主] 2008-03-20 11:04 fox23      

@下风 您好
1.输入的图标是什么意思?
2.如果是指那个闪烁的光标的话,建议你不要屏蔽它,也没有直接的API可以屏蔽它,M$不鼓励开发者开发出不符合用户习惯的应用程序。如果你基于某些考虑十分需要那样做,我只能告诉你一些间接的手段:
方法a.将Textbox设为不可用(enable = false?lock? or both ),编程更新它的text.
方法b.设置以lable获取焦点,隐藏一个TextBox,并用户输入的时候把结果显示到Lable中
3.以后问问题请到
http://winbile.net/bbs
以免影响其他用户阅读和评论,也可以让更多人来帮你解决问题

Regards
Freesc
 回复 引用 查看   

#10楼 2008-05-23 20:20 傻样精英      

我本机有个服务,endpoint命名是用机器名方式的,怎么设置让模拟器能访问本机的服务啊?总是提示:没有终结点对可能接受消息的Http://tp0659()/SCMISWCFHost/Login.svc经行监听。这通常是因为地址或SOAP操作不正确所致。有关更多详细信息,请查看InnerException的相关内容(如果存在)。  回复 引用 查看   

#11楼[楼主] 2008-05-25 00:18 fox23      

@傻样精英
模拟器cradle了没有?PC上能否访问到该服务?如果要在模拟器上使用机器名访问的话,请先确保服务已经正确挂载在IIS下
 回复 引用 查看   

#12楼 2009-05-04 08:11 BruceZhou      

@博主,有问题请教下,
我在模拟器上调试成功,但在我的手机 WM6.5+.net cf 3.5+无线上没有成功,
1.在手机用USB线连接电脑后,调用当前例子成功
2.断开USB线,连接 无线,上wap.baidu.com成功,
3.调用当前例子没成功,和10楼的错误一样
总是提示:没有终结点对可能接受消息的Http://*****经行监听。这通常是因为地址或SOAP操作不正确所致。有关更多详细信息,请查看InnerException的相关内容(如果存在)。

我反复测试,一直没有成功,博主大才,有没有遇到类似问题,
多谢
 回复 引用 查看   

#13楼 2010-11-18 11:08 qinget      

@BruceZhou
我已经测试成功!!请查看防火墙打开8000端口
 回复 引用 查看   

#14楼 2011-08-24 12:09 fdia      

总是提示:没有终结点对可能接受消息的Http://*****经行监听。这通常是因为地址或SOAP操作不正确所致。有关更多详细信息,请查看InnerException的相关内容(如果存在)。  回复 引用 查看   

#15楼 2011-08-24 12:10 fdia      

楼主给答复下吧  回复 引用 查看   

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

公告

These postings are provided "AS IS" with no warranties
and confer no rights.


Locations of visitors to this page

Blog Keywords
Embedded System,Visual Studio,.Net Framework,.Net Compact Framework,.Net Micro Framework,Windows Mobile,Windows Embedded CE,Emulator,WCF,CLR,Design & Pattern,C/C++,C#,Matlab,Algorithms
昵称:Freesc Huang
园龄:4年11个月
粉丝:32
关注:4

统计

  • 随笔 - 197
  • 文章 - 0
  • 评论 - 840

搜索

 

随笔分类(227)

随笔档案(197)

Blogs

Link

积分与排名

最新评论

阅读排行榜

评论排行榜

推荐排行榜