canmeng50401的专栏

统计

留言簿

阅读排行榜

评论排行榜

2012年4月9日 #

进程间通信:使用file-mapping kernel object(文件映射内核对象)

        一个机器上的两个进程间通信,可以使用很多种方式。但看《windows核心编程》说,其实归根结底都是使用了file-mapping kernel object。把那一章看了看,长了不少知识。但是我最感兴趣的地方还是:假设有两个线程:线程A和线程B。当线程A在运行的时候,线程B给A通信,使得A可以改变自己程序的运行轨迹。其实,这也算是为调试程序埋的伏笔吧。因为我现在的工作中就遇到一个这样的问题,程序A可以运行,但运行效果不是想要的,我想使用程序B给A发一些消息,改变A的运行轨迹。在VS2008的msdn文档中找了找,找到一个简单的例子,修改了一下。代码包括两部分,第一部分是程序A的代码,代码如下:

#include <windows.h>
#include 
<stdio.h>
#include 
<conio.h>

#define BUF_SIZE 256
TCHAR szName[]
=TEXT("Global\\MyFileMappingObject");

int main()
{
    HANDLE hMapFile;
    
char * pBuf;

    hMapFile 
= CreateFileMapping(
        INVALID_HANDLE_VALUE,    
// use paging file
        NULL,                    // default security 
        PAGE_READWRITE,          // read/write access
        0,                       // max. object size 
        BUF_SIZE,                // buffer size  
        szName);                 // name of mapping object

    
if (hMapFile == NULL) 
    

        printf(
"Could not create file mapping object (%d).\n"
            GetLastError());
        
return 1;
    }

    pBuf 
= (char *) MapViewOfFile(hMapFile,   // handle to map object
        FILE_MAP_ALL_ACCESS, // read/write permission
        0,                   
        
0,                   
        BUF_SIZE);           

    
if (pBuf == NULL) 
    

        printf(
"Could not map view of file (%d).\n"
            GetLastError()); 
        
return 2;
    }


    
while (1)
    
{
        
if (*pBuf == 'a')
        
{
            printf(
"hello world!\n");
        }

        
else
        
{
            printf(
"no hello\n");
        }

        Sleep(
2000);
    }


    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);

    
return 0;
}

第二部分是程序B的代码,代码如下:

#include <windows.h>
#include 
<stdio.h>
#include 
<conio.h>

#define BUF_SIZE 256
TCHAR szName[]
=TEXT("Global\\MyFileMappingObject");

int main()
{
    HANDLE hMapFile;
    
char * pBuf;

    hMapFile 
= OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   
// read/write access
        FALSE,                 // do not inherit the name
        szName);               // name of mapping object 

    
if (hMapFile == NULL) 
    

        printf(
"Could not open file mapping object (%d).\n"
            GetLastError());
        
return 1;
    }
 

    pBuf 
= (char *) MapViewOfFile(hMapFile, // handle to map object
        FILE_MAP_ALL_ACCESS,  // read/write permission
        0,                    
        
0,                    
        BUF_SIZE);                   

    
if (pBuf == NULL) 
    

        printf(
"Could not map view of file (%d).\n"
            GetLastError()); 
        
return 2;
    }


    printf(
"press a key!\n");
    _getch();
    
*pBuf = 'a';
    
    printf(
"press another key!\n");
    _getch();
    
*pBuf = 'b';

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);

    
return 0;
}

        平时的时候,程序A正常运行,每隔两秒打印一次“no hello”,启动程序B后,如果按下回车,则程序A会不断打印“hello world”,程序B中再次按回车,程序A又回到原来的运行轨迹,不断打印“no hello”。这样,通过file-mapping kernel object的方式,实现了进程间的通信。
        至于文件映射内核对象的原理之类的话,我就不多说了,使用google搜索,一搜一大把。大家也可以看书,《windows核心编程》讲的很好。
        其实,上面这个例子,我感觉挺傻的。不过现在没想到多么好的办法,可以使进程B通过通信的方式影响进程A的运行轨迹。 先把这种笨方法记录下来,等找到好的办法,再写上来,哈哈。
        网上还有好多不错的文章,贴出几个来:
       
