进程间的那些事~~

来源:互联网 发布:技术规格书 知乎 编辑:程序博客网 时间:2024/05/09 03:45

VC中调用外部程序方式总结(转)

 

三个SDK函数: WinExec,ShellExecute ,CreateProcess可以实现调用其他程序的要求,其中以WinExec最为简单,ShellExecute比WinExec灵活一些,CreateProcess最为复杂。

WinExec 两个参数,前一个指定路径,后一个指定显示方式。

ShellExecute 可以指定工作目录,并且还可以寻找文件的关联直接打开不用加载与文件关联的应用程序,ShellExecute还可以打开网页,启动相应的邮件关联发送邮件等等。

CreateProcess  一共有十个参数,不过大部分都可以用NULL代替,它可以指定进程的安全属性,继承信息,类的优先级等等。如果我们要得到足够多的关于新的进程的信息,控制新的进程的细节属性,若要达到这些目的,我们就需要使用CreateProcess函数了。

三个SDK函数( WinExec、ShellExec、CrateProcess )的语法:

(一)WinExec

这个函数最简单,只有两个参数,原型如下:

      UINT WinExec(

      LPCSTR lpCmdLine,   // 命令路径

      UINT uCmdShow      // 显示方式

      );

使用方法如下:

WinExec("Notepad.exe", SW_SHOW);  // 打开记事本

WinExec("D://Program Files//Test//Test.exe",SW_SHOWMAXIMIZED); // 以最大化的方式打开Test.exe

需要注意的是若用 SW_SHOWMAXMIZED 方式去加载一个无最大化按钮的程序,譬如Neterm,Calc 等等,就不会出现正常的 窗体,但是已经被加到任务列表里了。

这个函数只能打开exe文件。

需要的头文件:windows.h,winbase.h(前者是试验出来,后者是msdn上说明),另外,这两者的先后顺序不能变。

msdn上的说明:[url]http://msdn.microsoft.com/en-us/library/ms687393[/url](VS.85).aspx

(二)ShellExecute

原型如下:

      HINSTANCE ShellExecute(

      HWND hwnd,           //父窗口句柄

      LPCTSTR lpOperation,   //操作, 打开方式 "edit","explore","open","find","print","NULL"

      LPCTSTR lpFile,         //文件名,前面可加路径

      LPCTSTR lpParameters,   //参数

      LPCTSTR lpDirectory,    //默认文件夹

      INT nShowCmd          //显示方式

);

使用方法如下:

ShellExecute(NULL,"open","C://Test.txt",NULL,NULL,SW_SHOWNORMAL); // 打开C:/Test.txt 文件

ShellExecute(NULL, "open", "[url]http://www.google.com/[/url]",  NULL, NULL, SW_SHOWNORMAL); // 打开网页[url]www.google.com[/url]

ShellExecute(NULL,"explore", "D://C++",NULL,NULL,SW_SHOWNORMAL); // 打开目录D:/C++

ShellExecute(NULL,"print","C://Test.txt",NULL,NULL, SW_HIDE); // 打印文件C:/Test.txt

ShellExecute不支持定向输出。

这个函数可以打开任意文件,会调用系统注册的程序来打开对应后缀名的文件。

需要的头文件:windows.h,shellapi.h(前者是我试验出来的,后者是msdn说需要的)。另外这两者的先后顺序不能变。

msdn上说明:[url]http://msdn.microsoft.com/en-us/library/bb762153[/url](VS.85).aspx

(三)CreateProcess

原型如下:

      BOOL CreateProcess(

      LPCTSTR lpApplicationName, //执行程序名

      LPTSTR lpCommandLine,  // 参数行

      //下面两个参数描述了所创建的进程和线程的安全属性,如果为NULL则使用默认的安全属性

      LPSECURITY_ATTRIBUTES lpProcessAttributes,  // process security attributes

      LPSECURITY_ATTRIBUTES lpThreadAttributes,   // thread security attributes

      BOOL bInheritHandles,  // 继承标志

      DWORD dwCreationFlags, // 创建标志

      LPVOID lpEnvironment,  // 环境变量

      LPCTSTR lpCurrentDirectory,   // 运行该进程的初始目录

      LPSTARTUPINFO lpStartupInfo,  // 用于在创建子进程时设置各种属性

      LPPROCESS_INFORMATION lpProcessInformation //在进程创建后接受相关信息

      );

