How to treat compressed folders as files using ShellObject from WindowsAPICodePack? - c#

I am trying to implement a filesystem browser using the WindowsAPICodePack for C# (.Net 4), and it works pretty well, except that the ShellObject system treats zip files as folders, whereas I'd prefer they be files. Is there some way I can force it to work this way? The low-level interop it does is beyond me.
As far as I can tell, internally it asks if the item is a Folder or a Filesystem element. It then uses this (and some type checks) to figure out what it actually is. Is it safe to force it to treat it as a file if it's Compressed? Or do I have to do something else?

Ok, well first, I saw that there was a flag in ShellNativeMethods.SFGAO called SFGAO_COMPRESSED. This doesn't seem to actually appear ever though, maybe it was deprecated?
Failing that, I eventually just cheated and did the following in ShellObjectFactory.cs:
Below:
// Is this item a Folder?
bool isFolder = (sfgao & ShellNativeMethods.SFGAO.SFGAO_FOLDER) != 0;
I added:
// Is this a compressed Folder?
bool isCompressedFolder = (itemType == ".zip");
And then I replaced
else if (isFolder)
with
else if (isFolder && !isCompressedFolder)
This is a total hack, but it seems to work, so unless someone has a better idea I'm sticking with this. Hopefully it'll help someone else out in the future, posts on the WindowsAPICodePack seem pretty rare.

Related

Is it possible to "add to" a dll using another new dll

