﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-ivy-jie-文章分类-MFC</title><link>http://www.cppblog.com/ivy-jie/category/10519.html</link><description>progress ...</description><language>zh-cn</language><lastBuildDate>Wed, 24 Jun 2009 06:56:27 GMT</lastBuildDate><pubDate>Wed, 24 Jun 2009 06:56:27 GMT</pubDate><ttl>60</ttl><item><title>转:DLL 编程</title><link>http://www.cppblog.com/ivy-jie/articles/88319.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 22 Jun 2009 12:46:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/88319.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/88319.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/88319.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/88319.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/88319.html</trackback:ping><description><![CDATA[<p><strong>一、前言<br><br></strong>　　自从微软推出16位的Windows操作系统起，此后每种版本的Windows操作系统都非常依赖于动态链接库(DLL)中的函数和数据，实际上Windows操作系统中几乎所有的内容都由DLL以一种或另外一种形式代表着，例如显示的字体和图标存储在GDI DLL中、显示Windows桌面和处理用户的输入所需要的代码被存储在一个User DLL中、Windows编程所需要的大量的API函数也被包含在Kernel DLL中。<br><br>　　在Windows操作系统中使用DLL有很多优点，最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个DLL文件，真正实现了资源"共享"，大大缩小了应用程序的执行代码，更加有效的利用了内存；使用DLL的另一个优点是DLL文件作为一个单独的程序模块，封装性、独立性好，在软件需要升级的时候，开发人员只需要修改相应的DLL文件就可以了，而且，当DLL中的函数改变后，只要不是参数的改变,程序代码并不需要重新编译。这在编程时十分有用，大大提高了软件开发和维护的效率。<br><br>　　既然DLL那么重要，所以搞清楚什么是DLL、如何在Windows操作系统中开发使用DLL是程序开发人员不得不解决的一个问题。本文针对这些问题，通过一个简单的例子，即在一个DLL中实现比较最大、最小整数这两个简单函数，全面地解析了在Visual C++编译环境下编程实现DLL的过程，文章中所用到的程序代码在Windows98系统、Visual C++6.0编译环境下通过。<br><br>　　<strong>二、DLL的概念</strong><br><br>　　DLL是建立在客户/服务器通信的概念上，包含若干函数、类或资源的库文件，函数和数据被存储在一个DLL（服务器）上并由一个或多个客户导出而使用，这些客户可以是应用程序或者是其它的DLL。DLL库不同于静态库，在静态库情况下，函数和数据被编译进一个二进制文件（通常扩展名为*.LIB），Visual C++的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为"静态链接"，此时因为应用程序所需的全部内容都是从库中复制了出来，所以静态库本身并不需要与可执行文件一起发行。<br><br>　　在<span style="COLOR: #ff0000">动态库</span>的情况下，有两个文件，一<span style="COLOR: #ff0000">个是引入库（.LIB）文件，一个是DLL文件</span><span style="COLOR: #ff0000">，引入库文件包含被DLL导出的函数的名称和位置，DLL包含实际的函数和数据，</span>应用程序使用LIB文件链接到所需要使用的DLL文件，库中的函数和数据并不复制到可执行文件中，因此<span style="COLOR: #ff0000">在应用程序的可执行文件中，存放的不是被调用的函数代码，而是<u>DLL中所要调用的函数的内存地址</u></span><u>，</u>这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来，从而节省了内存资源。从上面的说明可以看出，<span style="COLOR: #ff0000">DLL和.LIB文件必须随应用程序一起发行，否则应用程序将会产生错误。<br></span><br>　　微软的Visual C++支持三种DLL，它们分别是Non-MFC Dll（<span style="COLOR: #ff0000">非MFC动态库</span>）、Regular Dll（<span style="COLOR: #ff0000">常规DLL</span>）、Extension Dll（<span style="COLOR: #ff0000">扩展DLL</span>）。<span style="COLOR: #ff0000">Non-MFC DLL指的是不用MFC的类库结构，直接用C语言写的DLL，其导出的函数是标准的C接口，能被非MFC或MFC编写的应用程序所调用。</span>Regular DLL:和下述的Extension Dlls一样，是用MFC类库编写的，它的一个明显的特点是在源文件里有一个继承CWinApp的类（注意：此类DLL虽然从CWinApp派生，但没有消息循环）,被导出的函数是C函数、C++类或者C++成员函数（注意不要把术语C++类与MFC的微软基础C++类相混淆），调用常规DLL的应用程序不必是MFC应用程序，只要是能调用类C函数的应用程序就可以，它们可以是在Visual C++、Dephi、Visual Basic、Borland C等编译环境下利用DLL开发应用程序。<br><br>　　常规DLL又可细分成静态链接到MFC和动态链接到MFC上的，这两种常规DLL的区别将在下面介绍。与常规DLL相比，使用扩展DLL用于导出增强MFC基础类的函数或子类，用这种类型的动态链接库，可以用来输出一个从MFC所继承下来的类。<br><br>　　<span style="COLOR: #ff0000">扩展DLL</span>是使用MFC的动态链接版本所创建的，并且<span style="COLOR: #ff0000">它只被用MFC类库所编写的应用程序所调用</span>。例如你已经创建了一个从MFC的CtoolBar类的派生类用于创建一个新的工具栏，为了导出这个类，你必须把它放到一个MFC扩展的DLL中。<span style="COLOR: #ff0000">扩展DLL 和常规DLL不一样，它没有一个从CWinApp继承而来的类的对象，所以，开发人员必须在DLL中的DllMain函数添加初始化代码和结束代码。</span><br></p>
<p><strong>三、动态链接库的创建<br><br></strong>　　在Visual C++6.0开发环境下，打开File\New\Project选项，可以选择Win32 Dynamic-Link Library或MFC AppWizard[dll]来以不同的方式来创建Non-MFC Dll、Regular Dll、Extension Dll等不同种类的动态链接库。<br><br>　　1． Win32 Dynamic-Link Library方式创建Non-MFC DLL动态链接库<br><br>　　每一个DLL必须有一个入口点，这就象我们用C编写的应用程序一样，必须有一个WINMAIN函数一样。在Non-MFC DLL中DllMain是一个缺省的入口函数，你不需要编写自己的DLL入口函数，用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化。如果应用程序的DLL需要<span style="COLOR: #ff0000">分配额外的内存或资源</span>时，或者说需要<span style="COLOR: #ff0000">对每个进程或线程初始化和清除</span>操作时，需要在相应的DLL工程的.CPP文件中对DllMain()函数按照下面的格式书写。<br></p>
<p>
<p>
<table cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>BOOL APIENTRY <span style="COLOR: #ff0000">DllMain</span>(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)<br>{<br>switch( ul_reason_for_call )<br>{<br>case DLL_PROCESS_ATTACH:<br>.......<br>case DLL_THREAD_ATTACH:<br>.......<br>case DLL_THREAD_DETACH:<br>.......<br>case DLL_PROCESS_DETACH:<br>.......<br>}<br>return TRUE;<br>}</td>
        </tr>
    </tbody>
