I have a class library written in C# that uses the google drive v3 API to keep a local file repository updated. This code works perfectly fine in a console application, however when I add a reference in the VBA tools to this DLL after applying the necessary measures (registering it etc), it does not run the updateFiles Method in VBA. It does however run other functions that do not depend on the google drive API. My question is, how can I get this dll working in VBA. If anyone could point me in the right direction that would be great.
Thanks in advance
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace fileUpdater
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Class1
{
private static DriveService service;
private static Dictionary<string, Google.Apis.Drive.v3.Data.File> files = new Dictionary<string, Google.Apis.Drive.v3.Data.File>();
private const string FOLDER_MIME_TYPE = "application/vnd.google-apps.folder";
private static double percentComplete;
private string AbsPath(Google.Apis.Drive.v3.Data.File file)
{
var name = file.Name;
if (file.Parents.Count() == 0)
{
return name;
}
var path = new List<string>();
while (true)
{
var parent = GetParent(file.Parents[0]);
if (parent.Parents == null || parent.Parents.Count() == 0)
{
break;
}
path.Insert(0, parent.Name);
file = parent;
}
path.Add(name);
return path.Aggregate((current, next) => Path.Combine(current, next));
}
private Google.Apis.Drive.v3.Data.File GetParent(string id)
{
if (files.ContainsKey(id))
{
return files[id];
}
var request = service.Files.Get(id);
request.Fields = "name,parents";
var parent = request.Execute();
files[id] = parent;
return parent;
}
public void updateFiles()
{
service = AuthenticateServiceAccount("serviceAccountEmail", #"serviceAccountCredentials");
if (service == null)
{
return;
}
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.Fields = "files(id, name, mimeType, parents, createdTime)";
listRequest.PageSize = 1000;
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
.Files;
double counter = 0.0f;
foreach (var file in files)
{
percentComplete = Math.Round((counter / files.Count) * 100, 2);
if (file.Parents != null)
{
string path = "downloadFile/" + AbsPath(file);
if (file.MimeType == FOLDER_MIME_TYPE)
{
if (!System.IO.Directory.Exists(path))
{
System.IO.Directory.CreateDirectory(path);
}
}
else
{
if (System.IO.File.Exists(path))
{
if (DateTime.Compare((DateTime)file.CreatedTime, System.IO.File.GetCreationTime(path)) > 0)
{
System.IO.File.Delete(path);
downloadFile(file, path);
}
}
else
{
downloadFile(file, path);
}
}
}
counter++;
}
}
private void downloadFile(Google.Apis.Drive.v3.Data.File file, string path)
{
var stream = new System.IO.MemoryStream();
service.Files.Get(file.Id).Download(stream);
System.IO.FileStream fileToWrite = new System.IO.FileStream(path, System.IO.FileMode.Create, System.IO.FileAccess.Write);
stream.WriteTo(fileToWrite);
fileToWrite.Close();
}
private DriveService AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
{
if (!System.IO.File.Exists(keyFilePath))
{
return null;
}
string[] scopes = new string[] { DriveService.Scope.Drive };
var certificate = new X509Certificate2(keyFilePath, "password", X509KeyStorageFlags.Exportable);
try
{
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample"
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
public double getPercentComplete()
{
return percentComplete;
}
}
}
Related
I am trying to develop a unity application that, among other things, controls a PTZ camera remotely. I'm having trouble actually getting the connection set up though.
I am using the Onvif Unity library made by Oneiros90: https://github.com/Oneiros90/UnityOnvif
When I go into the test scene in Unity-OnvifMain > core > test > scenes (which only has an empty game object and the test script) and hit play, the script initializes all clients without errors but upon reaching the mediaClient.getProfiles() line, it throws an error "WebException: There was an error on processing web request: Status code 400(BadRequest): Bad Request"
Here is the relevant test script code for reference:
using System.Linq;
using Onvif.Core;
using UnityEngine;
public class OnvifClientTest : MonoBehaviour
{
[SerializeField]
private string host = "";
[SerializeField]
private string username = "admin";
[SerializeField]
private string password = "";
[SerializeField]
private string ptzServiceAddress = "";
[SerializeField]
private string mediaServiceAddress = "";
private Vector2 ptzPanSpeedLimits;
private Vector2 ptzTiltSpeedLimits;
private Vector2 ptzZoomSpeedLimits;
private OnvifClient onvif;
private string currentProfile;
private PTZClient ptzClient;
private MediaClient mediaClient;
private async void Start()
{
onvif = new OnvifClient(host, username, password);
var success = await onvif.Init();
if (!success)
{
Debug.LogError($"Cannot connect to {host}");
return;
}
ptzClient = await onvif.CreatePTZClient(ptzServiceAddress);
mediaClient = await onvif.CreateMediaClient(mediaServiceAddress);
var profiles = mediaClient.GetProfiles();
currentProfile = profiles.First().token;
var ptzConf = mediaClient.GetProfile(currentProfile).PTZConfiguration;
Space2DDescription panTiltSpace = null;
Space1DDescription zoomSpace = null;
//other stuff that does not matter yet because it doesn't get this far
}
}
And the OnvifClient code also:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;
using Onvif.Security;
using UnityEngine;
namespace Onvif.Core
{
public class OnvifClient
{
private DeviceClient device;
private Binding binding;
private SoapSecurityHeaderBehavior securityHeader;
public Uri OnvifUri { get; private set; }
public string Host { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }
public Capabilities Capabilities { get; private set; }
public OnvifClient(string host, string username, string password)
{
Host = host;
OnvifUri = new Uri(host); //($"http://{host}/onvif/device_service");
Username = username;
Password = password;
}
public async Task<bool> Init()
{
try
{
//Have to set http continue to false since the server is not expecting it: https://forum.unity.com/threads/http-error-417-expectation-failed-unitywebrequest.651256/
System.Net.ServicePointManager.Expect100Continue = false;
binding = CreateBinding();
var endpoint = new EndpointAddress(OnvifUri);
device = new DeviceClient(binding, endpoint);
var timeShift = await GetDeviceTimeShift(device);
securityHeader = new SoapSecurityHeaderBehavior(Username, Password, timeShift);
device = new DeviceClient(binding, endpoint);
device.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
device.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
device.Open();
Capabilities = device.GetCapabilities(new CapabilityCategory[] { CapabilityCategory.All });
return true;
}
catch (Exception e)
{
Debug.LogError(e.Message);
return false;
}
}
public void Ping()
{
device.GetCapabilities(new CapabilityCategory[] { CapabilityCategory.All });
}
public void Close()
{
if (device != null)
device.Close();
}
public async Task<DeviceClient> GetDeviceClient()
{
if (device == null)
await Init();
return device;
}
//original
public async Task<MediaClient> CreateMediaClient()
{
if (device == null)
await Init();
var media = new MediaClient(binding, new EndpointAddress(new Uri(Capabilities.Media.XAddr)));
media.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
media.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return media;
}
//custom
public async Task<MediaClient> CreateMediaClient(string endpoint)
{
if (device == null)
await Init();
var media = new MediaClient(binding, new EndpointAddress(new Uri(endpoint)));
media.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
media.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return media;
}
//original
public async Task<PTZClient> CreatePTZClient()
{
if (device == null)
await Init();
var ptz = new PTZClient(binding, new EndpointAddress(new Uri(Capabilities.PTZ.XAddr)));
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return ptz;
}
//custom
public async Task<PTZClient> CreatePTZClient(string endpoint)
{
if (device == null)
await Init();
var ptz = new PTZClient(binding, new EndpointAddress(new Uri(endpoint)));
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return ptz;
}
public async Task<ImagingPortClient> CreateImagingClient()
{
if (device == null)
await Init();
var imaging = new ImagingPortClient(binding, new EndpointAddress(new Uri(Capabilities.Imaging.XAddr)));
imaging.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
imaging.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return imaging;
}
private static Binding CreateBinding()
{
var binding = new CustomBinding();
var textBindingElement = new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
var httpBindingElement = new HttpTransportBindingElement
{
AllowCookies = true,
MaxBufferSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue
};
binding.Elements.Add(textBindingElement);
binding.Elements.Add(httpBindingElement);
return binding;
}
private static Task<TimeSpan> GetDeviceTimeShift(DeviceClient device)
{
return Task.Run(() =>
{
var utc = device.GetSystemDateAndTime().UTCDateTime;
var dt = new System.DateTime(utc.Date.Year, utc.Date.Month, utc.Date.Day,
utc.Time.Hour, utc.Time.Minute, utc.Time.Second);
return dt - System.DateTime.UtcNow;
});
}
}
}
I'm very new to .net client connection and so forth, so I'm really not sure why it would throw a 400 error, as the username and password are both correct. I also know for a fact that connecting to a camera works as I am working off of an existing project that does it through c# (outside of unity though).
Alternatively, I have tried avoiding using the helper scripts provided by Oneiros90 and connecting to the devices using the core onfiv scripts, as per this post: C# .NET Framework 4.52 Zoom/Focus for PELCO Camera via ONVIF
But when I do that, it reaches the mediaClient.getProfiles() line and instead produces an "InvalidOperationException: Use ClientCredentials to specify a user name for required HTTP Digest authentication." error, even though I am clearly setting the http digest client credentials just above. This is also how it works in the aforementioned existing project, although there isn't as much going on with the security headers and so on in that.
My Code in unity:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Onvif.Core;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
public class PTZController_Local : MonoBehaviour
{
public string hostAddress = "http://<secret>/onvif/device_service";
public string mediaAddress = "http://<secret>/onvif/media_service";
public string PTZAddress = "http://<secret>/onvif/ptz_service";
public string username = "";
public string password = "";
private OnvifClient onvifClient;
private string currentProfile;
private PTZClient ptzClient;
private MediaClient mediaClient;
private DeviceClient device;
private CustomBinding binding;
private CustomBinding mediaBinding;
private void Start()
{
////connect to onvif device service
//onvifClient = new OnvifClient(hostAddress, username, password);
//var success = await onvifClient.Init();
//if (!success)
//{
// Debug.LogError($"PTZController: Cannot connect to {hostAddress}");
// return;
//}
//Debug.Log($"PTZController: Successfully connected to {hostAddress}");
//device = await onvifClient.GetDeviceClient();
System.Net.ServicePointManager.Expect100Continue = false;
HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
httpTransport.AuthenticationScheme = System.Net.AuthenticationSchemes.Digest;
httpTransport.MaxReceivedMessageSize = Int32.MaxValue;
httpTransport.MaxBufferSize = Int32.MaxValue;
httpTransport.ProxyAddress = null;
httpTransport.BypassProxyOnLocal = true;
httpTransport.UseDefaultWebProxy = false;
httpTransport.TransferMode = TransferMode.StreamedResponse;
binding = new CustomBinding(new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, System.Text.Encoding.UTF8), httpTransport);
binding.CloseTimeout = TimeSpan.FromSeconds(30.0);
binding.OpenTimeout = TimeSpan.FromSeconds(30.0);
binding.SendTimeout = TimeSpan.FromMinutes(10.0);
binding.ReceiveTimeout = TimeSpan.FromMinutes(3.0);
device = new DeviceClient(binding, new EndpointAddress(hostAddress));
device.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
device.ClientCredentials.HttpDigest.ClientCredential.UserName = username;
device.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var messageElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
HttpTransportBindingElement mediaHttpTransport = new HttpTransportBindingElement()
{
AuthenticationScheme = System.Net.AuthenticationSchemes.Digest
};
mediaBinding = new CustomBinding(messageElement, mediaHttpTransport);
mediaClient = new MediaClient(mediaBinding, new EndpointAddress(mediaAddress));
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = username;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var profiles = mediaClient.GetProfiles();
currentProfile = profiles.First().token;
//other code that is never reached
}
}
Original project code (i've tried copying this into unity exactly and get the same error about setting the http credentials):
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.ServiceModel;
using OnvifPipeClient.OnvifMedia10;
using OnvifPipeClient.OnvifPTZService;
using System.Timers;
using System.Diagnostics;
using System.Threading;
namespace OnvifPipeClient
{
public class PTZConnection
{
public static PTZClient ptzClient;
private static MediaClient mediaClient;
public static Profile profile;
// establish connection to PTZ
public static void OnvifLogin()
{
// create binding
System.Net.ServicePointManager.Expect100Continue = false;
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
// connect to PTZ interface
ptzClient = new PTZClient(binding, new EndpointAddress("http://<secret>/onvif/ptz_service"));
ptzClient.ClientCredentials.HttpDigest.ClientCredential.UserName = "secret";
ptzClient.ClientCredentials.HttpDigest.ClientCredential.Password = "secret";
ptzClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// connect to media client
mediaClient = new MediaClient(binding, new EndpointAddress("http://<secret>/onvif/media_service"));
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = "secret";
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = "secret";
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// load profiles and configurations
var profiles = mediaClient.GetProfiles();
profile = mediaClient.GetProfile(profiles[0].token);
//other code that is reached without issues
}
}
The only thread of a possible explanation I could find is this github issue(https://github.com/jamidon/ONVIF-test/issues/1) saying that another onvif c# library that oneiros90 had previously tried did not work with pelco cameras for some reason (the camera I'm trying to connect to is a pelco camera).
So, yeah, sorry for the big code dumps here but it's very difficult to find resources about this. Thanks in advance for any help.
My problem is probably stupid, but I can't figure out the solution by myself, so I hope someone here can help me.
I am trying to make a dll file for hosting a server on the localhost. I thought I did everything right, but I receive this error:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at minihttpc.Requests.HttpRequest.ParseHeaders(String[] requestLine) in C:\Users\deqnb\OneDrive\minihttpc\minihttpc\Requests\HttpRequest.cs:line 87
at minihttpc.Requests.HttpRequest.ParseRequest(String requestString) in C:\Users\deqnb\OneDrive\minihttpc\minihttpc\Requests\HttpRequest.cs:line 44
at minihttpc.Requests.HttpRequest..ctor(String requestString) in C:\Users\deqnb\OneDrive\minihttpc\minihttpc\Requests\HttpRequest.cs:line 21
at MiniServerHTTP.WebServer.ConnectionHandler.ReadRequest() in C:\Users\deqnb\OneDrive\minihttpc\MiniServerHTTP.WebServer\ConnectionHandler.cs:line 80
at MiniServerHTTP.WebServer.ConnectionHandler.ProcessRequest() in C:\Users\deqnb\OneDrive\minihttpc\MiniServerHTTP.WebServer\ConnectionHandler.cs:line 28
Here is my code - HttpRequest.cs:
using minihttpc.Common.CoreValidator;
using minihttpc.Common.GlobalConstants;
using minihttpc.Headers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace minihttpc.Requests
{
public class HttpRequest:IHttpRequest
{
public HttpRequest(string requestString)
{
CoreValidator.ThrowIfNullOrEmpty(requestString, nameof(requestString));
this.FormData = new Dictionary<string, object>();
this.QueryData = new Dictionary<string, object>();
this.Headers = new HttpHeaderCollection();
this.ParseRequest(requestString);
}
public string Path { get; private set; }
public string Url { get; private set; }
public Dictionary<string,object>FormData { get; }
public Dictionary<string, object> QueryData { get; }
public IHttpHeaderCollection Headers { get; private set; }
public HttpRequestMethod RequestMethod { get; private set; }
public void ParseRequest(string requestString)
{
string[] splitRequestContent = requestString.Split(new[] {
GlobalConstants.HttpNewLine }, StringSplitOptions.None);
string[] requestLine = splitRequestContent[0].Trim().Split(new[]
{ ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (!this.IsValidReqiestLine(requestLine))
{
throw new BadRequestException();
}
this.ParseRequestMethod(requestLine);
this.ParseRequestUrl(requestLine);
this.ParseRequestPath();
this.ParseHeaders(splitRequestContent.Skip(1).ToArray());
//this.ParseRequestQueryParameters();
this.ParseRequestParameters(splitRequestContent[splitRequestContent.Length - 1]);
}
bool IsValidRequestLine(string[] requestLine)
{
if (requestLine.Count() != 3 && requestLine[2] != "HTTP/1.1")
{
return false;
}
else
{
return true;
}
}
void ParseRequestMethod(string[] requestLine)
{
switch (requestLine[0])
{
case "GET": RequestMethod = HttpRequestMethod.Get; break;
case "POST": RequestMethod = HttpRequestMethod.Post; break;
case "PUT": RequestMethod = HttpRequestMethod.Put; break;
case "DELETE": RequestMethod = HttpRequestMethod.Delete; break;
}
}
void ParseRequestUrl(string [] requestLine)
{
this.Url = requestLine[1];
}
void ParseRequestPath()
{
this.Path = this.Url.Split("?").Take(1).First().ToString();
}
void ParseHeaders(string [] requestLine)
{
foreach(var line in requestLine)
{
Console.WriteLine(line); //a lot of info about the req
if (line == GlobalConstants.HttpNewLine) break;
string[] header = line.Split(' ').ToArray();
//Console.WriteLine(header[1]);
Headers.AddHeader(new HttpHeader(header[0],
header[1]));//seems fine //line 87
}
if (Headers.ContainsHeader("host"))
{
throw new BadRequestException();
}
}
void ParseRequestQueryParameters()
{
if (!(this.Url.Split('?').Length > 1)) return;
this.Url.Split('?', '#')[1].Split('&').Select(plainQueryParameter =>
plainQueryParameter.Split());//not finished !!!!
}
void ParseFormDataParameters(string formData)
{
if (!string.IsNullOrEmpty(formData))
{
formData.Split('&').Select(plainQueryParameter =>
plainQueryParameter.Split('=')).ToList().ForEach(queryParameterKeyValue =>
this.FormData.Add(queryParameterKeyValue[0],
queryParameterKeyValue[1]));
}
}
void ParseRequestParameters(string formData)//not being called
{
ParseRequestQueryParameters();
ParseFormDataParameters(formData);
}
}
}
ConnectionHandler.cs:
using minihttpc.Common.CoreValidator;
using minihttpc.Requests;
using MiniServerHTTP.WebServer.Results;
using MiniServerHTTP.WebServer.Routing;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace MiniServerHTTP.WebServer
{
public class ConnectionHandler
{
private readonly Socket client;
private readonly IServerRoutingTable table;
public ConnectionHandler(Socket client, IServerRoutingTable table)
{
CoreValidator.ThrowIfNull(client, nameof(client));
CoreValidator.ThrowIfNull(client, nameof(client));
this.client = client;
this.table = table;
}
public async Task ProcessRequest()
{
try
{
var httpRequest = await this.ReadRequest();
if (httpRequest != null)
{
Console.WriteLine($"Processing: {httpRequest.RequestMethod} {httpRequest.Path}...");
var httpResponse = this.HandleRequest((IHttpRequest)httpRequest);
this.ProcessResponse(httpResponse);
}
}
catch (BadRequestException e)//400
{
this.ProcessResponse(new TextResult(e.ToString(),
HttpResponseStatusCode.BadRequest));
}
catch (Exception e)//500
{
this.ProcessResponse(new TextResult(e.ToString(),
HttpResponseStatusCode.InternalServerError));
}
this.client.Shutdown(SocketShutdown.Both);
}
private async Task ProcessResponse(IHttpResponse httpResponse)
{
byte[] byteSegments = httpResponse.GetBytes();
await this.client.SendAsync(byteSegments, SocketFlags.None);
}
private IHttpResponse HandleRequest(IHttpRequest httpRequest)
{
if(!this.table.Contains(httpRequest.RequestMethod,
httpRequest.Path))
{
return new TextResult($"Route with method {httpRequest.RequestMethod} and path \"{httpRequest.Path}\"not found.",
HttpResponseStatusCode.NotFound);
}
return this.table.Get(httpRequest.RequestMethod,
httpRequest.Path).Invoke(httpRequest);
}
private async Task<HttpRequest> ReadRequest()
{
var result = new StringBuilder();
var data = new ArraySegment<byte>(new byte[1024]);
while (true)
{
int numberOfBytes = await this.client.ReceiveAsync(data.Array, SocketFlags.None);
if (numberOfBytes == 0) break;
var bytesAsString = Encoding.UTF8.GetString(data.Array, 0,
numberOfBytes);
result.Append(bytesAsString);
if (numberOfBytes < 1023) break;
}
if (result.Length == 0)
{
return null;
}
return new HttpRequest(result.ToString());
}
}
}
When I check the parameters manually and variables in minihttpc.Requests.HttpRequest.ParseHeaders(), everything seems fine. I can't get where the problem is.
string[] header = line.Split(' ').ToArray();
Headers.AddHeader(new HttpHeader(header[0], header[1]));
I can pretty much guarantee you're running into a line that doesn't have a space in it. Maybe there's a line that's empty, for example. That wouldn't be obvious from the Console.WriteLine() that you're doing, because empty lines are kind of invisible.
my problem is the following: I just want to run x requests at the same time depending on the user.
Well, it seems to work fine when the MaxConcurrentDownloads variable is equal to 1, but when I increase it, say 10: I have to wait for the 10taches to finish for it to execute so that Console.WriteLine as to write, when it's supposed to run asynchronously, right?
Can you help me? Here is a minimalist version of my "problem" (Also I want to specify that I have no compiler or syntax errors)
main.c
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace test_client
{
class Program
{
private static client cli = new client();
private static readonly string PATH = #Directory.GetCurrentDirectory();
private static int concurrency = 100;
private static async Task<bool> MakeJOB(int pos)
{
return await cli.NewRequest<bool>((HttpClient client)=>
{
try
{
HttpClientHandler handler = null;
if (cli.handler != null)
handler = cli.GethandlerIndexed(pos);
client = new HttpClient(handler);
cli.AssignDefaultHeaders(client);
using (HttpResponseMessage response = client.GetAsync("https://api.my-ip.io/ip.txt").Result)
using (HttpContent content = response.Content)
Console.WriteLine(content.ReadAsStringAsync().Result + " / " + Task.CurrentId);
return true;
}
catch { /* exception .. */ return false; }
});
}
static void Main(string[] args)
{
ServicePointManager.DefaultConnectionLimit = 100;
MainAsync(args).GetAwaiter().GetResult();
Console.ReadLine();
}
static async Task MainAsync(string[] args)
{
cli.SetConcurrentDownloads(concurrency);
var t = new Task[concurrency];
int pos = 0;
for (int i = 0; i < t.Length; i++, pos++)
t[i] = MakeJOB(pos++);
await Task.WhenAll(t);
}
}
}
client.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.Collections.Concurrent;
using System.Threading;
namespace test_client
{
public class client
{
private readonly ConcurrentDictionary<string, HttpClient> Clients;
public SemaphoreSlim Locker;
private CancellationTokenSource TokenSource = new CancellationTokenSource();
public HttpClientHandler[] handler { get; set; }
public string[] address { get; set; }
public string[] port { get; set; }
public string[] username { get; set; }
public string[] password { get; set; }
public int MaxConcurrentDownloads { get; set; }
private void initializeHandler(string address = "", string port = "", string user = "", string pass = "")
{
initializeHandler(new string[] { string.Concat(address, ":", port, ":", user, ":", pass) });
}
private void initializeHandler(string[] proxies_client)
{
if (proxies_client == null || proxies_client.Length == 0)
return;
this.address = new string[proxies_client.Length];
this.port = new string[proxies_client.Length];
this.username = new string[proxies_client.Length];
this.password = new string[proxies_client.Length];
for (int i = 0; i < proxies_client.Length; i++)
{
var split = proxies_client[i].Split(new char[] { ':' });
this.address[i] = split[0] != "" ? split[0] : "";
this.port[i] = split[1] != "" ? split[1] : "";
this.username[i] = split[2] != "" ? split[2] : "";
this.password[i] = split[3] != "" ? split[3] : "";
}
var proxies = new WebProxy[proxies_client.Length];
NetworkCredential[] credential = new NetworkCredential[proxies_client.Length];
for (int i = 0; i < proxies_client.Length; i++)
{
if (this.username[i] != "")
credential[i] = new NetworkCredential(this.username[i], this.password[i]);
else
credential[i] = CredentialCache.DefaultNetworkCredentials;
}
const string protocol = "http://";
for (int i = 0; i < proxies.Length; i++)
{
if (this.address[i] != "")
{
var uri = proxies_client[i].Split(new char[] { ':' });
if (!uri[0].Contains(protocol))
uri[0] = string.Concat(protocol, uri[0]);
proxies[i] = new WebProxy()
{
Address = new Uri(string.Concat(uri[0], ":", uri[1])),
Credentials = credential[i],
};
}
};
this.handler = new HttpClientHandler[proxies.Length];
for (int i = 0; i < proxies.Length; i++)
{
if (proxies[i].Address.AbsoluteUri != "")
this.handler[i] = new HttpClientHandler() { Proxy = proxies[i] };
else
this.handler[i] = new HttpClientHandler();
}
}
public HttpClientHandler GethandlerIndexed(int index)
{
return (this.handler[index % this.handler.Length]);
}
public void SetConcurrentDownloads(int nb = 1)
{
Locker = new SemaphoreSlim(nb, nb);
}
public client(string[] proxies = null)
{
Clients = new ConcurrentDictionary<string, HttpClient>();
if (Locker is null)
Locker = new SemaphoreSlim(1, 1);
if (proxies != null)
initializeHandler(proxies);
}
private async Task<HttpClient> CreateClient(string Name, bool persistent, CancellationToken token)
{
if (Clients.ContainsKey(Name))
return Clients[Name];
HttpClient newClient = new HttpClient();
if (persistent)
{
while (Clients.TryAdd(Name, newClient) is false)
{
token.ThrowIfCancellationRequested();
await Task.Delay(1, token);
}
}
return newClient;
}
public async Task<T> NewRequest<T>(Func<HttpClient, T> Expression, int? MaxTimeout = 2000, string Id = null)
{
await Locker.WaitAsync(MaxTimeout ?? 2000, TokenSource.Token);
bool persistent = true;
if (Id is null)
{
persistent = false;
Id = string.Empty;
}
try
{
HttpClient client = await CreateClient(Id, persistent, TokenSource.Token);
T result = await Task.Run<T>(() => Expression(client), TokenSource.Token);
if (persistent is false)
client?.Dispose();
return result;
}
finally
{
Locker.Release();
}
}
public void AssignDefaultHeaders(HttpClient client)
{
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");
//client.Timeout = TimeSpan.FromSeconds(3);
}
public async Task Cancel(string Name)
{
if (Clients.ContainsKey(Name))
{
CancellationToken token = TokenSource.Token;
HttpClient foundClient;
while (Clients.TryGetValue(Name, out foundClient) is false)
{
token.ThrowIfCancellationRequested();
await Task.Delay(1, token);
}
if (foundClient != null)
{
foundClient?.Dispose();
}
}
}
public void ForceCancelAll()
{
TokenSource?.Cancel();
TokenSource?.Dispose();
TokenSource = new CancellationTokenSource();
foreach (var item in Clients)
{
item.Value?.Dispose();
}
Clients.Clear();
}
}
}
One thing I spotted in a quick skim: Your line in main.cs::Program.MakeJOB:
using (HttpResponseMessage response = client.GetAsync("https://api.my-ip.io/ip.txt").Result)
should instead read
using (HttpResponseMessage response = await client.GetAsync("https://api.my-ip.io/ip.txt"))
This may not be your only issue, but by not awaiting the GetAsync method, you are effectively blocking on the request rather than yielding control back to the caller so that it can, for instance, context switch between tasks or queue up other tasks.
Same goes for
Console.WriteLine(content.ReadAsStringAsync().Result + " / " + Task.CurrentId);
which should be
Console.WriteLine((await content.ReadAsStringAsync()) + " / " + Task.CurrentId);
Although that one is not likely to block for a significant amount of time.
I have to import my file excel csv to mongodb, but I want use code c#.
public class AnimalRetriever : IAnimalRetriever
{
private readonly MongoClient _mongoClient;
public AnimalRetriever()
{
_mongoClient = new MongoClient("mongodb://localhost:27017");
}
private List<Animal> GetByContinent(string continent)
{
_mongoClient.GetDatabase("local")
.GetCollection<Animal>("Animal")
.ReplaceOne(
filter: new BsonDocument("Continent", continent),
options: new UpdateOptions { IsUpsert = true },
replacement: animal.csv); //file di testo da leggere invece di newDoc(csv extension)
return _mongoClient.GetDatabase("local")
.GetCollection<Animal>("Continent")
.Find("{\"Continent\":\"" + continent + "\"}")
.ToList();
}
using System;
using System.Collections.Generic;
using System.IO;
using FactoryExample.Continent;
using MongoDB.Bson;
using MongoDB.Driver;
using CsvHelper;
namespace FactoryExample
{
class MainApp
{
public static void Main()
{ try
{
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("local");
var coll = db.GetCollection<BsonDocument>("Animal");
var reader = new StreamReader("animal.csv");
var csv = new CsvReader(reader);
csv.Configuration.HasHeaderRecord = true;
var records = csv.GetRecords<Animals>();
var dizionario = new Dictionary<string,Animal>();
foreach (var animal in records)
{
if (dizionario.ContainsKey(animal.Continent))
{
dizionario[animal.Continent].Carnivor.Add(animal.Carnivor);
dizionario[animal.Continent].Herbivor.Add(animal.Herbivor);
}
else
{
var newanimal = new Animal
{
Continent = animal.Continent,
Carnivor = new List<string>(),
Herbivor = new List<string>()
};
newanimal.Carnivor.Add(animal.Carnivor);
newanimal.Herbivor.Add(animal.Herbivor);
dizionario.Add(newanimal.Continent,newanimal);
}
}
}
catch (Exception err)
{
Console.WriteLine("Error!!");
Console.WriteLine(err.Message);
}
Console.ReadKey();
//Console.Read();
var continentFactory = ContinentFactory.Get(ContinentType.AMERICA);
var carnivore = continentFactory.GetCarnivore();
var herbivore = continentFactory.GetHerbivore();
foreach (var h in herbivore)
{
Console.WriteLine(h);
}
foreach (var c in carnivore)
{
Console.WriteLine(c);
}
Console.ReadKey();
}
}
}
I am currently undertaking a project in which I am trying to make a RPG for a discord bot. I am currently struggling with how to implement a way to keep data for different servers separate. For example, I'm trying to store the location of the party for each server. I have tried testing moving from 'town' to 'forest'. It works on the server that the command is used, but all other servers that the bot is on also have their location updated to 'forest'. Since I'm new to c# as well I am struggling to work out a way to keep the location being updated on each server.
A possible solution would be to store an object for each guild in an array and reference it whenever guild specific data is required, however this doesn't seem like an elegant solution.
What would be the best way to achieve data separation between guilds?
MAIN
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestBot2.Modules;
namespace TestBot2
{
class Program
{
static void Main(string[] args){
new Program().RunBotAsync().GetAwaiter().GetResult();
}
private DiscordSocketClient _client;
private CommandService _command;
private IServiceProvider _service;
public async Task RunBotAsync()
{
_client = new DiscordSocketClient();
_command = new CommandService();
_service = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_command)
.BuildServiceProvider();
string botToken = *** BOT TOKEN ***;
//event subscription
_client.Log += Log;
await RegisterCommandAsync();
await _client.LoginAsync(TokenType.Bot, botToken);
await _client.StartAsync();
await Task.Delay(-1);
}
private Task Log(LogMessage arg)
{
Console.WriteLine(arg);
return null;
}
public async Task RegisterCommandAsync()
{
_client.MessageReceived += HandleCommandAsync;
await _command.AddModulesAsync(Assembly.GetEntryAssembly());
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if (!(message is SocketUserMessage) || message.Author.IsBot) {
return;
}
int argPos = 1;
if (message.HasStringPrefix("cf!", ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))
{
var context = new SocketCommandContext(_client, message);
var result = await _command.ExecuteAsync(context, argPos+1, _service);
if (!result.IsSuccess)
Console.WriteLine(result.ErrorReason);
}
}
}
}
UTILITY
static string location = "town"; //curent loc
static string[] locations = //array of vlaid loc
{
"town", "forest"
};
int[,] travelMtx = new int[,] //distance matrix
{
{0,2 },
{2,0 }
};
public string D6()
{
Random rnd = new Random();
string reply;
reply = Convert.ToString(rnd.Next(1, 7));
return reply;
}
public string charName(string charowner = "")
{
string charname;
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection();
conn.ConnectionString = [FILE LOCATION]
conn.Open();
String my_query = "SELECT CharName FROM Chars WHERE CharOwner='" + charowner + "'";
Console.WriteLine(my_query);
OleDbCommand cmd = new OleDbCommand(my_query, conn);
charname = (string)cmd.ExecuteScalar();
Console.Write(charname);
return charname;
}
public string usermention(string user = "")
{
return user;
}
public string getLoc()
{
return Utility.location;
}
public void setLoc(string location)
{
Utility.location = location;
}
public bool checkLoc(string dest)
{
for (int i = 0; i < locations.Length; i++)
{
if (dest.ToLower() == locations[i])
{
return true;
}
}
return false;
}
public int travelTime(string location, string dest)
{
int x = 0;
int y = 0;
for (int i = 0; i < locations.Length; i++)
{
if (location.ToLower() == locations[i])
{
x = i;
}
if (dest.ToLower() == locations[i])
{
y= i;
}
}
return travelMtx[x,y];
}
}
}
TRAVEL
public class Travel : ModuleBase<SocketCommandContext>
{
[Command("Travel")]
public async Task PingAsync(string dest = "")
{
Utility Util = new Utility();
string loc = Util.getLoc();
int travelTime = 0;
if (Util.checkLoc(dest) == true)
{
travelTime = Util.travelTime(loc, dest);
}
Util.setLoc(dest);
await ReplyAsync("you are now in " + dest + " it took " + travelTime + " days" );
}
}
}
I think you could try to store it together with the serverip, so that you can ask for CharOwner='" + charowner + "'" and ServerIp='" + serverip + "'" in you WHERE part of the sql.
It's just a guess, but maybe it works. :)