解决了 !
非常感谢 Sam Leach
这是我的工作 app.config 文件的示例:
<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>
我也找到了那个来源
编辑:原始问题在该行下方。
我正在使用 .NET 4.0 Framework,根据我的研究,我知道不再需要 System.Threading.Tasks 程序集(因为它是自动包含的)。我错了吗?
如果我是对的,我现在很确定该错误已引发,因为 google-api-dotnet-client 的开发人员使用的 System.Threading.Tasks 版本与 Visual Studio 2010 使用的版本不同。
我注意到当我删除一些行时检查我的应用程序的行为时。
这些行出来了:
gcal = new CalendarService(new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = APP_NAME,
});
所以,我的新问题是:
有没有办法强制在 VS2010 中使用一个特定版本的参考程序集?
感谢您帮助我,我相信它会帮助很多人,因为 google-calendar-api-v3 的文档记录很差。
亲切的问候,布鲁诺。
我的问题是我无法通过 VisualStudio 访问 Google API 作为服务。
我收到此错误:
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()
这是我的类(相同的命名空间),它尝试使用 Json Web 令牌连接到 Google Calendar API v3:
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);
}
}
我不明白为什么 System.Threading.Tasks 程序集上有 FileLoadException。我尝试向已经使用 Tasks 的应用程序添加新功能而没有问题。这个应用程序使用在不同线程中运行的 TabPage 对象。