</table>
<p>
<p>　　参数中，hMoudle是动态库被调用时所传递来的一个指向自己的句柄(实际上，它是指向_DGROUP段的一个选择符)；ul_reason_for_call是一个说明动态库被调原因的标志，当进程或线程装入或卸载动态链接库的时候，操作系统调用入口函数，并说明动态链接库被调用的原因，它所有的可能值为：DLL_PROCESS_ATTACH: 进程被调用、DLL_THREAD_ATTACH: 线程被调用、DLL_PROCESS_DETACH: 进程被停止、DLL_THREAD_DETACH: 线程被停止；lpReserved为保留参数。到此为止，DLL的入口函数已经写了，剩下部分的实现也不难，你可以在DLL工程中加入你所想要输出的函数或变量了。<br><br>　　我们已经知道DLL是包含若干个函数的库文件，应用程序使用DLL中的函数之前，应该先导出这些函数，以便供给应用程序使用。要<span style="COLOR: #ff0000">导出这些函数有两种方法</span>，<span style="COLOR: #ff0000">一是在定义函数时使用导出关键字_declspec(dllexport)，</span>另外一种方法<span style="COLOR: #ff0000">是在创建DLL文件时使用模块定义文件.Def</span>。需要读者注意的是在使用第一种方法的时候，不能使用DEF文件。下面通过两个例子来说明如何使用这两种方法创建DLL文件。<br><br>　　1）使用导出函数关键字_declspec(dllexport)创建MyDll.dll，该动态链接库中有两个函数，分别用来实现得到两个数的最大和最小数。在MyDll.h和MyDLL.cpp文件中分别输入如下原代码：<br></p>
<p>
<table cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>//MyDLL.h<br>extern "C" _declspec(dllexport) int Max(int a, int b);<br>extern "C" _declspec(dllexport) int Min(int a, int b);<br>//MyDll.cpp<br>#include&lt;stdio.h&gt;&lt;/stdio.h&gt;<br>#include"MyDll.h"<br>int Max(int a, int b)<br>{<br>if(a&gt;=b)return a;<br>else<br>return b;<br>}<br>int Min(int a, int b)<br>{<br>if(a&gt;=b)return b;<br>else<br>return a;<br>}</td>
        </tr>
    </tbody>
</table>
<p>
<p>　　该动态链接库编译成功后，打开MyDll工程中的debug目录，可以看到MyDll.dll、MyDll.lib两个文件。<span style="COLOR: #ff0000">LIB文件中包含DLL文件名和DLL文件中的函数名等，该LIB文件只是对应该DLL文件的"映像文件"，与DLL文件中，LIB文件的长度要小的多，在进行<u>隐式链接DLL</u>时要用到它。</span>读者可能已经注意到<span style="COLOR: #ff0000">在MyDll.h中有关键字</span><span style="COLOR: #ff0000">"extern C"，它可以使其他编程语言访问你编写的DLL中的函数</span>。<br><br>　　2）用.def文件创建工程MyDll<br><br>　　为了用.def文件创建DLL，请先删除上个例子创建的工程中的MyDll.h文件，保留MyDll.cpp并在该文件头删除#include MyDll.h语句，同时往该工程中加入一个文本文件，命名为MyDll.def，再在该文件中加入如下代码：<br><br>LIBRARY MyDll<br>EXPORTS<br>Max<br>Min<br><br>　　其中LIBRARY语句说明该def文件是属于相应DLL的，EXPORTS语句下列出要导出的函数名称。我们可以在<span style="COLOR: #ff0000">.def文件中的导出函数后加@n，如Max@1，Min@2，表示要导出的函数顺序号</span>，在进行显式连时可以用到它。该DLL编译成功后，打开工程中的Debug目录，同样也会看到MyDll.dll和MyDll.lib文件。<br><br>　　2．MFC AppWizard[dll]方式生成常规/扩展DLL<br><br>　　在MFC AppWizard[dll]下生成DLL文件又有三种方式，在创建DLL是，要根据实际情况选择创建DLL的方式。一种是<span style="COLOR: #ff0000">常规DLL静态链接到MFC</span>，另一种是<span style="COLOR: #ff0000">常规DLL动态链接到MFC</span>。两者的区别是：前者使用的是MFC的静态链接库，生成的DLL文件长度大，一般不使用这种方式，后者使用MFC的动态链接库，生成的DLL文件长度小；<span style="COLOR: #ff0000">动态链接到MFC的规则DLL所有输出的函数应该以如下语句开始</span>： <br></p>
<p>
<table cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>AFX_MANAGE_STATE(AfxGetStaticModuleState( )) //此语句用来正确地切换MFC模块状态</td>
        </tr>
    </tbody>
