Click here to Skip to main content
15,889,096 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a WPF/.NET4.5.2 application that is developed on a Windows 10 machine but deployed on a Windows 7 machine.

On my development machine...

Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments )

...correctly returns "C:/Users/<username>/Documents".

When I run this same code on the target Windows 7 machine, I get the same folder returned but, on the Windows 7 machine, the folder is named "C:/Users/<username>/My Documents", so my code fails to find the folder.

What I have tried:

I've found several articles about this on the net. In many of the articles, the author explains why this is the wrong way to do it (e.g. apparently accessing the Registry is not always going to work for reasons that I might understand if I had even less hair).

The only article I found that seems to have a convincing way to do it is Getting All "Special Folders" in .NET[^] , but this seems very complicated for such a common requirement.

I can't help feeling there is some simple piece of knowledge I am missing here. Does anyone know what may be going wrong here?
Posted
Updated 29-Jul-20 11:25am
Comments
Richard MacCutchan 2-Feb-18 5:13am    
Are you saying that on Windows 7 you receive the string "C:/Users/<username>/Documents", which is not the actual name?
Patrick Skelton 2-Feb-18 5:34am    
That is correct, yes. The actual folder is 'My Documents' whereas in my code I get back just 'Documents'. Oddly, if I ask for the user's profile directory and then manually append '\Documents' in my code, I still don't seem to be able to access anything in 'C:\Users\<username>\Documents'. I need to do more testing around this, though. This was a last minute result I got last night before I had to quickly log out of the target machine.
Richard MacCutchan 2-Feb-18 5:53am    
I have used this mechanism and not had a problem with it. Unfortunately I no longer have a Win 7 system so cannot retest.
Richard MacCutchan 2-Feb-18 5:55am    
Actually, now I think about it, I have a feeling that is correct. The actual folder is "Documents", and "My Documents" is a link.
CHill60 2-Feb-18 6:04am    
That's my recollection too

Please, refer thiss: My Documents vs Documents folder[^]. There you'll find short explanation about differences between documents and my documents folders.

As MSDN documentation states:
Quote:
The GetFolderPath method returns the locations associated with this enumeration. The locations of these folders can have different values on different operating systems, the user can change some of the locations, and the locations are localized.

For more information about special folders, see the KNOWNFOLDERID constants in the Windows documentation.


Conclusion: to get correct folder (documents instead of my documents) you have to use SHGetKnownFolderPath API function. An implementation you'll find here: VBnet� Visual Basic Developers Resource Centre[^]

Finally i'd strongly suggest to read this tip: Where should I store my data?[^]
 
Share this answer
 
Comments
Graeme_Grant 4-Feb-18 15:54pm    
5'd... Also, something to be aware of... If your app runs in Windows, but in a virtual machine on MacOS or Linux, then the folder returned by Environment.GetFolderPath can be invalid.
Maciej Los 4-Feb-18 18:22pm    
Thank you, Graeme.
Further to Maciej Los post, the Document Path not only varies on different Windows OSes, if the user is using a Virtual Machine (VM) on a foreign OS, the Documents folder path can be the base OS path and not the Windows OS Documents Folder path. If you try to access the path outside the VM, then an exception will be thrown.

Below is a solution that will fix this problem.

Machine Details POCO:
C#
public class VirtualMachineDetails
{
    public string Manufacturer { get; set; }
    public string Model { get; set; }
    public bool IsVirtual
    {
        get
        {
            return IsParallels || IsVirtualBox || IsVmWare || IsVirtualPc;
        }
    }
    public bool IsParallels { get; internal set; }
    public bool IsVmWare { get; set; }
    public bool IsVirtualBox { get; set; }
    public bool IsVirtualPc { get; set; }
    public string DocumentsPath { get; set; }
}

Extension methods to fill out the information:
C#
using System;
using System.IO;
using System.Management;

public static class VirtualMachineDetailsExtensions
{
    public static void Init(this VirtualMachineDetails host)
    {
        using (var searcher =
            new ManagementObjectSearcher("Select * from Win32_ComputerSystem"))
        {
            using (var items = searcher.Get())
            {
                foreach (var item in items)
                {
                    host.Manufacturer = item["Manufacturer"].ToString();
                    host.Model = item["Model"].ToString();

                    string manufacturer = host.Manufacturer.ToLower();
                    string model = host.Model.ToLower();

                    host.IsVirtualPc = 
                        manufacturer == "microsoft corporation" &&
                                        model.Contains("virtual");
                    host.IsParallels = manufacturer.Contains("parallels");
                    host.IsVmWare = manufacturer.Contains("vmware");
                    host.IsVirtualBox = model == "virtualbox";
                }
            }
        }
        host.DocumentsPath = host.FixDocPath();
    }

    private static string FixDocPath(this VirtualMachineDetails host)
    {
        string pathSeperator = @"\";

        string path = Environment.GetFolderPath(
                       Environment.SpecialFolder.MyDocuments);

        string userPath = Environment.GetFolderPath(
                           Environment.SpecialFolder.UserProfile);

        var fixPath = new Func<string, string, string>((p, u) => (p.Contains(u)) ? 
                p : 
                Path.Combine(u, p.Substring(p.LastIndexOf(pathSeperator))));

        if (host.IsVirtual)
        {
            string parallelsPath = @"\\Mac\Home";
            string vmWarePath = @"\\vmware-host\Shared Folders";

            if (host.IsParallels)
            {
                if (path.Contains(parallelsPath))
                    path = path.Replace(parallelsPath, userPath);
                else
                    fixPath(path, userPath);
            }

            else if (host.IsVmWare)
            {
                if (path.Contains(vmWarePath))
                    path = path.Replace(vmWarePath, userPath);
                else
                    fixPath(path, userPath);
            }

            else
                fixPath(path, userPath);
        }

        return path;
    }
}

To use:
C#
var vm = new VirtualMachineDetails();

vm.Init();

var documentsPath = vm.DocumentsPath;
 
Share this answer
 
Comments
Maciej Los 4-Feb-18 17:16pm    
5ed!
Graeme_Grant 4-Feb-18 17:58pm    
Thanks ... I have quite a few Mac users with VMs... At the time it was a fun problem to solve... Xamarin has reduced this problem greatly with native app dev however I still see users with VMs...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900