I am very new at this, I have no idea what I'm actually doing and currently the fact that what I "created" works is nothing more than a miracle to me.
Put simply, I was playing among us (look at me go..), a mod was made for it (town of us, you might have heard of it, I dunno), I tried it out and it was pretty cool, I wanted to make some additions/edits to it so I did, it all worked out, perfectly (which.. as I say, a miracle)
However due to the nature of it, it sort of becomes a pain for me to share this version with the people using the original since that means I'm making a separate version and I'd rather not pull away from the original, what I did however requires a bunch of what already exists.
Is there a way for me to create a separate dll which just adds my edits as a separate thing (in a way where it just sort of, puts them into the existing file) while it still pulls from the original one to get the information it needs
For example there's a list of things like this in the original
public static float RecentKill => Generate.RecentKill.Get();
public static bool DetectiveReportOn => Generate.DetectiveReportOn.Get();
public static float DetectiveRoleDuration => Generate.DetectiveRoleDuration.Get();
public static float DetectiveFactionDuration => Generate.DetectiveFactionDuration.Get();
and what I want to do is make it so when my plugin is loaded it just kind of.. adds my entry to it
(I would need to do this with multiple different things but I could probably figure that out if I know if what I currently want to do is actually possible.
So.. is this possible and if so how? (What I have tried currently just insists to me that there's conflicts and I sort of get that)
Please note this is like.. the first thing I've every actually attempted to do this with so I have no full idea of limitations and such
(Super sorry if this makes no sense, as I say, I'm new to any of this and I'm still surprised what I did in the first place even works)

How to determine if System.Drawing.Bitmap can open a file before trying to open?

We have some legacy code that has some... odd mannerisms. You can't really trust the file extension to know what kind of file it is. The big example is that the jpgs are actually tifs.
There may be other intricacies so I can't expect anything but the file's data itself to tell me if an image can be processed. I've wrapped it in a try/catch for now but I don't like using exceptions for flow control.
try
{
using (Bitmap current = (Bitmap)Bitmap.FromFile(file))
{
// Use current
}
}
catch
{
// Must not be an image file
}
We can't change legacy code to make sense. Is there a better way than this to know that file is an image?
It may sound dumb, but Bitmap opens files that are bmp or derivatives(png,jpeg, etc)
Could you try to check if the file extension has any of those extensions?
First off, you probably want to be using Image.FromFile instead of Bitmap.FromFile.
There really is no easy way to do what you're asking. FromFile will delegate to GDI+, and there's no way to know if GDI+ will like the file or not until you actually try to open it.
You could try to write code to do some basic sanity check the headers of all the image types you want to support. In the end though, it's still very possible that the header will be valid but the file itself will be corrupt. Also, you've added a new piece of complexity and possible vector for bugs in your code for processing image headers.
I would argue that this is in fact an exceptional case, and not an example of using exceptions for flow control. If a file that you think is an image turns out to not be, that's an exception.

System.Reflection in a Windows Mobile Project

Before I start, Let me clear up any confusion before anyone suggests or asks the question..
This Question is related to "Windows Mobile 6 Professional", NOT Windows Phone 7 and it can't be ported to windows phone 7 as it has to go on to an older device.
Now, with that out of the way...
I'm currently trying to port a C# library that I have the source for to run on a windows mobile 6 device, Iv'e coded these things it seems like for ever, but there's one thing Iv'e never had to deal with until now and that's reflection.
Now everyone know that the .NET compact framework does have some limitations, and it appears that lack of support for a lot of the methods and properties in the 'System.Reflection' namespace is one of them.
The actual desktop version of the library is set to target .NET V2.0, and I have devices that are running .NET 3.5 SP1 so for the most part Iv'e had very little problem in getting things to work, a cannot however seem to find a sensible to get the following 2 chunks of code working:
var a = AppDomain.CurrentDomain**.GetAssemblies**();
foreach (var assembly in a)
{
if (assembly is System.Reflection**.Emit.**AssemblyBuilder) continue;
if (assembly**.GetType().**FullName == "System.Reflection.Emit.InternalAssemblyBuilder") continue;
if (assembly**.GlobalAssemblyCache** && assembly**.CodeBase** == Assembly.GetExecutingAssembly()**.CodeBase**) continue;
foreach (var t in GetLoadableTypes(assembly))
{
if (t.IsInterface) continue;
if (t.IsAbstract) continue;
if (t.IsNotPublic) continue;
if (!typeof(IGeometryServices).IsAssignableFrom(t)) continue;
var constuctors = t.GetConstructors();
foreach (var constructorInfo in constuctors)
{
if (constructorInfo.IsPublic && constructorInfo.GetParameters().Length == 0)
return (IGeometryServices)Activator.CreateInstance(t);
}
}
}
And
catch (**ReflectionTypeLoadException** ex)
{
var types = ex**.Types**;
IList<Type> list = new List<Type>(types**.Length**);
foreach (var t in types)
if (t != null && t**.IsPublic**)
list.Add(t);
return list;
}
Specifically, those items in bold in the above code are the methods and properties that don't appear to be present in the compact framework, and after spending quite a chunk of time with intellisense and the object browser, Iv'e not found anything that returns (or makes available) the same types.
My question then is as follows:
Does anyone have any experience of using reflection in the Compact .NET framework, and can suggest how this code can be made to work as expected, or am I going to have to start writing custom stubs and functionality to replace the missing methods.
I know there is some reflection capabilities on the framework, so I'm sure there must be an equivalent way of achieving it.
Just on a final note, for anyone who may recognise the code. YES it is from the .NET topology suite, and yes it is that library I'm trying to build a WM6 version of, so if you know of anyone that has already done it please do put a comment on to that effect, and I'll go take a look at the easier path :-)
======================================================================
Update after posting
It appears 'Bold' text doesn't work in code snippets, so those methods / properties in the above code that are surrounded by ** are the parts supposed to be in bold.
So after studying some older builds and an experimental Silverlight build (Which apparently has a lot of the same limitations as Windows Mobile / CE)
I figured out how to make the magic work.
The first part was to fill the array with 'Assembly' structures representing the assemblies to search. Originally it was:
var a = AppDomain.CurrentDomain.GetAssemblies();
but since GetAssemblies doesn't exist in WM then the quickest way is to use the following:
var a = new[] {Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly()};
Now in my case this meant that I got two identical assemblies, but if I was calling an Assembly from my main exe which in turn used GeoAPI then 2 different assemblies would be shown here.
The next challenge was the "stripping out" of assemblies we didn't need to check:
if (assembly is System.Reflection.Emit.AssemblyBuilder) continue;
if (assembly.GetType().FullName == System.Reflection.Emit.InternalAssemblyBuilder") continue;
if (assembly.GlobalAssemblyCache && assembly.CodeBase == Assembly.GetExecutingAssembly().CodeBase) continue;
The first and second instances never crop up in WM6 so it's safe to just comment them out. The second one does work, but since you'll never see any assemblies with the given check name (Due to it being missing) on WM6 then again, you should be safe to comment it out. I didn't comment it out, but it also never got triggered as true.
The final part (at least as far as the original puzzle went anyway) was this:
foreach (var t in GetLoadableTypes(assembly))
{
if (t.IsInterface) continue;
if (t.IsAbstract) continue;
if (t.IsNotPublic) continue;
if (!typeof(IGeometryServices).IsAssignableFrom(t)) continue;
In my original attempt 'isInterface' & 'isNotPublic' where missing, however once I managed to fix up the contents of the 'var a' variable above with the data type it expected, everything started to work ok with nothing missing.
The final question is? Did this solve everything? Well not quite....
It turns out that the whole purpose of the 'ReflectInstance' method in GeoAPI was to find a user defined 'GeometryFactory' using an 'IGeometryServices' interface.
Since I was only reflecting over the assembly I was calling from (var a above) then the 'NetTopologySuite' (Where the geometry factory is defined) was not added to the select list (Obviously CurrentDomain.GetAssemblies includes this)
The net result was I still ended up hitting the exception at the end as no Assemblies of the correct type could be located.
It turns out however, that in the 'Geometry' constructor in GeoAPI has an overload that allows you to pass in a GeometryFactory, when you do this it completely ignores the ReflectInstance method and just uses what it's told.
or to put it another way , I never had to do any of this in the first place, I could have just set the method to return 'null' and passed in the geometry factory I wanted to use.
Anyway, if anyone is interested I now have a working copy of:
GeoAPI.NET
NetTopologySuite
Wintellect.PowerCollections
Built and working fine under Windows Mobile 6 and Windows CE with .NET CF 3.5.

How to find which resource cultures are available in a ResourceManager?

I have a situation where I would like to present a list of the "available languages" for my application (which, incidentally, is an ASP .NET MVC 3 application if that makes any odds). I thought that I could automatically get this list somehow since it should just be the resx files that are included in the build (I don't need to support English UK, German Austria or anything, just English or German) and I came up with a scheme that I will present below (implemented as a singleton since it is a bit of an intensive approach).
The problem is that on some machines it returns "Arabic" even though I have no such resource and on mine (since I installed VS 2012) it returns all of them (this makes more sense to me than returning just the two real cultures plus Arabic but it seems that the ResourceManager just wasn't designed to let me get at this information so I probably should not complain). Here is the scheme...
(I have a Strings.resx and a Strings.de.resx file)
IEnumerable<CultureInfo> cultures =
CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.Where(c =>
{
// Exclude the invariant culture and then load up
// an arbitrary string so the resource manager
// loads a resource set, then get the set for the
// current culture specifically and it is, sometimes
// (I thought always but I was wrong) null if no
// set exists
if (c.LCID == CultureInfo.InvariantCulture.LCID)
return false;
var rm = Strings.ResourceManager;
rm.GetString("HELLO", c);
return rm.GetResourceSet(c, false, false) != null;
});
So then I thought, well, I could do this based on whether the language-specific directory exists like so:
var neutralCulture = new[]
{
CultureInfo
.CreateSpecificCulture(((NeutralResourcesLanguageAttribute)
Assembly
.GetExecutingAssembly()
.GetCustomAttributes(
typeof (NeutralResourcesLanguageAttribute),
false)[0])
.CultureName)
};
IEnumerable<CultureInfo> cultures =
CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.Where(c => Directory.Exists(c.TwoLetterISOLanguageName))
.Union(neutralCulture);
This "works" (in so much as it returns English and German) but I think it is not a very stable approach being as it is prone to random problems like someone creating a folder and throwing it all out of whack. I can probably alleviate these issues with some more judicious checks (the where clause is crying out for more sophistication) but and here is the question (finally)...
Right now I am thinking of just going with a config file and keeping it totally simple since I do not really like where I have got to but is there a better way to do this (or: can it be done automatically in a safe way)?
I like your second approach for automatic detection. I would add though that you should only do this once (on application start or as part of a static constructor) and make it static instead of computing it every time you request the supported culture info.
I think the config approach would work as well, though it's not really automatic. My only thought for that case is that if you are localizing for a language, it's not something that's going to sneak into your application under the radar. That being said, adding a config value at that point seems like an easy thing to do (or to forget to do).
I'm not aware of anything built into the .NET Framework to give you this information with a method call.
I found no valid and reliable way of doing this.
In each of my assemblies I write a code like this that references the supported cultures. That's all we can do. You have to remember to update this array when you add a localization.
internal static class ThisAssembly
{
static readonly string[] SupportedCultures = new string[] { "en-US", "de-DE", };
}
I also tried to:
enumerate the loaded sattelite assemblies: there is no method for that;
get the value of ResourceManager._resourceSets: the dictionary of loaded sets gives correct and invalid values.
But to no avail.

How to know if an unexisting file can be created without trying to create it and catching an IOException

I know this question has been asked before (actually, i found many instances of more or less the same question but with no satisfying solution yet),but i'm gonna rephrase it a little differently..
I want to use FileInfo to get an indication of whether or not i can create a (new) file on a specific disk (hard disk/usb card/etc.) and directory.
The current problems I'm facing:
If the file that I want to create is located on a write-protected disk/USB card/etc. - An IOException will be thrown, only the problem is that it will be thrown after a certain delay (probably after the Flush() was invoked)
Since the file I want to create does not exist, FileInfo.IsReadOnly will always return true, even if the given path is not actually write-protected !
It will return false only in case where the file already exists and is not readonly.
So how can i know if it is possible to create a specific file BEFORE trying to actually create it ?
thanks..
This intrigued me enough to give it a go, albeit without thorough or much testing at all, here are a couple of things you might want to work with (lookout for exceptions too!):
static bool CanCreateFile(FileInfo fileInfo)
{
if (fileInfo.Exists) return false;
return !fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly);
}
static bool CanCreateFile2(FileInfo fileInfo)
{
if (fileInfo.Exists) return false;
return IsDirectoryWriteable(
Path.GetDirectoryName(fileInfo.FullName));
}
static bool IsDirectoryWriteable(string path)
{
var directoryInfo = new DirectoryInfo(path);
if (!directoryInfo.Exists)
{
return IsDirectoryWriteable(directoryInfo.Parent.FullName);
}
return !directoryInfo.Attributes.HasFlag(FileAttributes.ReadOnly);
}
But as stated in another answer, there is really no guarantee that when you actually return from 'validation' that something won't happen to change the validity of the situation before your next action of writing.
In general no. Even if you could the conditions might change between you do the check and you try to create the file. E.g. the user removes a thumb drive or someone changes file permissions.
But you can build a pretty good estimate by looking at the DirectoryInfo for the parent directory.
Check DirectoryInfo.Attributes for FileAttributes.ReadOnly and traverse all ACL:s in DirectoryInfo.GetAccessControl() to find out if the current user has enough permissions.
I guess it is much easier to just try to create the file and catch the exception.

Categories

Resources