Skip to main content
Email Password   helpLost your password?

Introduction

On occasion you might need to gain greater access from PHP to the underlying operating system. This article demonstrates basic steps of one technique for the mentioned purpose by creating an ATL COM DLL, registering it, and finally invoking it through PHP hosted in the Windows environment running the Apache web server.

Requirements

The requirements list the software used to create and test the demo and do not necessarily exclude other versions of the same software unless specifically noted.

  1. Windows XP Professional
  2. Apache 2.0.54 web server
  3. PHP 5.2.6. (in prior versions an access violation in oleaut32.dll was triggered)
  4. Visual Studio .NET Professional

Create the ATL COM DLL

In the following steps we will create an ATL COM DLL with a class and two member methods, one that will retrieve the approximate percentage of physical memory in use, and the second one that will restart the host computer.

  1. Start Visual Studio .NET
  2. Start a "New Project"
  3. Select "Visual C++ Projects"
  4. Select "ATL Project"
  5. Click "OK"
  6. Name the project "ATLDemo"
  7. Click "OK"
  8. As the default option is "Dynamic-link library (DLL)", click "Finish"

Visual Studio .NET will now generate the project and we are ready to proceed.

  1. Right-click on the project name
  2. Go to the "Add" option
  3. Select "Add Class…"
    Add a new class to the project
  4. Select "ATL Simple Object"
  5. Click "Open"
  6. Enter "MyClass" for the short name
    Enter class information
  7. Click "Finish" as we will accept all the default options

Our new class has now been added and you might notice the system has also generated an Interface class for us, in this case named IMyClass. As the next step we will add our two member methods by following the steps below:

  1. Right-click on the interface class named "IMyClass"
    Class interface
  2. Go to "Add"
  3. Select "Add Method"
  4. Enter GetMemoryLoad for the method name
  5. Check the "in" checkbox under the "Parameter attributes" section
  6. Select VARIANT* for the parameter type
  7. Name the parameter "vtMemoryLoad"
    Add new method to the interface
  8. Click "Add"
  9. Click "Finish"

The new member method has now been added and as the next step we will add code to it as the next step:

STDMETHODIMP CMyClass::GetMemoryLoad(VARIANT* vtMemoryLoad)
{
	// Create an instance of the MEMORYSTATUSEX structure
	MEMORYSTATUSEX memstatex;

	// Specify the length of the structure
	memstatex.dwLength = sizeof(memstatex);

	// Call the GlobalMemoryStatusEx function and pass to it
	//  a reference to our MEMORYSTATUSEX instance
	::GlobalMemoryStatusEx(&memstatex);

	// Set the ulVal (unsigned long value) of the VARIANT parameter
	//  passed by reference to the function with the dwMemoryLoad
	//   value of the MEMORYSTATUEX instance which specifies the
	//    approximate percentage of the physical memory currently
	//     in use.
	vtMemoryLoad->ulVal = memstatex.dwMemoryLoad;

	return S_OK;
}

We will now repeat steps 1-9 in order to add the second function as part of this demo.

  1. Right-click on the Interface class named "IMyClass"
  2. Go to "Add"
  3. Select "Add Method"
  4. Enter RestartHost for the method name
  5. Check the "in" checkbox under the "Parameter attributes" section
  6. Select VARIANT* for the parameter type
  7. Name the parameter "vtLastError"
  8. Click "Add"
  9. Click "Finish"

Add the following code to the new function:

STDMETHODIMP CMyClass::RestartHost(VARIANT* vtLastError)
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;

	// In order to be able to restart the local computer we need the 
	//  SE_SHUTDOWN_NAME privilege, in the following steps we will
	//   retrieve the privileges of the current process and enable
	//    the SE_SHUTDOWN_NAME privilege
	if(::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { 

		::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
		
		tkp.PrivilegeCount = 1;    
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0); 

		// If the adjusting of the privilege succeeded
		if(::GetLastError() == ERROR_SUCCESS) {

			// Initiate the system restart, note the fourth argument will
			//  force the shutdown of all applications, when testing please
			//   ensure that you have no unsaved data as it might be lost.
			//    The fifth argument will restart the computer.
			::InitiateSystemShutdown(NULL, _T("ATL COM DLL/PHP Demo"), 0, TRUE, TRUE);
		}
	}

	// Set the ulVal (unsigned long value) of the VARIANT parameter
	//  to the value returned by GetLastError as this might be useful in
	//   troubleshooting.
	vtLastError->ulVal = ::GetLastError();

	return S_OK;
}

