开发者

What's the best way to resolve a filepath?

开发者 https://www.devze.com 2022-12-24 10:01 出处:网络
I\'ve got a series of filepaths that look so开发者_如何学Gomething like this: C:\\Windows\\System32\\svchost.exe -k LocalSystemNetworkRestricted

I've got a series of filepaths that look so开发者_如何学Gomething like this:

  • C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted
  • C:\Windows\System32\svchost
  • C:\Program Files (x86)\Common Files\Steam\SteamService.exe /RunAsService
  • "C:\Program Files (x86)\Common Files\Steam\SteamService.exe" /RunAsService

and I need to find these paths' actual locations. So, respectively, the above would be:

  • C:\Windows\System32\svchost.exe
  • C:\Windows\System32\svchost.exe
  • C:\Program Files (x86)\Common Files\Steam\SteamService.exe
  • C:\Program Files (x86)\Common Files\Steam\SteamService.exe

What's the best way to go about doing this? Does windows have an API function to accomplish it? I essentially am trying to figure out what executable CreateProcess will call if I pass it that path.

Thanks!

Billy3

EDIT: This is the code I settled on for now:

#include <algorithm>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
#include <Windows.h>

namespace Path {

bool Exists(const std::wstring& path)
{
    DWORD result = GetFileAttributesW(path.c_str());
    return result != INVALID_FILE_ATTRIBUTES;
}

#define PATH_PREFIX_RESOLVE(path, prefix, environment) \
if (boost::algorithm::istarts_with(path, prefix)) { \
    ExpandEnvironmentStringsW(environment, buffer, MAX_PATH); \
    path.replace(0, (sizeof(prefix)/sizeof(wchar_t)) - 1, buffer); \
    if (Exists(path)) return path; \
}

std::wstring Resolve(std::wstring path)
{
    using namespace boost::algorithm;
    wchar_t buffer[MAX_PATH];
    trim(path);
    if (path.empty() || Exists(path)) return path;

    //Start by trying to see if we have a quoted path
    if (path[0] == L'"') {
        return std::wstring(path.begin() + 1, std::find(path.begin() + 1, path.end(), L'"'));
    }

    //Check for those nasty cases where the beginning of the path has no root
    PATH_PREFIX_RESOLVE(path, L"\\", L"");
    PATH_PREFIX_RESOLVE(path, L"?\?\\", L"");
    PATH_PREFIX_RESOLVE(path, L"\\?\\", L"");
    PATH_PREFIX_RESOLVE(path, L"globalroot\\", L"");
    PATH_PREFIX_RESOLVE(path, L"system32\\", L"%systemroot%\\System32\\");
    PATH_PREFIX_RESOLVE(path, L"systemroot\\", L"%systemroot%\\");

    static std::vector<std::wstring> pathExts;
    if (pathExts.empty()) {
        #define MAX_ENVVAR 32767
        wchar_t pathext[MAX_ENVVAR];
        DWORD length = GetEnvironmentVariableW(L"PATHEXT", pathext, MAX_ENVVAR);
        if (!length) WindowsApiException::ThrowFromLastError();
        split(pathExts, pathext, std::bind2nd(std::equal_to<wchar_t>(), L';'));
        pathExts.insert(pathExts.begin(), std::wstring());
    }
    std::wstring::iterator currentSpace = path.begin();
    do {
        currentSpace = std::find(currentSpace, path.end(), L' ');
        std::wstring currentPath(path.begin(), currentSpace);
        std::wstring::size_type currentPathLength = currentPath.size();
        typedef std::vector<std::wstring>::const_iterator ExtIteratorType;
        for(ExtIteratorType it = pathExts.begin(); it != pathExts.end(); it++) {
            currentPath.replace(currentPathLength, currentPath.size() - currentPathLength, *it);
            if (Exists(currentPath)) return currentPath;
        }
        if (currentSpace != path.end())
            currentSpace++;
    } while (currentSpace != path.end());

    return path;
}

}


Number 4 should be relatively easy. If the path starts with a " character, just read until the next " and that's the path. With the others, it's slightly more tricky, but the way Windows does it is by simply breaking the command line into parts, and trying one at a time, so looking at #3, it breaks it up into an array like this:

["C:\Program", "Files", "(x86)\Common", "Files\Steam\SteamService.exe", "/RunAsService"]

Then it simply starts from the left-most element and looks for files:

  1. C:\Program
  2. C:\Program Files
  3. C:\Program Files (x86)\Common
  4. C:\Program Files (x86)\Common Files\Stream\StreamService.exe
  5. C:\Program Files (x86)\Common Files\Steam\SteamService.exe /RunAsService

Each step, it checks whether a file with that name exists. If so, that's the one it chooses. It also tries appending ".exe" to the name. So in the first step, it checks whether there is a file called "C:\Program.exe" and if so, that's the first. If not, it moves to the second step and tries "C:\Program Files.exe". If that doesn't exist, it moves to the next one and so on.

There have been issues in the past with how this algorithm works, for example, see here.


See the Shell Path Handling Functions in shlwapi.h. Your examples should make it with ::PathRemoveArgs(sPath) followed by ::PathMatchSpec(sPath, _T("*.exe")).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号