使用方法如下:

             PROCESS_INFORMATION pi;

                       STARTUPINFO si;

                       memset(&si,0,sizeof(si));

                       si.cb=sizeof(si);

                      si.wShowWindow=SW_SHOW;

                       si.dwFlags=STARTF_USESHOWWINDOW;

                       bool fRet=CreateProcess("D://putty.exe",NULL,NULL,FALSE,NULL,NULL,NULL,NULL,&si,&pi);

这个函数可以打开任意文件,会调用系统注册的程序来打开对应后缀名的文件。

需要的头文件:windows.h,winbase.h(前者是试验出来,后者是msdn上说明),另外,这两者的先后顺序不能变。

msdn上的说明:[url]http://msdn.microsoft.com/en-us/library/ms682425[/url](VS.85).aspx

可以看出,通过上面的几个不同的方法,都可以实现在应用程序中打开其他应用程序的目的,其中有些方法可能会麻烦一点,所以就需要我们根据不同的目的去选择最适合自己的方法去实现自己的目的!

关于三个SDK函数: WinExec, ShellExecute,CreateProcess 的其他注意事项:

1、定义头文件

这个是引用新函数都必须注意的内容。但是MS的头文件引用顺序有点怪,比如上面的三种清理。另外,如果用了预编译,那么记得在任何源程序中的#include “stdafx.h”之前的引用都会失效,从其后才生效。(不可否认,预编译有他的好处,尤其当程序很大的时候,但是任何好处都是要付出代价的)

2、定义路径

C++中所表示的路径要用 " // "而不是平常所用的" / ",所以以上三个函数表示路径都为:

Disk://Directory//...//File name

WinExec("D://Program Files//Test//Test.exe",SW_SHOWMAXIMIZED);

ShellExecute(NULL,"open","C://Test.txt",NULL,NULL,SW_SHOWNORMAL);

bool fRet=CreateProcess("D://putty.exe",NULL,NULL,FALSE,NULL,NULL,NULL,NULL,&si,&pi)

3、注意文件的路径

在程序a调用程序b的时候,b原来的默认的当前路径都会变成a的当前路径。所以,一定要注意。可以养成使用绝对路径的习惯,另外,记得打开文件之类的操作,一定要验证是否有错。

 


 

终止进程的方法

 

终止一个进程最好是由WinMain函数返回。在主线程中调用ExitThread函数也可以。在当前进程终止另一个进程使用TerminateProcess函数。CE下的TerminateProcess函数要比其他Windows下TerminateProcess函数功能强大。CE下的TerminateProcess函数在使进程退出时,会通知每个加载的DLL并做出进程退出时该做的所有处理工作。

 

VC 结束进程

http://hi.baidu.com/jjzhang166/blog/item/64116d8ba3cf9c17c8fc7a81.html 

1.使用ExitProcess()结束进程

进程只是提供了一段地址空间和内核对象,其运行时通过在其地址空间内的主线程来体现的。当主线程的进入点函数返回时,进程也就随之结束。这种进程的终止方 式是进程的正常退出,进程中的所有线程资源都能够得到正确的清除。除了这种进程的正常退出方式外,有时还需要在程序中通过代码来强制结束本进程或其他进程 的运行。ExitProcess()函数的原型为:

void ExitProcess(UINT uExitCode);

