We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now

x

Unfolding the Windows path?

pepr
pepr asked
on
Medium Priority
1,337 Views
Last Modified: 2012-05-06
Hi,

I would like to ask you for checking the code below. The intention is to transform the Windows path with disk letter to the form of UNC path (for remote disks) or to the form of the path where the disk is not substed. I do use the QueryDosDevice() function for replacing the substitution in the loop. However, I am not sure about the path returned by the function -- namely about the \??\ prefix of the path. Could you point me to any documentation that explains the path prefix?  

Then, if the path is related to a remote drive, the drive letter is replaced by the mount point.

The related questions and simulated situations are discussed in http:Q_24142140.html

Thanks,
   Petr

#include <Windows.h>
#include <Winnetwk.h>  // WNetGetConnection()
#include <iostream>
#include <string>
 
using namespace std;
 
std::string driveTypeStr(UINT driveType);
 
void unfold3()
{
    string drive("R:");
    string driveRoot(drive + "\\");
    string path(drive + "\\b\\c\\file.txt");
    UINT driveType = ::GetDriveType(driveRoot.c_str());
 
    cout << "Unfold 3\n"
            "========\n"
            "     path: " << path << "\n"
         << "    drive: " << drive << "\n"
         << "driveRoot: " << driveRoot << "\n"
         << "driveType: " << driveType << " (" 
                          << driveTypeStr(driveType) << ")" << "\n"
         << "\n";
 
    char buf[1000];
    DWORD size = sizeof(buf);
    DWORD result = 0;
 
    // Replace the substed drives by their targets.
    //
    while (true)   
    {
        cout << "QueryDosDevice(\"" << drive << "\", ....): ";   
        result = QueryDosDevice(drive.c_str(), buf, size);  
                                  // returns 0 or number of chars
        string path2(buf);
        if (result == 0)
        {
            cout << "failed (" << GetLastError() << ")\n\n";
            break;  // when this can fail?
        }
        else
        {
            cout << path2 << "\n";  // returned by QueryDosDevice
            if (path2.find("\\??\\") == string::npos)
                break;              // it went too far. Stick with previous 
            
            // Get the path2 without the \??\ prefix and replace the 
            // previous drive (R:) letter by the substitution path.
            //
            path = path2.substr(4) + path.substr(2);
            drive = path.substr(0, 2);
            cout << "\nnew drive: " << drive << "\n";
            cout << " new path: " << path << "\n\n";
        }
    }
 
    if (driveType == DRIVE_REMOTE)
    {
        cout << "WNetGetConnection(\"" << drive << "\", ....): ";
        result = WNetGetConnection(drive.c_str(), buf, &size);
        if (result != NO_ERROR)
            cout << "failed (" << result << ")\n";
        else
        {   
            cout << "succeeded!\n\n";
            string path2(buf);
            path = path2 + path.substr(2);
        }
    }
 
    cout << "The wanted path: " << path << "\n\n";
}
 
 
int main()
{
    unfold3();
    return 0;
}
 
 
std::string driveTypeStr(UINT driveType)
{
    #define CASE(x) case x: return #x;
    switch (driveType)
    {
      CASE(DRIVE_UNKNOWN)
      CASE(DRIVE_NO_ROOT_DIR)
      CASE(DRIVE_REMOVABLE)
      CASE(DRIVE_FIXED)
      CASE(DRIVE_REMOTE)
      CASE(DRIVE_CDROM)
      CASE(DRIVE_RAMDISK)
    }
    return "unknown type";
}

Open in new window

Comment
Watch Question

>>> Could you point me to any documentation that explains the path prefix

http://stackoverflow.com/questions/341550/on-windows-when-should-you-use-the-filename-prefix

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
CERTIFIED EXPERT

Author

Commented:
Well, this is probably not the same. For the following subst

D:\Tutorial\PathUnfold\Debug>subst
P:\: => C:\tmp\test
Q:\: => P:\subdir
R:\: => Q:\a

The QueryDosDevice() returns paths like \??\P:\subdir, i.e. prefix with one backslash, two questionmarks, and one backslash... except in the last step that must be ignored -- see below

D:\Tutorial\PathUnfold\Debug>PathUnfold.exe
Unfold 3
========
     path: R:\b\c\file.txt
    drive: R:
