-1

I'm trying to launch an .exe file in windows using and a volume ID. The volume do not have a letter. Here is what I've tryed.

ShellExecute

When I call ShellExecute with the volume ID, Windows' explorer open the correct directory. Same thing if I try to open a folder inside the volume. However, if I try to open an exe file, nothing happen.

This is the way I call ShellExecute, which is part of windows.h :

char path[MAX_PATH] = R"(\\?\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\App.exe)";    
LONG_PTR returnValue = (LONG_PTR) ShellExecute(GetDesktopWindow(),NULL, path,NULL,NULL,SW_SHOW);

The error code returned is 2 :

The system cannot find the file specified.

CreateProcessW

After following the comments, I am now using . My new code look like this :

char path[MAX_PATH] = R"(\\?\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\launcher.exe)";

STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcessW(path, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
{
    WaitForSingleObject(processInfo.hProcess, INFINITE);
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);
}
else {
    DWORD dw = GetLastError();
    printf("ERROR CODE: %d", dw);
}

But it still doesn't work. Error is the same (2).

Changing the path notation

From "\\?\Volume{ID}" to "\\.\Volume{ID}". The strange thing is that I can list the drive's files or start a console process but I cannot launch any GUI EXE files. Error code remains 2.

Is the anythink I am missing here ? Any help are welcome. Thank you.


Answer

I've got confuse with the comments, using "\\.\Volume{ID}\myApp.exe" worked well if used with CreateProcess, not CreateProcessW.

