《Windows核心编程》---执行可执行文件

方法一:Shell调用:

Win32中可以通过WinExecShellExecute函数来执行另一个可执行程序。

WinExec函数原型如下:

UINT WINAPI WinExec(

  __in  LPCSTR lpCmdLine, //包含可执行文件名和命令行参数

  __in  UINT uCmdShow //指定窗口的显示方式,如果有窗口的话

);

如果文件被成功执行,那么函数返回一个大于31的值。使用WinExec函数执行文件和在Windows“开始”菜单的“运行”中键入命令在效果上是一样的。注意,该函数是为了兼容16Windows而保留的!!

 

函数ShellExecute即可以用来执行一个可执行文件,也可以指定一个数据文件名让Windows自动查找关联到这个数据文件的可执行文件,并执行这个可执行文件来处理指定的数据文件,数据文件名会以命令行参数的方式传递给可执行文件。函数原型如下:

HINSTANCE ShellExecute(

  __in_opt  HWND hwnd, //指定可执行文件显示的窗口的父窗口

  __in_opt  LPCTSTR lpOperation, //指向一个表示执行方式的字符串:

                   //open---文件被打开,这是lpFile可以是可执行文件名、数据文件名或目录名;

//若该参数为空,函数默认执行open操作

//print---文件被打印,这是lpFile指定的文件名必须是数据文件。

//如果指定的是可执行文件,函数当作“open”操作。

//explore---浏览lpFile参数中指定的目录

  __in      LPCTSTR lpFile, //指定文件名,即可以是可执行文件也可以是数据文件

  __in_opt  LPCTSTR lpParameters, //lpFile指定可执行文件时,此参数用来指定命令行参数;

                                     //lpFile指定数据文件时,此参数必须是NULL

  __in_opt  LPCTSTR lpDirectory, //执行或打开文件时默认的目录

  __in      INT nShowCmd //如果函数执行一个可执行文件,本参数指定窗口打开方式

);

函数执行成功,返回一个大于31的值。

 

方法二:创建进程:

WinExecShellExecute函数来执行文件是很方便的,调用这两个函数从某种意义上讲,相当于创建新的进程,但是函数返回以后,这些新建的进程却脱离了我们的控制,我们无法知道它们什么时候结束,也无法强制结束它们。要对进程进行后续的控制,必须使用函数CreateProcess来创建进程。

当一个进程被创建时,系统进行以下操作:

1)系统为进程创建一个内核对象,并将它的初始计数设置为1

2)系统为进程创建一个虚拟地址空间,并将可执行文件加载到这个地址空间中。系统同时处理可执行文件的导入表,将导入表中登记的所有dll文件装入。每个dll文件被装入内存时,dll的入口函数被执行,如果入口函数返回初始化失败信息的话,进程的初始化失败。可执行文件和所有dll文件都被看作是单独的模块,都被分配了一个实例句柄(实例句柄在数值上等于模块装入到地址空间中的线性地址)。

3)系统为进程建立一个主线程,主线程从可执行文件的入口地址开始执行。

 

创建进程时需要为新进程窗口的外观指定一些属性,这是通过STARTUPINFO结构来实现的:

typedef struct _STARTUPINFO {

  DWORD  cb; //本结构的长度

  LPTSTR lpReserved; //保留字段

  LPTSTR lpDesktop; //NT下使用,指定桌面名称

  LPTSTR lpTitle; //控制台程序使用,指定控制台窗口标题

  DWORD  dwX; //当新进程使用CW_USEDEFAULT参数创建窗口时,将使用这些位置和大小属性

  DWORD  dwY; //

  DWORD  dwXSize; //

  DWORD  dwYSize; //

  DWORD  dwXCountChars; //控制台程序使用,指定控制台窗口行数

  DWORD  dwYCountChars; //

  DWORD  dwFillAttribute; //控制台程序使用,指定控制台窗口背景色

  DWORD  dwFlags; //标志

  WORD   wShowWindow; //窗口的显示方式

  WORD   cbReserved2; //

  LPBYTE lpReserved2; //

  HANDLE hStdInput; //控制台程序使用:几个标准句柄

  HANDLE hStdOutput; //

  HANDLE hStdError; //

} STARTUPINFO, *LPSTARTUPINFO;

 