其参数uExitCode为进程设置了退出代码。该函数具有强制性,在执行完毕后进程即被结束,因此位于其后的任何代码将不能被执行。虽然 ExitProcess()函数可以在结束进程的同时通知与其关联的动态链接库,但是由于它的这种执行的强制性,使得ExitProcess()函数在使 用上将存在有安全隐患。例如,如果在程序调用ExitProcess()函数之前曾用new操作符申请过一段空间,那么将会由于ExitProcess ()函数的强制性而无法通过delete操作符将其释放,从而造成内存泄露。有鉴于ExitProcess()函数的强制性和不安全性,在使用时一定要引 起注意。

 

2.使用TerminateProcess()结束进程

ExitProcess()只能强制执行本进程的退出,如果要在一个进程中强制结束其他进程就要用TerminateProcess()来实现。与 ExitProcess()不同,TerminateProcess()函数执行后,被终止的进程是不会的到任何关于程序退出的通知的。也就是说,被终止 的进程是无法在结束运行前进行退出前的收尾工作的。所以,通常只有在其他任何方法都无法迫使进程退出时才会考虑使用TerminateProcess() 去强制结束进程。下面给出TerminateProcess()的函数原型: 字串3

BOOL TerminateProcess(HANDLE hProcess,UINT uExitCode);

参数hProcess和uExitCode分别为进程句柄和退出代码。如果被结束的是本进程,可以通过GetCurrentProcess()获取到句 柄。TerminateProcess()是异步执行的,在调用返回后并不能确定被终止进程是否已经真的退出,如果调用TerminateProcess ()的进程对此细节关心,可以通过WaitForSingleObject()来等待进程的真正结束。 

 

 在VC程序中如何结束系统正在运行的其他进程(该进程必须有窗口界面),其实很简单,按如下步骤进行即可:
    1.取得进程的句柄(利用FindWindow函数得到);
    2.获取进程ID号(用GetWindowThreadProcessId函数获取);
    3.打开进程,OpenProcess函数中的第一个参数设为PROCESS_TERMINATE,就可以获取处理该进程的句柄;
    4.利用TerminateProcess函数结束进程,将该函数的第二个参数设为4。

    代码如下:
//结束进程
int CStaticFunc::KillProcess(LPCSTR pszClassName, LPCSTR

pszWindowTitle)
{
    HANDLE hProcessHandle; 
    ULONG nProcessID;
    HWND TheWindow;

    TheWindow = ::FindWindow( NULL, pszWindowTitle );
    ::GetWindowThreadProcessId( TheWindow, &nProcessID );
    hProcessHandle = ::OpenProcess( PROCESS_TERMINATE, FALSE,

nProcessID );
    return ::TerminateProcess( hProcessHandle, 4 );
}

 

 

windows下VC结束进程的技巧

http://blog.sina.com.cn/s/blog_46e73e77010007xx.html
windows下用VC开发过程中,经常会遇到这样的情况:
一个主进程,负责与其他系统的通信,并根据与其他系统的通讯结果来启动另外的进程或结束已经存在的进程。
有时候,主进程由于某些原因可能退出、重启,而重启后的主进程,怎样管理之前由它启动的进程呢??
 
这几天开发就遇到了这样的问题。
我采用了下面的方法,使该问题得到了解决。
 
主进程对其所启动的其他进程的进程号进行记录、保存,并且,让主进程与子进程之间通过socket通信,子进程向主进程汇报其状态和进程号。
这样,如果主进程退出、重启,就从文件读取之前的子进程信息,然后,再根据子进程的回报情况,进行综合处理。
而结束子进程的方法,则要根据进程号来进行,具体方法就是,通过进程号得到进程句柄,然后再使用进程句柄,来结束进程。
 
之所以使用进程号而不用进程句柄,是由于进程句柄是有局限性的,句柄只是在当前的进程中有效,也就是,进城句柄是在当前进程中所给出的系统地址,如果主进程退出、重启后,该句柄已经无效了,因此,也就不能用其来操作进程了。
 
通过进程号结束进城的代码如下:

