Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing current Microsoft account from Visual Studio extension

I am writing a VS extension which will need to communicate with a server and identify the user, and I figured that if possible, since this extension will be the only client connecting to the server, using Visual Studio's built-in support for Microsoft accounts would make more sense than implementing my own account management infrastructure.

At first, because of the variety of useful APIs available to a Visual Studio developer, one might think that getting info on the current user would be easy. However, there actually don't seem to be any obvious APIs that can be used to access accounts; I checked here and there weren't any related services listed (the "profile" services just allow you to read/write settings that are stored for the current user).

Does anyone know of a (relatively) simple way to access the Microsoft account from a Visual Studio extension?

EDIT

I tried Hadi Brais's suggestion, and at first it appeared to work (I successfully retrieved the information); however, each time, Visual Studio would crash about 30s afterward. I commented out the lines that interacted with the registry and replaced them with static values for the variables, and the crashes stopped. Clearly, accessing Visual Studio's registry keys was causing it to crash. I even tried using statements and other safeguards, however there doesn't seem to be a way to safely access the Visual Studio registry keys from an extension. So does anyone know of any official APIs that can be used to retrieve this information without crashing Visual Studio?

like image 955
Nick Mertin Avatar asked Jul 11 '15 03:07

Nick Mertin


2 Answers

For Visual Studio 2015 (Version 14.0), this is how to get information about the user that is currently signed in in Visual Studio. You need to add using Microsoft.Win32;.

private static string GetUserEmailAddressVS14()
{
    // It's a good practice to request explicit permission from
    // the user that you want to use his email address and any
    // other information. This enables the user to be in control
    // of his/her privacy.

    // Assuming permission is granted, we obtain the email address.

    const string SubKey = "Software\\Microsoft\\VSCommon\\ConnectedUser\\IdeUser\\Cache";
    const string EmailAddressKeyName = "EmailAddress";
    const string UserNameKeyName = "DisplayName";

    RegistryKey root = Registry.CurrentUser;
    RegistryKey sk = root.OpenSubKey(SubKey);
    if (sk == null)
    {
        // The user is currently not signed in.
        return null;
    }
    else
    {
        // Get user email address.
        return (string)sk.GetValue(EmailAddressKeyName);

        // You can also get user name like this.
        // return (string)sk.GetValue(UserNameKeyName);
    }
}
like image 65
Hadi Brais Avatar answered Nov 15 '22 03:11

Hadi Brais


There are now multiple versions of the IdeUser key. I've reimplemented the algorithm from the other answer this way:

public static string GetUserEmailAddressFromVisualStudioRegistry()
{
    try
    {
        const string ConnectedUserSubKey = @"Software\Microsoft\VSCommon\ConnectedUser";
        const string EmailAddressKeyName = "EmailAddress";

        RegistryKey connectedUserSubKey = Registry.CurrentUser.OpenSubKey( ConnectedUserSubKey );

        string[] subKeyNames = connectedUserSubKey?.GetSubKeyNames();

        if ( subKeyNames == null || subKeyNames.Length == 0 )
        {
            return null;
        }

        int[] subKeysOrder = new int[subKeyNames.Length];

        for ( int i = 0; i < subKeyNames.Length; i++ )
        {
            Match match = Regex.Match( subKeyNames[i], @"^IdeUser(?:V(?<version>\d+))?$" );

            if ( !match.Success )
            {
                subKeysOrder[i] = -1;
                continue;
            }

            string versionString = match.Groups["version"]?.Value;

            if ( string.IsNullOrEmpty( versionString ) )
            {
                subKeysOrder[i] = 0;
            }
            else if ( !int.TryParse( versionString, out subKeysOrder[i] ) )
            {
                subKeysOrder[i] = -1;
            }
        }

        Array.Sort( subKeysOrder, subKeyNames );

        for ( int i = subKeyNames.Length - 1; i >= 0; i++ )
        {
            string cacheSubKeyName = $@"{subKeyNames[i]}\Cache";
            RegistryKey cacheKey = connectedUserSubKey.OpenSubKey( cacheSubKeyName );
            string emailAddress = cacheKey?.GetValue( EmailAddressKeyName ) as string;

            if ( !string.IsNullOrWhiteSpace( emailAddress ) )
            {
                return emailAddress;
            }
        }
    }
    catch
    {
        // Handle exceptions here if it's wanted.
    }

    return null;
}

This algorithm tries all versions from the newest one and then all other siblings. It returns null in case of a failure.

like image 25
Antonín Procházka Avatar answered Nov 15 '22 04:11

Antonín Procházka