gio - resolve path with symlinks - symlink

I have a gfile which points to a path containing symlinks.
E.g. there are:
/home/test/link --> /home/original
/home/original/myfile.txt
The gfile points to /home/test/link/myfile.txt. Does gio provide some method to resolve all links in this path, so that I can obtain /home/original/myfile.txt ?
Or do I need to implement that myself ?

Here a version which uses realpath, which might not be available on all systems:
g_file_get_resolved_path (GFile *file)
{
gchar *path;
gchar *real_path;
_thunar_return_val_if_fail (G_IS_FILE (file), NULL);
path = g_file_get_path (file);
/* No local path for file found */
if (path == NULL)
return NULL;
real_path = realpath (path, NULL);
if (real_path == NULL)
g_warning ("Failed to resolve path: '%s' Error: %s\n", path, strerror (errno));
g_free (path);
return real_path;
}

Still not sure if gio possibly provides some shortcut ... meanwhile I implemented it myself using gio:
/**
* g_file_get_resolved_path:
* #file : #GFile for which the path will be resolved
*
* Gets the local pathname with resolved symlinks for GFile, if one exists.
* If non-NULL, this is guaranteed to be an absolute, canonical path.
*
* This only will work if all components of the #GFile path actually do exist
*
* Return value: (nullable) (transfer full): A #gchar on success and %NULL on failure.
* The returned string should be freed with g_free() when no longer needed.
**/
char* g_file_get_resolved_path (GFile *file)
{
gchar *path;
gchar **tokenized_path;
GFileInfo *info;
gchar *real_path = NULL;
gchar *folder_name;
gchar *temp_path;
GFile *link_test;
gchar *link_target = NULL;
_thunar_return_val_if_fail (G_IS_FILE (file), NULL);
path = g_file_get_path (file);
/* No local path for file found */
if (path == NULL)
return NULL;
tokenized_path = g_strsplit (path ,"/", -1);
for (gsize i = 0; tokenized_path != NULL && tokenized_path[i] != NULL; ++i)
{
folder_name = tokenized_path[i];
temp_path = g_strjoin ("/", real_path, folder_name, NULL);
link_test = g_file_new_for_path (temp_path);
g_free (temp_path);
info = g_file_query_info (link_test, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
if (info != NULL)
{
link_target = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
g_object_unref (info);
if (link_target != NULL)
{
temp_path = real_path;
if (link_target[0] == '/')
{
/* It's an absolute link */
real_path = g_strdup (link_target);
}
else
{
/* It's a relative link */
real_path = g_strjoin ("/", real_path, link_target, NULL);
}
g_free (temp_path);
}
}
g_object_unref (link_test);
/* no symlink found ? --> just append the folder name */
if (link_target == NULL)
{
temp_path = real_path;
real_path = g_strjoin ("/", real_path, folder_name, NULL);
g_free (temp_path);
}
link_target = NULL;
}
g_strfreev (tokenized_path);
g_free (path);
return real_path;
}
Tested for some absolute and relative links.

Related

auto install NDIS filter driver

I'm trying to do an automatic install for the NDIS filter driver.
Kernel debugging is enabled on my machine so driver signing is not required.
P.s. some of the code, I took from this question, but it's still doesn't work.
It gives me this dialog, where the default path to dir wrong.
Also, I watch this topic, but links there aren't workable.
How I can set a default path to .sys file?
Thanks.
....
DWORD size = 0;
isCopied = SetupCopyOEMInfA(pathToInf, // ( C:\[SomeDirs]\[driverInfFile.inf] )
pathToBin, // ( C:\[SomeDirs]\ ) here is driverSysFile.sys
SPOST_PATH,
SP_COPY_NEWER,
NULL,
0,
&size,
NULL);
....
INetCfg *pnc = NULL;
INetCfgClassSetup *pncClassSetup = NULL;
HRESULT hr;
....
hr = CoCreateInstance( CLSID_CNetCfg,
NULL, CLSCTX_INPROC_SERVER,
IID_INetCfg,
(void**)&pnc );
....
INetCfgLock *pncfglock = NULL;
pnc->QueryInterface(IID_INetCfgLock, (LPVOID*)&pncfglock);
pncfglock->AcquireWriteLock(5000, L"MY CLIENT", &szwrClient)
....
hr = pnc->QueryNetCfgClass ( &GUID_DEVCLASS_NETSERVICE,
IID_INetCfgClassSetup,
(void**)&pncClassSetup );
....
OBO_TOKEN OboToken;
ZeroMemory( &OboToken, sizeof(OboToken) );
OboToken.Type = OBO_USER;
INetCfgComponent* NDIS_Component;
//
// I read, that this 2 param both need for automatic setup, and if one is set,
// the second must be setted too.
// But the second[pszwAnswerSections] need to be list of sections in the inf file.
// And it not so cool to parse inf file manually, why OS cant do this???
LPCWSTR pszwAnswerFile = NULL;
LPCWSTR pszwAnswerSections = NULL;
//
// this call fails:
hr = pncClassSetup->Install(COMPONENT_ID,
&OboToken,
NSF_POSTSYSINSTALL,
0,
pszwAnswerFile,
pszwAnswerSections ,
&NDIS_Component);
You can use below code for installing protocol driver. I had created a win32 application for installing protocol driver and assumed that inf and driver file are at same location where executable binary is present. Sometime there are chances that driver file does not get copied so copy it via code. I had used this and it is working perfectly.
#define NDISPROT_SERVICE_PNP_DEVICE_ID_A "PROTOCOL DEVICE NAME"
HRESULT InstallSpecifiedComponent(LPTSTR lpszInfFile, LPTSTR lpszPnpID, const GUID *pguidClass)
{
INetCfg *pnc = NULL;
LPTSTR lpszApp = NULL;
HRESULT hr = S_OK;
hr = HrGetINetCfg(TRUE, APP_NAME, &pnc, &lpszApp);
if(S_OK == hr)
{
//
// Install the network component.
//
PrintMsg(NULL, L"InstallSpecifiedComponent : HrGetINetCfg success.\n");
hr = HrInstallNetComponent(pnc, lpszPnpID, pguidClass, lpszInfFile);
if((S_OK == hr) || (NETCFG_S_REBOOT == hr))
{
PrintMsg(NULL, L"InstallSpecifiedComponent : HrInstallNetComponent success.\n");
hr = pnc->Apply();
if (S_OK == hr)
{
PrintMsg(NULL, L"InstallSpecifiedComponent : Apply success.\n");
}
else
{
PrintMsg(NULL, L"InstallSpecifiedComponent : Apply fail with error code %d.\n", GetLastError());
}
}
else
{
PrintMsg(NULL, L"InstallSpecifiedComponent : HrInstallNetComponent fail with error code %d.\n", GetLastError());
if(HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
{
PrintMsg(hr, L"InstallSpecifiedComponent : Couldn't install the network component.");
}
}
HrReleaseINetCfg(pnc, TRUE);
}
else
{
PrintMsg(NULL, L"InstallSpecifiedComponent : HrGetINetCfg fail with error code %d.\n", GetLastError());
if((NETCFG_E_NO_WRITE_LOCK == hr) && lpszApp )
{
PrintMsg(hr, L"InstallSpecifiedComponent : %s currently holds the lock, try later.", lpszApp);
CoTaskMemFree(lpszApp);
}
else
{
PrintMsg(hr, L"InstallSpecifiedComponent : Couldn't the get notify object interface.");
}
}
PrintMsg(NULL, L"InstallSpecifiedComponent : InstallSpecifiedComponent exit.\n");
return hr;
}
DWORD InstallDriver()
{
DWORD nResult = 0;
HRESULT hr = S_OK;
memset(g_szInfFileFullPath, 0, _MAX_PATH * sizeof(TCHAR));
// Get Path to Service INF File
// The INF file is assumed to be in the same folder as this application.
// Below function returns full path for inf file present in same folder
nResult = GetInfFilePath(g_szInfFileFullPath, MAX_PATH);
if(0 == nResult)
{
return nResult;
}
hr = InstallSpecifiedComponent(g_szInfFileFullPath, NDISPROT_SERVICE_PNP_DEVICE_ID, &GUID_DEVCLASS_NETTRANS);
if(S_OK == hr)
{
PrintMsg(NULL, L"InstallDriver : InstallSpecifiedComponent success.\n");
nResult = 1;
}
else
{
PrintMsg(hr, L"InstallDriver : InstallSpecifiedComponent fail with error code %d.\n", GetLastError());
}
PrintMsg(NULL, L"InstallDriver : InstallDriver exit.\n");
return nResult;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
//Driver Installation.
nRetCode = InstallDriver();
if(1 == nRetCode)
{
Sleep(1000 * 2);
// Sometimes driver file does not get copied into systme32\drivers folder, so just for precaution copy it using code
if(CopyDrvFile())
{
system("net start ekaprot6");
}
}
return nRetCode;
}

Structure of the certificate pointed to by NCRYPT_KEY_HANDLE

I've written a credential provider and a key storage provider to logon to windows via certificate. As the documentation in this points is quite vague I used different samples from Microsoft to get things working.
I think I'm nearly there, but the logon behaves unpredictably. Sometimes I get through to the kerberos server (which complains about the certificate), sometimes the process fails with 0x80090029 without any information and sometimes windows crashes. As these crashes all have to do with access violations or null pointers and happen to occur in various places (kerberos.dll, Windows.UI.Logon.dll, ...) I think it has something to do with my key structure that i point the given NCRYT_KEY_HANDLE to in my OpenKey-implementation.
The KeyStorageProviderSample in the CNG-Kit has an example, but relies on a RSA-key stored in %AppData%. I don't have the private key available as it is stored in secure hardware, I just have the public part (i.e. the public certificate), that I read from another device and import via the following code:
SECURITY_STATUS WINAPI KeyHandler::ReadPemCert(__inout KSP_KEY *keyHandle)
{
LOG_FUNCTION;
CERT_CONTEXT certContext = {};
DWORD readLength = 0;
LOG("Fetch certificate");
const int maxSizeInBytes = 4096;
char pemCertificateAsBytes[maxSizeInBytes];
BluetoothClient bluetoothClient = BluetoothClient();
bluetoothClient.getCertificate((PBYTE)pemCertificateAsBytes, readLength);
DWORD certAsDerLen = readLength;
BYTE* certAsDer = new BYTE[certAsDerLen];
LOG("convert PEM to DER");
if (!CryptStringToBinaryA(pemCertificateAsBytes, 0, CRYPT_STRING_BASE64, certAsDer, &certAsDerLen, NULL, NULL))
{
LOG_LAST_ERROR("CryptStringToBinary failed. Err:");
}
LOG_BYTES_AS_HEX("DER-Zertifikat", certAsDer, certAsDerLen);
PCCERT_CONTEXT pcCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, certAsDer, certAsDerLen);
certContext->pCertInfo = pcCertContext->pCertInfo;
certContext->cbCertEncoded = pcCertContext->cbCertEncoded;
certContext->pbCertEncoded = pcCertContext->pbCertEncoded;
certContext->dwCertEncodingType = pcCertContext->dwCertEncodingType;
CERT_INFO *certInfo;
certInfo = certContext.pCertInfo;
CERT_PUBLIC_KEY_INFO pubKeyInfo = certInfo->SubjectPublicKeyInfo;
LOG("Aquire cryptocontext");
HCRYPTPROV hProv = 0;
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
{
LOG_LAST_ERROR("CryptAcquireContext failed. Err:");
return -1;
}
}
LOG("Importing public key");
NCRYPT_KEY_HANDLE publicKeyHandle = NULL;
if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, &pubKeyInfo, &publicKeyHandle))
{
LOG_LAST_ERROR("CryptImportPublicKeyInfo failed. Err:");
return -1;
}
keyHandle->fFinished = TRUE;
keyHandle->hPublicKey = (BCRYPT_KEY_HANDLE)publicKeyHandle;
keyHandle->pszKeyBlobType = BCRYPT_RSAPUBLIC_BLOB;
LocalFree(certInfo);
return ERROR_SUCCESS;
}
The key structure is initialized this way:
SECURITY_STATUS
WINAPI
KeyHandler::CreateNewKeyObject(
__in_opt LPCWSTR pszKeyName,
__deref_out KSP_KEY **ppKey)
{
LOG_FUNCTION;
KSP_KEY *pKey = NULL;
DWORD cbKeyName = 0;
SECURITY_STATUS Status = NTE_INTERNAL_ERROR;
NTSTATUS ntStatus = STATUS_INTERNAL_ERROR;
pKey = (KSP_KEY *)HeapAlloc(GetProcessHeap(), 0, sizeof(KSP_KEY));
if (pKey == NULL)
{
return NTE_NO_MEMORY;
}
pKey->cbLength = sizeof(KSP_KEY);
pKey->dwMagic = KSP_KEY_MAGIC;
pKey->dwAlgID = KSP_RSA_ALGID;
pKey->pszKeyFilePath = NULL;
pKey->pszKeyBlobType = NULL;
pKey->dwKeyBitLength = 0;
pKey->fFinished = FALSE;
//Copy the keyname into the key struct.
if (pszKeyName != NULL)
{
cbKeyName = (DWORD)(wcslen(pszKeyName) + 1) * sizeof(WCHAR);
pKey->pszKeyName = (LPWSTR)HeapAlloc(
GetProcessHeap(),
0,
cbKeyName + sizeof(WCHAR));
if (pKey->pszKeyName == NULL)
{
return NTE_NO_MEMORY;
}
CopyMemory(pKey->pszKeyName, pszKeyName, cbKeyName);
pKey->pszKeyName[cbKeyName / sizeof(WCHAR)] = L'\0';
}
else
{
pKey->pszKeyName = NULL;
}
if (globalRSAProviderHandle == NULL)
{
ntStatus = BCryptOpenAlgorithmProvider(
&globalRSAProviderHandle,
BCRYPT_RSA_ALGORITHM,
NULL,
0);
if (!NT_SUCCESS(ntStatus))
{
return NormalizeNteStatus(ntStatus);
}
}
pKey->hProvider = globalRSAProviderHandle;
pKey->pbKeyFile = NULL;
pKey->cbKeyFile = 0;
pKey->pbPrivateKey = NULL;
pKey->cbPrivateKey = 0;
pKey->hPublicKey = NULL;
pKey->hPrivateKey = NULL;
pKey->dwExportPolicy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
pKey->dwKeyUsagePolicy = NCRYPT_ALLOW_DECRYPT_FLAG | NCRYPT_ALLOW_SIGNING_FLAG;
pKey->pbSecurityDescr = NULL;
pKey->cbSecurityDescr = 0;
InitializeListHead(&pKey->PropertyList);
*ppKey = pKey;
pKey = NULL;
return ERROR_SUCCESS;
}
Somewhere in there must be the mistake leading to the various memory errors. But as I'm quite new to windows programming and c/c++ I just can't spot the point and can't find any documentation about the datastructure that windows expects for the NCRYTP_KEY_HANDLE.
Does anybody know more about this structure?
NCRYPT_KEY_HANDLE is just a pointer to a structure that you defined.
Windows itself doesn't care about this structure and expect that your provider knows how to work with it.
In KeyHandler::ReadPemCert you mixed legacy CryptoAPI and CNG API. Since you are implementing KSP you should use only CNG API (CryptImportPublicKeyInfoEx2).
DWORD error = NTE_FAIL;
BCRYPT_KEY_HANDLE hKey = NULL;
...
PCCERT_CONTEXT pcCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, certAsDer, certAsDerLen);
if(!pcCertContext)
{
goto Exit;
}
if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &pcCertContext->pCertInfo->SubjectPublicKeyInfo, 0, nullptr, &hKey))
{
goto Exit;
}
/* Also you can export key and print out the result to make sure everything works
DWORD temp = 0;
status = BCryptExportKey(hKey, 0, BCRYPT_RSAPUBLIC_BLOB, nullptr, 0, &temp, 0);
if (status != ERROR_SUCCESS)
{
goto Exit;
}
std::vector<BYTE> key(temp);
status = BCryptExportKey(hKey, 0, BCRYPT_RSAPUBLIC_BLOB, key.data(), key.size(), &temp, 0);
if (status != ERROR_SUCCESS)
{
goto Exit;
}
for (auto const& i : key)
{
std::cout << std::hex << (int)i;
}
}
*/
keyHandle->fFinished = TRUE;
keyHandle->hPublicKey = hKey;
keyHandle->pszKeyBlobType = BCRYPT_RSAPUBLIC_BLOB;
erro = ERROR_SUCCESS;
Exit:
if(pcCertContext)
{
CertFreeCertificateContext(pcCertContext);
}
return error;