BOOL TerminateProcessFromId(DWORD dwID)
{
 BOOL bRet=FALSE;
 HANDLE hProcess=::OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwID);
 if(hProcess!=NULL)
 {  
  bRet=::TerminateProcess(hProcess,0);
 }
 else
 {
  //给出错误信息
  return FALSE;
 }
 CloseHandle(hProcess);
 return bRet;
}

 

 


 VC++中进程间相互通信的多种方法

 

进程通常被定义为一个正在运行的程序的实例,它由两个部分组成:
  一个是操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方
  另一个是地址空间,它包含所有的可执行模块或DLL模块的代码和数据。它还包含动态分配的空间。如线程堆栈和堆分配空间。每个进程被赋予它自己的虚拟地址空间,当进程中的一个线程正在运行时,该线程可以访问只属于它的进程的内存。属于其它进程的内存则是隐藏的,并不能被正在运行的线程访问。

  为了能在两个进程之间进行通讯,由以下几种方法可供参考:

  1、剪贴板Clipboard: 在16位时代常使用的方式,CWnd中提供支持

  2、窗口消息 标准的Windows消息以及专用的WM_COPYDATA消息 SENDMESSAGE()接收端必须有一个窗口

  3、使用共享内存方式(Shared Memory)
  a.设定一块共享内存区域
HANDLE CreateFileMapping(HANDLE,LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR)
  产生一个file-mapping核心对象
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAcess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap
);
  得到共享内存的指针

  b.找出共享内存
  决定这块内存要以点对点(peer to peer)的形式呈现每个进程都必须有相同的能力,产生共享内存并将它初始化。每个进程都应该调用CreateFileMapping(),然后调用GetLastError().如果传回的错误代码是ERROR_ALREADY_EXISTS,那么进程就可以假设这一共享内存区域已经被别的进程打开并初始化了,否则该进程就可以合理的认为自己排在第一位,并接下来将共享内存初始化。

  要使用client/server架构中只有server进程才应该产生并初始化共享内存。所有的进程都应该使用
HANDLE OpenFileMapping(DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName);
  再调用MapViewOfFile(),取得共享内存的指针

  c.同步处理(Mutex)

  d.清理(Cleaning up)
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
CloseHandle()

  4、动态数据交换(DDE)通过维护全局分配内存使的应用程序间传递成为可能
  其方式是在一块全局内存中手工放置大量的数据,然后使用窗口消息传递内存指针.这是16位WIN时代使用的方式,因为在WIN32下已经没有全局和局部内存 ,现在的内存只有一种就是虚存。

  5、消息管道(Message Pipe)
  用于设置应用程序间的一条永久通讯通道,通过该通道可以象自己的应用程序访问一个平面文件一样读写数据。
  匿名管道(Anonymous Pipes)
            单向流动,并且只能够在同一电脑上的各个进程之间流动。
  命名管道(Named Pipes)
         双向,跨网络,任何进程都可以轻易的抓住,放进管道的数据有固定的格式,而使用ReadFile()只能读取该大小的倍数。
  可以被使用于I/O Completion Ports

  6、邮件槽(Mailslots)
  广播式通信,在32系统中提供的新方法,可以在不同主机间交换数据,在 WIN9X下只支持邮件槽客户

  7、Windows套接字(Windows Socket)
  它具备消息管道所有的功能,但遵守一套通信标准使的不同操作系统之上的应 用程序之间可以互相通信。

  8、Internet通信 它让应用程序从Internet地址上载或下载文件

  9、RPC:远程过程调用,很少使用,因其与UNIX的RPC不兼容。

  10、串行/并行通信(Serial/Parallel Communication)
  它允许应用程序通过串行或并行端口与其他的应用程序通信

  11、COM/DCOM通过COM系统的代理存根方式进行进程间数据交换,但只能够表现在对接口 函数的调用时传送数据,通过DCOM可以在不同主机间传送数据。

http://blog.csdn.net/armman/archive/2007/02/26/1514644.aspx