玩转C科技.NET

每天都在学习,每天都在退步 为什么?世界发展太快! 怎么办?加快学习速度! 如何做?关注.NET社区 进阶中……

导航

<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

统计

公告

Subscribe to this feed Join My Community at MyBloglog!
Contact volnet online!

MSN群MyMSDN技术讨论群
群号:www.msdn@hotmail.com
Windows Live Alerts
欢迎大家踊跃加入讨论任何与技术有关的问题。
————————————
欢迎给我发送邮件:
volnet@tom.com
[标题格式]:[TO玩转C科技]<您的用户名/匿名>[<主题>]
————————————

 
您可以直接Gmail联系我噢!(Gtalk/Mail)
开机自启动,天天都在线哦!

LiveMessenger:
<My Library>
These postings are provided "AS IS" with no warranties, and confer no rights.The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

与我联系

常用链接

留言簿(4)

我参与的团队

我的标签

随笔分类(117)

随笔档案(103)

文章分类(14)

文章档案(15)

相册

家园建设

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

2008年7月18日 #

《从零到知道》(《from0toknow》)专题更新完毕!欢迎前往下载/在线阅读!

一个关于VC++的专题终于还是写完了,主要是关于VC++,MFC开发的部分知识。其中挺重要的两块,网络和数据库没有写进来,原因主要是因为这两块面比较广,而且也是很重点的,网上随便搜索都能搜索得到很多教程之类的。因此我就没有大费周章来这里写了。这个专题我取名叫《从零到知道》(《from0toknow》)。因为都是一些浅层的知识,对入门的门槛要求比较低。知识是比较陈旧的MFC,但是也是很实用的东西,基本思想等内容还是很受用的。
主要特点:《从零到知道》(《from0toknow》)之所以取这个名字,顾名思义是起个入门的作用,文章立足于动手实验,但是不乏理论指导,能提到点到的都记录着呢。学习,最好能看到成效,能做出点东西来,大家就看得比较过瘾,也比较有动力。但是很多朋友在看文档的时候或者由于不是很注意,或者由于文档有疏漏,常常很难编译通过。因此我特地把同步代码更新到相应的压缩包中了。下载压缩包可以获得全部附着代码文档和彩色图文解说的完整版(原因见下)。原因:写它们的时候我是使用WORD,因此有很多图片都贴进去了。在同步更新到Blog后,丢失了很多图片,可能会影响阅读。由于时间有限不能仔细修改,因此直接把他们分别打包起来了放在相应标题的旁边,大家可以点击下载!所有代码均经过测试,详见附录!
以下是本专题在CNBLOGS的文章链接,文章并未附图,如果因为缺失图片而影响阅读,请点旁边的链接下载后用AdobeReader进行阅读。相关代码的开发环境请见附录。

Contents

[1]用预编译指令符避免多文件工程中重复定义的问题  [文档代码下载]:《from0toknow》_c++_pdf.rar
[2]关于MFC画图的一些总结,MFC (Draw) [文档代码下载]:《from0toknow》_Draw_pdf_code.rar
[3]Text文本类的部分操作 [文档代码下载]:《from0toknow》_Text_pdf_code.rar
[4]Menu-静态菜单和动态菜单 [文档代码下载]:《from0toknow》_Menu_pdf_code.rar
[5]对话框资源的各种属性方法的使用 [文档代码下载]:《from0toknow》_Dialog_pdf_code.rar
[6]属性表单——向导的创建 [文档代码下载]:《from0toknow》_DialogControl_pdf_code.rar
[7]窗体类型的各种变换 [文档代码下载]:《from0toknow》_WndStyle_pdf_code.rar
[8]图像Graphic的相关处理 [文档代码下载]:《from0toknow》_Graphic_pdf_code.rar
[9]窗体重绘,图像更新 [文档代码下载]:《from0toknow》_WndUpdatePicRedraw_pdf_code.rar
[10]文件读写和注册表读写 [文档代码下载]:《from0toknow》_File_pdf_code.rar
[11]文档序列化(理论+实际) [文档代码下载]:《from0toknow》_DocSerial_pdf_code.rar
[12]ActiveX的设计 [文档代码下载]:《from0toknow》_ActiveX_pdf_code.rar
[13]动态链接库的设计(DLL) [文档代码下载]:《from0toknow》_dll_pdf_code.rar
[14]Hooks(钩子)_用于监听消息的方法 [文档代码下载]:《from0toknow》_Hooks_pdf_code.rar

