|< Windows Process & Threads Programming 7 | Main | Windows Process & Threads Programming 9 >| Site Index | Download |


 

 

 

 

MODULE U2

PROCESSES AND THREADS: Win32/WINDOWS APIs

Part 8: More Program Examples

 

 

 

 

What we will learn in this Module?

 

  1. Using Thread Local Storage.

  2. Creating a Child Process with Redirected Input and Output.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

My Training Period: yy hours. Before you begin, read some instruction here.

 

The expected abilities are:

  • Able to understand and use various processes and threads functions (Windows APIs/Win32).

  • Able to build and run your own simple Windows processes and threads programs.

  • Able to understand, build and run your own simple multithreading programs.

  • Able to extract, understand and use functions information from MSDN documentation.

Note: For Multithreaded program examples, you have to set your project to Multithread project type.

 

Using The Thread Local Storage

 

Thread local storage (TLS) enables multiple threads of the same process to use an index allocated by the TlsAlloc() function to store and retrieve a value that is local to the thread. In this example, an index is allocated when the process starts. When each thread starts, it allocates a block of dynamic memory and stores a pointer to this memory in the TLS slot using the TlsSetValue() function. The MyCommonFunction() function uses the TlsGetValue() function to access the data associated with the index that is local to the calling thread. Before each thread terminates, it releases its dynamic memory. Before the process terminates, it calls TlsFree() to release the index.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

#define THREADCOUNT 5

 

DWORD dwTlsIndex;

 

// Function declarations and definitions...

VOID ErrorExit(LPTSTR lpszMessage)

{

   fprintf(stderr, "%s\n", lpszMessage);

   ExitProcess(0);

}

void MyCommonFunction(void)

{

   LPVOID lpvData;

   // Retrieve a data pointer for the current thread...

   lpvData = TlsGetValue(dwTlsIndex);

   if ((lpvData == 0) && (GetLastError() != 0))

       ErrorExit("TlsGetValue() error!\n");

   else

       printf("TlsGetValue() is OK.\n");

   // Use the data stored for the current thread...

   printf("Common: thread %d: lpvData = %lx\n\n", GetCurrentThreadId(), lpvData);

}

 

DWORD WINAPI MyThreadFunc(void)

{

   LPVOID lpvData;

   // Initialize the TLS index for this thread.

   lpvData = (LPVOID) LocalAlloc(LPTR, 256);

   if (!TlsSetValue(dwTlsIndex, lpvData))

       ErrorExit("TlsSetValue() error!\n");

   else

       printf("TlsSetValue() is OK.\n");

   printf("Thread %d: lpvData = %lx\n", GetCurrentThreadId(), lpvData);

   MyCommonFunction();

   // Release the dynamic memory before the thread returns...

   lpvData = TlsGetValue(dwTlsIndex);

   if (lpvData != 0)

      LocalFree((HLOCAL) lpvData);

   return 0;

}

 

DWORD main(void)

{

   DWORD IDThread;

   HANDLE hThread[THREADCOUNT];

   int i;

   printf("Thread count is: %d\n", THREADCOUNT);

   // Allocate a TLS index...

   if ((dwTlsIndex = TlsAlloc()) == -1)

      ErrorExit("TlsAlloc() failed");

   else

      printf("\nTlsAlloc() is OK.\n\n");

   // Create multiple threads...

   for (i = 0; i < THREADCOUNT; i++)

   {

      hThread[i] = CreateThread(NULL,           // no security attributes

         0,                                                              // use default stack size

         (LPTHREAD_START_ROUTINE) MyThreadFunc, // thread function

         NULL,                                                     // no thread function argument

         0,                                                             // use default creation flags

         &IDThread);                                           // returns thread identifier

   // Check the return value for success...

   if (hThread[i] == NULL)

       ErrorExit("CreateThread() error.\n");

   else

       printf("hThread[%d] is OK.\n", i);

   }

   for (i = 0; i < THREADCOUNT; i++)

       WaitForSingleObject(hThread[i], INFINITE);

   if (TlsFree(dwTlsIndex) == 0)

       printf("TlsFree() failed!\n");

   else

       printf("TlsFree() is OK!\n");

   return 0;

}

 

A sample output:

Thread count is: 5

 

TlsAlloc() is OK.

 

hThread[0] is OK.

hThread[1] is OK.

hThread[2] is OK.

hThread[3] is OK.

hThread[4] is OK.

TlsSetValue() is OK.

Thread 572: lpvData = 1430d8

TlsGetValue() is OK.

Common: thread 572: lpvData = 1430d8

 

TlsSetValue() is OK.

Thread 956: lpvData = 1430d8

TlsGetValue() is OK.

Common: thread 956: lpvData = 1430d8

 

TlsSetValue() is OK.

Thread 1636: lpvData = 1430d8

TlsGetValue() is OK.

Common: thread 1636: lpvData = 1430d8

 

