我想用我的
PHP后端执行一个C程序. C程序负责从我的PC上删除usb棒等usb设备.当我使用CLI打开程序(位于单独的本地驱动器上)而没有管理权限时,程序将启动并正确完成作业.
当我用PHP启动程序时使用exec(“/ path / to / my / program.exe and-parameters”),这与CLI的方式完全相同,程序只是启动并返回“失败”,所以有些不同使用CLI时.
C代码:
//
// RemoveDriveByLetter.cpp by Uwe Sieber - www.uwe-sieber.de
//
// Simple demonstration how to prepare a disk drive for save removal
//
// Works with removable and fixed drives under W2K,XP,W2K3,Vista
//
// Console application - expects the drive letter of the drive to remove as parameter
//
// you are free to use this code in your projects
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <Setupapi.h>
#include <winioctl.h>
#include <winioctl.h>
#include <cfgmgr32.h>
#include <string>
//-------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber,UINT DriveType,char* szDosDeviceName);
//-------------------------------------------------
//-------------------------------------------------
int main(int argc,char* argv[])
{
/*if ( argc != 2 ) {
return 1;
}*/
char DriveLetter = argv[1][0];
DriveLetter &= ~0x20; // uppercase
if ( DriveLetter < 'A' || DriveLetter > 'Z' ) {
return 1;
}
std::string path = "";
path += DriveLetter;
path.append(":\\");
printf(path.c_str());
char szRootPath[sizeof(path)] ="";
strncpy(szRootPath,path.c_str(),sizeof(path));
std::string device = "";
device += DriveLetter;
device.append(":");
printf(device.c_str());
char szDevicePath[sizeof(device)] = "";
strncpy(szDevicePath,device.c_str(),sizeof(device));
std::string accesspath = "";
accesspath += "\\\\.\\";
accesspath += device;
printf(accesspath.c_str());
char szVolumeAccesspath[sizeof(accesspath)] = ""; // "\\.\X:" -> to open the volume
strncpy(szVolumeAccesspath,accesspath.c_str(),sizeof(accesspath));
long DeviceNumber = -1;
// open the storage volume
HANDLE hVolume = CreateFile(szVolumeAccesspath,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,NULL);
if (hVolume == INVALID_HANDLE_VALUE) {
return 1;
}
// get the volume's device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
long res = DeviceIoControl(hVolume,IOCTL_STORAGE_GET_DEVICE_NUMBER,&sdn,sizeof(sdn),&dwBytesReturned,NULL);
if ( res ) {
DeviceNumber = sdn.DeviceNumber;
}
CloseHandle(hVolume);
if ( DeviceNumber == -1 ) {
return 1;
}
// get the drive type which is required to match the device numbers correctely
UINT DriveType = GetDriveType(szRootPath);
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who kNows a better way?
char szDosDeviceName[MAX_PATH];
res = QueryDosDevice(szDevicePath,szDosDeviceName,MAX_PATH);
if ( !res ) {
return 1;
}
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber,DriveType,szDosDeviceName);
if ( DevInst == 0 ) {
return 1;
}
PNP_Veto_TYPE VetoType = PNP_VetoTypeUnkNown;
WCHAR VetoNameW[MAX_PATH];
VetoNameW[0] = 0;
bool bSuccess = false;
// get drives's parent,e.g. the USB bridge,the SATA port,an IDE channel with two drives!
DEVINST DevInstParent = 0;
res = CM_Get_Parent(&DevInstParent,DevInst,0);
for ( long tries=1; tries<=3; tries++ ) { // sometimes we need some tries...
VetoNameW[0] = 0;
// CM_Query_And_Remove_SubTree doesn't work for restricted users
//res = CM_Query_And_Remove_SubTreeW(DevInstParent,&VetoType,VetoNameW,MAX_PATH,CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
//res = CM_Query_And_Remove_SubTreeW(DevInstParent,CM_REMOVE_NO_RESTART); // with messageBox (W2K,Vista) or balloon (XP)
res = CM_Request_Device_EjectW(DevInstParent,0);
//res = CM_Request_Device_EjectW(DevInstParent,0); // with messageBox (W2K,Vista) or balloon (XP)
bSuccess = (res==CR_SUCCESS && VetoType==PNP_VetoTypeUnkNown);
if ( bSuccess ) {
break;
}
Sleep(500); // required to give the next tries a chance!
}
if ( bSuccess ) {
printf("Success\n\n");
return 0;
}
printf("Failed\n");
printf("Result=0x%2X\n",res);
if ( VetoNameW[0] ) {
printf("VetoName=%ws)\n\n",VetoNameW);
}
return 1;
}
//-----------------------------------------------------------
char* appendChartochararray(char* array,char a)
{
size_t len = strlen(array);
char* ret = new char[len+2];
strcpy(ret,array);
ret[len] = a;
ret[len+1] = '\0';
return ret;
}
//----------------------------------------------------------------------
// returns the device instance handle of a storage volume or 0 on error
//----------------------------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber,char* szDosDeviceName)
{
bool IsFloppy = (strstr(szDosDeviceName,"\\Floppy") != NULL); // who kNows a better way?
GUID* guid;
switch (DriveType) {
case DRIVE_REMOVABLE:
if ( IsFloppy ) {
guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
} else {
guid = (GUID*)&GUID_DEVINTERFACE_disK;
}
break;
case DRIVE_FIXED:
guid = (GUID*)&GUID_DEVINTERFACE_disK;
break;
case DRIVE_CDROM:
guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
break;
default:
return 0;
}
// Get device interface info set handle for all devices attached to system
HDEVINFO hDevInfo = SetupDiGetClassDevs(guid,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
return 0;
}
// Retrieve a context structure for a device interface of a device information set
DWORD dwIndex = 0;
long res;
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
spdid.cbSize = sizeof(spdid);
while ( true ) {
res = SetupDiEnumDeviceInterfaces(hDevInfo,guid,dwIndex,&spdid);
if ( !res ) {
break;
}
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo,&spdid,&dwSize,NULL); // check the buffer size
if ( dwSize!=0 && dwSize<=sizeof(Buf) ) {
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory(&spdd,sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res = SetupDiGetDeviceInterfaceDetail(hDevInfo,pspdidd,dwSize,&spdd);
if ( res ) {
// in case you are interested in the USB serial number:
// the device id string contains the serial number if the device has one,// otherwise a generated id that contains the '&' char...
/*
DEVINST DevInstParent = 0;
CM_Get_Parent(&DevInstParent,spdd.DevInst,0);
char szdeviceidString[MAX_PATH];
CM_Get_Device_ID(DevInstParent,szdeviceidString,0);
printf("deviceid=%s\n",szdeviceidString);
*/
// open the disk or cdrom or floppy
HANDLE hDrive = CreateFile(pspdidd->DevicePath,NULL);
if ( hDrive != INVALID_HANDLE_VALUE ) {
// get its device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
res = DeviceIoControl(hDrive,NULL);
if ( res ) {
if ( DeviceNumber == (long)sdn.DeviceNumber ) { // match the given device number with the one of the current device
CloseHandle(hDrive);
SetupDiDestroyDeviceInfoList(hDevInfo);
return spdd.DevInst;
}
}
CloseHandle(hDrive);
}
}
}
dwIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
//-----------------------------------------------------------
该计划返回:
array(2) ( [0] => (string) D:\D:\.\D:Failed [1] => (string)
Result=0x33 )
有人提出建议吗?
如果您在安全模式下运行PHP,则只允许运行safe_mode_exec_dir中的文件.
您似乎正在Windows环境中运行.您可能需要考虑使用Windows shell执行此操作,这样可以更好地控制外部执行程序,如果失败则可能返回其他信息,并帮助诊断exec()函数的基本问题.
在线PHP手册的评论是:
在后台启动Notepad.exe最小化:
<?PHP
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe",7,false);
?>
在后台启动一个不可见的shell命令:
<?PHP
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%",false);
?>
启动MSPaint最大化并等待您关闭它然后继续脚本:
<?PHP
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("mspaint.exe",3,true);
?>
有关Run()方法的更多信息,请访问:
https://msdn.microsoft.com/en-us/subscriptions/d5fk67ky(v=vs.84).aspx