C# WUApiLib know if a windows update needs a restart - c#

I use this code to get pending windows updates and also most of the informations of the update:
static List<PendingUpdate> GetPendingUpdates()
{
var updateSession = new UpdateSession();
var updateSearcher = updateSession.CreateUpdateSearcher();
updateSearcher.Online = false; //set to true if you want to search online
List<PendingUpdate> pendingUpdates = new List<PendingUpdate>();
try
{
var searchResult = updateSearcher.Search("IsInstalled=0 And IsHidden=0");
if (searchResult.Updates.Count > 0)
{
Console.WriteLine("There are updates available for installation");
foreach (IUpdate windowsUpdate in searchResult.Updates)
{
PendingUpdate update = new PendingUpdate();
update.Title = windowsUpdate.Title;
update.Description = windowsUpdate.Description;
update.Downloaded = windowsUpdate.IsDownloaded;
update.Urls = new List<string>();
foreach (string url in windowsUpdate.MoreInfoUrls)
{
update.Urls.Add(url);
}
foreach (dynamic category in windowsUpdate.Categories)
{
update.Categories += category.Name + ", ";
}
pendingUpdates.Add(update);
}
}
}
catch (Exception ex)
{
Console.WriteLine("ERROR");
throw ex;
}
return pendingUpdates;
}
I also use this code to get to know if the computer currently needs a restart to finish installed updates:
static bool needsRestart()
{
ISystemInformation systemInfo = new SystemInformation();
return systemInfo.RebootRequired;
}
Now my question is, is it possible to get to know if an pending update needs a computer restart to finish? In the first code I get a IUpdate object but I dont see informations about a needed restart after installing this update. I there a way to get this information?

For the asynchronous installation I use something like this:
rebootRequired = false;
UpdateSession updateSession = new UpdateSession();
updateSession.ClientApplicationID = SusClientID;
IUpdateInstaller updatesInstaller = updateSession.CreateUpdateInstaller();
IInstallationJob job = updatesInstaller.BeginInstall(InstallProgressCallback, installComplete, installState);
// here is your installer code and the checking if the installation is completed
IInstallationProgress jobProgress = job.GetProgress();
for (int updateindex = 0; updateindex < updatesInstaller.Updates.Count; updateindex++)
{
IUpdateInstallationResult updateInstallResult = jobProgress.GetUpdateResult(updateindex);
rebootRequired |= updateInstallResult.RebootRequired;
}
if(rebootRequired)
{
// any of the updates need a reboot
}

Related

Adwords Api How to Pause Ads on Ad level

I am trying to PAUSE the Ads with the AdId and AdGroupID. I have successfully paused an AdGroup but i want to pause Ads individually. Is this possible on Adwords API. I tried the code below but It seems it only works on AdGroup level. Also checked the AdService but seems that there is no option to edit the Status.
I am using Ads.AdWords.v201809
Thanks in advance
public void googleEnableDisableAds(AdWordsUser user, long adGroupId, long AdID, AdGroupAdStatus AdStatus)
{
using (AdGroupAdService adGroupAdService =
(AdGroupAdService)user.GetService(AdWordsService.v201809.AdGroupAdService))
{
List<AdGroupAdOperation> operations = new List<AdGroupAdOperation>();
// Create the expanded text ad.
ExpandedTextAd expandedTextAd = new ExpandedTextAd
{
//CR[i].
id = AdID
};
AdGroupAd expandedTextAdGroupAd = new AdGroupAd
{
adGroupId = adGroupId,
ad = expandedTextAd,
// Optional: Set the status.
status = AdStatus
};
// Create the operation.
AdGroupAdOperation operation = new AdGroupAdOperation
{
#operator = Operator.SET,
operand = expandedTextAdGroupAd
};
operations.Add(operation);
AdGroupAdReturnValue retVal = null;
try
{
if (operations.Count > 0)
{
// Create the ads.
retVal = adGroupAdService.mutate(operations.ToArray());
// Display the results.
if (retVal != null && retVal.value != null)
{
foreach (AdGroupAd adGroupAd in retVal.value)
{
ExpandedTextAd newAd = adGroupAd.ad as ExpandedTextAd;
Console.WriteLine(
"Expanded text ad with ID '{0}' and headline '{1} - {2}' " +
"was added.", newAd.id, newAd.headlinePart1, newAd.headlinePart2);
//adGroupId
}
}
else
{
Console.WriteLine("No expanded text ads were created.");
}
}
adGroupAdService.Close();
}
catch (Exception e)
{
throw new System.ApplicationException("Failed to create expanded text ad.", e);
}
}
}
Here is an example from API docs https://developers.google.com/adwords/api/docs/samples/csharp/basic-operations#pause-an-ad
The key idea is to set status property to PAUSED
AdGroupAdStatus status = AdGroupAdStatus.PAUSED