附录:
1、开发工具
Microsoft Visual Studio.NET 2003(VC++7.1)
2、预备知识
基本的C语言知识,类的概念,懂得类的功能和用途。有清晰的思路和敏捷的大脑思维能力和方式。
3、答疑回访
在学习中有任何疑问欢迎发邮件至SupportEmail:mymsdn@163.com或者登陆Blog:http://volnet.cnblogs.com进入VC++/C++相关专题内在相应文章的版面内评论(提问)。

posted @ 2008-07-18 22:43 volnet(可以叫我大V) 阅读(3069) | 评论 (8)编辑

[WCF]Instance Management

[有兴趣阅读本文的请从头至尾阅读,有兴趣帮助我解答疑问的请从尾至头读(红色部分),万分感谢!]
我们很容易理解在旧有编程模型中关于类实例的内容。设计模式中Singleton也就是在描述着档子事。但基于WCF并非适合于以上场景,Service与Client之间要保持良好的Instance模型则需要依靠很多其他机制。

Programming WCF Service Chapter4 对此进行了细致的描述。(更多细节请自行阅读~)

WCF支持三种类型的Instance管理:

InstanceManagement

1、pre-call services:每个客户端请求对应一个instance

2、Sessionful services:每个客户端连接对应一个instance

3、Singleton services:所有客户端共享一个instance

利用Behaviors可以解决这方面的问题(还有一些其他基于“服务端”的其他方面的问题可以通过使用behaviors来解决)。

注:客户端是不知道服务端设置了什么样的behaviors的。

VS2008MSDN:ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system.servicemodel/html/88efb135-d425-e5b1-57d6-01a67158c1a5.htm

Apply the ServiceBehaviorAttribute attribute to a service implementation to specify service-wide execution behavior. (To specify execution behavior at the method level, use the OperationBehaviorAttribute attribute.) This attribute can be applied only to service implementations.

ServiceBehaviorAttribute:仅应用于服务实现。

OperationBehaviorAttribute:用于方法级别。

//瞧这里什么属性都没有 
public interface IMyContract
{} 
//而是设置在了具体服务实现上 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyContract : IMyContract,IDisposable 
{}


设置instance模式类型由ServiceBehaviorAttribute的属性InstanceContextMode进行设置,默认值为PerSession.

Per-Call Services

只有当客户端调用的时候才有instance。

为了说明问题,书中用了很形象的例子。

Code:

public interface IMyContract
{
    [OperationContract]
    
string GetData(int value); 
    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite); 
    
// TODO: Add your service operations here
    [OperationContract]
    
void Count();
}
[ServiceBehavior(InstanceContextMode 
= InstanceContextMode.PerCall)]
public class MyContract : IMyContract,IDisposable
{
    
//Other Members
    public MyContract()
    {
        Trace.WriteLine(
"WcfServiceLibrary1.MyContract()");
    } 
    
#region IMyContract Members
    
int count = 0;
    
public void Count()
    {
        count
++;
        Trace.WriteLine(
"Counter = " + count);
    } 
    
#endregion 
    
#region IDisposable Members 
    
public void Dispose()
    {
        Trace.WriteLine(
"WcfServiceLibrary1.Dispose()");
    } 
    
#endregion
}
//Tester

ServiceReference1.MyContractClient proxy 
= new ConsoleApplication1.ServiceReference1.MyContractClient(); 

proxy.Count();
proxy.Count();
proxy.Count();

proxy.Close();
Console.ReadKey();

结果为:

WcfServiceLibrary1.MyContract()
Counter = 1
WcfServiceLibrary1.Dispose()
WcfServiceLibrary1.MyContract()
Counter = 1
WcfServiceLibrary1.Dispose()
WcfServiceLibrary1.MyContract()
Counter = 1
WcfServiceLibrary1.Dispose()

很明显,每次的值都是0+1的结果,这正说明了percall的方式是每个请求一个Instance的。

Per-Session Services

修改上面的例子:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

为:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]

结果为:

WcfServiceLibrary1.MyContract()
Counter = 1
Counter = 2
Counter = 3
WcfServiceLibrary1.Dispose()

很明显Instance只有一个了。