</table>
<p>
<p>　　最后一种是MFC<span style="COLOR: #ff0000">扩展DLL，这种DLL特点是用来<u>建立MFC的派生类</u></span>，Dll只被用MFC类库所编写的应用程序所调用。前面我们已经介绍过，Extension DLLs 和Regular DLLs不一样，它<span style="COLOR: #ff0000">没有一个从CWinApp继承而来的类的对象</span>，编译器默认了一个DLL入口函数DLLMain()作为对DLL的初始化，你可以在此函数中实现初始化,代码如下：<br></p>
<p>
<table cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>BOOL WINAPI APIENTRY <span style="COLOR: #ff0000">DLLMain</span>(HINSTANCE hinstDll，DWORD reason ，LPVOID flmpload)<br>{<br>switch(reason)<br>{<br>&#8230;&#8230;&#8230;&#8230;&#8230;//初始化代码；<br>}<br>return true;<br>}</td>
        </tr>
    </tbody>
</table>
<p>
<p>　　参数hinstDll存放DLL的句柄，参数reason指明调用函数的原因，lpReserved是一个被系统所保留的参数。对于隐式链接是一个非零值，对于显式链接值是零。<br><br>　　在MFC下建立DLL文件，会自动生成def文件框架，其它与建立传统的Non-MFC DLL没有什么区别，只要在相应的头文件写入关键字_declspec(dllexport)函数类型和函数名等，或在生成的def文件中EXPORTS下输入函数名就可以了。需要注意的是在向其它开发人员分发MFC扩展DLL 时，不要忘记提供描述DLL中类的头文件以及相应的.LIB文件和DLL本身，此后开发人员就能充分利用你开发的扩展DLL了。 <br></p>
<p><strong>四、动态链接库DLL的链接<br><br></strong>　　应用程序使用DLL可以采用两种方式：一种是隐式链接，另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。Visual C++6.0在VC\bin目录下提供了一个名为<span style="COLOR: #ff0000">Dumpbin.exe</span>的小程序，用它可以查看DLL文件中的函数结构。另外，Windows系统将遵循下面的搜索顺序来定位DLL： 1．<span style="COLOR: #ff0000">包含EXE文件的目录，2．进程的当前工作目录， 3．Windows系统目录， 4．Windows目录，5．列在Path环境变量中的一系列目录。<br></span><br>　　1．隐式链接<br><br>　　隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易，只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。下面的例子通过隐式链接调用MyDll.dll库中的Min函数。首先生成一个项目为TestDll，在DllTest.h、DllTest.cpp文件中分别输入如下代码：<br></p>
<p>
<p>
<table cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>//Dlltest.h<br>#pragma comment(lib，"MyDll.lib")<br>extern "C"_declspec(dllimport) int Max(int a,int b);<br>extern "C"_declspec(dllimport) int Min(int a,int b);<br>//TestDll.cpp<br>#include&lt;stdio.h&gt;&lt;/stdio.h&gt;<br>#include"Dlltest.h"<br>void main()<br>{int a;<br>a=min(8,10)<br>printf("比较的结果为%d\n"，a);<br>}<br></td>
        </tr>
    </tbody>
</table>
<p>
<p>　　在创建DllTest.exe文件之前，要先将MyDll.dll和MyDll.lib拷贝到当前工程所在的目录下面，也可以拷贝到windows的System目录下。如果DLL使用的是def文件，要删除TestDll.h文件中关键字extern "C"。TestDll.h文件中的关键字Progam commit是要Visual C+的编译器在link时，链接到MyDll.lib文件，当然，开发人员也可以不使用#pragma comment(lib，"MyDll.lib")语句，而直接在工程的Setting-&gt;Link页的Object/Moduls栏填入MyDll.lib既可。<br><br>　　2．显式链接<br><br>　　显式链接是应用程序在执行过程中随时可以加载DLL文件，也可以随时卸载DLL文件，这是隐式链接所无法作到的，所以显式链接具有更好的灵活性，对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序中用<span style="COLOR: #ff0000">LoadLibrary</span>或MFC提供的<span style="COLOR: #ff0000">AfxLoadLibrary显式的将自己所做的动态链接库调进来</span>，动态链接库的文件名即是上述两个函数的参数，此后再用GetProcAddress()获取想要引入的函数。自此，你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前，应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Max函数的例子。<br></p>
<p>
<table cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>#include &lt;studio.h&gt;&lt;/studio.h&gt;<br>#include&lt;widows.h&gt;&lt;/widows.h&gt;<br>void main(void)<br>{<br>typedef int(*pMax)(int a,int b);<br>typedef int(*pMin)(int a,int b);<br>HINSTANCE hDLL;<br>PMax Max<br>HDLL=LoadLibrary("MyDll.dll");//加载动态链接库MyDll.dll文件；<br>Max=(pMax)GetProcAddress(hDLL,"Max");<br>A=Max(5,8);<br>Printf("比较的结果为%d\n"，a);<br>FreeLibrary(hDLL);//卸载MyDll.dll文件；<br>}</td>
        </tr>
    </tbody>
</table>
<p>
<p>　　在上例中使用类型定义关键字typedef，定义指向和DLL中相同的函数原型指针，然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄，然后通过GetProcAddress()函数获取导入到应用程序中的函数指针，函数调用完毕后，使用FreeLibrary()卸载DLL文件。在编译程序之前，首先要将DLL文件拷贝到工程所在的目录或Windows系统目录下。<br><br>　　<span style="COLOR: #ff0000"><u><strong>使用显式链接应用程序编译时不需要使用相应的Lib文件</strong></u></span>。另外，使用GetProcAddress()函数时，可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号，如将GetProcAddress(hDLL,"Min")改为GetProcAddress(hDLL, MAKEINTRESOURCE(2))（函数Min()在DLL中的顺序号是2），这样调用DLL中的函数速度很快，但是要记住函数的使用序号，否则会发生错误。<br><br>　　本文通过通俗易懂的方式，全面介绍了动态链接库的概念、动态链接库的创建和动态链接库的链接，并给出个简单明了的例子，相信读者看了本文后，能够创建自己的动态链接库并应用到后续的软件开发当中去了，当然，读者要熟练操作DLL，还需要在大量的实践中不断摸索，希望本文能起到抛砖引玉的作用。</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/88319.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-22 20:46 <a href="http://www.cppblog.com/ivy-jie/articles/88319.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows进程通信的方法</title><link>http://www.cppblog.com/ivy-jie/articles/87517.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Fri, 12 Jun 2009 11:46:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/87517.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/87517.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/87517.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/87517.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/87517.html</trackback:ping><description><![CDATA[<p><strong>1 </strong><strong>进程与进程通信</strong> </p>
<p>&nbsp;</p>
<p>　　进程是装入内存并准备执行的程序，每个进程都有私有的虚拟地址空间，由代码、数据以及它可利用的系统资源(如文件、管道等)组成。多进程/多线程是Windows操作系统的一个基本特征。Microsoft Win32应用编程接口(Application Programming Interface, API)提供了大量支持应用程序间数据共享和交换的机制，这些机制行使的活动称为进程间通信(InterProcess Communication, IPC)，进程通信就是指不同进程间进行数据共享和数据交换。<br>　　正因为使用Win32 API进行进程通信方式有多种，如何选择恰当的通信方式就成为应用开发中的一个重要问题，下面本文将对Win32中进程通信的几种方法加以分析和比较。 </p>
<p>&nbsp;</p>
<p><strong>2 </strong><strong>进程通信方法</strong> </p>
<p><strong>2.1 </strong><strong>文件映射</strong><br>　　文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此，进程不必使用文件I/O操作，只需简单的指针操作就可读取和修改文件的内容。<br>　　Win32 API允许多个进程访问同一文件映射对象，各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针，不同进程就可以读或修改文件的内容，实现了对文件中数据的共享。<br>　　应用程序有三种方法来使多个进程共享一个文件映射对象。<br>　　(1)继承：第一个进程建立文件映射对象，它的子进程继承该对象的句柄。<br>　　(2)命名文件映射：第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外，第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。<br>　　(3)句柄复制：第一个进程建立文件映射对象，然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。<br>　　文件映射是在多个进程间共享数据的非常有效方法，有较好的安全性。但文件映射只能用于本地机器的进程之间，不能用于网络中，而开发者还必须控制进程间的同步。<br><strong>2.2 </strong><strong>共享内存</strong><br>　　Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE)，就表示了对应的文件映射对象是从操作系统页面文件访问内存，其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的，所以它也有较好的安全性，也只能运行于同一计算机上的进程之间。<br><strong>2.3 </strong><strong>匿名管道</strong><br>　　管道(Pipe)是一种具有两个端点的通信通道：有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向－一端是只读的，另一端点是只写的；也可以是双向的一管道的两端点既可读也可写。<br>　　匿名管道(Anonymous Pipe)是在父进程和子进程之间，或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道，然后由要通信的子进程继承通道的读端点句柄或写端点句柄，然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信，不需要通过父进程。<br>　　匿名管道是单机上实现子进程标准I/O重定向的有效方法，它不能在网上使用，也不能用于两个不相关的进程之间。<br><strong>2.4 </strong><strong>命名管道</strong><br>　　命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不同计算机之间使用，服务器建立命名管道时给它指定一个名字，任何进程都可以通过该名字打开管道的另一端，根据给定的权限和服务器进程通信。<br>　　命名管道提供了相对简单的编程接口，使通过网络传输数据并不比同一计算机上两进程之间通信更困难，不过如果要同时和多个进程通信它就力不从心了。<br><strong>2.5 </strong><strong>邮件槽</strong><br>　　邮件槽(Mailslots)提供进程间单向通信能力，任何进程都能建立邮件槽成为邮件槽服务器。其它进程，称为邮件槽客户，可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消息一直放在邮件槽中，直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户，因此可建立多个邮件槽实现进程间的双向通信。<br>　　通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节，非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。<br>　　邮件槽与命名管道相似，不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的，一旦网络发生错误则无法保证消息正确地接收，而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力，所以邮件槽不失为应用程序发送和接收消息的另一种选择。<br><strong>2.6 </strong><strong>剪贴板</strong><br>　　剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息，为Windows应用程序之间进行数据共享提供了一个中介，Windows已建立的剪切(复制)－粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时，应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据，从给定格式中选择适合自己的格式。<br>　　剪贴板是一个非常松散的交换媒介，可以支持任何数据格式，每一格式由一无符号整数标识，对标准(预定义)剪贴板格式，该值是Win32 API定义的常量；对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用，不能在网络上使用。<br><strong>2.7 </strong><strong>动态数据交换</strong><br>　　动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输，也可以当出现新数据时，通过发送更新值在应用程序间动态交换数据。<br>　　DDE和剪贴板一样既支持标准数据格式(如文本、位图等)，又可以支持自己定义的数据格式。但它们的数据传输机制却不同，一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答－如从菜单中选择Paste命令。尽管DDE也可以由用户启动，但它继续发挥作用一般不必用户进一步干预。DDE有三种数据交换方式：<br>　　(1) 冷链：数据交换是一次性数据传输，与剪贴板相同。<br>　　(2) 温链：当数据交换时服务器通知客户，然后客户必须请求新的数据。<br>　　(3) 热链：当数据交换时服务器自动给客户发送数据。<br>　　DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC，它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。<br><strong>2.8 </strong><strong>对象连接与嵌入</strong><br>　　应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档)，OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的服务。例如，OLE支持的字处理器可以嵌套电子表格，当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时，该表格已在原始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展，而如果使用DDE，用户要显式地启动电子表格编辑器。<br>　　同DDE技术相同，大多数基于Windows的应用程序都支持OLE技术。<br><strong>2.9 </strong><strong>动态连接库</strong><br>　　Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享，这就又给进程间通信开辟了一条新的途径，当然访问时要注意同步问题。<br>　　虽然可以通过DLL进行进程间数据共享，但从数据安全的角度考虑，我们并不提倡这种方法，使用带有访问权限控制的共享内存的方法更好一些。<br><strong>2.10 </strong><strong>远程过程调用</strong><br>　　Win32 API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数，这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既可以在单机不同进程间使用也可以在网络中使用。<br>　　由于Win32 API提供的RPC服从OSF-DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过Win32 API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程序。<br><strong>2.11 NetBios</strong><strong>函数</strong><br>　　Win32 API提供NetBios函数用于处理低级网络控制，这主要是为IBM NetBios系统编写与Windows的接口。除非那些有特殊低级网络功能要求的应用程序，其它应用程序最好不要使用NetBios函数来进行进程间通信。<br><strong>2.12 Sockets</strong><br>　　Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley Socket原有的库函数以外，还扩展了一组针对Windows的函数，使程序员可以充分利用Windows的消息机制进行编程。<br>　　现在通过Sockets实现进程通信的网络应用越来越多，这主要的原因是Sockets的跨平台性要比其它IPC机制好得多，另外WinSock 2.0不仅支持TCP/IP协议，而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作，这使得在单机的进程间进行简单数据传递不太方便，这时使用下面将介绍的WM_COPYDATA消息将更合适些。<br><strong>2.13 WM_COPYDATA</strong><strong>消息</strong><br>　　<span style="COLOR: red"><u><strong>WM_COPYDATA</strong></u></span>是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时，发送方只需使用调用SendMessage函数，参数是目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息，这样收发双方就实现了数据共享。<br>　　WM_COPYDATA是一种非常简单的方法，它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高，并且它只能用于Windows平台的单机环境下。 </p>
<p><strong>3 </strong><strong>结束语</strong> </p>
<p>　　Win32 API为应用程序实现进程间通信提供了如此多种选择方案，那么开发者如何进行选择呢？通常在决定使用哪种IPC方法之前应考虑下一些问题，如应用程序是在网络环境下还是在单机环境下工作等。<br></p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/87517.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-12 19:46 <a href="http://www.cppblog.com/ivy-jie/articles/87517.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程临界区（windows）/互斥(linux)</title><link>http://www.cppblog.com/ivy-jie/articles/87447.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Thu, 11 Jun 2009 15:55:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/87447.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/87447.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/87447.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/87447.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/87447.html</trackback:ping><description><![CDATA[<p>在VC6下做的，在LINUX下，将临界换成互斥就OK了。<br>题目如下：<br>启动4个线程，向4个文件A，B，C，D里写入数据，每个线程只能写一个值。<br>线程1：只写1<br>线程2：只写2<br>线程3：只写3<br>线程4：只写4<br>4个文件A，B，C，D。<br>程序运行起来，4个文件的写入结果如下：<br>A：12341234。。。。<br>B：23412341。。。。<br>C：34123412。。。。<br>D：41234123。。。。<br><br>// Multithread.cpp : Defines the entry point for the console application.<br>//</p>
<p>#include "stdafx.h"<br>#include &lt;iostream&gt;<br>#include &lt;windows.h&gt; <br>struct FILEINFO<br>{<br>&nbsp; int curnumber;//写入的当前数据<br>&nbsp; char filename[2];//文件名<br>&nbsp;<span style="COLOR: red"> CRITICAL_SECTION cs;//临界</span><br>&nbsp; <br>&nbsp; FILEINFO()<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; curnumber = 0;<br>&nbsp;<span style="COLOR: red">InitializeCriticalSection(&amp;cs);<br></span>&nbsp;memset(filename,0,2);<br>&nbsp; }<br>};</p>
<p>FILEINFO *pFileInfo =0;<br>const int MAXFILESIZE=4;<br>char szfilename[]="ABCD";<br>/////线程函数////////////////<br>DWORD WINAPI twritefile(LPVOID parm)<br>{<br>&nbsp; int number =(int) parm;<br>&nbsp; int nIndex = number-1;//保证线程i从文件i-1开始写<br>&nbsp; FILE *fp;<br>&nbsp; char szbuf[2];<br>&nbsp; sprintf(szbuf,"%d",number);<br>&nbsp; bool isfirst = true;<br>&nbsp; char sztmp[16]={0};//用来保存文件名</p>
<p>&nbsp; while(true)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; if(nIndex ==MAXFILESIZE)<br>&nbsp; &nbsp;nIndex = 0;<br>&nbsp;&nbsp; EnterCriticalSection(&amp;pFileInfo[nIndex].cs);<br>&nbsp;&nbsp; if((number-pFileInfo[nIndex].curnumber)!=1 &amp;&amp; !isfirst)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; LeaveCriticalSection(&amp;pFileInfo[nIndex].cs);<br>&nbsp;&nbsp;&nbsp;&nbsp; nIndex++;<br>&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>&nbsp; &nbsp;}<br>&nbsp;&nbsp;&nbsp; sprintf(sztmp,"%s",pFileInfo[nIndex].filename);<br>&nbsp;&nbsp; fp = fopen(sztmp,"a+b");<br>&nbsp;&nbsp; if(fp!=0)<br>&nbsp; { <br>&nbsp;&nbsp;&nbsp; fwrite(szbuf,1,1,fp);<br>&nbsp;&nbsp;&nbsp; fclose(fp);<br>&nbsp;&nbsp;&nbsp; if(number==MAXFILESIZE)&nbsp; //当为第4个线程时，文件结构的curnumber设置为0;否则,设置为线程ID<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pFileInfo[nIndex].curnumber=0;<br>&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pFileInfo[nIndex].curnumber = number;<br>&nbsp;&nbsp;isfirst=false;<br>&nbsp;}<br>&nbsp; LeaveCriticalSection(&amp;pFileInfo[nIndex].cs);<br>&nbsp; nIndex++;<br>&nbsp; }<br>}</p>
<p>&nbsp;&nbsp;int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp; pFileInfo = new FILEINFO[MAXFILESIZE];<br>&nbsp;&nbsp; DWORD TID;</p>
<p>&nbsp;&nbsp; for(int nIndex =0;nIndex&lt;MAXFILESIZE;nIndex++)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; pFileInfo[nIndex].filename[0] = szfilename[nIndex];<br>&nbsp; }<br>&nbsp; for(nIndex =1;nIndex&lt;=MAXFILESIZE;nIndex++)<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; CreateThread(NULL,0,twritefile,(void*)nIndex,0,&amp;TID);<br>&nbsp;}<br>&nbsp;while(1)<br>&nbsp;&nbsp;Sleep(100000);</p>
<p>}<br></p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/87447.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-11 23:55 <a href="http://www.cppblog.com/ivy-jie/articles/87447.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符串出现次数</title><link>http://www.cppblog.com/ivy-jie/articles/85497.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sat, 23 May 2009 02:45:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85497.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85497.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85497.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85497.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85497.html</trackback:ping><description><![CDATA[<p>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;iostream.h&gt;&nbsp; //for cout<br>#include &lt;ctype.h&gt;//使用字符函数<br>//isalpha(ch),islower(ch),isdigital(ch)<br>//从一个文件中取出字符串与另一个文件比较，计算字符串在该文件中出现的次数<br>//（字符串包含匹配性验证，比如?代表小写英文字母，*代表小写英文字母和0-9的数字），<br>//第一问要求你用程序实现该题目，第二问要求你对第一问中的程序写测试用例，包括基本<br>//测试、边界测试，*代表小写英文字母和0-9的数字<br>bool equal(char a,char b)<br>{<br>&nbsp; if(a==b) return true;<br>&nbsp; if(a == '?' &amp;&amp; b == '*' || a=='*' &amp;&amp; b=='?')<br>&nbsp;&nbsp; return true;<br>&nbsp; if((a=='?' &amp;&amp; b&gt;=97 &amp;&amp; b&lt;=122) || (a&gt;=97 &amp;&amp; a&lt;=123 &amp;&amp; b=='?'))<br>&nbsp;&nbsp; return true;<br>&nbsp; if((a=='*' &amp;&amp; b&gt;=97 &amp;&amp; b&lt;=122) || (a&gt;=97 &amp;&amp; a&lt;=122 &amp;&amp; b=='*') || (a=='*' &amp;&amp; b&gt;=48 &amp;&amp;b&lt;=57) || (a&gt;=48 &amp;&amp; a&lt;=57 &amp;&amp; b=='*'))<br>&nbsp;&nbsp; return true;<br>&nbsp; return false;<br>}<br>int main(int argc,char** argv)<br>{<br>&nbsp; char line[30];<br>&nbsp; FILE *fp;<br>&nbsp; int i=0,j=0;<br>&nbsp; if((fp = fopen(argv[1],"r")) != NULL)<br>&nbsp; { <br>&nbsp;&nbsp; if(fgets(line,100,fp)== NULL)//实际上取29个字符，最后加'\0'<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; printf("fgets error\n");<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; fclose(fp);<br>&nbsp; }<br>&nbsp; <br>&nbsp;&nbsp; int length = strlen(line);<br>&nbsp;&nbsp; int sum = 0;//次数初始为0 <br>&nbsp;&nbsp; char ch;<br>&nbsp;&nbsp; if(fopen(argv[2],"r") != NULL)<br>&nbsp;&nbsp; //在文件2中查找字符串，并计数<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; while((ch = fgetc(fp))!= EOF)<br>&nbsp; { <br>&nbsp;&nbsp;&nbsp; int test;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test = ftell(fp);<br>&nbsp;&nbsp;&nbsp; printf("file pointer value:%d\n",test);<br>&nbsp;&nbsp;&nbsp; if(equal(ch,line[j]))<br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp; j++;<br>&nbsp;&nbsp;&nbsp;&nbsp; if(j == length) //字符串匹配到尾部，成功，总数加1<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum+=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j =0;<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //否则的话回溯，重新匹配<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; i= ftell(fp);<br>&nbsp;&nbsp;&nbsp;&nbsp; fseek(fp,j,1);//向前回溯一个字符<br>&nbsp;&nbsp;&nbsp;&nbsp; j= 0;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;}<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; printf("string %s occurs %d times in file2\n",line,sum);<br>&nbsp;&nbsp;&nbsp; return sum;<br>}<br>/*边界测试<br>设计测试用例时要考虑边界输入和非法输入，这里统称为特殊输入，程序</p>
<p>员在编写代码时也要考虑特殊输入，要为特殊输入编写处理代码。在实际工作中，程序员没有考虑到某些</p>
<p>特殊输入是很常见的，这也是程序错误的一个重要来源。<br>*/</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85497.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-23 10:45 <a href="http://www.cppblog.com/ivy-jie/articles/85497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows进程间通信</title><link>http://www.cppblog.com/ivy-jie/articles/85493.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sat, 23 May 2009 01:45:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85493.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85493.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85493.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85493.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85493.html</trackback:ping><description><![CDATA[<p>进程是装入内存并准备执行的程序，每个进程都有私有的虚拟地址空间，由代码、数据以及它可利用的系统资源(如文件、管道等)组成。多进程/多线程是Windows操作系统的一个基本特征。Microsoft Win32应用编程接口(Application Programming Interface, API)提供了大量支持应用程序间数据共享和交换的机制，这些机制行使的活动称为进程间通信(InterProcess Communication, IPC)，进程通信就是指不同进程间进行数据共享和数据交换。<br>　　正因为使用Win32 API进行进程通信方式有多种，如何选择恰当的通信方式就成为应用开发中的一个重要问题，下面本文将对Win32中进程通信的几种方法加以分析和比较。<br>2 进程通信方法</p>
<p>2.1 文件映射<br>文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此，进程不必使用文件I/O操作，只需简单的指针操作就可读取和修改文件的内容。<br>Win32 API允许多个进程访问同一文件映射对象，各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针，不同进程就可以读或修改文件的内容，实现了对文件中数据的共享。<br>应用程序有三种方法来使多个进程共享一个文件映射对象。<br>(1)继承：第一个进程建立文件映射对象，它的子进程继承该对象的句柄。<br>(2)命名文件映射：第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外，第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。<br>(3)句柄复制：第一个进程建立文件映射对象，然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。<br>文件映射是在多个进程间共享数据的非常有效方法，有较好的安全性。但文件映射只能用于本地机器的进程之间，不能用于网络中，而开发者还必须控制进程间的同步。</p>
<p>2.2 共享内存<br>Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替 文件句柄(HANDLE)，就表示了对应的文件映射对象是从操作系统页面文件访问内存，其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用 文件映射实现的，所以它也有较好的安全性，也只能运行于同一计算机上的进程之间。</p>
<p>a.设定一块共享内存区域<br>HANDLE CreateFileMapping(HANDLE,LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR)// 产生一个file-mapping核心对象<br>LPVOID MapViewOfFile(<br>&nbsp;&nbsp;&nbsp; HANDLE hFileMappingObject,<br>&nbsp;&nbsp;&nbsp; DWORD dwDesiredAcess,<br>&nbsp;&nbsp;&nbsp; DWORD dwFileOffsetHigh,<br>&nbsp;&nbsp;&nbsp; DWORD dwFileOffsetLow,<br>&nbsp;&nbsp;&nbsp; DWORD dwNumberOfBytesToMap<br>);得到共享内存的指针</p>
<p>b.找出共享内存<br>&nbsp;&nbsp;&nbsp; 决定这块内存要以点对点（peer to peer)的形式呈现每个进程都必须有相同的能力，产生共享内存并将它初始化。每个进程都应该调用CreateFileMapping(),然后调用GetLastError().如果传回的错误代码是 ERROR_ALREADY_EXISTS,那么进程就可以假设这一共享内存区 域已经被别的进程打开并初始化了，否则该进程就可以合理的认为自己 排在第 一位，并接下来将共享内存初始化。还是要使用client/server架构中只有server进程才应该产生并初始化共享内存。所有的进程都应该使用<br>HANDLE OpenFileMapping(DWORD dwDesiredAccess,<br>BOOL bInheritHandle,<br>LPCTSTR lpName);<br>再调用MapViewOfFile(),取得共享内存的指针</p>
<p>c.同步处理(Mutex)</p>
<p>d.清理(Cleaning up) BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);</p>
<p>CloseHandle()</p>
<p><br>2.3 匿名管道<br>管道(Pipe)是一种具有两个端点的通信通道：有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向－一端是只读的，另一端点是只写的；也可以是双向的一管道的两端点既可读也可写。</p>
<p>匿名管道(Anonymous Pipe)是 在父进程和子进程之间，或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管 道，然后由要通信的子进程继承通道的读端点句柄或写 端点句柄，然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程 可以使用管道直接通信，不需要通过父进程。<br>匿名管道是单机上实现子进程标准I/O重定向的有效方法，它不能在网上使用，也不能用于两个不相关的进程之间。</p>
<p>2.4 命名管道<br>命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不 同计算机之间使用，服务器建立命名管道时给它指定一个名字，任何进程都可以通过该名字打开管道的另一端，根据给定的权限和服务器进程通信。<br>命名管道提供了相对简单的编程接口，使通过网络传输数据并不比同一计算机上两进程之间通信更困难，不过如果要同时和多个进程通信它就力不从心了。</p>
<p>2.5 邮件槽<br>邮件槽(Mailslots)提 供进程间单向通信能力，任何进程都能建立邮件槽成为邮件槽服务器。其它进程，称为邮件槽客户，可以通过邮件槽的名字给 邮件槽服务器进程发送消息。进来的消 息一直放在邮件槽中，直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户，因此可建立多个 邮件槽实现进程间的双向通信。<br>通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节，非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。<br>邮件槽与命名管道相似，不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的，一旦网络发生错误则无法保证消息正确地接收，而 命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力，所以邮件槽不失为应用程序发送 和接收消息的另一种选择。</p>
<p>2.6 剪贴板<br>　 　剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息，为Windows应用程序之间进行数据共享提供了一个 中介，Windows已建立的剪切(复制)－粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时，应 用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据，从给定格式中选择适合自己的格式。<br>剪贴板 是一个非常松散的交换媒介，可以支持任何数据格式，每一格式由一无符号整数标识，对标准(预定义)剪贴板格式，该值是Win32 API定义的常量；对非 标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以 转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用，不能在网络上使用。</p>
<p>2.7 动态数据交换<br>动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输，也可以当出现新数据时，通过发送更新值在应用程序间动态交换数据。<br>DDE和剪贴板一样既支持标准数据格式(如文本、位图等)，又可以支持自己定义的数据格式。但它们的数据传输机制却不同，一个明显区别是剪贴板操作几乎 总是用作对用户指定操作的一次性应答－如从菜单中选择Paste命令。尽管DDE也可以由用户启动，但它继续发挥作用一般不必用户进一步干预。DDE有三 种数据交换方式：<br>(1) 冷链：数据交换是一次性数据传输，与剪贴板相同。<br>(2) 温链：当数据交换时服务器通知客户，然后客户必须请求新的数据。<br>(3) 热链：当数据交换时服务器自动给客户发送数据。<br>DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC，它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。</p>
<p>2.8 对象连接与嵌入<br>应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档)，OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的服 务。例如，OLE支持的字处理器可以嵌套电子表格，当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时，该表格已在原 始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展，而如果使用DDE，用户要显式地启动电子表格编辑器。<br>同DDE技术相同，大多数基于Windows的应用程序都支持OLE技术。</p>
<p>2.9 动态连接库<br>Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享，这就又给进程间通信开辟了一条新的途径，当然访问时要注意同步问题。<br>虽然可以通过DLL进行进程间数据共享，但从数据安全的角度考虑，我们并不提倡这种方法，使用带有访问权限控制的共享内存的方法更好一些。</p>
<p>2.10 远程过程调用<br>Win32 API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数，这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既可以在单机不同进程间使用也可以在网络中使用。<br>由于Win32 API提供的RPC服从OSF-DCE (Open Software Foundation Distributed Computing Environment)标准。所以通过 Win32 API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程 序。</p>
<p>2.11 NetBios函数<br>Win32 API提供NetBios函数用于处理低级网络控制，这主要是为IBM NetBios系统编写与Windows的接口。除非那些有特殊低级网络功能要求的应用程序，其它应用程序最好不要使用NetBios函数来进行进程间通信。</p>
<p>2.12 Sockets<br>Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网 络编程接口。除了Berkeley Socket原有的库函数以外，还扩展了一组针对Windows的函数，使程序员可以充分利用Windows的消息机 制进行编程。<br>现在通过Sockets实现进程通信的网络应用越来越多，这主要的原因是Sockets的跨平台性要比其它IPC机制好得多，另 外WinSock 2.0不仅支持TCP/IP协议，而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作，这使得在单机 的进程间进行简单数据传递不太方便，这时使用下面将介绍的WM_COPYDATA消息将更合适些。</p>
<p><span style="COLOR: red">2.13 WM_COPYDATA消息<br>WM_COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时，发送方只需使用调用SendMessage函数，参数是目 的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息，这样收发双方就实现了 数据共享。<br>WM_COPYDATA是一种非常简单的方法，它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高，并且它只能用于Windows平台的单机环境下。</span></p>
<p>引自：<a href="http://blog.csdn.net/metasearch/archive/2008/03/05/2148117.aspx"><u><font color=#0000ff>http://blog</font></u></a></p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85493.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-23 09:45 <a href="http://www.cppblog.com/ivy-jie/articles/85493.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SendMessage发WM_COPYDATA</title><link>http://www.cppblog.com/ivy-jie/articles/85492.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sat, 23 May 2009 01:42:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85492.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85492.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85492.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85492.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85492.html</trackback:ping><description><![CDATA[1　发送消息的程序中比较简单，在要发送的地方加入如下代码就行了： <br>void CSendDlg::OnSend() <br>{ <br>// TODO: Add your control notification handler code here <br><br>CString str="这是我要发送的给另外一个进程字符串。"; <br><span style="COLOR: red">COPYDATASTRUCT myCopyDATA; <br></span>myCopyDATA.cbData=str.GetLength(); <br>myCopyDATA.lpData=str.GetBuffer(0); <br>str.ReleaseBuffer(); <br><span style="COLOR: red">HWND hwnd=::FindWindow(NULL,"Receive"); //假设目标程序窗口标题为"Receive</span>" <br>if (hwnd) <br><span style="COLOR: red">::SendMessage(hwnd,WM_COPYDATA,NULL,(LPARAM)&amp;myCopyDATA);</span> <br>else <br>AfxMessageBox("目标程序没有运行。"); <br><br>} <br>2　接收消息的程序(这里假设是基于对话框的工程)请按如下步骤来实现： <br>先给对话框类CReceiveDlg(如果你的接收程序为SDI工程的话，把CReceiveDlg类换成CMainFrame类)添加一个消息处理函数LRESULT OnReceive(WPARAM wParam,LPARAM lParam)，代码如下： <br>LRESULT CReceiveDlg::OnReceive(WPARAM wParam,LPARAM lParam) <br>{ <br>COPYDATASTRUCT *p = NULL ; <br>p = (COPYDATASTRUCT*)lParam; <br>CString strTemp; <br><span style="COLOR: red">memcpy(strTemp.GetBuffer((int)p-&gt;cbData),p-&gt;lpData,p-&gt;cbData); <br>strTemp.ReleaseBuffer(); <br>AfxMessageBox(strTemp);</span> <br>return 0; <br>} <br>接着再给工程设定刚才添加的函数OnReceive为WM_COPYDATA消息的响应函数，在ReceiveDlg.cpp文件中如下地方加入一行代码： <br>BEGIN_MESSAGE_MAP(CReceiveDlg, CDialog) <br>//{{AFX_MSG_MAP(CReceiveDlg) <br>ON_WM_PAINT() <br>ON_WM_QUERYDRAGICON() <br><span style="COLOR: red">ON_MESSAGE(WM_COPYDATA,OnReceive)</span> //只要添加这一行进行WM_COPYDATA消息映射 <br>//}}AFX_MSG_MAP <br>END_MESSAGE_MAP() <br><br><br>好了，现在分别运行两个程序，应该可以在A程序中给B程序发送消息了，B收到消息后会弹出个MessageBox来显示从A程序发送来消息了。
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85492.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-23 09:42 <a href="http://www.cppblog.com/ivy-jie/articles/85492.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于VC控制台程序中英文字符处理  问和答</title><link>http://www.cppblog.com/ivy-jie/articles/83007.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Fri, 15 May 2009 00:29:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/83007.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/83007.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/83007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/83007.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/83007.html</trackback:ping><description><![CDATA[<br>问： 我是一位初学者，现在遇到一个问题很是困绕。我想从一个文本文件里读出每行（每行长度不固定），然后对拿到的字符串按固定长度进行分割，再根据分割后得到的字符串插入数据库。插数据库的问题，经过几天来对ODBC API的学习，感觉可以了，至少知道去看什么资料，我也做了些小小测试。但是对字符串的处理很困饶。 <br><br>根据我的设想，肯定要对从文本文件里得到的每行字符串进行长度计算，但是VC默认的是宽字符MBCS，中文是2字节字符，strlen等函数都算出来的是单字节长度，比如&#8220;VCC程序&#8221;，打出来的长度是7，而我想得到5。如果定义字符串变量为WCHAR，用WCSLEN得到的是5，但是用PRINTF打印出来的是乱码。怎么解决这个问题呢？谁能给我一个例子，关于截中英文混合字符串按固定长度截取的控制台程序的代码范例。我现在是不知道用什么函数，不知道包含什么头文件。另外，想从基础做起，暂不用MFC。请大侠帮忙，不甚感谢！<br><br>答1：用string类可以输出中英文混合的字符串啊。 <br>string a="我a"; <br>cout &lt;&lt; a.c_str() &lt;&lt; endl; <br><br>至于要得到长度，可以编一个函数来实现。该函数从字符串头部向后扫描，碰到小于128的，统计变量加1，碰到大于128的，则说明是中文，跳过下一个字符，统计变量加1。<br><br>答2：我已经搞清楚了，谢谢，直接用一个_tcsclen()函数就可以了。谢谢。包含Tchar.h。</ca></cd>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/83007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-15 08:29 <a href="http://www.cppblog.com/ivy-jie/articles/83007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>