Multithreaded c# console app to scrape data from sites

I have written an app that goes through our own properties and scraps the data. To make sure I don't run through the same URLs, I am using a MySQL database to store the URL, flag it once its processed. All this was being done in a single thread and it's fine if I had only few thousand entries. But I have few hundred thousand entries that I need to parse so I need to make changes in the code (I am newbie in multithreading in general). I found an example and was trying to copy the style but doesn't seem to work. Anyone know what the issue is with the following code?
EDIT: Sorry didn't mean to make people guess the issue but was stupid of me to include the exception. Here is the exception
"System.InValidCastException: 'Specified cast is not valid.'"
When I start the process it collects the URLs from the database and then never hits DoWork method
//This will get the entries from the database
List<Mappings> items = bot.GetUrlsToProcess(100);
if (items != null)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Worker.Done = new Worker.DoneDelegate(WorkerDone);
foreach (var item in items)
{
urls.Add(item.Url);
WaitingTasks.Enqueue(new Task(id => new Worker().DoWork((int)id, item.Url, token), item.Url, token));
}
LaunchTasks();
}
static async void LaunchTasks()
{
// keep checking until we're done
while ((WaitingTasks.Count > 0) || (RunningTasks.Count > 0))
{
// launch tasks when there's room
while ((WaitingTasks.Count > 0) && (RunningTasks.Count < MaxRunningTasks))
{
Task task = WaitingTasks.Dequeue();
lock (RunningTasks) RunningTasks.Add((int)task.AsyncState, task);
task.Start();
}
UpdateConsole();
await Task.Delay(300); // wait before checking again
}
UpdateConsole(); // all done
}
static void UpdateConsole()
{
Console.Write(string.Format("\rwaiting: {0,3:##0} running: {1,3:##0} ", WaitingTasks.Count, RunningTasks.Count));
}
static void WorkerDone(int id)
{
lock (RunningTasks) RunningTasks.Remove(id);
}
public class Worker
{
public delegate void DoneDelegate(int taskId);
public static DoneDelegate Done { private get; set; }
public async void DoWork(object id, string url, CancellationToken token)
{
if (token.IsCancellationRequested) return;
Content obj;
try
{
int tries = 0;
bool IsUrlProcessed = true;
DateTime dtStart = DateTime.Now;
string articleDate = string.Empty;
try
{
ScrapeWeb bot = new ScrapeWeb();
SearchApi searchApi = new SearchApi();
SearchHits searchHits = searchApi.Url(url, 5, 0);
if (searchHits.Hits.Count() == 0)
{
obj = await bot.ReturnArticleObject(url);
if (obj.Code != HttpStatusCode.OK)
{
Console.WriteLine(string.Format("\r Status is {0}", obj.Code));
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = false;
itemfound.HttpCode = obj.Code;
}
else
{
string title = obj.Title;
string content = obj.Contents;
string description = obj.Description;
Articles article = new Articles();
article.Site = url.GetSite();
article.Content = content;
article.Title = title;
article.Url = url.ToLower();
article.Description = description;
string strThumbNail = HtmlHelper.GetImageUrl(url, obj.RawResponse);
article.Author = HtmlHelper.GetAuthor(url, obj.RawResponse);
if (!string.IsNullOrEmpty(strThumbNail))
{
//This condition needs to be added to remove ?n=<number> from EP thumbnails
if (strThumbNail.Contains("?"))
{
article.ImageUrl = strThumbNail.Substring(0, strThumbNail.IndexOf("?")).Replace("http:", "https:");
}
else
article.ImageUrl = strThumbNail.Replace("http:", "https:");
}
else
{
article.ImageUrl = string.IsNullOrEmpty(strThumbNail) ? article.Url.GetDefaultImageUrls() : strThumbNail.Replace("http:", "https:");
}
articleDate = HtmlHelper.GetPublishDate(url, obj.RawResponse);
if (string.IsNullOrEmpty(articleDate))
article.Pubdate = DateTime.Now;
else
article.Pubdate = DateTime.Parse(articleDate);
var client = new Index(searchApi);
var result = client.Upsert(article);
itemfound.HttpCode = obj.Code;
if (result)
{
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate);
UpdateItem(itemfound);
}
else
{
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = false;
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
UpdateItem(itemfound, tries, IsUrlProcessed);
}
}
}
else
{
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = true;
itemfound.HttpCode = HttpStatusCode.OK;
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
}
}
catch (Exception e)
{
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = false;
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
}
finally
{
DateTime dtEnd = DateTime.Now;
Console.WriteLine(string.Format("\r Total time taken to process items is {0}", (dtEnd - dtStart).TotalSeconds));
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Done((int)id);
}
}
All this code is based from Best multi-thread approach for multiple web requests this link. Can someone tell me how to get this approach running?
I think the problem is in the way you're creating your tasks:
new Task(id => new Worker().DoWork((int)id, item.Url, token), item.Url, token)
This Task constructor overload expected Action<object> delegate. That means id will be typed as object and you need to cast it back to something useful first.
Parameters
action
Type: System.Action<Object>
The delegate that represents the code to execute in the task.
state
Type: System.Object
An object representing data to be used by the action.
cancellationToken
Type: System.Threading.CancellationToken
-The CancellationToken that that the new task will observe.
You decided to cast it to int by calling (int)id, but you're passing item.Url as the object itself. I can't tell you 100% what the type of Url is but I don't expect Url-named property to be of type int.
Based on what #MarcinJuraszek said I just went back to my code and added an int as I couldn't find another way to resolve it. Here is the change I made
int i=0
foreach (var item in items)
{
urls.Add(item.Url);
WaitingTasks.Enqueue(new Task(id => new Worker().DoWork((string)id, item.Url, token), item.Url, token));
i++;
}

looking for the best way to see when my task is complete C#

So I'm pulling in a list of items and for each item I'm creating an instance of an object to run a task on that item. All the objects are the same, they updated based off of a received message every three seconds. This update does not all occur at once though, sometimes it takes 3.1 seconds, etc. This is data I need to serialize in XML once it all exists so I'm looking for a way to see when its all done.
I've explored tasks in .net 4.6 but that initiates a task and it reports complete and then to run again the task class would initiate it again but in my case that won't work because each instance stays alive and initiates itself when a new message comes in.
What is the best way to have it report it reached the last line of code and then look at a list of these instances and say when all of them show as complete then run task to serialize?
I've included code below of the instance that is running.
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
var eventArgs = new CallDataReceivedEventArgs();
this.OnCallDataReceived(eventArgs);
try
{
List<Tuple<String, TimeSpan>> availInItems = new List<Tuple<string, TimeSpan>>();
List<Tuple<string, int, TimeSpan, string, string, string>> agentlist = new List<Tuple<string, int, TimeSpan, string, string, string>>();
if (e == null)
{
return;
}
List<TimeSpan> listOfTimeSpans = new List<TimeSpan>();
if (e.CmsData != null)
{
#region Gathering Agent Information
// Create a list of all timespans for all _agents in a queue using the property AgentTimeInState
foreach (var item in e.CmsData.Agents)
{
//AgentData = new ScoreBoardAgentDataModel(AgentName, AgentExtension, AgentTimeInState, AgentAuxReason, AgentId, AgentAdcState);
_agentData.AgentName = item.AgName;
_agentData.AgentExtension = item.Extension;
_agentData.AgentAuxReason = item.AuxReasonDescription;
_agentData.AgentId = item.LoginId;
_agentData.AgentAcdState = item.WorkModeDirectionDescription;
_agentData.AgentTimeInState = DateTime.Now - item.DateTimeUpdated;
_agentData.TimeSubmitted = DateTime.Now;
agentlist.Add(Tuple.Create(_agentData.AgentName, _agentData.AgentExtension, _agentData.AgentTimeInState, _agentData.AgentId, _agentData.AgentAcdState, _agentData.AgentAuxReason));
if (_agentData.AgentAcdState == "AVAIL")
{
listOfTimeSpans.Add(_agentData.AgentTimeInState);
availInItems.Add(Tuple.Create(_agentData.AgentName, _agentData.AgentTimeInState));
}
availInItems.Sort((t1, t2) => t1.Item2.CompareTo(t2.Item2));
}
var availInAgents =
agentlist
.Where(ag => ag.Item5 == "AVAIL")
.ToList();
availInAgents.Sort((t1, t2) =>
t1.Item3.CompareTo(t2.Item3));
var max3 = availInAgents.Skip(availInAgents.Count - 3);
max3.Reverse();
_agents.AgentsOnBreak = 0;
foreach (var agent in agentlist)
{
if (!string.IsNullOrEmpty(agent.Item6) && agent.Item6.StartsWith("Break"))
{
_agents.AgentsOnBreak++;
}
}
_agents.AgentsOnLunch = 0;
foreach (var agent in agentlist)
{
//If the current agent's aux reason is Lunch
if (!string.IsNullOrEmpty(agent.Item6) && agent.Item6.StartsWith("Lunch"))
{
//add one to agentsonlunch
_agents.AgentsOnLunch++;
}
}
_agents.NextInLine = string.Empty;
foreach (var agent in max3.Reverse())
{
//assign agent to NextInLine and start a new line
_agents.NextInLine += agent.Item1 + Environment.NewLine;
//reverse NextInLine
_agents.NextInLine.Reverse();
}
_agents.TimeSubmitted = DateTime.Now;
#endregion
#region Gathering Skill Information
_skillData.OldestCall = e.CmsData.Skill.OldestCall;
_skillData.AgentsStaffed = e.CmsData.Skill.AgentsStaffed;
_skillData.AgentsAuxed = e.CmsData.Skill.AgentsInAux;
_skillData.AgentsAvailable = e.CmsData.Skill.AgentsAvailable;
_skillData.AgentsOnCalls = e.CmsData.Skill.AgentsOnAcdCall;
_skillData.CallsWaitingInQueue = e.CmsData.Skill.InQueueInRing;
_skillData.Asa = e.CmsData.Skill.AnswerTimePerAcdCall;
_skillData.TimeSubmitted = DateTime.Now;
_skillData.EstimatedHoldTimeLow = e.CmsData.Skill.ExpectedWaitTimeLow;
_skillData.EstimatedHoldTimeMedium = e.CmsData.Skill.ExpectedWaitTimeMedium;
_skillData.EstimatedHoldTimeHigh = e.CmsData.Skill.ExpectedWaitTimeHigh;
#endregion
}
}
catch (Exception ex)
{
_logger.Info(ex.Message, ex);
}
}
With tasks you can start many at the same time and wait for them all to finish like this:
var taskList = new List<Task>();
foreach (var thingToDo in work)
{
taskList.Add(thingToDo.StartTask());
}
Task.WaitAll(taskList.ToArray());
This way you can run everything in parallel and wont get after the last line until everything is done.
Edit following your comment
You can embed your work in a task with this:
public async Task DoWork()
{
var taskList = new List<Task>();
foreach (var thingToDo in work)
{
taskList.Add(thingToDo.StartTask());
}
await Task.WhenAll(taskList.ToArray());
}

