Cannot load System.Threading.Tasks assembly, using Google Calendar API

bruno.boi picture bruno.boi · Jul 10, 2013 · Viewed 14.3k times · Source

SOLVED !

Huge thank to Sam Leach

Here is a sample of my working app.config file:

<configuration>
       ...
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-2.5.19.0" newVersion="2.5.19.0"/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="System.Net.Http" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-2.1.10.0" newVersion="2.1.10.0"/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-2.1.10.0" newVersion="2.1.10.0"/>
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>

I have also found that source


EDIT: The original question is below the line.

I am using the .NET 4.0 Framework and from my research I know that the System.Threading.Tasks assembly is no longer needed (because it's included automatically). Am I wrong about this?

If I am right, I am now pretty sure that the error is raised because the version of System.Threading.Tasks used by the developers of the google-api-dotnet-client and the one used by Visual Studio 2010 are different.

I noticed that when inspecting the behavior of my app when I remove some lines.

And these lines came out:

gcal = new CalendarService(new BaseClientService.Initializer()
{
    Authenticator = auth,
    ApplicationName = APP_NAME,
});

So, my new question is:

Is there a way to force the usage of one specific version of a reference assembly in VS2010 ?

Thank you for helping me, I am sure it will help lots of people because the google-calendar-api-v3 is badly documented.

Kind regards, Bruno.


My problem is that I can't get access to the Google API as a service through VisualStudio.

I get this error:

L'exception System.IO.FileLoadException n'a pas été gérée
  Message=Impossible de charger le fichier ou l'assembly 'System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' ou une de ses dépendances. La définition trouvée du manifeste de l'assembly ne correspond pas à la référence de l'assembly. (Exception de HRESULT : 0x80131040)
  Source=MVMA
  FileName=System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  FusionLog==== Informations d'état de liaison préalable ===
JRN : utilisateur = MODAL\brbo
JRN : DisplayName = System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 (Fully-specified)
JRN : Appbase = file:///C:/Users/brbo/Documents/Visual Studio 2010/Projects/MVMA-V5.0 (With Gantt)/MVMA/bin/Debug/
JRN : PrivatePath initial = NULL
Assembly appelant : Google.Apis, Version=1.4.0.28227, Culture=neutral, PublicKeyToken=null.
===
JRN : cette liaison démarre dans le contexte de chargement de default.
JRN : utilisation du fichier de configuration de l'application : C:\Users\brbo\Documents\Visual Studio 2010\Projects\MVMA-V5.0 (With Gantt)\MVMA\bin\Debug\MVMA.vshost.exe.Config
JRN : utilisation du fichier de configuration d'hôte : 
JRN : utilisation du fichier de configuration de l'ordinateur à partir de C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
JRN : référence post-stratégie : System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
JRN : tentative de téléchargement de la nouvelle URL file:///C:/Users/brbo/Documents/Visual Studio 2010/Projects/MVMA-V5.0 (With Gantt)/MVMA/bin/Debug/System.Threading.Tasks.DLL.
AVT : la comparaison du nom de l'assembly a entraîné l'incompatibilité : Version principale
ERR : impossible de terminer l'installation de l'assembly (hr = 0x80131040). Détection terminée.

  StackTrace:
   à MVMA.Classes.GoogleCalendar.BuildCalendarService()
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

And here is my classes (same namespace) which tries to connect to the Google Calendar API v3 using Json Web Tokens:

public class GoogleCalendar
{

    // Chaînes d'accès aux services Google
    public const string APP_NAME = "HIDDEN";
    public const string CLIENT_ID = "HIDDEN";
    public const string CLIENT_SECRET = "HIDDEN";
    public const string SERVICE_CLIENT_ID = "HIDDEN";
    public const string EMAIL_ADDRESS = "HIDDEN";
    public const string PUB_KEY = "HIDDEN";
    public const string PRIV_KEY_PATH = @"C:\MVMA\HIDDEN-privatekey.p12";
    public const string PRIV_KEY_SECRET = "notasecret";
    public const string SIMPLE_API_KEY = "HIDDEN";
    public const string SCOPE_CALENDAR = "https://www.googleapis.com/auth/calendar";
    public const string SCOPE_CALENDAR_READONLY = "https://www.googleapis.com/auth/calendar.readonly";

    private static CalendarService gcal;

    public static void ImportFromMVMA()
    {
        Thread yat = new Thread(new ThreadStart(BuildCalendarService));
        yat.Start();
    }

    // Permet de récupérer le service calendrier
    // Define the method that receives a callback when the results are available.
    private static void BuildCalendarService() {

        var certificate = new X509Certificate2(PRIV_KEY_PATH, PRIV_KEY_SECRET, X509KeyStorageFlags.Exportable);
        var privateKey = certificate.Export(X509ContentType.Cert);

        var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
        {
            ServiceAccountId = EMAIL_ADDRESS,
            Scope = SCOPE_CALENDAR
        };

        var auth = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);

        gcal = new CalendarService(new BaseClientService.Initializer()
                        {
                            Authenticator = auth,
                            ApplicationName = APP_NAME,
                        });
    }
}

