Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Create an ATL COM DLL and invoke it through PHP

0.00/5 (No votes)
27 Oct 2008 1  
Demonstrates creating a simple ATL COM DLL and invoking it through PHP.

Introduction

On occasion you might need to gain greater access from PHP to the underlying Operating System. This article demonstrates the 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…"
  4. Add a new class to the project

  5. Select "ATL Simple Object"
  6. Click "Open"
  7. Enter "MyClass" for the short name
  8. Enter class information

  9. 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"
  2. Class interface

  3. Go to "Add"
  4. Select "Add Method"
  5. Enter GetMemoryLoad for the method name
  6. Check the "in" checkbox under the "Parameter attributes" section
  7. Select VARIANT* for the parameter type
  8. Name the parameter "vtMemoryLoad"
  9. Add new method to the interface

  10. Click "Add"
  11. Click "Finish"

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

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 object, you would have to 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 objects. For details, please see http://us.php.net/com.

Our first step will be to identify the ProgID 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)
  5. 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here