我们WcfServiceLibrary默认的Bind是wsHttpBinding,但若是basicHttpBinding,由于每个http到达服务端都是一个新的连接,因此服务端无法判断是哪个连接。

增加服务端app.config中Endpoint。

<endpoint address="basic" binding="basicHttpBinding" name="basic" contract="WcfServiceLibrary1.IMyContract" />

重新导入后修改Program里的程序:

ServiceReference1.MyContractClient proxy = new ConsoleApplication1.ServiceReference1.MyContractClient("basic");

ServiceReference1.MyContractClient proxy = new ConsoleApplication1.ServiceReference1.MyContractClient("WSHttpBinding_IMyContract");

其中basic和WSHttpBinding_IMyContract为两种不同形式的服务在客户端的Endpoint.Name。

之前默认WSHttpBinding_IMyContract,现在由于存在多个Endpoint,则需要显示指定。

现指定为basic。再次运行,结果:

WcfServiceLibrary1.MyContract()
Counter = 1
WcfServiceLibrary1.Dispose()
WcfServiceLibrary1.MyContract()
Counter = 1
WcfServiceLibrary1.Dispose()
WcfServiceLibrary1.MyContract()
Counter = 1
WcfServiceLibrary1.Dispose()

其结果与PerCall是相同的。

通过SessionId可以获得Instance的SessionId

使用Per-Session方式可以通过设置SessionMode属性(允许、必须、不允许三种枚举)。

SessionMode:Gets or sets a value that indicates whether a session is required by the contract.

SessionMode 枚举http://msdn2.microsoft.com/zh-cn/library/system.servicemodel.sessionmode.aspx

Allowed(允许)
Specifies that the contract supports sessions if the incoming binding supports them.

如果绑定支持Session的话,则让其支持,否则按照可以支持的方式,比如PerCall的方式进行支持。

Required(必须)
Specifies that the contract requires a sessionful binding. An exception is thrown if the binding is not configured to support session.

指定契约必须使用Sessionful的方式。如果不支持,则抛出异常。

NotAllowed(不允许)
Specifies that the contract never supports bindings that initiate sessions.

指定不能使用Sessionful的方式。作者推荐是用NotAllowed的时候仅用PerCall方式。

刚才由于我添加了basic的方式,因为默认选中Allowed,因此刚才的之所以结果与PerCall相同,是因为它,下面我将其修改为Required。

[ServiceContract]

修改为

[ServiceContract(SessionMode=SessionMode.Required)]

结果为一个运行时错误:

System.InvalidOperationException: Contract requires Session, but Binding 'BasicHttpBinding' doesn't support it or isn't configured properly to support it.
   at System.ServiceModel.Description.DispatcherBuilder.BuildChannelListener(StuffPerListenUriInfo stuff, ServiceHostBase serviceHost, Uri listenUri, ListenUriMode listenUriMode, Boolean supportContextSession, IChannelListener& result)
   at System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)
   at System.ServiceModel.ServiceHostBase.InitializeRuntime()
   at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open()
   at Microsoft.Tools.SvcHost.ServiceHostHelper.OpenService(ServiceInfo info)

但是若使用wsHttpBinding,但却without security and without reliable messaging也将无法维持transport-level session。

添加如下代码到app.config(Service)(指定其为无安全并且不可靠消息)

<system.serviceModel><!-- Other service Model -->

<bindings>
    
<wsHttpBinding>
      
<binding name="wsBinding">
        
<reliableSession enabled="false" />
        
<security mode="None" />
      
</binding>
    
</wsHttpBinding>
  
</bindings>
</system.serviceModel>

 

 

<endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary1.IMyContract" bindingConfiguration="wsBinding" >


增加绑定配置为wsBinding

确保服务端程序为:
[ServiceContract(SessionMode = SessionMode.Allowed)]
(或者没设)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]

运行结果与PerCall的结果相同。其原因也就是因为wsHttpBinding未设置安全可靠的Session。

超时

inactivityTimeout:超时时间

在连接空闲的情况下,以客户端和服务端的超时时间中最短的那个来决定是否移除Instance,若之后再调用则会抛出异常。

作者额外注明可以采用:AutomaticSessionShutdown属性。其设置为true则当proxy.Close()的时候自动关闭Session,设为false的时候则只有在服务端将服务关闭才会关闭Session。

但是,若将其修改为NotAllowed