在需要定制新进程的窗口的时候,才需要手工填写STARTUPINFO结构,在大部分情况下,并不需要新进程的窗口有什么特殊之处,这时只要使用GetStartupInfo获取当前进程的STARTUPINFO并使用它的默认值就可以了:

VOID WINAPI GetStartupInfo(

  __out  LPSTARTUPINFO lpStartupInfo

);

 

获得STARTUPINFO结构后,就可以调用CreateProcess函数来创建进程了:

BOOL WINAPI CreateProcess(

  __in_opt     LPCTSTR lpApplicationName, //指定可执行文件名的字符串,当该参数为NULL时,

                            //可执行文件名可以在lpCommandLine参数指定的命令行参数中包括

  __inout_opt  LPTSTR lpCommandLine, //指定命令行参数的字符串

  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes, //指定新进程的安全属性

  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes, //指定新线程的安全属性

  __in         BOOL bInheritHandles, //指定当前进程的句柄是否可以被新进程继承

  __in         DWORD dwCreationFlags, //创建标志,指定新进程的优先级以及其他标志

  __in_opt     LPVOID lpEnvironment, //指向新进程的环境变量块,若该参数为NULL,则让

                            //Windows拷贝当前进程的环境块当作子进程的环境块

  __in_opt     LPCTSTR lpCurrentDirectory, //指向一个路径字符串,用来指定子进程的当前驱动器

                            //和当前目录,如果为NULL,子进程将引用父进程的当前路径

  __in         LPSTARTUPINFO lpStartupInfo, //指向STARTUPINFO结构

  __out        LPPROCESS_INFORMATION lpProcessInformation //指向一个

                   //PROCESS_INFORMATION结构,这个结构用来供函数返回新建进程的相关信息

);

 

函数执行成功时,返回非0值,否则返回0值。

PROCESS_INFORMATION结构如下:

typedef struct _PROCESS_INFORMATION {

  HANDLE hProcess; //新建进程句柄

  HANDLE hThread; //进程的主线程句柄

  DWORD  dwProcessId; //新建进程ID

  DWORD  dwThreadId; //进程的主线程ID

} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

 

创建进程的简单例子如下:

#include <windows.h>

#include <stdio.h>

#include <tchar.h>

 

void _tmain( int argc, TCHAR *argv[] )

{

    STARTUPINFO si;

    PROCESS_INFORMATION pi;

 

    ZeroMemory( &si, sizeof(si) );

    si.cb = sizeof(si);

    ZeroMemory( &pi, sizeof(pi) );

 

    if( argc != 2 )

    {

        printf("Usage: %s [cmdline]/n", argv[0]);

        return;

    }

 

    // Start the child process.

    if( !CreateProcess( NULL,   // No module name (use command line)

        argv[1],        // Command line

        NULL,           // Process handle not inheritable

        NULL,           // Thread handle not inheritable

        FALSE,          // Set handle inheritance to FALSE

        0,              // No creation flags

        NULL,           // Use parent's environment block

        NULL,           // Use parent's starting directory

        &si,            // Pointer to STARTUPINFO structure

        &pi )           // Pointer to PROCESS_INFORMATION structure

    )

    {

        printf( "CreateProcess failed (%d)./n", GetLastError() );

        return;

    }

 

    // Wait until child process exits.

    WaitForSingleObject( pi.hProcess, INFINITE );

 

    // Close process and thread handles.

    CloseHandle( pi.hProcess );

    CloseHandle( pi.hThread );

}

 

要结束当前进程,可以使用函数ExitProcess

VOID WINAPI ExitProcess(

  __in  UINT uExitCode //进程退出码

);

 

要结束其他进程,包括当前进程的子进程时,需要使用函数TerminateProcess

BOOL WINAPI TerminateProcess(

  __in  HANDLE hProcess, //需要结束的进程的句柄

  __in  UINT uExitCode //进程的退出码

);

注意:TerminateProcess函数不是一个推荐使用的函数,一般仅在很极端的情况下使用(如任务管理器用来结束停止响应的进程),因为它将目标进程无条件结束,被结束的进程根本没有机会进行扫尾工作。同时,目标进程使用的dll文件也不会收到结束通知,所有极有可能造成数据丢失。

 

当一个进程被正常结束时,系统将做下面的工作:

1)进程创建或打开的所有对象句柄被关闭;

2)进程中所有线程被终止;

