技术标签: 学习笔记
目录
在C++中,创建一个进程,需要利用WINDOWS API函数,该函数的原型如下:
BOOL CreateProcessA(
LPCSTR lpApplicationName, //启动进程的程序模块
LPSTR lpCommandLine, //程序执行的命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, //进程继承属性
LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程继承属性
BOOL bInheritHandles, //句柄继承标志
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
参数说明:
lpApplicationName:
此处的程序指的是基于windows的可执行程序,也可以是在当前系统中安装的其他子系统的可执行文件。
此处的字符串必须包含一个扩展名,系统不会默认任何扩展名。
此处的路径可以是相对路径,也可以是绝对路径。如果是相对路径,需要指出的是程序默认会在当前目录下寻找相应的程序。
此处为NULL,则会启动cmd.exe进程。同时,lpCommandLine参数必须以一个空格开头,执行lpCommandLine的命令。
lpCommandLine:
执行的命令行参数。
lpProcessAttributes:
指向SECURITY_ATTRIBUTES 结构的指针,用来决定返回的进程对象能否被子进程继承,如果为NULL,则不可继承。
lpThreadAttributes:
指向SECURITY_ATTRIBUTES 结构的指针,用来决定返回的线程对象能否被子进程继承,如果为NULL,则不可继承。
在使用CreateProcess创建进程的时候,会创建一个进程和一个主线程。在其他子线程中,根据该进程创建时的标志来决定是否可以继承此函数创建的进程与线程。
bInheritHandles:
指示新进程是否从调用进程处继承了句柄。
如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限。
dwCreationFlags:
指定附加的、用来控制优先类和进程的创建的标志。以下的创建标志可以以除下面列出的方式外的任何方式组合后指定。
⑴值:CREATE_DEFAULT_ERROR_MODE
含义:新的进程不继承调用进程的错误模式。CreateProcess函数赋予新进程当前的默认错误模式作为替代。应用程序可以调用SetErrorMode函数设置当前的默认错误模式。
这个标志对于那些运行在没有硬件错误环境下的多线程shell程序是十分有用的。
对于CreateProcess函数,默认的行为是为新进程继承调用者的错误模式。设置这个标志以改变默认的处理方式。
⑵值:CREATE_NEW_CONSOLE
含义:新的进程将使用一个新的控制台,而不是继承父进程的控制台。这个标志不能与DETACHED_PROCESS标志一起使用。
⑶值:CREATE_NEW_PROCESS_GROUP
含义:新进程将是一个进程树的根进程。进程树中的全部进程都是根进程的子进程。新进程树的用户标识符与这个进程的标识符是相同的,由lpProcessInformation参数返回。进程树经常使用GenerateConsoleCtrlEvent函数允许发送CTRL+C或CTRL+BREAK信号到一组控制台进程。
⑷值:CREATE_SEPARATE_WOW_VDM
如果被设置,新进程将会在一个私有的虚拟DOS机(VDM)中运行。另外,默认情况下所有的16位Windows应用程序都会在同一个共享的VDM中以线程的方式运行。单独运行一个16位程序的优点是一个应用程序的崩溃只会结束这一个VDM的运行;其他那些在不同VDM中运行的程序会继续正常的运行。同样的,在不同VDM中运行的16位Windows应用程序拥有不同的输入队列,这意味着如果一个程序暂时失去响应,在独立的VDM中的应用程序能够继续获得输入。
⑸值:CREATE_SHARED_WOW_VDM
如果WIN.INI中的Windows段的DefaultSeparateVDM选项被设置为真,这个标识使得CreateProcess函数越过这个选项并在共享的虚拟DOS机中运行新进程。
⑹值:CREATE_SUSPENDED
含义:新进程的主线程会以暂停的状态被创建,直到调用ResumeThread函数被调用时才运行。
⑺值:CREATE_UNICODE_ENVIRONMENT
含义:如果被设置,由lpEnvironment参数指定的环境块使用Unicode字符,如果为空,环境块使用ANSI字符。
⑻值:DEBUG_PROCESS
含义:如果这个标志被设置,调用进程将被当做一个调试程序,并且新进程会被当做被调试的进程。系统把被调试程序发生的所有调试事件通知给调试器。
如果你使用这个标志创建进程,只有调用进程(调用CreateProcess函数的进程)可以调用WaitForDebugEvent函数。
⑼值:DEBUG_ONLY_THIS_PROCESS
含义:如果此标志没有被设置且调用进程正在被调试,新进程将成为调试调用进程的调试器的另一个调试对象。如果调用进程没有被调试,有关调试的行为就不会产生。
⑽值:DETACHED_PROCESS
含义:对于控制台进程,新进程没有访问父进程控制台的权限。新进程可以通过AllocConsole函数自己创建一个新的控制台。这个标志不可以与CREATE_NEW_CONSOLE标志一起使用。
〔11〕值:CREATE_NO_WINDOW
含义:系统不为新进程创建CUI窗口,使用该标志可以创建不含窗口的CUI程序。
dwCreationFlags:
还用来控制新进程的优先类,优先类用来决定此进程的线程调度的优先级。如果下面的优先级类标志都没有被指定,那么默认的优先类是NORMAL_PRIORITY_CLASS,除非被创建的进程是IDLE_PRIORITY_CLASS。在这种情况下子进程的默认优先类是IDLE_PRIORITY_CLASS。
可以选择下面的标志中的一个:
优先级:HIGH_PRIORITY_CLASS
含义:指示这个进程将执行时间临界的任务,所以它必须被立即运行以保证正确。这个优先级的程序优先于正常优先级或空闲优先级的程序。一个例子是Windows任务列表,为了保证当用户调用时可以立刻响应,放弃了对系统负荷的考虑。确保在使用高优先级时应该足够谨慎,因为一个高优先级的CPU关联应用程序可以占用几乎全部的CPU可用时间。
优先级:IDLE_PRIORITY_CLASS
含义:指示这个进程的线程只有在系统空闲时才会运行并且可以被任何高优先级的任务打断。例如屏幕保护程序。空闲优先级会被子进程继承。
优先级:NORMAL_PRIORITY_CLASS
含义:指示这个进程没有特殊的任务调度要求。
优先级:REALTIME_PRIORITY_CLASS
含义:指示这个进程拥有可用的最高优先级。一个拥有实时优先级的进程的线程可以打断所有其他进程线程的执行,包括正在执行重要任务的系统进程。例如,一个执行时间稍长一点的实时进程可能导致磁盘缓存不足或鼠标反映迟钝。
lpEnvironment:
指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。
一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。每个字符串都是name=value的形式。
与其使用应用程序提供的环境块,不如直接把这个参数设为空,系统驱动器上的当前目录信息不会被自动传递给新创建的进程。对于这个情况的探讨和如何处理,请参见注释一节。
环境块可以包含Unicode或ANSI字符。如果lpEnvironment指向的环境块包含Unicode字符,那么dwCreationFlags字段的CREATE_UNICODE_ENⅥRONMENT标志将被设置。如果块包含ANSI字符,该标志将被清空。
请注意一个ANSI环境块是由两个零字节结束的:一个是字符串的结尾,另一个用来结束这个快。一个Unicode环境块是由四个零字节结束的:两个代表字符串结束,另两个用来结束块。
lpCurrentDirectory:
指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动应用程序并指定它们的驱动器和工作目录的shell程序的主要条件。
lpStartupInfo:
指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
lpProcessInformation
指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体
// CreateProcess.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
void _tmain( int argc, TCHAR *argv[] )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
CString s_Cmd = "ipconfig /?";
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)
s_Cmd.GetBuffer(), // 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 );
getchar();
system("pause");
}
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#include <afxwin.h>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
// TODO: 在此处引用程序需要的其他头文件
运行结果如下:
管道是一块共享内存,用来进行进程间的通信。有两种方式的管道,一种是匿名管道(anonymous pipes),一种是命名管道( named pipes)。匿名管道比命名管道需求较小,但同样,其功能也受到更多的限制。
匿名管道是一种未命名的单向管道,通常在父进程和子进程之间传输数据。匿名管道始终是本地的;它们不能用于通过网络进行通信。
创建管道函数
BOOL CreatePipe(
PHANDLE hReadPipe, //指向接收管道的读取句柄的变量的指针
PHANDLE hWritePipe, //指向接收管道的写句柄的变量的指针。
LPSECURITY_ATTRIBUTES lpPipeAttributes,//指向SECURITY_ATTRIBUTES结构体的指针
DWORD nSize //管道的大小
);
读取管道函数
BOOL ReadFile(
HANDLE hFile, //读取句柄
LPVOID lpBuffer, //读取之后的数据保存在该缓冲区中
DWORD nNumberOfBytesToRead, //指示最大的读取字符数
LPDWORD lpNumberOfBytesRead, //实际的读取字节数
LPOVERLAPPED lpOverlapped //OVERLAPPED数据结构的指针
);
写入管道函数
BOOL WriteFile(
HANDLE hFile, //可供写入的句柄
LPCVOID lpBuffer, //写入数据的来源
DWORD nNumberOfBytesToWrite, //最大的写入字节
LPDWORD lpNumberOfBytesWritten, //实际写入的字节
LPOVERLAPPED lpOverlapped //OVERLAPPED数据结构的指针
);
CreatePipe函数可以创建一个匿名管道,同时返回两个句柄:一个读管道句柄和一个写管道句柄。写句柄仅能向管道写,读句柄仅能从管道中读。
通常,管道服务端会通过继承的方式传递一个管道句柄给另一个进程,当然,也可以通过DuplicateHandle函数在两个不相关的进程间通过其他技术(DDE或共享内存)复制管道句柄。
根据另一个进程是接收信息还是发送信息,管道服务端即可以将写句柄传递给另一个进程,也可以将读句柄传递给另一个进程。
要从管道中读取,调用ReadFile函数使用管道的读取句柄从管道中读取数据;在另一端写完或写句柄关闭、出错的情况,会调用ReadFile从管道中读取数据。
要向管道中写数据,调用WriteFile函数使用管道的写入句柄向管道中写入数据;写入句柄不会关闭直到数据写完或出现错误,如果写入的数据缓冲区写满,写入句柄不会关闭,会在另一端读取数据之后,利用空闲的空间继续写入。
缓冲区的大小在CreatePipe函数中指定。
管道句柄被继承的方式:
在本例中,是利用CreateProcess通过父进程创建一个子进程,通过管道,将子进程的标准输入与输出重定位到父进程中来,其基本的思路如下图。需要两个管道将子进程的写入与读取分别重定向。
在下图中,
父进程持有 ChildStd_OUT 管道的读句柄,ChildStd_IN 的写句柄
子进程持有 ChildStd_OUT 管道的写句柄,ChildStd_IN 的读句柄
代码如下:
// ParentProcess.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.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");
// 设置 bInheritHandle 标志,以使管道句柄可被继承
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// 创建一个子进程标准输出的管道
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
// 我们只允许将子进程的输出重定向到父进程,因此,对于标准输出管道的读句柄,不能被继承
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
// 创建一个子进程标准输入的管道
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// 我们只允许将子进程的输入重定向到父进程,因此,对于标准输入管道的写句柄,不能被继承
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
//创建子进程
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("ChildProcess");
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);
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
}
}
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);
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);
}
子进程代码如下:
// ChildProcess.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#define BUFSIZE 4096
int _tmain(int argc, _TCHAR* argv[])
{
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;
}
生成文件如下:
运行程序
利用MFC创建如下界面的程序:
添加命令执行函数,代码如下:
CString CMyMFCPipeDemoDlg::ExecCMD(CString str_Cmd)
{
//声明四个句柄,分别代表cmd的输入、输出,将CMD的标准输入与输出重定向到管道
HANDLE hCmdStdIN_Rd = NULL ;//cmd 标准输入的读句柄
HANDLE hCmdStdIN_Wr = NULL ;//cmd 标准输入的写句柄
HANDLE hCmdStdOUT_Rd = NULL ;//cmd 标准输出的读句柄
HANDLE hCmdStdOUT_Wr = NULL ;//cmd 标准输出的写句柄
//---------------创建管道-------------------------
//声明SECURITY_ATTRIBUTES 结构体变量
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
//标准输入的管道
if(!CreatePipe(&hCmdStdIN_Rd,&hCmdStdIN_Wr,&sa,0))
{
MessageBox(_T("标准输入管道创建失败"),_T("tips"),MB_OK);
ExitProcess(1);
}
//标准输出管道
if(!CreatePipe(&hCmdStdOUT_Rd,&hCmdStdOUT_Wr,&sa,0))
{
MessageBox(_T("标准输入管道创建失败"),_T("tips"),MB_OK);
ExitProcess(1);
}
//-------------------创建子进程-------------------------
//声明STARTUPINFO 结构体变量
STARTUPINFO si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = hCmdStdOUT_Wr;
si.hStdOutput = hCmdStdOUT_Wr;
si.hStdInput = hCmdStdIN_Rd;
si.dwFlags = STARTF_USESTDHANDLES;
CString strCmd=_T("");
if(str_Cmd.GetLength() < 1)
{
strCmd+=TEXT("ipconfig /?");
}
else
{
strCmd+=str_Cmd;
}
int len = strCmd.GetLength();
TCHAR* tch_Cmd = strCmd.GetBuffer(len);
strCmd.ReleaseBuffer();
//声明 PROCESS_INFORMATION结构体变量
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
if(!CreateProcess(
NULL, //此处指向windows cmd程序
tch_Cmd, //获得命令行程序
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
&pi
))
{
ErrorExit(_T("子进程创建失败"));
}
else
{
//关闭不需要的句柄
CloseHandle(hCmdStdOUT_Wr);
CloseHandle(hCmdStdIN_Rd);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
//-------------------读取数据-------------------------
DWORD dwRead,dwWrite;
CHAR chBUF[BUFSIZ];
BOOL bSuccess = FALSE;
CString output;
while(TRUE){
memset(chBUF, 0, BUFSIZ);
bSuccess=ReadFile(hCmdStdOUT_Rd,chBUF,BUFSIZ,&dwRead,NULL);
if(!bSuccess || dwRead ==0 )
{
break;
}
output +=chBUF;
}
CloseHandle(hCmdStdOUT_Rd);
CloseHandle(hCmdStdIN_Wr);
return output;
}
文章浏览阅读4.4k次,点赞2次,收藏16次。写这篇文章的由来是因为后边要用这个工具,但是由于某些原因有部分小伙伴和童鞋们可能不会安装此工具,为了方便小伙伴们和童鞋们的后续学习和不打击他们的积极性,因为80%的人都是死在工具的安装这第一道门槛上,这门槛说高也不高说低也不是太低。所以就抽时间水了这一篇文章。_eclipse安装教程
文章浏览阅读4.1w次,点赞12次,收藏193次。小编为大家收集了11个web前端开发,大企业实战项目案例+5W行源码!拿走玩去吧!1)小米官网项目描述:首先选择小米官网为第一个实战案例,是因为刚开始入门,有个参考点,另外站点比较偏向目前的卡片式设计,实现常见效果。目的为学者练习编写小米官网,熟悉div+css布局。学习资料的话可以加下web前端开发学习裙:600加上610再加上151自己去群里下载下。项目技术:HTML+CSS+Div布局2)迅雷官网项目描述:此站点特效较多,所以通过练习编写次站点,学生可以更多练习CSS3的新特性过渡与动画的实_前端项目实战案例
文章浏览阅读73次。素数,不同的质数,各种各样的问题总是遇到的素数。以下我们来说一下求素数的一种比較有效的算法。就是筛法。由于这个要求得1-n区间的素数仅仅须要O(nloglogn)的时间复杂度。以下来说一下它的思路。思路:如今又1-n的数字。素数嘛就是除了1和本身之外没有其它的约数。所以有约数的都不是素数。我们从2開始往后遍历,是2的倍数的都不是素数。所以我们把他们划掉然后如...
文章浏览阅读532次,点赞9次,收藏14次。探索Keras DCGAN:深度学习中的创新图像生成项目地址:https://gitcode.com/jacobgil/keras-dcgan在数据驱动的时代,图像生成模型已经成为人工智能的一个重要领域。其中,Keras DCGAN 是一个基于 Keras 的实现,用于构建和训练 Deep Convolutional Generative Adversarial Networks(深度卷积生...
文章浏览阅读116次。今天在搭建springcloud项目时,发现如上错误,顺便整理一下这个异常:1. mapper.xml的命名空间(namespace)是否跟mapper的接口路径一致<mapper namespace="com.baicun.springcloudprovider.mapper.SysUserMapper">2.mapper.xml接口名是否和mapper.java接..._spring-could org.apache.ibatis.binding.bindingexception: invalid bound state
文章浏览阅读1.1k次。四种高效数据库设计思想——提高查询效率:设计数据库表结构时,我们首先要按照数据库的三大范式进行建立数据。1. 1NF每列不可拆分2. 2NF确保每个表只做一件事情3. 3NF满足2NF,消除表中的依赖传递。三大范式的出现是在上世纪70年代,由于内存资源比较昂贵,所以严格按照三大范式进行数据库设计。而如今内存变得越来越廉价,在考虑效率和内存的基础上我们可以做出最优选择以达到最高效率。_数据库为什么能提高效率
文章浏览阅读302次。一. HTML标签分类1.根据标签个数分类。 单标签:只有一个标签。 <br>, <hr>,<img>,<meta>, 实现一个特定的功能。 双标签:既有开始标签,也有结束标签。 Html,head,Body,title,h1~h6,p,a,ul,li,ol,strong,em。2.根据标签特性分类(网页效果)。 2.1行属性..._ol是单标记还是双标记
文章浏览阅读1.6k次。应用程序在启动和运行的时候往往需要读取一些配置信息,配置基本上伴随着应用程序的整个生命周期,比如:数 据库连接参数、启动参数等。配置主要有以下几个特点:配置是独立于程序的只读变量配置对于程序是只读的,程序通过读取配置来改变自己的行为,但是程序不应该去改变配置配置伴随应用的整个生命周期配置贯穿于应用的整个生命周期,应用在启动时通过读取配置来初始化,在运行时根据配置调整行为。比如:启动时需要读取服务的端口号、系统在运行过程中需要读取定时策略执行定时任务等。配置可以有多种加载方式常见的有程序内部_基于配置是什么意思
文章浏览阅读170次。Glib库实现了一个非常重要的基础类--GObject,这个类中封装了许多我们在定义和实现类时经常用到的机制: 引用计数式的内存管理 对象的构造与析构 通用的属性(Property)机制 Signal的简单使用方式 很多使用GObject..._
文章浏览阅读6.3k次,点赞2次,收藏9次。在 golang 中若写定时脚本,有两种实现。一、基于原生语法组装func DocSyncTaskCronJob() { ticker := time.NewTicker(time.Minute * 5) // 每分钟执行一次 for range ticker.C { ProcTask() }}func ProcTask() { log.Println("hello world")}二、基于 github 中封装的 cron 库实现package taskimport (_golang 定时任务
文章浏览阅读2.1k次。 来源:http://blog.csdn.net/clever101/archive/2008/10/18/3096049.aspx 声明:本文章是我整合网上的资料而成的,其中的大部分文字不是我所为的,我所起的作用只是归纳整理并添加我的一些看法。非常感谢引用到的文字的作者的辛勤劳动,所参考的文献在文章最后我已一一列出。 对关注性能的程序开发人员而言,一个好的计时部件既是益友,也_vc 通过线程和 sleep 获取精准时间
文章浏览阅读58次。公司突然说要进行wap开发了,以前从没了解过,但我却异常的兴奋,因为可以学习新东西了,呵呵,我们大家一起努力吧。首先说说环境的搭建。可以把.wml的文件看做是另一种的html进行信息的展示,但并不是所有的浏览器都支持,好用的有Opera,还有WinWap。编写wml文件语法比较严格,不好的是我还没有找到好的提示工具,就先用纯文本吧。我找到了一个很好的学习网站:http://w3sc..._winwap学习