How can I use (to avoid PathTooLongException):
System.IO.FileInfo
with paths bigger than 260 chars?
Are there similar classes/methods that return the same result of FileInfo class?
From what I know it is not easily possible. While it is possible to use workaround for streams as phoenix mentioned, it is not possible for file names handling. Internally every class that works with file names perform checks for long file names.
You can instantiate FileInfo and fill private memebers using reflection (however this is not recommended) and get FileInfo pointing to file with long path. But when you try to use this object you will still receive PathTooLongException exceptions, because for example, Path class (used heavily by FileInfo) checks for long path on every method call.
So, there is only one right way to get problem free long path support - implement your own set of classes that will mimic FileInfo behavior. It is not very complex (only security maybe), but time-consuming.
Update: Here even two ready solutions for this problem: AlpfaFS and Zeta Long Paths
Here at work we deal with long paths quite frequently, and we therefore had to basically roll our own System.IO to do it. Well not really, but we rewrote File, Directory, FileInfo, DirectoryInfo and Path just to name a few. The basic premise is that it's all possible from a Win32 API perspective, so all you really need to do at the end of the day is invoke the Unicode versions of the Win32 API functions, and then you're good. It's alot of work, and can be a pain in the ass at times, but there's really no better way to do it.
There's a great library on Microsoft TechNet for overcoming the long filenames problem, it's called
Delimon.Win32.IO Library (V4.0) and it has its own versions of key methods from System.IO
For example, you would replace:
System.IO.Directory.GetFiles
with
Delimon.Win32.IO.Directory.GetFiles
which will let you handle long files and folders.
From the website:
Delimon.Win32.IO replaces basic file functions of System.IO and
supports File & Folder names up to up to 32,767 Characters.
This Library is written on .NET Framework 4.0 and can be used either
on x86 & x64 systems. The File & Folder limitations of the standard
System.IO namespace can work with files that have 260 characters in a
filename and 240 characters in a folder name (MAX_PATH is usually
configured as 260 characters). Typically you run into the
System.IO.PathTooLongException Error with the Standard .NET Library.
I only needed to use the FullName property but was also receiving the PathTooLongException.
Using reflection to extract the FullPath value was enough to solve my problem:
private static string GetFullPath(FileInfo src)
{
return (string)src.GetType()
.GetField("FullPath", BindingFlags.Instance|BindingFlags.NonPublic)
.GetValue(src);
}
Related
I may be missing something very simple here, but what's the benefit of using reflection to retrieve an embedded resource from the same assembly that contains the resource as opposed to simply retrieving it via an .resx file? I see this a lot but don't get it - is there a reason to use Assembly.GetExecutingAssembly().GetManifestResourceStream(resource) compared to resx file Resources.resource? Even Microsoft does it: How to embed and access resources.
What I mean exactly: suppose I have an assembly MyAssembly that contains an embedded resource Config.xml. The assembly has MyClass that implements a method that returns said resource as a string:
public string GetConfigXML() // returns the content of Config.xml as a string
Often, I see this implemented like this, using reflection to retrieve the resource:
public string GetConfigXML()
{
Stream xmlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyAssembly.Config.xml");
string xml = GetStringFromStream(xmlStream);
return xml;
}
Why use GetManifestResourceStream() when you can:
add a resource file (Resource.resx) to the MyAssembly project in Visual Studio;
add Config.xml to the resource's 'Files';
get the content of Config.xml in a much simpler way: string xml = Resource.Config;
I don't know how Visual Studio handles .resx files internally, but I doubt it simply copies the resource into the .resx file (in which case you'd end up with duplicated resources). I assume it doesn't use reflection internally either, so why not simply use .resx files in situations like this, which seems much more performance-friendly to me?
but what's the benefit of using reflection to retrieve an embedded resource
The common benefit that's behind any reason to convert data from one format to another. Speed, speed, speed and convenience.
XML is a pretty decent format to keep your resources stored in. You'll have a very good guarantee that you can still retrieve the original resource 10 years from now when the original got lost in the fog of time and a couple of machine changes without good backups. But it is quite a sucky format to have to read from, XML is very verbose and locating a fragment requires reading from the start of the file.
Problems that disappear when Resgen.exe compiles the .xml file into a .resource file. A binary format that's fit to be linked into your assembly metadata and contains the original bytes in the resource. And is directly mapped into memory when your assembly is loaded, no need to find another file and open it, read it and convert the data. Big difference.
Do use the Resource Designer to avoid having to use GetManifestResourceStream() directly. Yet more convenience.
I've ran into a bit of a stupid problem today:
In my project I have to use a library (that I can't replace), he problem is that I'm using MemoryStream instead of frequently saving to the HDD (because there are many files, and they are small in size, so it's perfect for MemoryStream). The problem is that the library API is built around filesystem access - and one of the functions accepts only direct path to file.
How can I still send a string (path) to the method, which makes a new FileStream without actually touch the hard-drive?
For example "\MEMORY\myfile.bin"?
Well - that's thought.
Basically, you have three possible solutions:
You can use a reflector to modify the library given.
You can inspect the appropriate method, and then, by using some reflection magic you might be able to modify the object at runtime (very un-recommended)
You can play around with system calls and API - and by going into low-level ring0 assembly modify kernal.dll to referrer I/O queries from your path to the memory. (maybe that's possible without ring0 access - I am not sure).
Obviously, the most recommended is to use a reflector to modify the library given. otherwise, I can't see a solution for you.
In respond to the first comment, you can:
use RAMDrive (a program which allocates small chunks of the system memory and show it as partition)
If the file must exist on the disk (and only disk paths are accepted), then the main option is a virtual filesystem which lets you expose custom data as a filesystem. There exist several options, such as now-dead Dokan, our Solid File System OS Edition and Callback File System (see description of our Virtual Storage product line) and maybe Pismo File Mount would work (never looked at it closely).
It all depends on how the library is constructed.
If it's a 100% managed library that uses a FileStream, you are probably stuck.
If it takes the provided filename and call a native WIN32 CreateFile function, it's possible to give it something else than a file such as a named pipe.
To test quickly if it's possible, pass #"\\.\pipe\random_name" to the method: if it responds by saying explicitely that it can't open pipes and filenames begining with \\.\, well, sorry. ON the other hand, if it says it can't find the file, you have a chance to make it work.
You can then create a NamedPipeServerStream and use the same name for your library method call prepended with \\.\pipe\.
You can't "represent" it as a file, but you could "convert" it to a file using a StreamWriter class.
Someone I know is claiming that it does and I am decompiling System.IO and looking in the Path class and I can't see it making networking calls. The only suspect is in NormalizePath, where it calls to PathHelper which has calls into Win32Native.GetFullPathName. I don't know what that does.
They are also claiming that System.Uri makes networking calls when created, which I find very incredible. I just can't believe that it would do that given how unbelievably slow that would be and how intrinsic these methods are.
Can anyone enlighten me?
Edit:
It turns out that Path.Combine(p) doesn't ever call the network but Path.GetFullName(p) can. In the scenario where you have a UNC path with a short filename ("\\server\abcdef~1.txt" for example) it will actually call out to the network and try to expand the path, which blows my mind frankly.
No, the Path.Combine method simply performs the required string manipulation to generate a legal path string, given the path separator. It explicitly does not check to see if you've given it a valid path, or a valid file name, or whatever.
The reference source code for .NET 4 is available, if you're curious, and you can see that the work is done entirely in managed code, no native method calls, and is basically:
return path1 + (path1.EndsWidth("\") ? "" : "\") + path2;
(A lot more robust and flexible, of course, but that's the idea.)
Similarly, the constructors for the Uri class do mostly string parsing (though orders of magnitude more complex than the Path stuff) but still, no network calls that I can see.
You could also check this yourself by running a packet capture utility such as Wireshark while executing such commands in a C# app.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C# localization , really confusing me
Could someone please share their localization steps for huge C# applications?
I'm pretty sure that the basic resource-based strategy might work when talking about small to medium projects.
However, if we speak about large products, this approach should be paired up with custom build steps and some 3rd party applications used specifically by linguists.
So, could you please advise / share some global localization strategy that is used in your applications (big enough, obviously :)
Thank you.
Basic resource-based strategy works even in large enterprise applications. This is built-in and easily understandable solution, therefore each and every programmer could use it without a problem.
The only problem is, you need to somehow transform your resource files into Translation Memory files (i.e. tmx) and back - so that translators could use their standard tools.
So what you need is actually a Localization process. Is it different for large applications? Well, if you set-up correct process it would scale. Now onto process. From my point of view it should look like this:
Copy resource files into appropriate folder structure (Localization Engineers should not work directly with application code base). The appropriate folder structure should be somehow similar to:
[Project Name]
.
.
[neutral] [German] [Japanese] [French]
.
.
(each folder contains translatable resources in given language, neutral is usually English)
Of course you would need to transform your code base into folder structure somehow, but this could be automated.
Process your translatable resources and create transkits - zip archives containing files that need to be translated (in this case it seems like all of them). The files should be probably transformed, so you won't end-up sending out resx files. The transformation application should read contents of resx files and put translatable strings into some file of format agreed with translators (it could be simply Excel but I won't recommend this solution). Now, I can't give you the names of such tools, although I know that some commercial applications exist, for I have only worked with custom ones.
Send transkits to the translators (most likely translation vendors).
Upon receiving translated files (transkit) back, you need to verify it (this step is crucial). You need to ensure that transkit is complete (i.e. no translatable strings are missing) and technically correct (i.e. file encoding is correct, usually UTF-8 or UTF-16). Also it is at least good to take a glance at the file to see if there are no strange characters like 1/2, 3/4 or something - this usually mean broken encoding.
Import your transkit. This is the reverse step of 2 - you need to put translated strings back to appropriate files.
Copy translated files back to the original code base and run "Localization" build.
Test your application for Localization problems (i.e. overlapping controls, clipping strings, incorrect encoding, etc. - this usually mean that i18n is not done right).
Fix Localization/Internationalization (Localizability) defects.
Proceed to 1 until UI/String freeze period. This assumes that translators would use Translation Memory of some kind and won't charge (or charge less) you for re-translating previously translated strings.
Automate all possible steps and your done.
Apart from that you might won't to establish your common glossary of terms and do linguistic review on translated content.
I think you can rely heavily on the resource framework provided by .NET with a few modifications to make it more appropriate for large projects, namely to build and maintain resources independently of the application and to eliminate the generated properties that refer to each resource by name. If there are other goals appropriate for large project localization that aren't addressed below, please describe them so I can consider them too.
Create a stand-alone project to represent your resources that can be loaded as a separate DLL.
Add a "Resources" file to your project by selecting the link on the Resources tab of the project properties: "This project does not contain a default resources file. Click here to create one."
Add another resource with the same root name to represent another language, for example "Resource.de.resx" for German. (Visual Studio apparently uses the filename to determine the language that the resource file represents). Move it to the same directory/folder as the default Resources file. (Repeat for every language.)
In the properties of the Resources.resx file, delete "ResXFileCodeGenerator" from the Custom Tool property to prevent default code generation in the Application's "Properties" namespace. (Repeat for every language.)
Explicitly/manually declare your own resource manager that loads the newly created resources with a line like:
static System.Resources.ResourceManager resourceMan =
new System.Resources.ResourceManager(
"LocalizeDemo.Properties.Resources", typeof(Resources).Assembly);
Implement a file that can be generated that contains a list of all the resources you can refer to (see figure 1)
Implement a function to retrieve and format strings (see figure 2).
Now you have enough that you can refer to translated strings from any number of applications (see figure 3).
Use System.Resources.ResXResourceWriter (from System.Windows.Forms.dll) or System.Resources.ResourceWriter (System.dll) to generate the resources instead of having the Resx files be your primary source. In our project, we have an SQL database that defines all of our strings in each language and part of our build process generates all the Resx files before building the resources project.
Now that you can generate your Resx files from any format, you can use any format you want (in our case an SQL database, which we export to and import from Excel spreadsheets) to provide files to send out to translators.
Also notice that the translated resources are building as satellite DLLs. You could conceivably build each language independently with the right command line tools. If that is part of your question (how to do that) let me know. But for the moment, I'll assume you know about that since you already mentioned custom build steps.
Figure 1 - enum identifying all available resources:
namespace MyResources
{
public enum StrId
{
Street
....
}
}
Figure 2 - Code to load and return formatted resource strings:
namespace MyResources
{
public class Resources
{
static System.Resources.ResourceManager resourceMan =
new System.Resources.ResourceManager("MyResources.Properties.Resources",
typeof(Resources).Assembly);
public static string GetString(StrId name,
System.Globalization.CultureInfo culture = null, params string[] substitutions)
{
if (culture == null) culture = System.Threading.Thread.CurrentThread.CurrentUICulture;
string format = resourceMan.GetString(name.ToString(), culture);
if (format != null)
{
return string.Format(format, substitutions);
}
return name.ToString();
}
}
}
Figure 3 - accessing resources:
using MyResources;
namespace LocalizationDemo
{
class Program
{
static void Main(string[] args)
{
System.Threading.Thread.CurrentThread.CurrentUICulture =
new System.Globalization.CultureInfo("de-DE");
Console.WriteLine(Resources.GetString(StrId.Street));
}
}
}
I have used GetShortPathName frequently with no problem. However, now I'm having a problem.
In the past I have done, for example, #"C:\LongFoldername\LonfolderName\"
Now I'm using UNC like this #"\\MyServerName\TheLongFolderName"
But it does not get shortened. It stays the same.
I have tried #"\\?\MyServerName\TheLongFolderName"
But that returns "".
I have read GetShortPathName Function But it did not help.
What am I missing?
Thanks!
I doubt very much that GetShortPathName will work on network names because they wouldn't be unique anymore and who would manage the mappings.
In a filesystem the short path name in guaranteed unique on the whole file system and it is created when the file with the long name is created or renamed. You cannot make ensure this in a network.
But even on a file system it is not guaranteed that a given file has a short file name, this may depend on system settings.
THe share name is too long - it must be under 11 chars for compability with GetShortPathName. I think you may be warned of this when creating long share names in some instances.
http://groups.google.co.uk/group/microsoft.public.win32.programmer.kernel/msg/88454e39076262ab