2015-06-17 19 views
7

Chcę wykonać program C++ z moim zapleczem PHP. Program C++ jest odpowiedzialny za usuwanie urządzeń USB, takich jak pamięci USB z mojego komputera. Kiedy otwieram program (który znajduje się na oddzielnym dysku lokalnym) z CLI bez praw administracyjnych, program uruchamia się i kończy poprawnie pracę.Wykonany program PHP kończy się niepowodzeniem.

Po uruchomieniu programu z PHP przy użyciu exec("/path/to/my/program.exe and-parameters"), który jest dosłownie taki sam jak z CLI, program po prostu uruchamia się i zwraca "nie powiodło się", więc coś jest inne podczas korzystania z interfejsu CLI.

C++ Kod:

// 
// 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; 
} 
//----------------------------------------------------------- 

program powraca:

tablicy (2) ([0] => (łańcuch) d: \ U: \ \ D. Udało [ 1] => (ciąg) Wynik = 0x33)

Ktoś jest propozycją?

+3

Ponieważ funkcja 'exec' uruchamia żądany program przy użyciu tego samego użytkownika, co serwer WWW? Które prawdopodobnie nie mają prawa robić tego, co chcesz robić. –

+0

to również była moja pierwsza myśl, ale uwierzytelniony użytkownik otrzymał te same prawa, co system. – Snickbrack

+3

po prostu pokaż 'ls -l' tego pliku exe i powinieneś uzyskać więcej informacji zwrotnych od exec wyświetlać wszystkie błędy i tak dalej na – Zgr3doo

Odpowiedz

5

Jeśli korzystasz z PHP w trybie awaryjnym, to tylko pliki z safe_mode_exec_dir będą mogły być uruchamiane.

Wygląda na to, że działasz w środowisku Windows. Możesz rozważyć wykonanie tego przy użyciu powłoki systemu Windows, która zapewnia większą kontrolę nad zewnętrznymi programami wykonawczymi i może zwrócić dodatkowe informacje, jeśli się nie powiedzie i pomoże zdiagnozować podstawowy problem związany z funkcją exec().

Komentarze z podręcznika PHP-line to:

początek Notepad.exe zminimalizowane w tle:

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

uruchomić polecenie powłoki niewidoczne w tle:

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

początek MSPaint zmaksymalizować i poczekać, aż zamkniesz go przed kontynuowaniem skryptu:

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

Aby uzyskać więcej informacji na temat metody run() przejdź do: https://msdn.microsoft.com/en-us/subscriptions/d5fk67ky(v=vs.84).aspx

+0

to rozwiązanie nie działa dla mnie, PHP crahses na COM-Line – Snickbrack

+0

to wciąż nie działa, ale nie chcę marnować nagrody. – Snickbrack

+0

Czy używasz systemu Windows? Jeśli tak, to jaki smak? Jaki jest błąd, który otrzymujesz? Spróbuj zainstalować WSH: https://www.microsoft.com/en-us/download/details.aspx?id=8247 – RyanNerd

9

Najpierw uruchom polecenie za pomocą php client (z powłoki).

<?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 

Jeśli to działa, najprawdopodobniej jest to problem uprawnień.

+0

czy może program uruchomiony przez PHP/IIS nie ma uprawnień do zmiany/edycji innego dysku? – Snickbrack

+0

Jestem facetem z Linuksa, więc nie wiem. Ale możesz zadać to pytanie tutaj: http://serverfault.com/questions/tagged/iis – michaelbn

+0

twoje rozwiązanie za pomocą kluczy rejestru nie działa dla mnie, ponieważ nie wiem, jak ustawić te wymagane klucze – Snickbrack

1

This article opisuje jak skonfigurować pulę aplikacji i skojarzyć ją z usługą PHP. Gdy to zrobisz, kliknij prawym przyciskiem myszy utworzoną pulę aplikacji i wybierz "Ustawienia zaawansowane". Pod nagłówkiem "Model procesu" zobaczysz ustawienie o nazwie "Tożsamość"; zmień wartość na konto z odpowiednimi uprawnieniami (np. "System lokalny" lub "Usługa lokalna").

Tożsamość domyślnej puli aplikacji (w ramach której aktualnie działa usługa PHP) nie jest wystarczająco wydajna dla twoich celów!

Powiązane problemy