3)进程及进程中所有线程的状态被改为置位状态,以便让WaitForSingleObject函数正确检测;

4)进程对象中退出码字段从STILL_ACTIVE被改为指定的退出码。

 

要检测一个进程的退出码,可以使用GetExitCodeProcess函数:

BOOL WINAPI GetExitCodeProcess(

  __in   HANDLE hProcess, //被检测进程的进程句柄

  __out  LPDWORD lpExitCode //用来接收函数返回的退出码

);

如果被检测进程还没有结束,那么返回到lpExitCode的是STILL_ACTIVE

 

下面的代码演示了创建子进程,使用匿名管道来重定向子进程的标准输入和输出:

先是父进程的代码:

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

#include <strsafe.h>

 

#define BUFSIZE 4096

 

HANDLE g_hChildStd_IN_Rd = NULL;

HANDLE g_hChildStd_IN_Wr = NULL;

HANDLE g_hChildStd_OUT_Rd = NULL;

HANDLE g_hChildStd_OUT_Wr = NULL;

 

HANDLE g_hInputFile = NULL;

 

void CreateChildProcess(void);

void WriteToPipe(void);

void ReadFromPipe(void);

void ErrorExit(PTSTR);

 

int _tmain(int argc, TCHAR *argv[])

{

   SECURITY_ATTRIBUTES saAttr;

 

   printf("/n->Start of parent execution./n");

 

// Set the bInheritHandle flag so pipe handles are inherited.

 

   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

   saAttr.bInheritHandle = TRUE;

   saAttr.lpSecurityDescriptor = NULL;

 

// Create a pipe for the child process's STDOUT.

 

   if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )

      ErrorExit(TEXT("StdoutRd CreatePipe"));

 

// Ensure the read handle to the pipe for STDOUT is not inherited.

 

   if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )

      ErrorExit(TEXT("Stdout SetHandleInformation"));

 

// Create a pipe for the child process's STDIN.

 

   if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))

      ErrorExit(TEXT("Stdin CreatePipe"));

 

// Ensure the write handle to the pipe for STDIN is not inherited.

 

   if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )

      ErrorExit(TEXT("Stdin SetHandleInformation"));

 

// Create the child process.

  

   CreateChildProcess();

 

// Get a handle to an input file for the parent.

// This example assumes a plain text file and uses string output to verify data flow.

 

   if (argc == 1)

      ErrorExit(TEXT("Please specify an input file./n"));

 

   g_hInputFile = CreateFile(

       argv[1],

       GENERIC_READ,

       0,

       NULL,

       OPEN_EXISTING,

       FILE_ATTRIBUTE_READONLY,

       NULL);

 

   if ( g_hInputFile == INVALID_HANDLE_VALUE )

      ErrorExit(TEXT("CreateFile"));

 

// Write to the pipe that is the standard input for a child process.

// Data is written to the pipe's buffers, so it is not necessary to wait

// until the child process is running before writing data.

 

   WriteToPipe();

   printf( "/n->Contents of %s written to child STDIN pipe./n", argv[1]);

 

// Read from pipe that is the standard output for child process.

 

   printf( "/n->Contents of child process STDOUT:/n/n", argv[1]);

   ReadFromPipe();

 

   printf("/n->End of parent execution./n");

 

// The remaining open handles are cleaned up when this process terminates.

// To avoid resource leaks in a larger application, close handles explicitly.

 

   return 0;

}

 

void CreateChildProcess()

// Create a child process that uses the previously created pipes for STDIN and STDOUT.

{

   TCHAR szCmdline[]=TEXT("child");

   PROCESS_INFORMATION piProcInfo;

   STARTUPINFO siStartInfo;

   BOOL bSuccess = FALSE;

 

// Set up members of the PROCESS_INFORMATION structure.

 

   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

 

// Set up members of the STARTUPINFO structure.

// This structure specifies the STDIN and STDOUT handles for redirection.

 

   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );

   siStartInfo.cb = sizeof(STARTUPINFO);

   siStartInfo.hStdError = g_hChildStd_OUT_Wr;

   siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;

   siStartInfo.hStdInput = g_hChildStd_IN_Rd;

   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

 

