Top

PHP Scripting in Win32 Apps

March 22, 2008

This is a repost of something I put together a couple years ago, and as such it may be out of date. There are much better ways to add scripting support to your apps these days, but if you are dead-set on using PHP, this may help you do it.

In order to use the cli version of php for scripting in your win32 app, you’ll need to connect your app to PHP, send it PHP code, and read the processed output.

To do this, of course, requires a means of interprocess communication between the two applications. Experienced programmers instantly think
“pipes!”and search MSDN for the relevant information. Well, I did that and turned up a lot of information on a function called _popen() — a function that seems wonderful, since it does so much for you. It opens the pipe(s), launches the program, and returns a FILE *that you can read or write to (and, if some of what I’ve read is to believed, you can even open a bidirectional pipe to read AND write from!)

Sounds great, doesn’t it? Well, there’s just one problem. Microsoft, in my opinion, worded this terribly when they said:

Note: If used in a Windows program, the _popen function returns an invalid file pointer that will cause the program to hang indefinitely. _popen works properly in a Console application.

If that is clear to you, congratulations, you speak Microsoft. But for the rest of us, let me translate: do not use _popen() unless both of the applications (parent and child) are console applications. Otherwise, it won’t work.

So what you really need to do is create your own pipes.

Here are the steps involved in creating two pipes and redirecting stdin and stdout for the child process to these pipes:

  1. Create the two pipes using CreatePipe()
  2. Let the system know that we want our child to inherit these two pipes
  3. Spawn the child process
  4. Immediately close the end of each pipe that we aren’t using (in other words, close the ends that the child will be using)
  5. Read and write to and from the pipes as needed
  6. Close the pipes when done

Clear? Ok then, let’s look at an abreviated version of this process, and we’re even spawning php.exe as our child process:

  1. // We will run php.exe as a child process after creating
  2. // two pipes and attaching them to stdin and stdout
  3. // of the child process
  4. // Define sa struct such that child inherits our handles
  5.  
  6. SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
  7. sa.bInheritHandle = TRUE;
  8. sa.lpSecurityDescriptor = NULL;
  9.  
  10. // Create the handles for our two pipes (two handles per pipe, one for each end)
  11. // We will have one pipe for stdin, and one for stdout, each with a READ and WRITE end
  12. HANDLE hStdoutRd, hStdoutWr, hStdinRd, hStdinWr;
  13.  
  14. // Now create the pipes, and make them inheritable
  15. CreatePipe (&hStdoutRd, &hStdoutWr, &sa, 0))
  16. SetHandleInformation(hStdoutRd, HANDLE_FLAG_INHERIT, 0);
  17. CreatePipe (&hStdinRd, &hStdinWr, &sa, 0)
  18. SetHandleInformation(hStdinWr, HANDLE_FLAG_INHERIT, 0);
  19.  
  20. // Now we have two pipes, we can create the process
  21. // First, fill out the usage structs
  22. STARTUPINFO si = { sizeof(STARTUPINFO) };
  23. PROCESS_INFORMATION pi;
  24. si.dwFlags = STARTF_USESTDHANDLES;
  25. si.hStdOutput = hStdoutWr;
  26. si.hStdInput  = hStdinRd;
  27.  
  28. // And finally, create the process
  29. CreateProcess (NULL, "c:\\php\\php-win.exe", NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, π);
  30.  
  31. // Close the handles we aren’t using
  32. CloseHandle(hStdoutWr);
  33. CloseHandle(hStdinRd);
  34.  
  35. // Now that we have the process running, we can start pushing PHP at it
  36. WriteFile(hStdinWr, "", 9, &dwWritten, NULL);
  37.  
  38. // When we’re done writing to stdin, we close that pipe
  39. CloseHandle(hStdinWr);
  40.  
  41. // Reading from stdout is only slightly more complicated
  42. int i;
  43.  
  44. std::string processed("");
  45. char buf[128];
  46.  
  47. while ( (ReadFile(hStdoutRd, buf, 128, &dwRead, NULL) && (dwRead != 0)) ) {
  48.     for (i = 0; i < dwRead; i++)
  49.         processed += buf[i];
  50. }  
  51.  
  52. // Done reading, so close this handle too
  53. CloseHandle(hStdoutRd);

Comments

One Response to “PHP Scripting in Win32 Apps”

  1. Devolusion on March 31st, 2008 3:33 pm

    PHP is like chinese to me, i’m glad i have a good scripter :P

Got something to say?





Bottom