http://blog.codingnow.com/2005/10/interprocess_communications.html

        http://learn.akae.cn/media/ch30s04.html

posted @ 2012-04-09 23:08 纪灿萌 阅读(340) | 评论 (0)编辑 收藏

2011年11月15日 #

再论extern “C”:C代码调用C++代码

首先简单说明两点:
1.   编译器编译的基本单位是一个C文件或Cpp文件,并不对头文件进行编译。
2.   extern “C”只能被C++编译器处理,C编译器并不认识这个标志。

还是使用上次分享的那篇文章中的示例代码,代码如下:
/*CppHeader.h*/
#ifndef CPP_HEADER
#define CPP_HEADER

extern "C" void print(int i);

#endif CPP_HEADER

/*CppHeader.cpp*/
#include 
"cppHeader.h"
#include 
<stdio.h>
#include 
<iostream.h>
void print(int i)
{
    printf(
"cppHeader %d\n",i);
}


/*c.c*/
extern void print(int i);
int main(int argc,char** argv)
{
    print(
3);
    
return 0;
}

还是有3个文件:CppHeader.h,CppHeader.cpp和c.c。使用下面的命令手工那个手工进行编译和链接:
cl /c /Tp CppHeader.cpp
cl /c /Tc c.c
link c.obj CppHeader.obj
会生成c.exe文件,而且运行正常。
现在,我们把CppHeader.cpp中的#include “cppHeader.h”去掉。再进行编译,还是同样的命令,就发现不会生成c.exe文件了。提示的错误为:
c.obj : error LNK2001: unresolved external symbol _print
c.exe : fatal error LNK1120: 1 unresolved externals
前后比较一下,我们发现只要在CppHeader.cpp文件中加入一句 extern “C” void print(int i) ,程序就能够正常编译链接了。extern “C” 告诉C++编译器,你把print()函数按照C的方式来编译吧。这样,C++编译器就把print(int i)这个函数编译为_print符号了,使用dumpbin工具可以看出来。
小小总结一些extern “C”的作用:
在某个C++源文件中,如果有extern “C”语句,那很可能有两种情况:
1.被extern “C”修饰的函数在本文件中实现,那么C++编译器就把这个函数按照C的方式来进行编译,本文件中别的函数调用此函数时按照C的方式来调用,或者说,按照C的方式链接。
2.被extern “C”修饰的函数在其它文件中实现,那么本文件中的函数调用此函数,就按照C的方式来调用,或者说,按照C的方式链接。

posted @ 2011-11-15 22:55 纪灿萌 阅读(185) | 评论 (0)编辑 收藏

2011年11月14日 #

再论extern “C”:C++代码调用C代码 .

我昨天分享了一篇文章:C++项目中的extern “C” {} ,后来感觉这篇文章中有的地方和自己的认识不同。就重新写一篇吧。重点在C++代码调用C代码的方面和C代码调用C++代码方面。
先说两点:
1.VC编译器既是一个C编译器,又是一个C++编译器。
   默认情况下,对于后缀名为.c的文件,VC编译器cl会使用C的方式去编译;对于后缀名为.cpp的文件,VC编译器会使用C++的方式去编译。
   还可以显式指定是按照C的方式还是C++的方式去编译源文件。调出VC编译器cl,敲入命令 cl /? ,就可以看到了。
      /Tc<source file> compile file as .c
      /Tp<source file> compile file as .cpp
      /TC compile all files as .c
      /TP compile all files as .cpp