则结果与PerCall相同(手动写为PerSession)。(不管服务配置如何,它总会是PerCall。因为TCP和IPC协议总是维持transport level,你不能将它们配置SessionMode.NotAllowed,它们会在服务载入时进行验证。作者建议是“在选择使用SessionMode.NotAllowed的同时,将服务配置为PerCall”。)

Singleton Service

Singleton,顾名思义就是仅有一个Instance,供所有客户端调用。

在说明问题之前先修改上面的例子:

//[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
//[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
为:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

Tester中:

static void Main(string[] args)
{
    ServiceReference1.MyContractClient proxy 
= new ConsoleApplication1.ServiceReference1.MyContractClient("WSHttpBinding_IMyContract");
    proxy.Count();
    proxy.Count();
    proxy.Count();
    Console.WriteLine(proxy.Endpoint.Name);
    Console.WriteLine(proxy.InnerChannel.SessionId);
    proxy.Close();
    Console.WriteLine(proxy.Endpoint.Name);
    Console.WriteLine(proxy.InnerChannel.SessionId);
    Console.WriteLine(
"_________________________________________________________");
    Console.ReadKey(); 
    ServiceReference1.MyContractClient proxy1 
= new ConsoleApplication1.ServiceReference1.MyContractClient("WSHttpBinding_IMyContract"); 
    proxy1.Count();
    proxy1.Count();
    proxy1.Count();
    Console.WriteLine(proxy1.Endpoint.Name);
    Console.WriteLine(proxy1.InnerChannel.SessionId);
    proxy1.Close();
    Console.WriteLine(proxy1.Endpoint.Name);
    Console.WriteLine(proxy1.InnerChannel.SessionId);
    Console.WriteLine(
"_________________________________________________________");
    Console.ReadKey(); 
    ServiceReference1.MyContractClient proxy2 
= new ConsoleApplication1.ServiceReference1.MyContractClient("basic"); 
    proxy2.Count();
    proxy2.Count();
    proxy2.Count();
    Console.WriteLine(proxy2.Endpoint.Name);
    Console.WriteLine(proxy2.InnerChannel.SessionId);
    proxy2.Close();
    Console.WriteLine(proxy2.Endpoint.Name);
    Console.WriteLine(proxy2.InnerChannel.SessionId);
    Console.WriteLine(
"_________________________________________________________");
    Console.ReadKey();
}


运行的结果:(Output<Debug>)

//客户端调用前

//...
//其他代码
//...
WcfServiceLibrary1.MyContract()
//...
//其他代码
//...

//客户端调用后

//...
//其他代码
//...
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 6
Counter = 7
Counter = 8
Counter = 9
//...
//其他代码
//...

从Counter的值看来,多个proxy调用的是同一个Instance。

值得一提的是WcfServiceLibrary1.MyContract(),也就是构造函数的调用时间是在Service启动的时候,而PerCall与Sessionful构造函数调用时间都是在proxy调用之时。而且只有当Host关闭的时候才会Dispose()。

MSDN:

If the InstanceContextMode value is set to Single the result is that your service can only process one message at a time unless you also set the ConcurrencyMode value to Multiple.

也就是说除非将服务设置成多线程的,否则在一个时间只能处理一个消息请求。

从HOST端控制SingletonInstance

方式一:

修改代码:

static void Main(string[] args)
{
    Uri baseAddress 
= new Uri(http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/MyContract/);
    WcfServiceLibrary1.MyContract singleInstance = new WcfServiceLibrary1.MyContract();
    singleInstance.CountProperty 
= 200;//设置初始值200
    System.ServiceModel.ServiceHost host = new System.ServiceModel.ServiceHost(singleInstance);
    host.AddServiceEndpoint(
typeof(WcfServiceLibrary1.IMyContract), new WSHttpBinding(), baseAddress);
    host.Open();
    Console.WriteLine(
"host state = {0}", host.State.ToString()); 
    ServiceReference1.MyContractClient proxy 
= new ConsoleApplication1.ServiceReference1.MyContractClient("WSHttpBinding_IMyContract");
    proxy.Count();
    proxy.Count();
    singleInstance.CountProperty 
= 100;//修改singletonInstance.CountProperty为100
    proxy.Count();
    Console.WriteLine(proxy.Endpoint.Name);
    Console.WriteLine(proxy.InnerChannel.SessionId);
    proxy.Close();
    Console.WriteLine(proxy.Endpoint.Name);
    Console.WriteLine(proxy.InnerChannel.SessionId);
    Console.WriteLine(
"_________________________________________________________");
    Console.ReadKey(); 
    host.Close();
}