Refreshing a folder that doesn't exist in the file system

In my shell extension I have folders that don't actually exist in the file system, but only appear so to the user.
When the content of those folders is changed, I want to refresh them, and currently I do it in the same method I do for regular folders:
Win32.SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Whereas PIDL is a list of shell folders IDs, as required by SHCNF_IDLIST.
The problem is that explorer doesn't handle my non existing folders. Instead of refreshing them, it sends me back to the root folder.
I know that I construct the PIDL correctly since this mechanism works for existing folders, as previously mentioned.
How can I override the handler to SHChangeNotify? Or is there a better way for calling refresh?
Edit:
How my PIDL is generated:
IntPtr GetPIDL(IFolderItem target)
{
Stack stack = new Stack(5);
IntPtr data = IntPtr.Zero;
byte[] rootPIDL = null;
IFolderItem curr = target;
while (curr != null)
{
if (curr.rootPIDL != null)
{
rootPIDL = curr.rootPIDL;
}
else
{
data = curr.SerializeInt();
stack.Push(data);
}
curr = curr.ParentFolder;
}
if (rootPIDL == null && stack.Count == 0)
return IntPtr.Zero;
object[] x = stack.ToArray();
IntPtr[] pidls = null;
int count = stack.Count;
if (count > 0)
{
pidls = new IntPtr[stack.Count];
for (int i = 0; i < count; i++)
{
pidls[i] = (IntPtr)stack.Pop();
}
}
return CreatePIDL(rootPIDL, pidls);
}
My CreatePIDL implementation:
internal unsafe static IntPtr CreatePIDL(byte[] rootPIDL,IntPtr[] pidls)
{
int headerSize = Marshal.SizeOf(typeof(ushort));
int totalSize = headerSize;
if (rootPIDL != null)
totalSize += rootPIDL.Length - headerSize;
if (pidls!=null && pidls.Length > 0)
{
foreach (IntPtr data in pidls)
{
totalSize += PIDLSize(data);
}
}
IntPtr ret = PIDLAlloc(totalSize);
IntPtr currPos = ret;
if(rootPIDL!=null)
{
Marshal.Copy(rootPIDL, 0, currPos, rootLPIFQ.Length - headerSize);
currPos = Win32.AdvancePtr(currPos, rootLPIFQ.Length - headerSize);
}
if (pidls != null && pidls.Length>0)
{
foreach (IntPtr data in pidls)
{
int dataLength = PIDLSize(data);
Win32.CopyMemory(currPos, data, dataLength);
currPos = Win32.AdvancePtr(currPos, dataLength);
}
}
Marshal.WriteInt16(currPos, (short)0);
return ret;
}
internal static unsafe int PIDLSize(IntPtr ptr)
{
return (int) (*((ushort*)ptr));
}
internal unsafe static IntPtr PIDLAlloc(int size)
{
IntPtr ret = Marshal.AllocCoTaskMem(size);
if (ret == IntPtr.Zero)
throw new OutOfMemoryException();
return ret;
}
I found a workaround. It is not pretty nor optimal, yet it works well.
Instead of calling the notify with SHCNE_UPDATEDIR, I'm executing all three of the following notifiers in sequence:
Win32.SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);