// Create the child process.

   

   bSuccess = CreateProcess(NULL,

      szCmdline,     // command line

      NULL,          // process security attributes

      NULL,          // primary thread security attributes

      TRUE,          // handles are inherited

      0,             // creation flags

      NULL,          // use parent's environment

      NULL,          // use parent's current directory

      &siStartInfo,  // STARTUPINFO pointer

      &piProcInfo);  // receives PROCESS_INFORMATION

  

   // If an error occurs, exit the application.

   if ( ! bSuccess )

      ErrorExit(TEXT("CreateProcess"));

   else

   {

      // Close handles to the child process and its primary thread.

           // Some applications might keep these handles to monitor the status

           // of the child process, for example.

 

      CloseHandle(piProcInfo.hProcess);

      CloseHandle(piProcInfo.hThread);

   }

}

 

void WriteToPipe(void)

 

// Read from a file and write its contents to the pipe for the child's STDIN.

// Stop when there is no more data.

{

   DWORD dwRead, dwWritten;

   CHAR chBuf[BUFSIZE];

   BOOL bSuccess = FALSE;

 

   for (;;)

   {

      bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);

      if ( ! bSuccess || dwRead == 0 ) break;

     

      bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);

      if ( ! bSuccess ) break;

   }

 

// Close the pipe handle so the child process stops reading.

 

   if ( ! CloseHandle(g_hChildStd_IN_Wr) )

      ErrorExit(TEXT("StdInWr CloseHandle"));

}

 

void ReadFromPipe(void)

 

// Read output from the child process's pipe for STDOUT

// and write to the parent process's pipe for STDOUT.

// Stop when there is no more data.

{

   DWORD dwRead, dwWritten;

   CHAR chBuf[BUFSIZE];

   BOOL bSuccess = FALSE;

   HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

 

// Close the write end of the pipe before reading from the

// read end of the pipe, to control child process execution.

// The pipe is assumed to have enough buffer space to hold the

// data the child process has already written to it.

 

   if (!CloseHandle(g_hChildStd_OUT_Wr))

      ErrorExit(TEXT("StdOutWr CloseHandle"));

 

   for (;;)

   {

      bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);

      if( ! bSuccess || dwRead == 0 ) break;

 

      bSuccess = WriteFile(hParentStdOut, chBuf,

                           dwRead, &dwWritten, NULL);

      if (! bSuccess ) break;

   }

}

 

void ErrorExit(PTSTR lpszFunction)

 

// Format a readable error message, display a message box,

// and exit from the application.

{

    LPVOID lpMsgBuf;

    LPVOID lpDisplayBuf;

    DWORD dw = GetLastError();

 

    FormatMessage(

        FORMAT_MESSAGE_ALLOCATE_BUFFER |

        FORMAT_MESSAGE_FROM_SYSTEM |

        FORMAT_MESSAGE_IGNORE_INSERTS,

        NULL,

        dw,

        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

        (LPTSTR) &lpMsgBuf,

        0, NULL );

 

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,

        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));

    StringCchPrintf((LPTSTR)lpDisplayBuf,

        LocalSize(lpDisplayBuf) / sizeof(TCHAR),

        TEXT("%s failed with error %d: %s"),

        lpszFunction, dw, lpMsgBuf);

    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

 

    LocalFree(lpMsgBuf);

    LocalFree(lpDisplayBuf);

    ExitProcess(1);

}

 

接下来是子进程的代码:

#include <windows.h>

#include <stdio.h>

 

#define BUFSIZE 4096

 

int main(void)

{

   CHAR chBuf[BUFSIZE];

   DWORD dwRead, dwWritten;

   HANDLE hStdin, hStdout;

   BOOL bSuccess;

 

   hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

   hStdin = GetStdHandle(STD_INPUT_HANDLE);

   if (

       (hStdout == INVALID_HANDLE_VALUE) ||

       (hStdin == INVALID_HANDLE_VALUE)

      )

      ExitProcess(1);

 

   // Send something to this process's stdout using printf.

   printf("/n ** This is a message from the child process. ** /n");

 

   // This simple algorithm uses the existence of the pipes to control execution.

   // It relies on the pipe buffers to ensure that no data is lost.

   // Larger applications would use more advanced process control.

 

   for (;;)

   {

   // Read from standard input and stop on error or no data.

      bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

     

      if (! bSuccess || dwRead == 0)

         break;

 

   // Write to standard output and stop on error.

      bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);

     

      if (! bSuccess)

         break;

   }

   return 0;

}

 

 

 

 

你可能感兴趣的:(windows)