Bloomberg web service call for single field and single instrument taking more than 1 min

I am making a Bloomberg web service GetData call for the "DEBT_TO_EQUITY_FUNDAMENTALS_TKR" field. I am setting secmaster = true and asking for a single instrument with a CUSIP identifier (with yellowkey = MarketSector.Corp).
This strikes me as a fairly lightweight call having seen people asking for thousands of instruments and dozens of fields at once.
I have played around with setting lots of different settings but I just can't get this request to return in a few seconds. It gives me the correct return value but it takes longer than 60 seconds.
Any idea if it is possible to get such a request to execute and return in a few seconds?
Thanks
EDIT - Here is the code I am running:
public string GetFundamentalTicker(string identifier, InstrumentType identifierType = InstrumentType.CUSIP)
{
PerSecurityWS ps = new PerSecurityWS();
try
{
log.DebugFormat("Cert path is: {0}", CertPath);
X509Certificate2 clientCert = new X509Certificate2(CertPath, "<password_redacted>");
ps.ClientCertificates.Add(clientCert);
}
catch (Exception e)
{
log.ErrorFormat("Error in cert setup - {0} - {1}", e.Message, e.InnerException == null ? "" : e.InnerException.Message);
return null;
}
//Set request header
GetDataHeaders getDataHeaders = new GetDataHeaders();
getDataHeaders.secmaster = true;
getDataHeaders.secmasterSpecified = true;
//getDataHeaders.fundamentals = true;
//getDataHeaders.fundamentalsSpecified = true;
//getDataHeaders.programflag = ProgramFlag.oneshot;//unnecessary - defaults to this anyway
//getDataHeaders.programflagSpecified = true;
//getDataHeaders.pricing = true;
getDataHeaders.secid = identifierType;
getDataHeaders.secidSpecified = true;
SubmitGetDataRequest sbmtGtDtreq = new SubmitGetDataRequest();
sbmtGtDtreq.headers = getDataHeaders;
sbmtGtDtreq.fields = new string[] {
"DEBT_TO_EQUITY_FUNDAMENTALS_TKR"
};
int currentFundYear = DateTime.Now.Year;
//var fundYears = new List<int>();
List<Instrument> fundYearInstruments = new List<Instrument>();
Instrument fundYearInstrument = null;
fundYearInstrument = new Instrument();
fundYearInstrument.id = identifier;
fundYearInstrument.typeSpecified = true;
fundYearInstrument.type = identifierType;
fundYearInstrument.yellowkey = MarketSector.Corp;
fundYearInstrument.yellowkeySpecified = true;
//fundYearInstrument.overrides = new Override[] {};//{ new Override() { field = "EQY_FUND_YEAR", value = currentFundYear.ToString() } };
fundYearInstruments.Add(fundYearInstrument);
//fundYears.Add(-1);
Instrument[] instr = fundYearInstruments.ToArray();
Instruments instrs = new Instruments();
instrs.instrument = instr;
sbmtGtDtreq.instruments = instrs;
try
{
SubmitGetDataResponse sbmtGtDtResp = ps.submitGetDataRequest(sbmtGtDtreq);
RetrieveGetDataRequest rtrvGtDrReq = new RetrieveGetDataRequest();
rtrvGtDrReq.responseId = sbmtGtDtResp.responseId;
RetrieveGetDataResponse rtrvGtDrResp;
do
{
System.Threading.Thread.Sleep(POLL_INTERVAL);
rtrvGtDrResp = ps.retrieveGetDataResponse(rtrvGtDrReq);
}
while (rtrvGtDrResp.statusCode.code == DATA_NOT_AVAILABLE);
if (rtrvGtDrResp.statusCode.code == SUCCESS)
{
for (int i = 0; i < rtrvGtDrResp.instrumentDatas.Length; i++)
{
for (int j = 0; j < rtrvGtDrResp.instrumentDatas[i].data.Length; j++)
{
if (rtrvGtDrResp.instrumentDatas[i].data[j].value == "N.A." || rtrvGtDrResp.instrumentDatas[i].data[j].value == "N.S." || rtrvGtDrResp.instrumentDatas[i].data[j].value == "N.D.")
rtrvGtDrResp.instrumentDatas[i].data[j].value = null;
return rtrvGtDrResp.instrumentDatas[i].data[j].value;
}
}
return null;
}
else if (rtrvGtDrResp.statusCode.code == REQUEST_ERROR)
{
log.ErrorFormat("Error in the submitted request: {0}", rtrvGtDrResp.statusCode.description);
return null;
}
}
catch (Exception e)
{
log.ErrorFormat("Error in GetData - {0} - {1}", e.Message, e.InnerException == null ? "" : e.InnerException.Message);
return null;
}
return null;
}
Poll interval is 5 seconds and the SOAP web service url is:
https://software.bloomberg.com/datalicensewp/dlws.wsdl
I am having the same issue. I found out that there is a difference between making the same call to Bloomberg API from, for example, console app (works very fast) and web service (takes a lot of time to start session). And the difference is that console app runs under the same user as bbcomm process, whereas web service (or actually iis process) runs under System account. You can try to log out all users on the PC where web service is hosted and then try to make the call. In this case, I guess, bbcomm goes under System account as no one else is logged in and works fast. It worked for me and the call was answered instantly.