TlsSetValue() is OK.

Thread 1740: lpvData = 1430d8

TlsGetValue() is OK.

Common: thread 1740: lpvData = 1430d8

 

TlsSetValue() is OK.

Thread 1696: lpvData = 1430d8

TlsGetValue() is OK.

Common: thread 1696: lpvData = 1430d8

 

TlsFree() is OK!

Press any key to continue

Creating a Child Process with Redirected Input and Output

 

The example in this topic demonstrates how to create a child process using the CreateProcess() function from a console process. It also demonstrates a technique for using anonymous pipes to redirect the child process's standard input and output handles. Note that named pipes can also be used to redirect process I/O. The CreatePipe() function uses the SECURITY_ATTRIBUTES structure to create inheritable handles to the read and write ends of two pipes. The read end of one pipe serves as standard input for the child process, and the write end of the other pipe is the standard output for the child process. These pipe handles are specified in the STARTUPINFO structure, which makes them the standard handles inherited by the child process.

The parent process uses the other ends of the pipes to write to the child process's input and read the child process's output. The handles to these ends of the pipe are also inheritable. However, the handle must not be inherited. Before creating the child process, the parent process must use DuplicateHandle() to create a duplicate of the application-defined hChildStdinWr global variable that cannot be inherited. It then uses CloseHandle() to close the inheritable handle. The following is the code for the parent process. It takes a single command-line argument: the name of a text file.  Furthermore, you also need to read a story about pipe.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

#define BUFSIZE 4096

 

// Declaring handles...

HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup, hInputFile, hStdout;

 

// Prototypes...

BOOL CreateChildProcess(VOID);

VOID WriteToPipe(VOID);

VOID ReadFromPipe(VOID);

VOID MyErrorExit(LPTSTR);

VOID ErrMsg(LPTSTR, BOOL);

 

// This program takes a single command-line argument, the name of a text file

DWORD main(int argc, char *argv[])

{

   SECURITY_ATTRIBUTES saAttr;

   BOOL fSuccess;

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

   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

   saAttr.bInheritHandle = TRUE;

   saAttr.lpSecurityDescriptor = NULL;

   // Get the handle to the current STDOUT.

   hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

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

   if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))

      MyErrorExit("CreatePipe(), a pipe for the child process's STDOUT creation failed\n");

   else

      printf("CreatePipe(), a pipe for the child process's STDOUT creation is OK.\n");

    // Create noninheritable read handle and close the inheritable read handle.

    fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup , 0, FALSE, DUPLICATE_SAME_ACCESS);

    if (!fSuccess)

       {

              MyErrorExit("DuplicateHandle() for noninheritable read handle failed");

              CloseHandle(hChildStdoutRd);

       }

       else

              printf("DuplicateHandle() for noninheritable read handle is OK.\n");

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

   if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))

      MyErrorExit("A pipe for the child process's STDIN creation failed\n");

   else

      printf("A pipe for the child process's STDIN creation is OK.\n");

   // Duplicate the write handle to the pipe so it is not inherited.

   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0,

      FALSE,                  // not inherited

      DUPLICATE_SAME_ACCESS);

   if (!fSuccess)

      MyErrorExit("DuplicateHandle() for the write handle to the pipe failed");

   else

      printf("DuplicateHandle() for the write handle to the pipe is OK.\n");

   CloseHandle(hChildStdinWr);

   // Now create the child process.

   fSuccess = CreateChildProcess();

   if (! fSuccess)

      MyErrorExit("CreateChildProcess(), a child process failed");

   else

      printf("CreateChildProcess(), a child process is OK.\n");

   // Get a handle to the parent's input file.

   printf("Getting a handle to the parent's input file.\n");

   if (argc == 1)

          MyErrorExit("Please specify an input file!");

   else

          printf("An input file is %s\n.", argv[1]);

   hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

   if (hInputFile == INVALID_HANDLE_VALUE)

          MyErrorExit("CreateFile(), open existing failed\n");

   else

          printf("CreateFile(), open existing %s is OK.\n", argv[1]);

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

   printf("Try writing to pipe...\n");

   WriteToPipe();

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

   printf("Try reading from pipe...\n");

   ReadFromPipe();

   return 0;

}

//================================================

BOOL CreateChildProcess()

{

   PROCESS_INFORMATION piProcInfo;

   STARTUPINFO siStartInfo;

   BOOL bFuncRetn = FALSE;

   // Set up members of the PROCESS_INFORMATION structure.

   ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));

   // Set up members of the STARTUPINFO structure.

   ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));

   siStartInfo.cb = sizeof(STARTUPINFO);

   siStartInfo.hStdError = hChildStdoutWr;

   siStartInfo.hStdOutput = hChildStdoutWr;

   siStartInfo.hStdInput = hChildStdinRd;

  

   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

   // Create the child process.

   bFuncRetn = CreateProcess(NULL,                       // Use the following command line

         "c:\\Windows\\system32\\systeminfo.exe",       // 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 (bFuncRetn == 0)

      MyErrorExit("CreateProcess() for child failed.");

   else

   {

printf("CreateProcess for child is OK.\n");

CloseHandle(piProcInfo.hProcess);

CloseHandle(piProcInfo.hThread);

return bFuncRetn;

   }

}

 