driveRoot: R:\
driveType: 3 (DRIVE_FIXED)

QueryDosDevice("R:", ....): \??\Q:\a

new drive: Q:
 new path: Q:\a\b\c\file.txt

QueryDosDevice("Q:", ....): \??\P:\subdir

new drive: P:
 new path: P:\subdir\a\b\c\file.txt

QueryDosDevice("P:", ....): \??\C:\tmp\test

new drive: C:
 new path: C:\tmp\test\subdir\a\b\c\file.txt

QueryDosDevice("C:", ....): \Device\HarddiskVolume3
The wanted path: C:\tmp\test\subdir\a\b\c\file.txt

Ok, I tried myself, and got the \??\ when I pass a drive-letter defined by subst. So, it seems to be the standard output of QueryDosDevice for these drive letters. I would assume it is output (only) an  to tell that QueryDosDevice couldn't find a physical device  associated with the path. But that's a guess. And I wonder why non of the docs mention it though I have no idea how to escape a string like '\??\' so that I could find it in an article or in the web.
CERTIFIED EXPERT

Author

Commented:
Yes, exactly :)  You are reading my mind. I have found it once somewhere via Google, but it also did not mention any documentation. And I forgot what lead me to the article (definitely not the \??\ :)
Commented:
Maybe the following sample code dould be of help. It uses QueryDosDevice to get a list of "normal" volume GUID paths with the "\\?\" prefix:
http://msdn.microsoft.com/en-us/library/cc542456(VS.85).aspx
I tested the code of toribar (it was of a newer version of VS and it took me some time to get it compiled with VC7.1) and the output was


Found a device:
 \Device\HarddiskVolume1
Volume name: \\?\Volume{ba252f37-a88e-11dc-9711-806d6172696f}\
Paths:  C:\

Found a device:
 \Device\CdRom0
Volume name: \\?\Volume{ba252f36-a88e-11dc-9711-806d6172696f}\
Paths:  D:\

Unfortunately, I have a dozen of network devices and two drive letters defined with subst, which all were ignored by the above code.

CERTIFIED EXPERT

Author

Commented:
Well, I want to use the "unfolded" path in combination with SID prefix of the computer to which the path belongs to generate the data licence file (data bound to the path on that machine; the application refuses to work with copy of the data unless the location is unlocked). I am going to consider only DRIVE_REMOTE or DRIVE_FIXED.

Any comment to the approach?

Thanks for your help,
    Petr
I don't know whether I understand correctly. But a license file which only is valid if a specific network path currently is available, is a very restrictive method. What is when the server was not available or must be exchanged by another. Do you think the administrator wants to reconfigure each client for that case.

You better would put your license information int a server dbms somewhere in the LAN. Then the clients would connect to the dbms and check their license key using that db-connection. In case of a server exchange they would need to reestablish the db connections anyhow, so that normally was no additional efforts. If the license server was not available you could allow to use a local client dbms for some time in case your application doesn't need a central dbms anyhow.
CERTIFIED EXPERT

Author

Commented:
To itsmeandnobodyelse: Yes. It is rather restrictive. On the other hand, the administrator can call us to get the key (for free) to unlock the new location. Also, the number of users of our application at one location is not restricted (no end-user license). The application is not client-server yet. This way, making the application non-functional when "stolen" is probably the only (even though poor) way to get some form of security.

The earlier approach used locking the application to the client workstation. This way it was even more restrictive from the administrator's point of view as he/she was forced to unlock (via phone) every new workplace.

Yes. The first planned step is to move the data to a decent SQL server to ensure better security or to make the application client-server and make the data physically unreachable -- the database has still the legacy, DBF form.

What we try to get is a tradeoff. The application would work without that. However, the customers want something like that. Some of their employees may want to steal the data. The data has the form that is not very useful on its own. The application makes them useful.
I wouldn't make much efforts to resolve drive letters or network paths. When I firstly got knowledge of UNC paths the docs told me to not making any assumptions on teh syntax or naming of these paths. And indeed I wathced the whole mess over years and till now some applications were not able to handle all the existing paths correctly. I would go the database approach (SQLServer or not) and if it was for the license database only. It doesn't require a client-server for the first approach. You simply use a database connection which was established at client setup for access to your license information.
CERTIFIED EXPERT

Author

Commented:
Thanks both for the help.
  Petr
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.