How can I find the full name of a calling method in C#? I have seen solutions:
How I can get the calling methods in C#
How can I find the method that called the current method?
Get Calling function name from Called function
But they only give me the top level. Consider the example:
namespace Sandbox
{
class Program
{
static void Main(string[] args)
{
test();
}
static void test()
{
var stackTrace = new StackTrace();
var methodBase = stackTrace.GetFrame(1).GetMethod();
Console.WriteLine(methodBase.Name);
}
}
}
This simply outputs 'Main'. How can I get it to print 'Sandbox.Program.Main'?
It's for a simple logging framework that I am working on.
Adding onto Matzi's Answer:
Here is the solution:
namespace Sandbox
{
class Program
{
static void Main(string[] args)
{
test();
}
static void test()
{
var stackTrace = new StackTrace();
var methodBase = stackTrace.GetFrame(1).GetMethod();
var Class = methodBase.ReflectedType;
var Namespace = Class.Namespace; // Added finding the namespace
Console.WriteLine(Namespace + "." + Class.Name + "." + methodBase.Name);
}
}
}
It produces 'Sandbox.Program.Main' like it should.
This is something like here.
MethodBase method = stackTrace.GetFrame(1).GetMethod();
string methodName = method.Name;
string className = method.ReflectedType.Name;
Console.WriteLine(className + "." + methodName);
I think the best way to get the full name is:
this.GetType().FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;
Or try this:
string method = string.Format("{0}.{1}", MethodBase.GetCurrentMethod().DeclaringType.FullName, MethodBase.GetCurrentMethod().Name);
And if you want to display the most recent function call, you can use:
StackTrace st = new StackTrace();
StackFrame sf = st.GetFrame(0);
var methodName = sf.GetMethod();
But if you want to display the tree of calling functions, you can do it like this:
if (st.FrameCount >1)
{
// Display the highest-level function call
// in the trace.
StackFrame sf = st.GetFrame(st.FrameCount-1);
Console.WriteLine(" Original function call at top of call stack):");
Console.WriteLine(" {0}", sf.GetMethod());
}
For more information.
With this method you can reliably get the full name
public void HandleException(Exception ex, [CallerMemberName] string caller = "")
{
if (ex != null)
{
while (ex.InnerException != null)
ex = ex.InnerException;
foreach (var method in new StackTrace().GetFrames())
{
if (method.GetMethod().Name == caller)
{
caller = $"{method.GetMethod().ReflectedType.Name}.{caller}";
break;
}
}
Console.WriteLine($"Exception: {ex.Message} Caller: {caller}()");
}
}
In the System.Reflection.MethodBase method GetCurrentMethod, you can find full information about the call stack using classes, etc.
The current calling namespace which is not equal as the current namespace:
var mNamespace = new StackTrace().GetFrames()?.Select(x =>
{
try
{
return x.GetMethod().ReflectedType?.Namespace;
}
catch (Exception)
{
return string.Empty;
}
}).First(x => x != new StackTrace().GetFrame(0).GetMethod().ReflectedType?.Namespace);
Related
static void Main(string[] args)
{
token objtoken = new token();
var location = AddLocations();
OutPutResults outPutResultsApi = new OutPutResults();
GCPcall gCPcall = new GCPcall();
OutPutResults finaloutPutResultsApi = new OutPutResults();
var addressdt = new AddressDataDetails();
finaloutPutResultsApi.addressDatas = new List<AddressDataDetails>();
Console.WriteLine("Hello World!");
List<string> placeId = new List<string>();
var baseUrl = "https://maps.googleapis.com/maps/api/place/textsearch/json?";
var apiKey = "&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".Trim();
foreach (var itemlocations in location)
{
var searchtext = "query=" + itemlocations.Trim();
var finalUrl = baseUrl + searchtext + apiKey;
gCPcall.RecursiveApiCall(finalUrl, ref placeId, objtoken.NextToken);
}
var ids = gCPcall.myPalceid;
}
public List<string> RecursiveApiCall(string finalUrl, ref List<string> placeId, string nextToken = null)
{
try
{
var token = "&pagetoken=" + nextToken;
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(finalUrl + token);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsStringAsync();
readTask.Wait();
var students = readTask.Result;
Rootobject studentsmodel = JsonConvert.DeserializeObject<Rootobject>(students);
nextToken = studentsmodel.next_page_token;
foreach (var item in studentsmodel.results)
{
placeId.Add(item.place_id);
}
}
}
if (nextToken != null)
{
RecursiveApiCall(finalUrl, ref placeId, nextToken);
}
return placeId;
}
catch (Exception ex)
{
throw;
}
}
My recursive method has some issue. Here whenever I am debugging this code it work fine. It goes in recursive call twice.
As debugging result I am getting list place_id with 20 items in first call and next call 9 items total 29 items in place_id object which is correct in static main method.
But if I run without debugging mode I am getting only 20 place_id. next recursive iteration data is missing even if it has next valid token.
I don't have any clue why this is happening. Can someone tell me what is the issue with my code?
Here are my suggestions, which may or may not solve the problem:
// First of all, let's fix the signature : Go async _all the way_
//public List<string> RecursiveApiCall(string finalUrl, ref List<string> placeId, string nextToken = null)
// also reuse the HttpClient!
public async Task ApiCallAsync(HttpClient client, string finalUrl, List<string> placeId, string nextToken = null)
{
// Loop, don't recurse
while(!(nextToken is null)) // C# 9: while(nextToken is not null)
{
try
{
var token = "&pagetoken=" + nextToken;
// again async all the way
var result = await client.GetAsync(finalUrl+token);
if (result.IsSuccessStatusCode)
{
// async all the way!
var students = await result.Content.ReadAsStringAsync();
Rootobject studentsmodel = JsonConvert.DeserializeObject<Rootobject>(students);
nextToken = studentsmodel.next_page_token;
foreach (var item in studentsmodel.results)
{
// Will be reflected in main, so no need to return or `ref` keyword
placeId.Add(item.place_id);
}
}
// NO recursion needed!
// if (nextToken != null)
// {
// RecursiveApiCall(finalUrl, ref placeId, nextToken);
// }
}
catch (Exception ex)
{
// rethrow, only is somewhat useless
// I'd suggest using a logging framework and
// log.Error(ex, "Some useful message");
throw;
// OR remove try/catch here all together and wrap the call to this method
// in try / catch with logging.
}
}
Mind that you'll need to make your main :
async Task Main(string[] args)
and call this as
await ApiCallAsync(client, finalUrl, placeId, nextToken);
Also create an HttpClient in main and reuse that:
"HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors." - Remarks
... which shouldn't do much harm here, but it's a "best practice" anyhow.
Now, as to why you get only 20 instead of expected 29 items, I cannot say if this resolves that issue. I'd highly recommend to introduce a Logging Framework and make log entries accordingly, so you may find the culprid.
I'm having hard time figuring out what the problem is. I'm trying to make sort of process monitor which loads processes list, ID, username of owner,memory usage and description.. and this error is giving me really big headache.
private void Button1_Click(object sender, EventArgs e)
{
Process[] procList = Process.GetProcesses();
foreach (Process process in procList)
{
// get status
string status = (process.Responding == true ? "Responding" : "Not responding");
// get username and description
string query = "SELECT * FROM Win32_Process WHERE ProcessID = " + process.Id;
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection processList = searcher.Get();
dynamic response = new ExpandoObject();
response.Description = "";
response.Username = "Unknown";
foreach (ManagementObject obj in processList)
{
// get username
string[] argList = new string[] { string.Empty, string.Empty };
int returnValue = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnValue == 0)
response.Username = argList[0];
if (obj["ExecutablePath"] != null)
{
try
{
FileVersionInfo info = FileVersionInfo.GetVersionInfo(obj["ExecutablePath"].ToString());
response.Description = info.FileDescription;
}
catch { }
}
}
// get memory usage
int memsize = 0; // memsize in Megabyte
PerformanceCounter PC = new PerformanceCounter();
PC.CategoryName = "Process";
PC.CounterName = "Working Set - Private";
PC.InstanceName = process.ProcessName;
memsize = Convert.ToInt32(PC.NextValue()) / (int)(1024);
memsize = (memsize / 1024);
PC.Close();
PC.Dispose();
ListViewItem item = new ListViewItem();
item.Text = process.Id.ToString();
item.SubItems.Add(process.ProcessName);
item.SubItems.Add(status);
item.SubItems.Add(response.Username);
item.SubItems.Add(memsize.ToString() + " MB");
item.SubItems.Add(response.Description);
listView1.Items.Add(item);
}
}
When i try debugging the program, it outputs few of them without any problem, (see here -> https://i.imgur.com/D4ftBgb.png) and then error shows up -> https://i.imgur.com/m1R90hz.png
Because you use dynamic, method overload resolution is delayed until runtime. You have a null response.Username or response.Description, so the dynamic runtime doesn't know which overload to call. Compare:
public class Test
{
public static void Main()
{
dynamic bar = null;
try
{
Foo(bar);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void Foo(string f) { }
private static void Foo(int? o) { }
}
This throws the same exception, because both overloads can accept a null, and there is no further type information present.
To resolve this, either specify the overload explicitly by casting to string:
Foo((string)bar);
Or in your case, SubItems.Add((string)response.Username).
Or simply don't use dynamic to stuff your variables in, but keep them both declared as separate variables: string description = "", username = "".
The type of both your response.Username and response.Description is dynamic. The ListViewSubItemCollection.Add() can't decide which overload to use, therefore, you need to convert them to string.
Try the following:
string username = Convert.ToString(response.Username);
string description = Convert.ToString(response.Description);
ListViewItem item = new ListViewItem();
item.Text = process.Id.ToString();
item.SubItems.Add(process.ProcessName);
item.SubItems.Add(status);
item.SubItems.Add(username);
item.SubItems.Add(memsize.ToString() + " MB");
item.SubItems.Add(description);
listView1.Items.Add(item);
The best long term solution is to remove your use of dynamic and use an explicit class with Description and Username properties.
The most direct fix is to change:
response.Description = info.FileDescription;
to:
response.Description = info.FileDescription ?? "";
Why is that necessary (the ?? "")? It will allows the overload resolution to work correctly since Description will never be null. The reason why it doesn't work when null is that a null property of an ExpandoObject has no type associated with it. This is different to a normal class whereby the compiler knows that the type of the property is string.
I've implemented a custom exception filter based on this blog post https://blog.kloud.com.au/2016/03/23/aspnet-core-tips-and-tricks-global-exception-handling/ .
I now noticed that I cannot get the file name or the line number where the exception occurred from StackFrame; the value for method GetFileLineNumber is always 0.
A quick example of what I tried:
StackTrace trace = new StackTrace(exception, true);
StackFrame[] stackFrames = trace.GetFrames();
if (stackFrames != null) {
foreach (StackFrame traceFrame in stackFrames) {
sourceFile = traceFrame.GetFileName();
sourceLineNumber = traceFrame.GetFileLineNumber();
}
}
Just to mention: GetFileName returns also null, but I can get the namespace and class name from various locations, so it's not a problem.
Noticed somewhere that I should have the PDB file on the folder for this to work and I checked that I already have it. And even if it worked, this should work in production environment as well.
Tried also using Caller Information (https://msdn.microsoft.com/en-us/library/mt653988.aspx) but there's a bit different problem. The exception filter has to implement IExceptionFilter interface and use it's method. Therefore I cannot add the attributes even if they are optional.
Here's the code part from Startup.cs:
public void ConfigureServices(IServiceCollection services) {
...
services.AddMvc(config => {
config.Filters.Add(new ApplicationExceptionFilter());
});
}
And here's the custom Filter
public class ApplicationExceptionFilter : IExceptionFilter {
public void OnException(ExceptionContext context) {
ApplicationExceptionHandler.handleException(context.HttpContext, context.Exception);
}
}
And what I tried for getting the Caller Information was:
public void OnException(ExceptionContext context,
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) {
But that will not work since it doesn't implement the interface anymore.
How can I get the line number while still handling the exceptions in a single place?
Assumed that my problem is the fact that I'm not getting line number from stack trace and reproduced the problem with:
public ActionResult ServerError() {
Int32 x = 0;
try {
return Content((1 / x).ToString());
} catch (Exception ex) {
StackTrace trace = new StackTrace(ex, true);
StackFrame[] stackFrames = trace.GetFrames();
if (stackFrames != null) {
foreach (StackFrame traceFrame in stackFrames) {
String sourceFile = traceFrame.GetFileName();
int sourceLineNumber = traceFrame.GetFileLineNumber();
}
}
}
return Content("");
}
Still not getting values for sourceFile (it's null) or sourceLineNumber (it's 0).
Take a look at the DeveloperErrorPageMiddleware implementation:
private IEnumerable<ErrorDetails> GetErrorDetails(Exception ex)
{
for (var scan = ex; scan != null; scan = scan.InnerException)
{
var stackTrace = ex.StackTrace;
yield return new ErrorDetails
{
Error = scan,
StackFrames = StackTraceHelper.GetFrames(ex)
.Select(frame => GetStackFrame(frame.MethodDisplayInfo.ToString(), frame.FilePath, frame.LineNumber))
};
};
}
// make it internal to enable unit testing
internal StackFrame GetStackFrame(string method, string filePath, int lineNumber)
{
var stackFrame = new StackFrame
{
Function = method,
File = filePath,
Line = lineNumber
};
if (string.IsNullOrEmpty(stackFrame.File))
{
return stackFrame;
}
IEnumerable<string> lines = null;
if (File.Exists(stackFrame.File))
{
lines = File.ReadLines(stackFrame.File);
}
else
{
// Handle relative paths and embedded files
var fileInfo = _fileProvider.GetFileInfo(stackFrame.File);
if (fileInfo.Exists)
{
// ReadLines doesn't accept a stream. Use ReadLines as its more efficient
// relative to reading lines via stream reader
if (!string.IsNullOrEmpty(fileInfo.PhysicalPath))
{
lines = File.ReadLines(fileInfo.PhysicalPath);
}
else
{
lines = ReadLines(fileInfo);
}
}
}
if (lines != null)
{
ReadFrameContent(stackFrame, lines, stackFrame.Line, stackFrame.Line);
}
return stackFrame;
}
Help is very welcome and extremely appreciated, thank you. What this program is, is a ProxyChecker, because i've bought a bunch and will continue to do so of proxies with different user/passes etc, however some have expired. I added a break point and what it's doing is actually skipping the ProxyClient code and going straight to for each var item in 1, if item accepts connection etc, it then just returns false and finishes.
private static void CheckProxy(object state)
{
var u = user[0];
var p = pass[0];
var l = new List<MyIP>();
Parallel.ForEach(l.ToArray(), (ip_item) =>
{
try
{
string ip = ip_item.IP;
using (var client = new ProxyClient(ip, u, p))
{
Console.WriteLine(ip, user, pass);
client.Connect();
ip_item.AcceptsConnection = client.IsConnected;
}
}
catch
{
l.Remove(ip_item);
}
});
foreach (var item in l)
{
if (item.AcceptsConnection == true)
{
WriteToFile(user[0], pass[0]);
}
Console.WriteLine(item.IP + " is " + (item.AcceptsConnection) + " accepts connections" + " doesn not accept connections");
}
}
Load ips function:#
private static void loadips()
{
using (TextReader tr = new StreamReader("ips.txt"))
{
var l = new List<MyIP>();
string line = null;
while ((line = tr.ReadLine()) != null)
{
l.Add(new MyIP { IP = line });
}
}
}
I have added this in response to the answer. I believe this is a variable issue as the variable is locally declared not publicly any ideas how to fix? i'm unable to find a way to get this working seems like i'm being dumb. thanks.
Your code "skips" the Parallel.ForEach because the local variable l is an empty list (you just created it with new List<MyIP>().
Your loadips() method only fills a list referenced only by (another) local variable l, which is out of scope when the using block is left.
There are several ways to solve that. If both methods are in the same class, you could declare a member variable in that class of type List<MyIP> and fill that list in your loadips().
My suggestion would be to change loadips() so that it returns the list of read ip addresses and call this from CheckProxy():
private static List<MyIP> loadips() // changed return type to List<MyIP>
{
using (TextReader tr = new StreamReader("ips.txt"))
{
var l = new List<MyIP>();
string line = null;
while ((line = tr.ReadLine()) != null)
{
l.Add(new MyIP { IP = line });
}
return l; // return the list!
}
}
and the CheckProxy:
private static void CheckProxy(object state)
{
var u = user[0];
var p = pass[0];
var l = loadips(); // load IPs here
Parallel.ForEach(l.ToArray(), (ip_item) =>
{
...
I have a method with parameters and i want to call it in my global application class, but when i pass null arguments it will give an error. I'm sharing my code please guide me.
Method:
public static async Task getMessage(Controller page,string Email, int? PersonId, int? OrderDetailId, int? TicketDetailId)
{
using (var client = new ImapClient("imap.gmail.com", true))
{
// Connecting
if (client.Connect())
{
// Sign in
if (client.Login("abc#gmail.com", "*****"))
{
var excludeLabels = new string[] { "Processed" };
var senders = new string[] { Email };
// Building the search query
var query = string.Format("X-GM-RAW \"{0} -({1})\"",
string.Join(" OR ", senders.Select(sender => "(from:" + sender + ")")),
string.Join(" OR ", excludeLabels.Select(label => "(label:" + label + ")")));
var messages = client.Folders.Inbox.Search(query, MessageFetchMode.ClientDefault, 1000);
foreach (var msg in messages)
{
// Mark the message as seen
msg.Seen = true;
string plainTextBody = msg.Body.HasText ? msg.Body.Text : "";
string htmlBody = msg.Body.HasHtml ? msg.Body.Html : "";
var time = DateTime.SpecifyKind(msg.Date.HasValue ? msg.Date.Value : DateTime.Now, DateTimeKind.Utc);
if (msg.Attachments.Count() > 0)
{
foreach (var file in msg.Attachments)
{
var folder = Server.MapPath("~/Data/ConversationAttachments");
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
string guid = Guid.NewGuid().ToString();
string webPath = null;
msg.Download(MessageFetchMode.Full);
int posOfDot = file.FileName.LastIndexOf(".");
string fName = Guid.NewGuid().ToString() + file.FileName.Substring(posOfDot);
webPath = "~/Data/ConversationAttachments/" + fName;
file.Save(Server.MapPath("~/Data/ConversationAttachments"), fName);
db.MailSystems.AddOrUpdate(c => c.MESSAGEID, new MailSystem
{
Message = htmlBody,
Date = time,
Attachment = webPath,
EmailType = "IMAP",
Subject = string.IsNullOrEmpty(msg.Subject) ? "RE: Ticket ID " + TicketDetailId.Value.ToString() : msg.Subject,
Sender = Email,
PersonID = PersonId.Value,
TicketDetailId = TicketDetailId.Value,
MESSAGEID = msg.MessageId
});
}
}
await db.SaveChangesAsync();
}
}
}
}
}
Global Application Class:
protected void ThreadFunc()
{
System.Timers.Timer t = new System.Timers.Timer();
t.Elapsed += new System.Timers.ElapsedEventHandler(TimerWorker);
t.Interval = 10000;
t.Enabled = true;
t.AutoReset = true;
t.Start();
}
protected void TimerWorker(object sender, System.Timers.ElapsedEventArgs e)
{
GetMail.getMessage();
}
getMessage method is a static member of static GetMail class. Please guide me how i can solve this issue. I want to start the getMessage method automatically after every 30 seconds.
GetMail.getMessage();
Doesnt work, because there aren't a method with these signature. the parameters can be null (int**?**), but they aren't optional..
Change to:
GetMail.getMessage(null, null, null, null, null);
Or create a new method with no parameters... and then call your method
public static void getMessage()
{
GetMessage(null, null, null, null, null);
}
Or set default values
public static void getMessage(Person page = null, string Email ="", int? PersonId =0, int? OrderDetailId=0, int? TicketDetailId=0) { ... }
It will work... but.. it is a creepy code :-)
Your problem is that your code attempts to call other methods and properties on the nullable types that are passed into your method.
I.e.
PersonId.Value
TicketDetailId.Value
If you want to have nullable arguments in your method signature and be able to pass null values for those arguments, you need to fix your code so it works with those arguments being null.
In short, there's no obvious answer here, you can't pass in null arguments and expect to be able to use their properties and methods without encountering an exception.
Your usual solutions are, pass in types that can't be null:
public void MyMethod(int myValue)
Or do a null check before:
if (myThing == null)
{
return;
}
Or:
if (myThing != null)
{
// Do stuff.
}
Or apply the '??' operator to apply a default value if null:
string myResult = myThing ?? string.Empty;
EDIT
Your other problem is that you're calling the getMessage() method with no parameters. The signature of getMessage looks like:
getMessage(Controller page, string email, int? personId, int? orderDetailId, int? ticketDetailId)
(Note I've lowercased the first letter of your argument names, as per typical C# standards)
You should be passing some parameters when calling it:
getMessage("myPage", "test#hotmail.com", 2, 37, 92);
You need to understand a lot more about the very basics of C# and Object Oriented programming in general. Try reading some introductory books on C# development.