//================================================================

VOID WriteToPipe(VOID)

{

   DWORD dwRead, dwWritten;

   CHAR chBuf[BUFSIZE];

   // Read from a file and write its contents to a pipe.

   printf("In WriteToPipe(), try to read from a file and write its contents to a pipe.\n");

   for (;;) // More read control should be implemented here such as EOF etc...

   {

      if (!(ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0))

         printf("ReadFile() failed.\n");

         break;

      if (!WriteFile(hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL))

         printf("WriteFile() failed.\n");

         break;

        }

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

   if (!CloseHandle(hChildStdinWrDup))

      MyErrorExit("Close pipe failed");

}

 

//================================================================

VOID ReadFromPipe(VOID)

{

   DWORD dwRead, dwWritten;

   CHAR chBuf[BUFSIZE];

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

   if (!CloseHandle(hChildStdoutWr))

      MyErrorExit("CloseHandle() failed");

   else

      printf("CloseHandle() is OK.\n");

   // Read output from the child process, and write to parent's STDOUT.

   printf("In ReadFromPipe(), try to read output from the child process, and write to parent's STDOUT.\n");

   for (;;) // More read control should be implemented here such as EOF etc...

   {

      if (!ReadFile(hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0)

        printf("ReadFile() failed.\n");

        break;

      if (!WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL))

         printf("WriteFile() failed.\n");

         break;

        }

}

//================================================================

VOID MyErrorExit(LPTSTR lpszMessage)

{

   fprintf(stderr, "%s\n", lpszMessage);

   // Exit peacefully...

   ExitProcess(0);

}

 

A sample output:

F:\myproject\myprocess\Debug>myprocess c:\test.txt

CreatePipe(), a pipe for the child process's STDOUT creation is OK.

DuplicateHandle() for noninheritable read handle is OK.

A pipe for the child process's STDIN creation is OK.

DuplicateHandle() for the write handle to the pipe is OK.

CreateProcess for child is OK.

CreateChildProcess(), a child process is OK.

Getting a handle to the parent's input file.

An input file is c:\test.txt

.CreateFile(), open existing c:\test.txt is OK.

Try writing to pipe...

In WriteToPipe(), try to read from a file and write its contents to a pipe.

Try reading from pipe...

CloseHandle() is OK.

In ReadFromPipe(), try to read output from the child process, and write to parent's STDOUT.

 

F:\myproject\myprocess\Debug>

 

A verification through Windows Task Manager is shown below.

 

verification through Windows Task Manager

 

The following is the code for the child process. It uses the inherited handles for STDIN and STDOUT to access the pipe created by the parent. The parent process reads from its input file and writes the information to a pipe. The child receives text through the pipe using STDIN and writes to the pipe using STDOUT. The parent reads from the read end of the pipe and displays the information to its STDOUT.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

#define BUFSIZE 4096

 

int main(void)

{

   CHAR chBuf[BUFSIZE];

   DWORD dwRead, dwWritten;

   HANDLE hStdin, hStdout;

   BOOL fSuccess;

   hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

   hStdin = GetStdHandle(STD_INPUT_HANDLE);

   if ((hStdout == INVALID_HANDLE_VALUE) || (hStdin == INVALID_HANDLE_VALUE))

       // Exit with error...

       ExitProcess(1);

   else

printf("GetStdHandle() for standard input and output is OK.\n");

   for (;;) // May use other program control here to stop automatically :o)

   {

      // Read from standard input.

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

      if (!(fSuccess || dwRead == 0))

         {

printf("ReadFile(), from standard input failed.\n");

break;

         }

      else

              printf("ReadFile(), from standard input is OK.\n");

      // Write to standard output...

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

      if (!fSuccess)

      {

printf("WriteFile(), to standard output failed.\n");

              break;

         }

      else

              printf("WriteFile(), to standard output is OK.\n");

   }

   return 0;

}

 

A sample output:

F:\myproject\childprocess\Debug>childprocess

GetStdHandle() for standard input and output is OK.

Testing some text from standard input...

ReadFile(), from standard input is OK.

Testing some text from standard input...

WriteFile(), to standard output is OK.

Need to stop the program manually lol!

ReadFile(), from standard input is OK.

Need to stop the program manually lol!

WriteFile(), to standard output is OK.

^C

F:\myproject\childprocess\Debug>

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. Microsoft Visual C++, online MSDN.

  2. For Multibytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).

  3. Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.

  4. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

 

 

 

 

 

 |< Windows Process & Threads Programming 7 | Main | Windows Process & Threads Programming 9 >| Site Index | Download |