I couldn't find any information on this through professor Google, so here I am. Take the given path name and paste it into Windows Explorer. I stumbled across this after discovering bug in my code that generated the paths with an extra '.' in the path name before a directory \ separator...
#"C:\\pathto.\file.ext"
In code, .NET will accept the path when calling File.Create and a file will be generated, but at this path:
#"C:\\pathto\file.ext"
Copying C:\\pathto.\file.ext into Windows Explorer's address bar and watch the '.' disappear and take you to C:\\pathto\file.ext
Is it normal behavior for .NET and Windows to It's not causing an issue because the '.' is being removed by both .NET and Windows when passed into file operations. The real issue is that all the files in the DB have filenames with a '.\', but exists in paths that do not have a '.\'... and File.Exists() works too, although the path is not the 'real' physical location...
What's going on here?
This is a "feature" of the Windows file system. MSDN:
Do not end a file or directory name with a space or a period. Although the underlying file system may support such names, the Windows shell and user interface does not. However, it is acceptable to specify a period as the first character of a name. For example, ".temp".
All normal Windows APIs ignore/remove trailing dots in the file/folder names when regular path is passed in.
If you really need support for trailing dot you need to use "\\?\" prefixed paths and interop all calls yourself (as .Net does not support this file format). See MSDN: Naming Files, Paths, and Namespaces, How to delete a folder that name ended with a dot (".")? and You cannot delete a file or a folder on an NTFS file system volume for more info.
Here is related question showing how to PInvoke function that accepts long file path: c# call Win32 API for long file paths?
Related
I have a program that tracks changes on a local folder using a FileSystemWatcher object.
The issue is that sometimes, on some environments and situations (I do not know which ones), this watcher gives me an event on a DOS path ("/Hello/How/Are/You" becomes something like "/HE~1/HO~1/AR~1/YO~1").
What I am looking for is a way to force this path back into its full and normal aspect.
Or at least something that can tell me that the path is indeed a DOS path, so I can process the entry differently.
EDIT: it has to work on long paths (+260 chars), so Path.GetFullPath(sShortPath) does not work for me here!
Path.GetFullPath(#"/HE~1/HO~1/AR~1/YO~1") should do what you need.
The best method depends what you are looking for, if you just want to access the file once then the 8byte file names will work for internal file references
if you want to display to the user or store then there are 2 option
Path contains most of the tools you need to manipulate paths
fullPath = Path.GetFullPath(path1);
FileInfo and DirectoryInfo these 2 classes provide persistent access to files and directory information and while they can be created with any valid path both have a Full name property that provides access to the full path
As others said, Path.GetFullPath(sShortPath) works fine if not used on very long paths (+260 chars).
Here is a link I followed that worked for me.
GetLongPathName from kernel32.dll worked fine with me, I just had to change the 255 StringBuilder limit to a higher value to make it work with long paths.
I am using WritePrivateProfileString in c# (through DllImport) to store paths taken from textboxes on the interface. And the .ini file name is hardcoded in my application
string ini_file = ".\\config.ini";
However, when the file writing happens, the configuration file is written to the first path taken from the interface instead of writing it to the exe directory. Which is quite odd.
Debugging shows that the values are sent correctly to the WritePrivateProfileString but it still is written to the wrong location. Anyone knows why is that happenening?
I'd guess that something is changing the working directory of your process, most likely your code in the process. Note that the documentation has this to say:
If the lpFileName parameter does not contain a full path and file name for the file, WritePrivateProfileString searches the Windows directory for the file. If the file does not exist, this function creates the file in the Windows directory.
Now my guess is that this applies if you supply just a file name. Because your file name starts with . I believe that will force the function to start from the current working directory.
Having said all of that, and no matter what the cause of the problem is, you should use a fully-qualified path in order to make sure the file is written where you want it to be written. Whenever you want the file to go in a specific directory, it's always easiest to force that by using fully-qualified paths.
You can find the path to your executable using Application.ExecutablePath and then remove the file name part.
Another point to make is that the same directory as the executable may be a bad choice. If your program is installed under the Program Files directory then the directory which contains the executable will not be generally writeable. I think you should consider using a directory under in the user profile. Look for one of the Environment.SpecialFolder values.
Further to David Heffernan's answer - you can use
Path.GetDirectoryName(Application.ExecutablePath);
to safely get just the running application's folder part.
If you're in a dll rather than an executable, you can use
Path.GetDirectoryName(Assembly.GetAssembly(typeof(MyClass)).CodeBase);
Both require System.IO, and were originally posted here. Second example also requires System.Reflection).
Application data files are supposed to be written to the LocalApplicationData special folder.
string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
You typically will not have permissions to write into the Program Files folder etc.
I am trying to calculate sha1 hash for some of the files from location %system%\drivers\ using C#. I know files are at the exact location but when i use
FILE.Exists("c:\\Windows\\System32\\Drivers\\1394ohci.sys")
it always retuns false.
C:\Users\administrator>dir c:\Windows\System32\drivers\1394ohci.sys
Volume in drive C has no label.
Volume Serial Number is 5A4F-1E60
Directory of c:\Windows\System32\drivers
11/21/2010 08:53 AM 229,888 1394ohci.sys
1 File(s) 229,888 bytes
0 Dir(s) 19,521,245,184 bytes free
C:\Users\administrator>fciv -sha1 c:\Windows\system32\drivers\1394ohci.sys
//
// File Checksum Integrity Verifier version 2.05.
//
c:\windows\system32\drivers\1394ohci.sys\*
Error msg : The system cannot find the path specified.
Error code : 3
I even tried fciv.exe on the file and it also generate the same output. I tried running the command as administratror but it did not help.
I did lot of web search but nothing worked. Please help and let me know how to fix this issue.
Appreciate your help.
Thank you,
If I understand your problem correctly then you need to look at File System Redirector
The %windir%\System32 directory is reserved for 64-bit applications.
Most DLL file names were not changed when 64-bit versions of the DLLs
were created, so 32-bit versions of the DLLs are stored in a
different directory. WOW64 hides this difference using a file
system redirector.
In most cases, whenever a 32-bit application attempts to access
%windir%\System32, the access is redirected to %windir%\SysWOW64.
Access to %windir%\lastgood\system32 is redirected to
%windir%\lastgood\SysWOW64. Access to %windir%\regedit.exe is
redirected to %windir%\SysWOW64\regedit.exe.
Also there is small sample at the bottom of page if you can try that one
string system32Directory = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");
if(Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
// For 32-bit processes on 64-bit systems, %windir%\system32 folder
// can only be accessed by specifying %windir%\sysnative folder.
system32Directory = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "sysnative");
}
As others have mentioned, this is the file system redirector at work. The workaround is to replace system32 with sysnative in the filepath.
This was driving me bonkers too, and it took too much work to find the simple workaround. I kept landing on pages with advanced scripting and complicated, obscure tangentially-related solutions. So I thought I'd share the "easy mode".
Run your program in Administrator Mode.
From http://msdn.microsoft.com/en-us/library/system.io.file.exists.aspx:
The Exists method returns false if any error occurs while trying to determine if the specified file exists. This can occur in situations that raise exceptions such as passing a file name with invalid characters or too many characters, a failing or missing disk, or if the caller does not have permission to read the file.
How can you convert a drive relative path such as D:test.xml into an absolute path that a function such as XDocument.Load() will accept. The D drive could have D:\data as its current working directory, for example, so D:test.xml would mean D:\data\test.xml . I've already tried such concoctions as D:.\test.xml .
Here is the error I get for something like D:test.xml:
Invalid URI: A Dos path must be rooted, for example, 'c:\'
You could use GetFullPath. For example:
// should return "D:\data\test.xml" if the current working dir is "D:\data"
string absolutePath = Path.GetFullPath("D:test.xml");
If the CWD is "D:\Data", Path.GetFullPath("D:test.xml") would indeed return "D:\data\test.xml", as it would getting full path directly for "test.xml".
However, if using a different drive letter than the one used for the CWD, the result will not be consistent. For example Path.GetFullPath("C:test.xml") would return "C:\test.xml".
This is by design. Take a look at http://msdn.microsoft.com/en-us/library/aa365247.aspx#fully_qualified_vs._relative_paths.
If a file name begins with only a disk designator but not the
backslash after the colon, it is interpreted as a relative path to the
current directory on the drive with the specified letter. Note that
the current directory may or may not be the root directory depending
on what it was set to during the most recent "change directory"
operation on that disk.
[Emphasis added by me]
You should simply use "test.xml" as a relative path and if the current folder is "D:\data", the full path would be resolved to "D:\Data\test.xml". This is also illustrated in the MSDN example for the Load() method: http://msdn.microsoft.com/en-us/library/bb343181.aspx
Please note that "driveLetter:fileName" is not a relative path in .Net. (Please read Update)
You can transform a relative path into a full path using Path.GetFullPath(), but you do not have to do that XDocument.Load(), since it will also accept relative paths.
Update
LukeH, thanks for pointing this out! "driveLetter:fileName" are accepted by Path.GetFullPath() and are computed as relative paths to the current directory of the specified drive, as specified here.
The concept of current directory at drive level is inherited from the very old days of the DOS. Not necessarily a feature on which I would build modern applications.
Path.GetFullPath can work. see the doc here
As noted in Luke's answer, the solution is due to how the current working directory is used. As an addendum to that answer here is some background. There is seemingly strange behavior of whether the current working directory is per-drive or not. Here is a blog article from Raymond Chen of Microsoft which explains this.
I had to dig around a bit to find a good link for this, so I've copied the contents as well in case it goes bad.
Why does each drive have its own current directory?
Commenter Dean Earley asks, "Why is there a 'current directory' AND
an current drive? Why not merge them?"
Pithy answer: Originally, each drive had its own current directory,
but now they don't, but it looks like they do.
Okay, let's unwrap that sentence. You actually know enough to answer
the question yourself; you just have to put the pieces together.
Set the wayback machine to DOS 1.0. Each volume was represented by a
drive letter. There were no subdirectories. This behavior was carried
forward from CP/M.
Programs from the DOS 1.0 era didn't understand subdirectories; they
referred to files by just drive letter and file name, for example,
B:PROGRAM.LST. Let's fire up the assembler (compilers were for rich
people) and assemble a program whose source code is on the A drive,
but sending the output to the B drive.
A>asm foo the ".asm" extension on "foo" is implied
Assembler version blah blah blah
Source File: FOO.ASM
Listing file [FOO.LST]: NUL throw away the listing file
Object file [FOO.OBJ]: B: send the object file to drive B
Since we gave only a drive letter in response to the Object file prompt, the assembler defaults to a file name of FOO.OBJ, resulting in
the object file being generated as B:FOO.OBJ.
Okay, now let's introduce subdirectories into DOS 2.0. Suppose you
have want to assemble A:\SRC\FOO.ASM and put the result into
B:\OBJ\FOO.OBJ. Here's how you do it:
A> B:
B> CD \OBJ
B> A:
A> CD \SRC
A> asm foo
Assembler version blah blah blah
Source File: FOO.ASM
Listing file [FOO.LST]: NUL
Object file [FOO.OBJ]: B:
The assembler reads from A:FOO.ASM and writes to B:FOO.OBJ, but since
the current directory is tracked on a per-drive basis, the results are
A:\SRC\FOO.ASM and B:\OBJ\FOO.OBJ as desired. If the current directory
were not tracked on a per-drive basis, then there would be no way to
tell the assembler to put its output into a subdirectory. As a result,
DOS 1.0 programs were effectively limited to operating on files in the
root directory, which means that nobody would put files in
subdirectories (because their programs couldn't access them).
From a DOS 1.0 standpoint, changing the current directory on a drive
performs the logical equivalent of changing media. "Oh look, a
completely different set of files!"
Short attention span.
Remembering the current directory for each drive has been preserved
ever since, at least for batch files, although there isn't actually
such a concept as a per-drive current directory in Win32. In Win32,
all you have is a current directory. The appearance that each drive
has its own current directory is a fake-out by cmd.exe, which uses
strange environment variables to create the illusion to batch files
that each drive has its own current directory.
Dean continues, "Why not merge them? I have to set both the dir and
drive if i want a specific working dir."
The answer to the second question is, "They already are merged. It's
cmd.exe that tries to pretend that they aren't." And if you want to
set the directory and the drive from the command prompt or a batch
file, just use the /D option to the CHDIR command:
D:\> CD /D C:\Program Files\Windows NT
C:\Program Files\Windows NT> _
(Notice that the CHDIR command lets you omit quotation marks around
paths which contain spaces: Since the command takes only one path
argument, the lack of quotation marks does not introduce ambiguity.
There are several related questions on stackoverflow but either my situation is different or I am too dumb to relate those to situation. I am hoping someone can help me with this. Further I am not even much of a .NET developer so I apologize in advance for any wrong terminology use.
My scenario is as follows: The tool that is used to deploy our .net application (One Click?) puts it in a directory whose full name exceeds 300 characters. The application uses a third party component -- lets call it dbstore -- that processes the specified file that resides in the application deployment directory.
So far we were using Assembly.GetExecutingAssembly().GetName().CodeBase to construct the fully qualified name of the file to pass to dbstore. But dbstore uses old style APIs and fails when it tries to open the file.
Since dbstore is not expected to change soon, it was recommended that the application chdir to the deployment directory and pass a relative path name in current directory to it. This is also the approach described in the accepted response PathTooLongException in C# code
However I find that Directory.SetCurrentDirectory also throws PathTooLongException. This happens even when I am using UNC path name, e.g a name starting with \\?\0000000000000\...
Am I doing something fundamentally wrong? Is there another function to use?
EDIT: It seems there is no way to achieve what I am looking for. Far as I can tell there is no way to set current directory to a long path.
Do you get a similar result when using Environment.SetCurrentDirectory() ?
If so, you may want to change your directory subfolder after subfolder.
EDIT:
Windows actually sets a limitation of 255 chars for a file path (WinXP) or 260 chars (Vista).
Note that this limitation does not apply for the filesystem, so you can have a file stored in such a long directory path, but Windows Explorer and many Windows services cannot read from such path.
Actually it seems to also include .NET framework methods since you cannot access such files. You may need to write your own filesystem API, but that's a bit too much overhead. Can't you just shorten the file path ? Does Windows offer a shortened way to address a file (like 8 octet file names) ?
Source: http://labnol.blogspot.com/2006/10/limitations-with-long-file-names-on.html