2.昨天分享的那篇文章中有这样一句话:注意:extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。这里我感觉应该这样说:extern "C"指令中的C,表示的一种编译规约和连接约,而不是一种语言。具体来说,对于extern “C”修饰的函数来说,是一种编译规则,告诉编译器按照C的方式来编译这个函数;对于其它的函数来说,是一种链接规则,告诉其它函数按照C的方式去链接这个被extern “C”修饰的函数。
好了,来看例子吧。先看C++代码调用C代码的例子。以昨天分享的代码为例:

 1/*CHeader.h*/
 2#ifndef C_HEADER
 3#define C_HEADER
 4
 5extern void print(int i);
 6
 7#endif C_HEADER
 8
 9/*CHeader.c*/
10#include <stdio.h>
11#include "cHeader.h"
12void print(int i)
13{
14    printf("cHeader %d\n",i);
15}

16
17/*C++.cpp*/
18extern "C"{
19#include "cHeader.h"
20}

21
22int main(int argc,char** argv)
23{
24    print(3);
25    return 0;
26}

总共三个文件:CHeader.h,CHeader.c和C++.cpp。手工编译CHeader.c和C++.cpp,命令如下:
cl /c /Tc CHeader.c
cl /c /Tp C++.cpp
我们可以使用dumpbin工具来查看一下由CHeader.c生成的CHeader.obj文件,命令如下:
dumpbin /symbols CHeader.obj
如下图所示:

可以看到,print函数被编译为_print。然后使用link将CHeader.obj和C++.obj链接起来,命令如下:
link CHeader.obj C++.obj
生成CHeader.exe。执行一下,会输出“cHeader 3”,运行正常。
那么,如果使用C++的方式编译CHeader.c文件,又会怎么样。我试了一试,如下命令:
cl /c /Tc CHeader.c
cl /c /Tp C++.cpp
link CHeader.obj C++.obj
编译都通过,但是链接不成功,错误。如下图所示:

提示说找不到_print这个symbol。因为在C++.cpp文件中,我们使用了extern “C” ,认为print函数是按照C的方式编译的,会编译成_print,但实际上我们编译CHeader.c文件的时候,是按照C++的方式编译的,没有把print函数编译成_print。我们可以再次使用dumpbin工具查看一下:

看到了吧,使用C++方式将print函数编译成了?print@@YAXH@Z这个symbol,这样,链接的时候当然找不到_print这个symbol了。
算了,今天太晚了,下次再说C代码调用C++代码的例子吧。其实都差不多的。

posted @ 2011-11-14 23:49 纪灿萌 阅读(236) | 评论 (0)编辑 收藏

2011年9月17日 #

在console mode 中使用 C/C++ 编译器 .

     摘要: vc console mode  阅读全文

posted @ 2011-09-17 18:43 纪灿萌 阅读(110) | 评论 (0)编辑 收藏

2011年9月13日 #

DOS批处理中%cd%和%~dp0的异同

    在DOS的批处理中,有时候需要知道当前的路径。在DOS中,有两个环境变量可以跟当前路径有关,一个是%cd%, 一个是%~dp0。
    这两个变量的用法和代表的内容一般是不同的。
    1. %cd% 可以用在批处理文件中,也可以用在命令行中;展开后,是驱动器盘符:+当前目录,如在dos窗口中进入c:\dir目录下面,
输入:echo %cd% ,则显示为:c:\dir 。
   %cd%的时间内容是可以被改变的,比如CD命令就可以改变它的内容。
    2.%~dp0只可以用在批处理文件中,它是由它所在的批处理文件的目录位置决定的,是批处理文件所在的盘符:+路径。在执行这个批处理文件的过程中,它展开后的内容是不可以改变的。比如在D盘下有个批处理文件,dirshow.bat,其内容为
@echo off   
echo 
this is %%cd%%  %cd%   
echo 
this is %%~dp0 %~dp0 
在C:\ 下执行它,输出为:
C:\>D:\dirshow.bat
this is %cd% C:\   
this is %~dp0 D:\ 
 



posted @ 2011-09-13 22:49 纪灿萌 阅读(617) | 评论 (0)编辑 收藏

仅列出标题