I use the following code to get metadata with Microsoft.Web.Administration.ServerManager:
var manager = new ServerManager();
var site = manager.Sites["siteName"];
var metadata = site.GetMetadata("metaKey");
Now, if "metaKey" is not there, the GetMetadata will throw a System.Runtime.InteropServices.COMException exception with the message "The request is not supported". If I first set a metadata value like this
site.SetMetadata("metaKey", "hello")
the GetMetadata("metaKey") won't throw an error but instead kindly return the value "hello". How do I check if a key exists before I attempt to retreieve it? I want to avoid a try catch if it's possible.
I've tried to dig it a little in disassembling tool. Internally it uses Microsoft.Web.Administration.Interop assembly over native API (as you've already guessed, that is why there is a COMException). Methods to work with metadata are described in IAppHostElement interface.
From it's GetMetadata method's description:
Return value:
S_OK: Indicates that the operation was successful.
ERROR_NOT_SUPPORTED: Indicates that the requested metadata is not recognized.
As you can see, there is no method to check if metadata exists, and interop throws exception in any case when returning result is not S_OK. So unfortunately I guess there is no way to know about the existence of specific metadata without calling GetMatadata method and catching exceptions.
Related
I'm using some external library though COM interface.
I have generic class for that.
Database.Connector connector = new Database.Connector();
string connectString = "srvr=nonexisthost;database=test;"; // bogus connect string
try
{
var database = connector.Connect(connectString);
}
catch (COMException ex)
{
Console.WriteLine(ex.Message);
}
Trying to build a error proof logic I intentionally provoke an exception.
And I have discovered that C# COMException contains only generic info, like:
Error HRESULT E_FAIL has been returned from a call to a COM component.
while executing samey code in PowerShell results in more detailed description:
$connector = New-Object -ComObject Database.Connector
$connectString = "srvr=nonexisthost;database=test;"
$database = $connector.Connect($connectString)
Error while executing transaction with an information base
server_addr=nonexisthost descr=11001(0x00002AF9): Host is unknown.
line=1048 file=src\DataExchangeCommon.cpp
My question is: what should I do in order to get same error info in C# (if it is possible)?
I'm not a COM Interop expert but I'll try to answer what I know and hope it will help you.
From the managed side
If the HRESULT is recognized by the runtime (CLR), the runtime automatically creates a specific managed exception for the error (e.g. FileNotFoundException). Otherwise, the runtime creates a generic COMException object which says "I don't know what this HRESULT meaning".
If the unmanaged code provides error info, you will see it in the ErrorCode property, otherwise, you will see just the HRESULT code.
You can try to search for this code (google\github) to obtain more info.
From the unmanaged side
You need to implement ISupportErrorInfo and IErrorInfo interfaces to provide more info.
So to answer your question, in C#, you can't get more info in the COMException object if this info is not provided.
For more info:
COMException, Handling COM Interop Exceptions, IErrorInfo, ISupportErrorInfo, HRESULT's mapping, Common HRESULT values
I am working on a application which needs to communicate via COM interface with multiple CAD applications (not in the same time). I want to have nice and reusable code, but I came across problems with type casting of COM objects when I made generic application handle getter method.
What I tried so far:
This is the attempt I would like the most if it worked.
public static TCadAppType CadApp<TCadAppType>()
{
dynamic cadApp = default(TCadAppType);
//Here under Dynamic View/Message there is already an error
// Message = "Element not found. (Exception from HRESULT: 0x8002802B (TYPE_E_ELEMENTNOTFOUND))"
// cadVersion.Value evaluates to "SldWorks.Application"
cadApp = (TCadAppType)Marshal.GetActiveObject(cadVersion.Value);
//Following 2 lines of code are for testing purposes only, i am testing with Solidworks API
AssemblyDoc Assembly;
//The exception is thrown when I try to access some method from the Solidworks API
Assembly = (AssemblyDoc)cadApp.OpenDoc6("some parametras...");
}
Attempt using Convert class
// Another attempt using Convert class
public static TCadAppType CadApp<TCadAppType>()
{
dynamic cadApp = default(TCadAppType);
// cadVersion.Value evaluates to "SldWorks.Application"
cadApp = Marshal.GetActiveObject(cadVersion.Value);
cadApp = Convert.ChangeType(cadApp, typeof(SldWorks.SldWorks));
// Exception is thrown with the following message:
// Message = "Object must implement IConvertible."
}
I really thought that I am on the right track, since there is an article on Microsoft Docs website explaining how dynamic can help you with com interopt: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic#com-interop
Any ideas how I can do this runtime casting a keep my code as reusable as possible?
My software setup:
Win 10
Project is targeted for .NET 4.7.2
First Tests are with Solidworks 2019
Turns out that the my coding attempt 1 was valid c# code indeed.
I tried it using with Autodesk Inventor, and it works.
So the only thing left for me is to conclude that this is some bug from Solidworks and their COM interfacing.
Thank you Optional Option for your interest in the topic.
I am using ILMerge and Quartz.NET in a C# .NET 4.0 Windows Service application. The app runs fine without using ILMerge, but now that we're nearing shipping release, I wanted to combine all DLLs into a single executable.
Problem is, that ILMerge seems to work fine, but when I run the combined executable, it throws this exception:
Unhandled Exception: Quartz.SchedulerException: ThreadPool type 'Quartz.Simpl.SimpleThreadPool' could not be instantiated. ---> System.InvalidCastException: Unable to cast object of type 'Quartz.Simpl.SimpleThreadPool' to type 'Quartz.Spi.IThreadPool'.
at Quartz.Util.ObjectUtils.InstantiateType[T](Type type) in :line 0
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
--- End of inner exception stack trace ---
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
at Quartz.Impl.StdSchedulerFactory.GetScheduler() in :line 0
Does anyone have any idea why this is? I have been wasting over 4 hours already and I can't figure it out. If I don't combine with ILMerge, then everything runs fine (with the Quartz.dll and Common.Logging.dll in the same directory).
I'm sure someone must have tried packaging Quartz.net up like this before, any ideas?
Disclaimer: I don't know Quartz.NET at all, although I spent some time struggling with ILMerge. When I finally understood its limitations... I stopped using it.
ILMerge'd application tends to have problems with everything which contains the word "reflection".
I can guess (I've never used Quartz.NET) that some classes are resolved using reflection and driven by configuration files.
Class is not only identified by its name (with namespace) but also by assembly it is coming from (unfortunatelly it doesn't get displayed in exception message).
So, let's assume you had (before ILMerging) two assemblies A (for you Application) and Q (for Quartz.NET).
Assembly 'A' was referencing assembly 'Q' and was using a class 'Q:QClass' which was implementing 'Q:QIntf'.
After merging, those classes became 'A:QClass' and 'A:QIntf' (they were moved from assembly Q to A) and all the references in code has been replaced to use those (completely) new classes/interfaces, so "A:QClass" is implementing "A:QIntf" now.
But, it did not change any config files/embedded strings which may still reference "Q:QClass".
So when application is reading those not-updated config files it still loads "Q:QClass" (why it CAN find it is a different question, maybe you left assembly 'Q' in current folder or maybe it is in GAC - see 1).
Anyway, "Q:QClass" DOES NOT implement "A:QIntf", it still implements "Q:QIntf" even if they are binary identical - so you can't cast 'Q:QClass' to 'A:QIntf'.
The not-ideal-but-working solution is to "embed" assemblies instead of "merging" them. I wrote a open-source tool which does it (embedding instead of merging) but it is not related to this question. So if you decide to embed just ask me.
You can test it by removing (hiding, whatever works for you) every single instance of Q.dll on your PC. If I'm right, the exception should say now 'FileNotFound'.
You could try creating your own ISchedulerFactory and avoid using reflection to load all of your types.
The StdSchedulerFactory uses this code to creat a threadpool. It's where your error is happening and would be the place to start looking at making changes:
Type tpType = loadHelper.LoadType(cfg.GetStringProperty(PropertyThreadPoolType)) ?? typeof(SimpleThreadPool);
try
{
tp = ObjectUtils.InstantiateType<IThreadPool>(tpType);
}
catch (Exception e)
{
initException = new SchedulerException("ThreadPool type '{0}' could not be instantiated.".FormatInvariant(tpType), e);
throw initException;
}
The ObjectUtils.InstantiateType method that is called is this one, and the last line is the one throwing your exception:
public static T InstantiateType<T>(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type", "Cannot instantiate null");
}
ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
if (ci == null)
{
throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
}
return (T) ci.Invoke(new object[0]);
}
Right after this section in the factory, datasources are loaded using the same pattern and then the jobs themselves are also loaded dynamically which means you'd also have to write your own JobFactory. Since Quartz.Net loads a bunch of bits and pieces dynamically at runtime going down this road means you might end up rewriting a fair amount of things.
I have an XML file that is validated against an XSD file.
When a validation exception occurs, the handler is called:
ValidationCallBack(object sender, System.Xml.Schema.ValidationEventArgs args)
I have the exception thrown by the framework, but I need to customize this exception in order to have the XSD line that have thrown this error and also the value from the XML that was not correct.
Can I parse the exception's message in order to extract this kind of information? I mean, can I rely on a regular expression for this?
You can get this information from Exception member.
static void ValidationCallback(object sender, ValidationEventArgs args)
{
// Not sure if the exception is guaranteed to not be null.
if (args.Exception != null)
{
// Remember to always retain the InnerException (last argument is args.Exception).
throw new MyException(args.Exception.LineNumber, args.Exception.LinePosition, args.Exception.Message, args.Exception);
}
// If the exception is null do what we can.
throw new MyException(-1, -1, args.Message, args.Exception);
}
EDIT: Just noticed that you wanted the original element/thing that caused the problem. Firstly, you can get the schema entity that caused the validation problem from SchemaObject. Getting the original item that caused the problem is way more difficult. You are probably going to have to re-read the document (with a non-validating reader) and search for the node with the matching line/position.
Side note: Please don't use regex, ever. You will get really nasty problems if your application is run on another locale: as all the built-in .Net exceptions have translations (so your regex won't work).
You can find the details of the validation exception by looking at the public properties of the XmlSchemaException class (the instances of which are available via the Exception property of the args argument):
LineNumber
LinePosition
SourceSchemaObject
I have found the solution, by using the xmlns:ex="anySchemaNamespace" attribute for the xsd document element. Then, I can add for example this unhandled attribute ex:exception="My custom exception" to any element and then take that custom exception message from the element in code, when an exception occurs.
Thanks for your answers.
I made an application that search for some files in some directories. When a directory isn't there it throws the DirectoryNotFoundException. I catch that exception but it doesn't have a DirectoryName property or something like that like the FileNotFoundException (FileName). How can I find the Directory Name from the exception properties?
There's no way to natively do this.
Add this class somewhere to your project:
public static class DirectoryNotFoundExceptionExtentions
{
public static string GetPath(this DirectoryNotFoundException dnfe)
{
System.Text.RegularExpressions.Regex pathMatcher = new System.Text.RegularExpressions.Regex(#"[^']+");
return pathMatcher.Matches(dnfe.Message)[1].Value;
}
}
Catch the exception and use the type extension like this:
catch (DirectoryNotFoundException dnfe)
{
Console.WriteLine(dnfe.GetPath());
}
It looks like a hack, but you can extract the path from the Message property. As for me, I would prefer to check if the directory exists first, by using the Directory.Exists method.
catch (DirectoryNotFoundException e)
{
// Result will be: Could not find a part of the path "C:\incorrect\path".
Console.WriteLine(e.Message);
// Result will be: C:\incorrect\path
Console.WriteLine(e.Message
.Replace("Could not find a part of the path \"", "")
.Replace("\".", ""));
}
It is a little inconsistent that FileNotFoundException has the file name, but DirectoryNotFoundException doesn't have the directory name, isn't it?
Here's a work around: Before you throw the exception, associate the errant directory name using Exception's Data property.
Immediately before you attempt to find files in a directory, save the name of the directory in a variable. Then begin a try block for the code that looks in that directory. You now have the directory name available should that block of code throw.
For example:
// ... somewhere in some method that's about to search a directory.
var dirName = directories[i]; // or something -- how do you get it before you pass it to DirectoryInfo?
try
{
SearchDirectory(dirName); // or a block of code that does the work
}
catch(Exception e)
{
// at this point, you know dirName. You can log it, add it to a list of erroring
// directories, or whatever. You could throw here, or swallow the error after logging it, etc.
}
Check that it exists first with Directory.Exists
If you are only looking to stomp this one bug in your IDE, then you can try doing this:
In Visual Studio, go to Debug -> Exceptions, then check the Thrown box for Common Language Runtime Exceptions. This will take you right to an exception when it happens, instead of waiting to get caught.
Sorry to dig up an old post, but as other has said it is pretty stupid that DirectoryNotFoundException does not have the directory as a property when FileNotFoundException does.
I've put it in as feature request for .NET:
http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4472498-directorynotfoundexception-should-expose-the-name-
The format of the Message member of the DirectoryNotFoundException thrown from most Directory class methods is "Directory 'input' not found.". It shouldn't be hard to extract the input from this string.
Question thou, why would you need to get the input parameter from the exception if you're the one invoking the method with that exact parameter?