There is the fully functional code (Don't forget to include windows.h).

char path[MAX_PATH] = R"(\\.\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\App.exe)";

STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcess(path, NULL, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo))
{
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);
}
else 
{
    DWORD dw = GetLastError();
    printf("ERROR CODE WHILE STARTING: %d", dw);
}
  • Where is your [MCVE]? – Lightness Races in Orbit May 30 '17 at 11:12
  • 1
    C and C++ are different languages. Remove the unrelated tag when you post the [mcve]. – too honest for this site May 30 '17 at 11:28
  • Actualy, this line remains the same in both C and C++. I thought this was the mnimal complete, the only thing missing should be "#include " and the value of "path" witch is my VolumeId, which is different on every computer. I will edit my post to complete the informations. – Jean-Charbel VANNIER May 30 '17 at 12:05
  • There are no raw string literals in C, so the code doesn't even compile as C. Besides, it's time to stop using `ShellExecute`, because it doesn't properly report errors. Use `ShellExecuteEx` instead. And make sure to initialize COM on the calling thread, as [documented](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153.aspx). – IInspectable May 30 '17 at 12:29
  • There is in C11, the code compile on my computer. As I said, if I remove "App.exe" from "path", it work and open the folder in explorer. I will have a look at ShellExecuteEx. TY. – Jean-Charbel VANNIER May 30 '17 at 12:41
  • [CreateProcess](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425.aspx): *"If the function fails, the return value is zero. To get extended error information, call [GetLastError](https://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx)."* – IInspectable May 31 '17 at 15:05
  • @IInspectable Yes, thank you. I did that actually as I 've said in the POST. Error code is 2. That correspond to _The system cannot find the file specified_ – Jean-Charbel VANNIER May 31 '17 at 15:21
  • Nothing in your question indicates what the return value of `GetLastError` is in case `CreateProcess` fails. – IInspectable May 31 '17 at 17:48
  • You could try to run [Process Monitor](https://technet.microsoft.com/en-us/sysinternals/processmonitor.aspx) to find out, which file cannot be found. If there is a difference between console and GUI applications, it's likely, that it's a dependency, that cannot be resolved. (Although I'm not sure that'd generate error code 2.) – IInspectable Jun 01 '17 at 12:59

1 Answers1

1

problem in win32 subsystem - it not use native for windows NT Paths, but Win32 Paths which first must be converted to NT Paths before passing to kernel.

also some functions, like ShellExecuteEx or CreateProcessW accept not any valid win32 path, but only restricted subset - so called Drive Letter form. if pass path in form \\?\Volume{guid}\* - ShellExecuteEx and CreateProcessW always fail with this path (so called volume path) even if path correct (CreateFileW open this path). funny that CreateProcessW will be work with path \\.\Volume{guid}\* (if replace ? to . at [2] position) but ShellExecuteEx not worked with this path too.

only one reliable solution here - convert this volume form path to Drive Letter form. this can be done with help IOCTL_MOUNTMGR_QUERY_POINTS - need get list of all MOUNTMGR_MOUNT_POINT in system and do 2 loops by this list - first found device name by existing volume symlink. then in loop #2 - by already known device name found - dos-device name and got it drive letter

#include <mountmgr.h>

ULONG NtVolumePathToDosPath(PUNICODE_STRING VolumePath)
{
    if (!MOUNTMGR_IS_NT_VOLUME_NAME(VolumePath))
    {
        return ERROR_PATH_NOT_FOUND;
    }

    static volatile UCHAR guz;
    PVOID stack = alloca(guz);
    ULONG cb = 0, rcb = 0x400;

    union {
        PVOID buf;
        PMOUNTMGR_MOUNT_POINTS pmmp;
    };

    HANDLE hFile = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    static MOUNTMGR_MOUNT_POINT mmp;

    ULONG dwError = NOERROR;
    do 
    {
        if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);

        if (!DeviceIoControl(hFile, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(mmp), buf, cb, &rcb, 0))
        {
            dwError = GetLastError();
            rcb = pmmp->Size;
            continue;
        }

        dwError = ERROR_PATH_NOT_FOUND;

        if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
        {
            PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;

            //loop #1: search for DeviceName linked to VolumePath
            do 
            {
                UNICODE_STRING SymbolicLinkName = {
                    MountPoints->SymbolicLinkNameLength,
                    SymbolicLinkName.Length,
                    (PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
                };

                if (MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName))
                {
                    if (RtlEqualUnicodeString(&SymbolicLinkName, VolumePath, TRUE))
                    {
                        // found DeviceName
                        UNICODE_STRING _DeviceName = {
                            MountPoints->DeviceNameLength,
                            _DeviceName.Length,
                            (PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
                        };

                        NumberOfMountPoints = pmmp->NumberOfMountPoints;
                        MountPoints = pmmp->MountPoints;

                        // loop #2: search for "drive letter" linked to DeviceName
                        do 
                        {
                            UNICODE_STRING DeviceName = {
                                MountPoints->DeviceNameLength,
                                DeviceName.Length,
                                (PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
                            };

                            if (RtlEqualUnicodeString(&_DeviceName, &DeviceName, FALSE))
                            {
                                SymbolicLinkName.MaximumLength = SymbolicLinkName.Length = MountPoints->SymbolicLinkNameLength;
                                SymbolicLinkName.Buffer = (PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset);

                                if (MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
                                {
                                    PWSTR szVolumePath = VolumePath->Buffer + 48;
                                    *--szVolumePath = ':';
                                    *--szVolumePath = SymbolicLinkName.Buffer[12];
                                    *--szVolumePath = '\\';
                                    *--szVolumePath = '?';
                                    *--szVolumePath = '\\';
                                    *--szVolumePath = '\\';
                                    VolumePath->Buffer = szVolumePath;
                                    dwError = NOERROR;
                                    break;
                                }
                            }

                        } while (MountPoints++, --NumberOfMountPoints);

                        break;
                    }
                }

            } while (MountPoints++, --NumberOfMountPoints);
        }
        break;

    } while (dwError == ERROR_MORE_DATA);

    CloseHandle(hFile);

    return dwError;
}

ULONG TestExecByVolumePath(PCWSTR szVolumePath)
{
    size_t size = wcslen(szVolumePath) * sizeof(WCHAR);

    if (size >= MAXUSHORT || size < 98)
    {
        return ERROR_PATH_NOT_FOUND;
    }

    UNICODE_STRING VolumePath;

    VolumePath.Length = 96;
    VolumePath.MaximumLength = (USHORT)size + sizeof(WCHAR);
    memcpy(VolumePath.Buffer = (PWSTR)alloca(VolumePath.MaximumLength), szVolumePath, VolumePath.MaximumLength);

    if (!MOUNTMGR_IS_DOS_VOLUME_NAME(&VolumePath))
    {
        return ERROR_PATH_NOT_FOUND;
    }

    VolumePath.Buffer[1] = '?';

    ULONG dwErr = NtVolumePathToDosPath(&VolumePath);

    if (dwErr == NOERROR)
    {
        SHELLEXECUTEINFOW sei = {sizeof(sei), 0, 0, L"open", VolumePath.Buffer, 0, 0, SW_SHOWDEFAULT };
        if (!ShellExecuteExW(&sei))
        {
            dwErr = GetLastError();
        }
    }

    return dwErr;
}

    TestExecByVolumePath(L"\\\\?\\Volume{***}\\Windows\\System32\\notepad.exe");
RbMm
  • 31,280
  • 3
  • 35
  • 56
  • `PVOID stack = alloca(guz);` is undefined behavior. – IInspectable May 31 '17 at 08:33
  • @IInspectable - not agree. i many years use this in very different code (in kernel mode too) and ensure that this correct worked. compiler (ok *CL* - i use only it) exactly return to us pointer to current stack. call `PVOID stack = alloca(0);` - this is really *UB* because with optimization i note CL simply drop call `alloca(0);` and `stack` became undefined. but the **volatile** keyword make self task - with this the `alloca` is always called (checked on all *CL* from 2005 vs up to latest builds) however this not related to question. – RbMm May 31 '17 at 08:43
  • if you not like this style (and look like only i do this kind of codding for cumulative alloca (need off RT checks for this) on loop) you can use new/delete malloc/free or what ever else here. this is already another question – RbMm May 31 '17 at 08:44
  • @IInspectable - and from *MSDN* - [*If size is 0, _alloca allocates a zero-length item and returns a valid pointer to that item.*](https://msdn.microsoft.com/en-us/library/wb1s57t5.aspx) - this is exactly what i need **valid pointer** to zero-length item *stack*. *stack* is not storage, but mark, by which i calculated actual buffer size *buf* - `RtlPointerToOffset(buf = alloca(rcb - cb), stack);` - so what you men under *UB* here ? direct call `alloca(0)` - this is *UB* because how i note *CL* with optimization simply drop this instruction at all ! – RbMm May 31 '17 at 09:09
  • and `alloca(guz)` will drop if `guz` declared without *volatile*. but when we declare it as **volatile** all is fine – RbMm May 31 '17 at 09:09
  • Sorry, `guz` looked like an uninitialized variable, but isn't (at least in C99, not sure about C89). Besides that you don't appear to understand what undefined behavior is. – IInspectable May 31 '17 at 09:14
  • @IInspectable - think understand what you mean. i not direct init `guz` to 0. however i use indirect zero init - `static` – RbMm May 31 '17 at 09:19
  • [*When you declare a variable, the variable has static duration and the compiler initializes it to 0 unless you specify another value.*](https://msdn.microsoft.com/en-us/library/s1sb61xd(v=vs.100).aspx) – RbMm May 31 '17 at 09:24
  • Hi, first off all thank you for your time and answere me. The problem is a bit deeper. In fact, I'm trying to access to a drive that do not have any Drive Letter assigned. – Jean-Charbel VANNIER May 31 '17 at 13:30
  • In fact, I can launch a console app using the _Volume Path_ and `CreateProcess` but with the exact same code, I cannot launch any GUI app. – Jean-Charbel VANNIER May 31 '17 at 13:33
  • @Jean-CharbelVANNIER - `I'm trying to access to a drive` this is possible, but without `ShellExecuteExW` - it will ne not worked. file functions - at all no any problems. if need process exec - use `CreateProcess` only and `"\\\\.\\"`prefix. you even can use Nt-path by *`"\\\\.\\globalroot\\device\\hardiskvolumeX\\.."`* - about GUI or console - this is absolute not important here. say for notepad this is 100% worked – RbMm May 31 '17 at 13:35
  • @Jean-CharbelVANNIER - this code fine work - `STARTUPINFO si = {sizeof(si)}; PROCESS_INFORMATION pi; if (CreateProcessW(L"\\\\.\\globalroot\\systemroot\\notepad.exe", 0, 0, 0, 0, 0,0, 0, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); }` – RbMm May 31 '17 at 13:40
  • @Jean-CharbelVANNIER - `drive that do not have any Drive Letter assigned.` and as variant you can easy assign some free letter to this drive yourself by call `DefineDosDevice` or `ZwCreateSymbolicLinkObject` and use it – RbMm May 31 '17 at 14:12
  • I am trully sorry but the code provided doesn't work either. Error cod is still 2. – Jean-Charbel VANNIER May 31 '17 at 15:31
  • strange, i test this code on all systems from xp to win10 (both x64 and x86) and it nice worked. you exactly copied string ? `L"\\\\.\\globalroot\\systemroot\\notepad.exe‌​"` ? "notepad.exe" exist in your system ? if call `CreateFile` with this path ? what will be result ? – RbMm May 31 '17 at 15:41
  • @Jean-CharbelVANNIER - `CreateFile(L"\\\\.\\globalroot\\systemroot\\notepad.exe", 0,0,0,OPEN_EXISTING, 0, 0);` must open file,if system not damaged – RbMm May 31 '17 at 15:44
  • @RbMm Notepad.exe do exist in my system. I am not trying to run the code in your answere, but the one in your comment. I've tested in 3 different computer without success. So I've tryed to create the process in C# with this exact path "\\\\.\\globalroot\\systemroot\\notepad.exe‌". The error message tell me that it cannot access to network drive. Also, I must say that when I use the "\\\.\\Volume{ID}" notation, I can perform many operation like listing the directory content but I can't run the exe file. – Jean-Charbel VANNIER Jun 01 '17 at 09:15
  • `I've tested in 3 different computer without success.` - strange, because in all my test `CreateProcessW(L"\\\\.\\globalroot\\systemroot\\notepad.exe‌​", 0, 0, 0, 0, 0,0, 0, &si, &pi)` working.. – RbMm Jun 01 '17 at 11:02
  • @Jean-CharbelVANNIER - if you call CreateFile(L"\\\\.\\globalroot\\systemroot\\notepad.exe", 0,0,0,OPEN_EXISTING, 0, 0); this is worked ? and may be in string task (c#) ? may be not convert \\ to single \ ? – RbMm Jun 01 '17 at 11:06
  • exactly string must be `\\.\globalroot\systemroot\notepad.exe‌‌` - any way - what result of CreateFile (OPEN_EXISTING) on this path ? – RbMm Jun 01 '17 at 11:07
  • @Jean-CharbelVANNIER - you absolute mistake. that cmd.exe can call `CreateProcessW` ok - only prove that - some error in your code. cmd.exe not doing any magic stuff. in simply take string as is and call `CreateProcess` - if in `cmd.exe` - this must be and ok in your - **if no mistakes** – RbMm Jun 01 '17 at 12:37
  • @Jean-CharbelVANNIER - `By default, CreateProcess on windows uses cmd.exe` - absolute false – RbMm Jun 01 '17 at 12:38
  • @Jean-CharbelVANNIER - again simply run cmd.exe and execute `\\.\globalroot\systemroot\notepad.exe‌‌` command. this will be work or no ? if yes - **error in how you** call `CreateProcessW`. `\.\Volume{guid}\myApp.exe` as cmd parameter worked for me - but and `CreateProcessW` work with this !! cmd.exe and call `CreateProcessW` with same line. – RbMm Jun 01 '17 at 12:41
  • Yeah, my bad, I've got confuse with {system()}. Anyway, replacing CreateProcessW by CreateProcess worked when using dot instead of question mark. – Jean-Charbel VANNIER Jun 01 '17 at 13:04
  • @Jean-CharbelVANNIER - i wrote about this you in anser at very begin `"funny that CreateProcessW will be work with path \\.\Volume{guid}\* (if replace ? to . at [2] position) but ShellExecuteEx not worked with this path too. "` and what you mean under `replacing CreateProcessW by CreateProcess` ? – RbMm Jun 01 '17 at 13:11
  • Exactly the oposite. It doesn't work with CreateProcessW but only with CreateProcess. – Jean-Charbel VANNIER Jun 01 '17 at 13:12
  • @Jean-CharbelVANNIER - you again mistake!! at first not exist function `CreateProcess` - exist `CreateProcessA` and `CreateProcessW`. what i wrote of course worked with both `A` and `W` versions. and use `W` of course much more better -`A` must not be used. you make some mistake. may be pass const commant line to W – RbMm Jun 01 '17 at 13:19
  • @Jean-CharbelVANNIER - so do **so many** mistakes. permanent - mistake by mistake – RbMm Jun 01 '17 at 13:19