As the next step we will compile the DLL. By default a project remains in Debug mode and after a successful compilation and thorough testing we can switch it to the Release mode before deploying the DLL. We will do so immediately for the purpose of this demo:

  1. Click on "Project" on the menu
  2. Click "ATLDemo Properties"
  3. Change the "Configuration:" option to "Release"
  4. Click "OK"
  5. Click "Build" on the menu
  6. Click "Rebuild Solution"

This concludes the process of creating our sample ATL COM DLL.

Registering the ATL COM DLL

Now that we have created the ATL COM DLL the next step will be to register it so that it is available for use by other applications through the COM interface.

  1. Locate the DLL by browsing to the "Release" directory of the project or by searching for the DLL, in this case by its name "ATLDemo.dll"
  2. Move the DLL to its final destination, in this case we will place the DLL into the "C:\WINDOWS\system32" directory
  3. Register the DLL through the command line by calling the REGSVR32 utility:
    1. Click "Start"
    2. Click "Run"
    3. Enter "CMD"
    4. In the command line prompt enter: REGSVR32 "C:\WINDOWS\system32\ATLDemo.dll"
    5. You should see a message box informing you that the registration has been successful.

This concludes registering our sample ATL COM DLL and making it available for use by COM aware applications.

Invoking the ATL COM DLL through PHP

In this section of the article we will write a PHP page that will invoke our newly created ATL COM DLL and make use of it. Please note that at this point it is assumed that you have an Apache web server running in the Windows environment. In this demo the Apache web server is run as an executable under an account that has full administrator privileges. Security settings might present an obstacle depending on your configuration of the web server, e.g. if you are running the Apache web server as a service under an account with insufficient privileges to be able to create a COM objects you would have adjust the privileges for that account. The same applies to running IIS instead of Apache however as this is a simple concept article the security settings will not be discussed in detail.

PHP for Windows in its core features the ability to invoke COM object. For details please see http://us.php.net/com.

Our first step will be to identify the Prog ID for our class in order to be able to instantiate the instance of the class; the easiest technique will be by accessing the registry and learning the value:

  1. Click "Start"
  2. Click "Run"
  3. Enter REGEDIT
  4. Click "OK"
  5. Under the HKEY_CLASSES_ROOT locate the name of the DLL
  6. The value has been found and identified as ATLDemo.MyClass

Next, we will write the PHP code that will create an instance of the class, immediately display the approximate percentage of the available physical memory and offer the ability to restart the host computer.

<?php

    // Create an instance of our COM object
    $obj = new COM("ATLDemo.MyClass");
    
    // Create an instance of the VARIANT structure,
    //  initial value 0,
    //   VT_UI4 (data type, see VARIANT data structure for details)
    $vtMemoryLoad = new VARIANT(0, VT_UI4);
    
    // Retrieve the current memory load
    $obj->GetMemoryLoad($vtMemoryLoad);
    
    print '<p>Physical memory in use : '.$vtMemoryLoad.'%</p>';
    
    // If the "Restart Host" button was pressed
    if($_POST['btnRestart']) {
    
        // VARIANT to restore the value returned by the GetLastError() function
        $vtLastError = new VARIANT(0, VT_UI4);
        
        // Initiate host restart
        $obj->RestartHost($vtLastError);

        print '<p>Initiated host restart...</p>';
        print '<p>Last Error Code: '.$vtLastError.'</p>';
    }
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
 <input type="submit" value="Restart Host" name="btnRestart" id="btnRestart" />
</form>
  1. Save the file as "index.php" in a new web server directory
  2. Run it through the browser
  3. Notice the memory use value, subsequent calls to the page will change the output depending on use
  4. Click the "Restart Host" button to restart the computer (NOTE: Please save any pending work before testing)
    Add new method to the interface

Conclusion

Thank you very much for reading this article. I hope the article or at least some aspects of it might be useful to you. Constructive criticism is appreciated and will be taken into consideration.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralMixed topics are rarely any good Pin
Vitaly Tomilov
3:45 29 Oct '08  
GeneralExcellent Article Pin
Syed M Hussain
0:24 28 Oct '08  


Last Updated 27 Oct 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009