2015-06-17 20 views
7

Voglio eseguire un programma C++ con il mio back-end PHP. Il programma C++ è responsabile della rimozione di dispositivi USB come chiavette USB dal mio PC. Quando apro il programma (che si trova su un'unità locale separata) con la CLI senza diritti amministrativi, il programma inizia e termina il lavoro correttamente.Il programma eseguito PHP non riesce

Quando avvio il programma con PHP utilizzando exec("/path/to/my/program.exe and-parameters") che è letteralmente lo stesso della CLI, il programma si avvia e ritorna "non riuscito", quindi qualcosa è diverso quando si utilizza la CLI.

Il Codice 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, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, 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, NULL, 0, &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, NULL, NULL, 0, CM_REMOVE_NO_RESTART); // with messagebox (W2K, Vista) or balloon (XP) 

     res = CM_Request_Device_EjectW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, 0); 
     //res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 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, UINT DriveType, 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, NULL, NULL, 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, NULL, guid, dwIndex, &spdid); 
     if (!res) { 
      break; 
     } 

     dwSize = 0; 
     SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &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, &spdid, pspdidd, dwSize, &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, MAX_PATH, 0); 
       printf("DeviceId=%s\n", szDeviceIdString); 
       */ 

       // open the disk or cdrom or floppy 
       HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 
       if (hDrive != INVALID_HANDLE_VALUE) { 
        // get its device number 
        STORAGE_DEVICE_NUMBER sdn; 
        DWORD dwBytesReturned = 0; 
        res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, 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; 
} 
//----------------------------------------------------------- 

Il programma ritorna:

array (2) ([0] => (stringa) D: \ D: \ \ D:. Fallito [ 1] => (stringa) Risultato = 0x33)

Qualcuno un suggerimento?

+3

Perché la funzione 'exec' esegue il programma richiesto utilizzando lo stesso utente del server Web? Che probabilmente non hanno i diritti per fare ciò che vuoi fare. –

+0

questo è stato anche il mio primo pensiero, ma l'utente autenticato ha gli stessi diritti del sistema. – Snickbrack

+3

mostra solo 'ls -l' di questo file exe più dovresti ottenere più feedback da exec visualizzare tutti gli errori e così via – Zgr3doo

risposta

5

Se si esegue PHP in modalità provvisoria, solo i file nella safe_mode_exec_dir potranno essere eseguiti.

Sembra che tu stia correndo nell'ambiente Windows. Si consiglia di prendere in considerazione l'esecuzione di questo utilizzando la shell di Windows che offre maggiore controllo sui programmi di esecuzione esterni e potrebbe restituire ulteriori informazioni in caso di errore e aiutare a diagnosticare quale problema è presente con la funzione exec().

Commenti del manuale di PHP on-line sono:

inizio Notepad.exe minimizzato in background:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("notepad.exe", 7, false); 
?> 

avviare un comando di shell invisibile in background:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false); 
?> 

inizio MSPaint massimizzato e attendi che lo chiudi prima di continuare lo script:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("mspaint.exe", 3, true); 
?> 

Per ulteriori informazioni sul metodo Run() vai a: https://msdn.microsoft.com/en-us/subscriptions/d5fk67ky(v=vs.84).aspx

+0

questa soluzione non funziona per me, crahses PHP sulla linea COM – Snickbrack

+0

non funziona ancora, ma non voglio sprecare il taglia. – Snickbrack

+0

Stai utilizzando Windows? Se sì, che sapore? Qual è l'errore che stai ottenendo? Prova a installare WSH: https://www.microsoft.com/en-us/download/details.aspx?id=8247 – RyanNerd

9

Prima eseguire il comando tramite php client (dalla shell).

<?php 
exec("/path/to/my/program.exe and-parameters"); 

.

$ php.exe -f file.php //Check link above. I am used to Linux and you might need diffrent params 

Se funziona, è molto probabile che si tratti di un problema di autorizzazione.

+0

potrebbe essere che un programma avviato da PHP/IIS non ha il permesso di cambiare/modificare un altro disco? – Snickbrack

+0

Sono un ragazzo di Linux quindi non lo so. Ma puoi porre questa domanda qui: http://serverfault.com/questions/tagged/iis – michaelbn

+0

la tua soluzione con le chiavi di registro non funziona per me perché non so come impostare queste chiavi richieste – Snickbrack

1

This article descrive come configurare un pool di applicazioni e associarlo con il proprio servizio di PHP. Una volta fatto, fai clic con il pulsante destro del mouse sul pool di applicazioni che hai creato e seleziona "Impostazioni avanzate". Sotto l'intestazione "Process Model", vedrai un'impostazione chiamata "Identity"; cambia il valore in un account con le autorizzazioni che cerchi (come "Sistema locale" o "Servizio locale").

L'identità del pool di applicazioni predefinito (al di sotto del quale è in esecuzione il servizio PHP) non è abbastanza potente per i propri scopi!

Problemi correlati