Getting the Windows Control Panel's virtual folder

Why would GetPath always return E_FAIL when querying FOLDERID_ControlPanelFolder? Other FOLDERIDs actually do work:
HRESULT hr = S_OK;
*path = '\0';
LPWSTR pwcPath = NULL;
CoInitialize(NULL);
IKnownFolderManager *pFolderManager = NULL;
if ((hr = CoCreateInstance(__uuidof(KnownFolderManager), NULL, CLSCTX_INPROC_SERVER, __uuidof(IKnownFolderManager), (LPVOID *)&pFolderManager)) == S_OK)
{
IKnownFolder *pControlPanelFolder = NULL;
if ((hr = pFolderManager->GetFolder(FOLDERID_ControlPanelFolder, &pControlPanelFolder)) == S_OK)
{
hr = pControlPanelFolder->GetPath(0, &pwcPath);
if (hr == S_OK && pwcPath)
{
int nSize = wcslen(pwcPath);
WideCharToMultiByte(CP_ACP, 0, pwcPath, nSize, path, nSize+2, NULL, NULL);
path[nSize] = '\0';
CoTaskMemFree(pwcPath);
}
pControlPanelFolder->Release();
pControlPanelFolder = NULL;
}
pFolderManager->Release();
pFolderManager = NULL;
}
CoUninitialize();
(Yes, I stumbled upon this question but I don't have need for all that enumeration stuff.)
The Control Panel has no directory path because it does not exist on the disc. You can get its PIDL, and even a Desktop Absolute Parsing "display name" (via GetShellItem and GetDisplayName), but not a directory path.
Reason why I needed the path was that I wanted to open the controp panel with a ShellExecute "open". I now execute the control panel program directly, with the benefit of being able to select the desired applet right away (in this case "Sound"). I hope it's not too pretentious that I post this as answer:
char controlpanelpath[2000];
UINT controlpanelpathbuffersize = sizeof(controlpanelpath);
int actualcontrolpanelpathsize;
if (actualcontrolpanelpathsize = GetSystemDirectory(controlpanelpath, controlpanelpathbuffersize))
{
char *parameters = "\\control.exe mmsys.cpl,,0";
if (actualcontrolpanelpathsize + strlen(parameters) < controlpanelpathbuffersize)
{
strcat(controlpanelpath, parameters);
WinExec(controlpanelpath, SW_NORMAL);
}
}

Resolve Windows device path to drive letter

How do you resolve an NT style device path, e.g. \Device\CdRom0, to its logical drive letter, e.g. G:\ ?
Edit: A Volume Name isn't the same as a Device Path so unfortunately GetVolumePathNamesForVolumeName() won't work.
Hopefully the following piece of code will give you enough to solve this - after you've initialised it, you just need to iterate through the collection to find your match. You may want to convert everything to upper/lower case before you insert into the collection to help with lookup performance.
typedef basic_string<TCHAR> tstring;
typedef map<tstring, tstring> HardDiskCollection;
void Initialise( HardDiskCollection &_hardDiskCollection )
{
TCHAR tszLinkName[MAX_PATH] = { 0 };
TCHAR tszDevName[MAX_PATH] = { 0 };
TCHAR tcDrive = 0;
_tcscpy_s( tszLinkName, MAX_PATH, _T("a:") );
for ( tcDrive = _T('a'); tcDrive < _T('z'); ++tcDrive )
{
tszLinkName[0] = tcDrive;
if ( QueryDosDevice( tszLinkName, tszDevName, MAX_PATH ) )
{
_hardDiskCollection.insert( pair<tstring, tstring>( tszLinkName, tszDevName ) );
}
}
}
Maybe you could use GetVolumeNameForMountPoint and iterate through all mount points A:\ through Z:\, breaking when you find a match?
http://msdn.microsoft.com/en-us/library/aa364994(VS.85).aspx
(I haven't tried this)
Following function does the job using C only
BOOL GetWin32FileName(const TCHAR* pszNativeFileName, TCHAR *pszWin32FileName)
{
BOOL bFound = FALSE;
// Translate path with device name to drive letters.
TCHAR szTemp[MAX_PATH];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(MAX_PATH - 1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
{
size_t uNameLen = _tcslen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszNativeFileName, szName, uNameLen) == 0
&& *(pszNativeFileName + uNameLen) == _T('\\');
if (bFound)
{
// Replace device path with DOS path
StringCchPrintf(pszWin32FileName,
MAX_PATH,
TEXT("%s%s"),
szDrive,
pszNativeFileName + uNameLen);
}
}
}
// Go to the next NULL character.
while (*p++);
} while (!bFound && *p);
}
return(bFound);
}
You can lookup all volumes' name to match a device name and get drive letter.Here is a sample:
int DeviceNameToVolumePathName(WCHAR *filepath) {
WCHAR fileDevName[MAX_PATH];
WCHAR devName[MAX_PATH];
WCHAR fileName[MAX_PATH];
HANDLE FindHandle = INVALID_HANDLE_VALUE;
WCHAR VolumeName[MAX_PATH];
DWORD Error = ERROR_SUCCESS;
size_t Index = 0;
DWORD CharCount = MAX_PATH + 1;
int index = 0;
// \Device\HarddiskVolume1\windows,locate \windows.
for (int i = 0; i < lstrlenW(filepath); i++) {
if (!memcmp(&filepath[i], L"\\", 2)) {
index++;
if (index == 3) {
index = i;
break;
}
}
}
filepath[index] = L'\0';
memcpy(fileDevName, filepath, (index + 1) * sizeof(WCHAR));
FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
if (FindHandle == INVALID_HANDLE_VALUE)
{
Error = GetLastError();
wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);
return FALSE;
}
for (;;)
{
// Skip the \\?\ prefix and remove the trailing backslash.
Index = wcslen(VolumeName) - 1;
if (VolumeName[0] != L'\\' ||
VolumeName[1] != L'\\' ||
VolumeName[2] != L'?' ||
VolumeName[3] != L'\\' ||
VolumeName[Index] != L'\\')
{
Error = ERROR_BAD_PATHNAME;
wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);
break;
}
VolumeName[Index] = L'\0';
CharCount = QueryDosDeviceW(&VolumeName[4], devName, 100);
if (CharCount == 0)
{
Error = GetLastError();
wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);
break;
}
if (!lstrcmpW(devName, filepath)) {
VolumeName[Index] = L'\\';
Error = GetVolumePathNamesForVolumeNameW(VolumeName, fileName, CharCount, &CharCount);
if (!Error) {
Error = GetLastError();
wprintf(L"GetVolumePathNamesForVolumeNameW failed with error code %d\n", Error);
break;
}
// concat drive letter to path
lstrcatW(fileName, &filepath[index + 1]);
lstrcpyW(filepath, fileName);
Error = ERROR_SUCCESS;
break;
}
Error = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
if (!Error)
{
Error = GetLastError();
if (Error != ERROR_NO_MORE_FILES)
{
wprintf(L"FindNextVolumeW failed with error code %d\n", Error);
break;
}
//
// Finished iterating
// through all the volumes.
Error = ERROR_BAD_PATHNAME;
break;
}
}
FindVolumeClose(FindHandle);
if (Error != ERROR_SUCCESS)
return FALSE;
return TRUE;
}
If you want to resolve it in driver,you can check this link for reference.
Here is refactored version of the solution.
I replaced TChAR with wchar_t because afaik it's not a good idea to use it in most projects.
std::map<std::wstring, std::wstring> GetDosPathDevicePathMap()
{
// It's not really related to MAX_PATH, but I guess it should be enough.
// Though the docs say "The first null-terminated string stored into the buffer is the current mapping for the device.
// The other null-terminated strings represent undeleted prior mappings for the device."
wchar_t devicePath[MAX_PATH] = { 0 };
std::map<std::wstring, std::wstring> result;
std::wstring dosPath = L"A:";
for (wchar_t letter = L'A'; letter <= L'Z'; ++letter)
{
dosPath[0] = letter;
if (QueryDosDeviceW(dosPath.c_str(), devicePath, MAX_PATH)) // may want to properly handle errors instead ... e.g. check ERROR_INSUFFICIENT_BUFFER
{
result[dosPath] = devicePath;
}
}
return result;
}

Resources