public enum JwtHashAlgorithm
{
    RS256,
    HS384,
    HS512
}

public class JsonWebToken
{


    private static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;

    static JsonWebToken()
    {
        HashAlgorithms = new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>>
        {
            { JwtHashAlgorithm.RS256, (key, value) => { using (var sha = new HMACSHA256(key)) { return sha.ComputeHash(value); } } },
            { JwtHashAlgorithm.HS384, (key, value) => { using (var sha = new HMACSHA384(key)) { return sha.ComputeHash(value); } } },
            { JwtHashAlgorithm.HS512, (key, value) => { using (var sha = new HMACSHA512(key)) { return sha.ComputeHash(value); } } }
        };
    }

    public static string Encode(object payload, string key, JwtHashAlgorithm algorithm)
    {
        return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm);
    }

    public static string Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm)
    {
        var segments = new List<string>();
        var header = new { alg = algorithm.ToString(), typ = "JWT" };

        byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
        byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));

        segments.Add(Base64UrlEncode(headerBytes));
        segments.Add(Base64UrlEncode(payloadBytes));

        var stringToSign = string.Join(".", segments.ToArray());

        var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

        byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign);
        segments.Add(Base64UrlEncode(signature));

        return string.Join(".", segments.ToArray());
    }

    public static string Decode(string token, string key)
    {
        return Decode(token, key, true);
    }

    public static string Decode(string token, string key, bool verify)
    {
        var parts = token.Split('.');
        var header = parts[0];
        var payload = parts[1];
        byte[] crypto = Base64UrlDecode(parts[2]);

        var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
        var headerData = JObject.Parse(headerJson);
        var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
        var payloadData = JObject.Parse(payloadJson);

        if (verify)
        {
            var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
            var keyBytes = Encoding.UTF8.GetBytes(key);
            var algorithm = (string)headerData["alg"];

            var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
            var decodedCrypto = Convert.ToBase64String(crypto);
            var decodedSignature = Convert.ToBase64String(signature);

            if (decodedCrypto != decodedSignature)
            {
                throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
            }
        }

        return payloadData.ToString();
    }

    private static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
    {
        switch (algorithm)
        {
            case "RS256": return JwtHashAlgorithm.RS256;
            case "HS384": return JwtHashAlgorithm.HS384;
            case "HS512": return JwtHashAlgorithm.HS512;
            default: throw new InvalidOperationException("Algorithm not supported.");
        }
    }

    // from JWT spec
    private static string Base64UrlEncode(byte[] input)
    {
        var output = Convert.ToBase64String(input);
        output = output.Split('=')[0]; // Remove any trailing '='s
        output = output.Replace('+', '-'); // 62nd char of encoding
        output = output.Replace('/', '_'); // 63rd char of encoding
        return output;
    }

    // from JWT spec
    private static byte[] Base64UrlDecode(string input)
    {
        var output = input;
        output = output.Replace('-', '+'); // 62nd char of encoding
        output = output.Replace('_', '/'); // 63rd char of encoding
        switch (output.Length % 4) // Pad with trailing '='s
        {
            case 0: break; // No pad chars in this case
            case 2: output += "=="; break; // Two pad chars
            case 3: output += "="; break; // One pad char
            default: throw new System.Exception("Illegal base64url string!");
        }
        var converted = Convert.FromBase64String(output); // Standard base64 decoder
        return converted;
    }
}

public class GoogleJsonWebToken
{

    public static string GetAccessToken(string email, string certificateFilePath, string serviceScope)
    {
        var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        var issueTime = DateTime.Now;

        var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
        var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds; // Expiration time is up to 1 hour, but lets play on safe side

        var payload = new
        {
            iss = email,
            scope = serviceScope,
            aud = "https://accounts.google.com/o/oauth2/token",
            exp = exp,
            iat = iat
        };

        var certificate = new X509Certificate2(certificateFilePath, GoogleCalendar.PRIV_KEY_SECRET);

        var privateKey = certificate.Export(X509ContentType.Cert);

        return JsonWebToken.Encode(payload, privateKey, JwtHashAlgorithm.RS256);
    }
}

I don't understand why I have a FileLoadException on the System.Threading.Tasks assembly. I try to add a new functionality to an application which already use Tasks without problems. This app uses TabPage objects which run in different threads.

Answer

Sam Leach picture Sam Leach · Jul 10, 2013

Remove all references to System.Threading.Tasks and then add in the one from whatever .NET version you are using (.NET 4.0).

The Google Calendar API might be using a different version of .NET

Manually Redirecting Assembly

You can specify which version of the assembly to use in your app.config

  <dependentAssembly>
    <assemblyIdentity name="someAssembly"
      publicKeyToken="32ab4ba45e0a69a1"
      culture="en-us" />

    <bindingRedirect oldVersion="7.0.0.0" newVersion="8.0.0.0" />
  </dependentAssembly>