using C# to get an ec2-instance tag

I'm not a developer so maybe the answer is out there for a different solution but I can't really translate it from python or something else.
I'm trying to use the AWS .NET SDK to find an instance and then get the instance's tags. I've gotten as far as being able to determine if an instance is up and running or not. I also see how I can create and delete tags (not in code example below). But I don't see an easy way to actually check if a tag exists and get the value of the tag if it does exist.
Sorry if I'm missing the obvious but this is all new to me. Here's an example of the code I'm using to check if an instance is running.
instanceID = "i-myInstanceID";
do {
var myrequest = new DescribeInstanceStatusRequest();
DescribeInstanceStatusResponse myresponse = ec2.DescribeInstanceStatus(myrequest);
int isCount = myresponse.DescribeInstanceStatusResult.InstanceStatuses.Count;
for (int isc=0; isc < isCount; isc++) {
InstanceStatus instanceStatus = myresponse.DescribeInstanceStatusResult.InstanceStatuses[isc];
if (instanceStatus.InstanceId.Contains(instanceID)) {
Console.WriteLine("It looks like instance "+instanceID+" is running.");
idIdx = isc;
foundID = true;
break;
}
}
if ((foundID==false) && (secondCounter==1)) {
Console.Write("Looking for instance "+instanceID);
} else {
Console.Write(".");
}
Thread.Sleep(1000);
secondCounter++;
if (secondCounter > 5) {
break;
}
} while (foundID == false) ;
First send a DescribeInstancesRequest to get the list of Instances:
public DescribeInstancesResult GetInstances(Ec2Key ec2Key)
{
_logger.Debug("GetInstances Start.");
AmazonEC2 ec2 = CreateAmazonEc2Client(ec2Key);
var ec2Request = new DescribeInstancesRequest();
DescribeInstancesResponse describeInstancesResponse = ec2.DescribeInstances(ec2Request);
DescribeInstancesResult result = describeInstancesResponse.DescribeInstancesResult;
_logger.Debug("GetInstances End.");
return result;
}
Then loop through the instances until you find the one you want, and then use the Tag.GetTagValueByKey method:
// This just calls the above code
DescribeInstancesResult ec2Instances = _ec2ResourceAccess.GetInstances(ec2Key);
var returnInstances = new List<Ec2UtilityInstance>();
foreach (var reservation in ec2Instances.Reservation)
{
foreach (var runningInstance in reservation.RunningInstance)
{
var returnInstance = new Ec2UtilityInstance();
returnInstance.InstanceId = runningInstance.InstanceId;
returnInstance.InstanceName = runningInstance.Tag.GetTagValueByKey("Name");
returnInstance.Status = (Ec2UtilityInstanceStatus)Enum.Parse(typeof(Ec2UtilityInstanceStatus), runningInstance.InstanceState.Name, true);
returnInstance.DefaultIp = runningInstance.Tag.GetTagValueByKey("DefaultIp");
returnInstance.InstanceType = runningInstance.InstanceType;
returnInstance.ImageId = runningInstance.ImageId;
returnInstances.Add(returnInstance);
}
}
Here is the link for full source that this was taken from:
https://github.com/escherrer/EC2Utilities
Common\Manager
and
Common\ResourceAccess

Categories

Resources