public class MyContract
添加属性:
public int CountProperty
{
    
set
    {
        count 
= value;
    }
}

无须启动WcfServiceLibrary1(将用外部Host进行启动)直接运行客户端程序:

结果:

//客户端调用前

//...
//其他代码
//...
WcfServiceLibrary1.MyContract()
//...
//其他代码
//...

//客户端调用后

//...
//其他代码
//...
Counter = 201
Counter = 202
Counter = 101
//...
//其他代码
//...

方式二:

通过OperationContext.Current.Host 来获取当前进程的host

此方法我暂时未调出来,大家有想到或做到的麻烦告诉我!

 

ServiceHost host = OperationContext.Current.Host as ServiceHost;
MyContract singletonInstance 
= host.SingletonInstance as MyContract;
if (singletonInstance != null)
    singletonInstance.CountProperty 
= DateTime.Now.Second; 

 

先假设以上方法可行吧。

现在我遇到的问题:

1、OperationContext.Current这里的(MSDN:Gets or sets the execution context for the current thread.
)current thread是指我ConsoleApplication也就是Client的线程呢,还是指Service端的线程?(据我常理分析应该是服务端的线程)。理论上我应该在Count()方法内写这段代码,但是问题又涉及到通过Client端进行调用时是使用proxy,这样真正的情况会是怎样呢?

2、因此我又写了一个在一个ConsoleApplication里完成服务的代码,此时在host.SingletonInstance的确是被赋值了(不再是null了),但是紧接着我调用OperationContext.Current却发现其为null,也就是说这里的OperationContext并没有被赋值。继而将其转移到同在一个程序内的MyContract.Count()方法中,但是其值仍然为空,因此此法再度失效。

希望作为高手的您能够提供一个使用OperationContext.Current的场景。什么样算是OperationContext的当前线程?

 

推荐阅读:

http://www.microsoft.com/china/MSDN/library/Windev/WindowsVista/WCFEssentials.mspx?mfr=true

posted @ 2008-07-18 20:53 volnet(可以叫我大V) 阅读(964) | 评论 (1)编辑

用预编译指令符避免多文件工程中重复定义的问题

1、用预编译指令符可以避免在多文件工程中调用文件的时候可能出现的重复定义的现象。

比如:

Main.cpp

#include “Animal.h”

#include “Fish.h”

……

 

Animal.h

class Animal()

{

                  

}

 

Fish.h

#include “Animal.h”

class Fish():public Animal

{

        

}

 

因此在调用Main.cpp的时候先运行

1#include “Animal.h”      复制Animal.h过来

class Animal()

{

                           

}

 

2#include “Fish.h” 复制Fish.h过来

#include “Animal.h”     复制Animal.h过来

class Animal()

{

                                    

}

 

class Fish():public Animal

{

                  

}

 

……

因此最后的文件应该是形如:

class Animal()

{

                           

}

class Animal()

{

                           

}

class Fish():public Animal

{

                  

}

因此重复定义了类

class Animal()

{

                           

}

是显而易见的。

这时候引入预编译指令符的方法来避免这样的现象来发生。

假设在最后的文件中我们来补充预编译指令符的方法:

 

                   #ifndef      ABCD       //如果没有定义ABCD,否则转向endif

                   #define      ABCD       //那么就定义ABCD

class Animal()

{

                           

}

#endif

 

#ifndef      ABCD       //如果没有定义ABCD,否则转向endif

                   #define      ABCD       //那么就定义ABCD

class Animal()

{

                           

}

#endif

 

class Fish():public Animal

{

                  

}

添加蓝色部分就可以避免重复定义了。

因此可以在多文本文件中做如下修改:

Main.cpp

#include “Animal.h”

#include “Fish.h”

……

 

Animal.h

#ifndef      ABCD       //如果没有定义ABCD,否则转向endif

#define      ABCD       //那么就定义ABCD

class Animal()

{

                  

}

#endif

 

Fish.h

#include “Animal.h”

#ifndef      ABCD       //如果没有定义ABCD,否则转向endif

#define      ABCD       //那么就定义ABCD