|
Interestingly, your code samples didn't work, but the ones I thought I was trying to write previous to my post are now working. Here is what I have working:
BinaryFormatter bfReader = new BinaryFormatter();
using (AlternateDataStreamInfo ads = Storage.GetAlternateDataStream(streamName, FileMode.Open))
using (FileStream stream = ads.Open(FileMode.OpenOrCreate, FileAccess.Read))
metaData = metaData.FromString(bfReader.Deserialize(stream).ToString());
|
|
|
|
|
So in working with the various examples I have been able to glean from this post, I have found that you do not need a BinaryWriter object in order to save data to the alternate stream. In fact, I get an "The best overloaded method match for 'System.Runtime.Serialization.Binary.BinaryFormatter.Serialize(System.IO.Stream, object)' has some invalid arguments" exception when I use this block of code:
BinaryFormatter bfSettings = new BinaryFormatter();
AlternateDataStreamInfo ads = Storage.GetAlternateDataStream(_altStreamName);
using (FileStream fsSettings = ads.Open(FileMode.OpenOrCreate, FileAccess.Write))
using (BinaryWriter bwSettings = new BinaryWriter(fsSettings))
bfSettings.Serialize(bwSettings, Settings.ToString());
But if I do not use the BinaryWriter like this:
BinaryFormatter bfSettings = new BinaryFormatter();
AlternateDataStreamInfo ads = Storage.GetAlternateDataStream(_altStreamName);
using (FileStream fsSettings = ads.Open(FileMode.OpenOrCreate, FileAccess.Write))
bfSettings.Serialize(fsSettings, Settings.ToString());
it works fine.
Just posting this in case anyone else encounters the same issue.
modified 3-May-12 7:33am.
|
|
|
|
|
|
Prince iraq wrote: Error in code
Thanks for that highly-informative error report. I'll get right on fixing that.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
pleas can you upload source code in my Email
I need your programs
my email
alovenoor@gmail.com
|
|
|
|
|
First, thank you for the project! It really works on my project!
But now I have a problem, as I declare it like this:
AlternateDataStreamInfo dataStream = FileSystem.GetAlternateDataStream("C:\123.txt", "stream1");
When I first use the function
FileStream fs = dataStream.OpenWrite()
to get a filestream, nothing unusual.
and then
fs.Close();
In another function in my project, I also declare a local var like above, but this time, when the program runs at dataStream.OpenRead(). It says that the stream is used by another process.
I wanna to find out some function to close the stream, even the Finalize() function inherited from object is missing. How to end the connection between the process and the stream?
|
|
|
|
|
I can't reproduce this problem. Are you absolutely sure you're closing the streams?
I would recommend using a using block rather than an explicit Close call, but as long as there are no errors, it doesn't seem to make a difference.
Try this:
using System;
using System.IO;
using Trinet.Core.IO.Ntfs;
static class Program
{
static void Main()
{
try
{
const string Path = @"C:\123.txt";
const string StreamName = "stream1";
FileInfo file = new FileInfo(Path);
if (!file.Exists) throw new FileNotFoundException(null, Path);
CreateStream(file, StreamName);
ReadStream(file, StreamName, false);
UpdateStream(file, StreamName);
ReadStream(file, StreamName, true);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void CreateStream(FileInfo file, string streamName)
{
if (!file.AlternateDataStreamExists(streamName))
{
Console.WriteLine("Stream not found; creating it...");
using (var stream = file.GetAlternateDataStream(streamName).OpenWrite())
using (var writer = new StreamWriter(stream))
{
writer.WriteLine("This is some text...");
}
}
}
static void UpdateStream(FileInfo file, string streamName)
{
Console.WriteLine("Overwriting the stream...");
using (var s = file.GetAlternateDataStream(streamName).OpenWrite())
using (var writer = new StreamWriter(s))
{
writer.WriteLine("Overwriting the stream");
writer.WriteLine("This is a test");
}
}
static void ReadStream(FileInfo file, string streamName, bool delete)
{
if (file.AlternateDataStreamExists(streamName))
{
Console.WriteLine("Stream found; reading it...");
using (var stream = file.GetAlternateDataStream(streamName).OpenRead())
using (var reader = new StreamReader(stream))
{
string line;
while (null != (line = reader.ReadLine()))
{
Console.WriteLine("> {0}", line);
}
}
if (delete)
{
file.DeleteAlternateDataStream(streamName);
}
}
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks for your help!
Actually, I made some mistake on the code like this:
AlternateDataStreamInfo data1 = FileSystem.GetAlternateDataStream(Path, StreamName);
FileStream fsReader1 = data1.OpenRead();
byte[] buffer = new byte[100];
fsReader1.Read(buffer, 0, Convert.ToInt32(fsReader1.Length));
FileStream fsWriter1 = data1.OpenWrite();
fsWriter1.WriteByte(49);
fsWriter1.Close();
I originally thought that there would nothing wrong happens if I take the read action first and then write the file
|
|
|
|
|
I wrote some convenience extension methods for simpler read/write of metadata.
public static class FileMetaData
{
public static T GetMetaData<T>(this FileInfo fileInfo, string name)
{
T value;
string text = null;
var dataStream = fileInfo.GetAlternateDataStream(name, FileMode.OpenOrCreate);
using (var reader = dataStream.OpenText())
{
text = reader.ReadToEnd();
}
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
try
{
value = (T)converter.ConvertFromString(text);
}
catch
{
value = default(T);
}
return value;
}
public static void SetMetaData<T>(this FileInfo fileInfo, string name, T value)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
string text = null;
text = converter.ConvertToString(value);
var dataStream = fileInfo.GetAlternateDataStream(name, FileMode.OpenOrCreate);
using (var fileStream = dataStream.OpenWrite())
using (var writer = new StreamWriter(fileStream))
{
writer.Write(text);
}
}
}
This way you can write metadata like this:
FileInfo fi = new FileInfo("path to the file");
fi.SetMetaData<int>("MyAttribute", 5);
Console.WriteLine(fi.GetMetaData<int>("MyAttribute"));
|
|
|
|
|
Hi there.
Maybe I don't get it right, but I can't get the console app "TestNTFS" from your folder "\other\Compatibility wrapper" running properly.
I always get the exception (it's a german one)
System.ComponentModel.Win32Exception was unhandled
Message=Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch
Source=Trinet.Core.IO.Ntfs
ErrorCode=-2147467259
NativeErrorCode=123
StackTrace:
at Trinet.Core.IO.Ntfs.SafeNativeMethods.ThrowLastWin32Error() in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs\SafeNativeMethods.cs:line 182
at Trinet.Core.IO.Ntfs.SafeNativeMethods.SafeGetFileAttributes(String name) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs\SafeNativeMethods.cs:line 222
at Trinet.Core.IO.Ntfs.FileSystem.GetAlternateDataStream(FileSystemInfo file, String streamName, FileMode mode) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs\FileSystem.cs:line 231
at NTFS.FileStreams.Add(String streamName) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs.Tests\FileStreams.cs:line 138
at NTFS.Test.TestNTFS.TestStream() in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs.Tests\TestNTFS.cs:line 52
at NTFS.Test.TestNTFS.Main(String[] args) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs.Tests\TestNTFS.cs:line 17
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
The above exception says: "The filename, directory name, or volume label syntax is incorrect", and I have no idea why this is so.
The testsystem is XP32 SP2 with Visual Studio 2010 (and because of VC2010 it is of course .NET 4.0).
As it was mentioned some postings below I have to admit there is something special about the drive "N:" I use. It is a mapped network drive and the VS2010 is running in an virtualized environment (VMWare).
The mapped network drive IS causing my troubles! I tried the demo app in a path "C:\\tmp" and it worked.
Some postings below there was someone with a similar problem, but as the comment says "mapped network drives are working properly" I hoped it would work out. But it does not.
Does anybody know a clean solution for this problem, if the app does not know that the drive is a mapped network drive?
Any help is appreciated!
Thanks in advance!
LZ
UPDATE #1:
Ok, I annoy you now with my progress in solving the problem. It is unsolved yet
As a first try I added a "networkdrive to UNC path" method which i borrowed here: GetUNCPath-Method
This transforms my original mapped network path from
"N:\\CSharp\\MemoryClones\\MeMoMeSharply\\trunk\\Trinet.Core.IO.Ntfs.Tests\\bin\\Debug\\test.jpg:thumb:$DATA"
to
"\\\\vmware-host\\Shared Folders\\Projekte\\CSharp\\MemoryClones\\MeMoMeSharply\\trunk\\Trinet.Core.IO.Ntfs.Tests\\bin\\Debug\\test.jpg:thumb:$DATA"
But this changed nothing. It still works on the local HDD path but not on the remote network path.
So I still need help from you, if possible
modified on Tuesday, June 15, 2010 2:43 AM
|
|
|
|
|
Strange - it works for me using mapped network drives or UNC paths. Vinzy was having a problem (below) with mapped network drives, but fixed it using the UNC path.
I've tried recompiling everything with .NET 4, and it doesn't seem to make any difference.
Are you sure that drive N: is using NTFS, and that you have sufficient permissions to access it? I've not done much with VMWare, but are there any settings on the host which could be blocking access?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hi, thank you somuch for the quick answer
I am still investigating the cause of my problems, but it can sum up the following special circumstances in my testbed:
1. My Visual Studio is running inside a VMWare which hosts Win XP32
2. Drive N:\ is a mapped network drive inside the VMWare
3. The Filesystem is definitely NTFS and the user in XP is admin
4. The host system of the VMWare image is Windows 7 x64
5. The "physical drive" on the host has the same drive letter N:\
The special thing about this drive N:\ is that it is connected via LAN to the host machine, but this happens fully transparent via drivers, which can "fool" the OS and even the most sophisticated HDD recovery software I have used so far. They all believe that it is a physical HDD installed in the host itself. So I guess this is not the problem. (btw. it ia a "NetDisk" 351UNE by IOCell)
I am going to do a test outside the VMWare and will report after that what I found out.
My guess is that the software will work properly even on the LAN drive N:\, and I will try to run it on another network drive which is not connected via driver but via network drive mapping only.
It will take a while until I have transferred all necessary programs to the outer world, but I will tell you ASAP
UPDATE #1:
I used the compiled x32 executables on my windows 7 x64 on the Pseudo-Network Drive N:\ and it worked fine, nothing to complain
Because of this we can be sure that the drive itself is not the problem, i.e. the LAN-to-Localdrive drivers work perfectly, as does your example in this case!
Still to be done: use a real mapped network drive outside of the vmware and do the same test. Currently I work on my network to provide such an environment, my first test with a mapped Buffalo LAN Drive failed due to other reasons (UNC path was not available, I don't know why, working on it Stupid me! Buffalo uses a Linux filesystem, no NTFS ).
stay tuned...
UPDATE #2:
Sorry for the delay, I am absorbed at the moment by business-needs, but will come back to my problem ASAP .
Believe it or not: my current problem in testing your code on a real mapped network drive is to find a second network device to share some folders from...
I have only one big machine at home and have everything else virtualized!
So you may understand why I want to make it run inside a VM Ware! LOL!
My current guess is: VMWare is doing something strange to mapped NTFS Network drives!
UPDATE #3:
Ok, here we go.
I tried a mapped network drive OUTSIDE of a VMWare and it worked, as you expected (with UNC names only of course).
No I am really frustrated... by the VMWare, not by your code Obviously there is a difference between mapped drives in a "real" environment and such a construct in a virtualized environment.
So I have to investigate further how to circumvent my problem. Until I found a cure, I sadly cannot use your code as intended!
Thank you so much for this great piece of software and your effort to help me! If I find a solution, I will post it here, promised!!
Keep on coding
LZ
PS: I just realized that any file that is copied from inside a VMWare to the outside world via a mapped Network drive, loses all alternate streams! Phew....
modified on Saturday, June 19, 2010 8:41 AM
|
|
|
|
|
Thought it's been almost two years now, but I think posting this will help others looking at this articles.
Folders shared via VMWare Workstation is not the same as folders shared via Windows. You should understand that sharing folders from your host computer in vmware Workstation doesn't DIRECTLY uses the NetBios protocol to communicate between host and guest OS.
Didn't you notice that sharing folders from VMWare Workstation doesn't require you to have a network link between the the host and guest? So, don't blame VMWare Workstation
|
|
|
|
|
Thank you for posting this article in the first place and then updating it even to 64bit! I was just about to code an ADS lib (for 64bit) myself, when I thought "have a look if it hasn't been done before"
I am going to use ADS for keeping track of FileMetaData which shall not be held in a database but as close to the files as possible to make sure the user can move the files around as he or she likes.
I know that ADS works only on NTFS file systems, but I think we will have to stick with that filesystem for a while anyway Do you think ADS will have some future or equivalent if Microsoft might introduce their WinFS some day?
I love the idea of storing custom MetaData in a file without interfering with any app that does not no anything about it!!
LZ
|
|
|
|
|
It would be nice if the library mapped the error code to the appropriate exception. Check out Microsoft's source code for .NET to see how this is done. Specifically, the WinIOError function of the __Error.cs file. (Using .NET Reflector, do a "Search Member" for WinIOError .)
internal static void WinIOError() {
int errorCode = Marshal.GetLastWin32Error();
WinIOError(errorCode, String.Empty);
}
internal static void WinIOError(int errorCode, String maybeFullPath) {
bool isInvalidPath = errorCode == Win32Native.ERROR_INVALID_NAME || errorCode == Win32Native.ERROR_BAD_PATHNAME;
String str = GetDisplayablePath(maybeFullPath, isInvalidPath);
switch (errorCode) {
case Win32Native.ERROR_FILE_NOT_FOUND:
if (str.Length == 0)
throw new FileNotFoundException(Environment.GetResourceString("IO.FileNotFound"));
else
throw new FileNotFoundException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.FileNotFound_FileName"), str), str);
case Win32Native.ERROR_PATH_NOT_FOUND:
if (str.Length == 0)
throw new DirectoryNotFoundException(Environment.GetResourceString("IO.PathNotFound_NoPathName"));
else
throw new DirectoryNotFoundException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.PathNotFound_Path"), str));
case Win32Native.ERROR_ACCESS_DENIED:
if (str.Length == 0)
throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_IODenied_NoPathName"));
else
throw new UnauthorizedAccessException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("UnauthorizedAccess_IODenied_Path"), str));
case Win32Native.ERROR_ALREADY_EXISTS:
if (str.Length == 0)
goto default;
throw new IOException(Environment.GetResourceString("IO.IO_AlreadyExists_Name", str), Win32Native.MakeHRFromErrorCode(errorCode), maybeFullPath);
case Win32Native.ERROR_FILENAME_EXCED_RANGE:
throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
case Win32Native.ERROR_INVALID_DRIVE:
throw new DriveNotFoundException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.DriveNotFound_Drive"), str));
case Win32Native.ERROR_INVALID_PARAMETER:
throw new IOException(Win32Native.GetMessage(errorCode), Win32Native.MakeHRFromErrorCode(errorCode), maybeFullPath);
case Win32Native.ERROR_SHARING_VIOLATION:
if (str.Length == 0)
throw new IOException(Environment.GetResourceString("IO.IO_SharingViolation_NoFileName"), Win32Native.MakeHRFromErrorCode(errorCode), maybeFullPath);
else
throw new IOException(Environment.GetResourceString("IO.IO_SharingViolation_File", str), Win32Native.MakeHRFromErrorCode(errorCode), maybeFullPath);
case Win32Native.ERROR_FILE_EXISTS:
if (str.Length == 0)
goto default;
throw new IOException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.IO_FileExists_Name"), str), Win32Native.MakeHRFromErrorCode(errorCode), maybeFullPath);
case Win32Native.ERROR_OPERATION_ABORTED:
throw new OperationCanceledException();
default:
throw new IOException(Win32Native.GetMessage(errorCode), Win32Native.MakeHRFromErrorCode(errorCode), maybeFullPath);
}
}
|
|
|
|
|
Thanks for the feedback. I've finally got around to submitting an update to resolve this and the other issue you reported (ASCII codes 1-31 in stream names).
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
ValidateStreamName incorrectly uses Path.GetInvalidFileNameChars to validate a stream name. A stream is allow characters whose integer representations are between 1 and 31. I have included a fix, as shown below.
private static char[] _invalidStreamNameChars;
public static char[] GetInvalidStreamNameChar() {
if (_invalidStreamNameChars == null) {
List<char> oResult = new List<char>();
foreach (char i in System.IO.Path.GetInvalidFileNameChars()) {
if ((i > 0) && (i < 32)) {
} else {
oResult.Add(i);
}
}
_invalidStreamNameChars = oResult.ToArray();
}
return _invalidStreamNameChars;
}
public static void ValidateStreamName(string streamName)
{
if (!string.IsNullOrEmpty(streamName) && -1 != streamName.IndexOfAny(GetInvalidStreamNameChar()))
{
throw new ArgumentException(Resources.Error_InvalidFileChars);
}
}
|
|
|
|
|
Hi,
I am having problems accessing the alternate data stream of a network shared drive. I get the error "The filename, directory name, or volume label syntax is incorrect" when I use the method FileSystem.AlternateDataStreamExists("z:\\", "xx"). Am I doing something wrong in here. Thanks in advance for your help.
--
Vinzy
|
|
|
|
|
The AlternateDataStreamExists method uses the GetFileAttributes API to determine whether a stream exists. According to the documentation[^]:
If you call GetFileAttributes for a network share, the function fails, and GetLastError returns ERROR_BAD_NETPATH . You must specify a path to a subfolder on that share.
However, I haven't been able to reproduce your error message. I've tried calling AlternateDataStreamExists with a mapped network drive and a UNC path, and both work as expected. If I pass a drive which isn't mapped, I get "The system cannot find the path specified". If I pass a UNC path without a share name, I get "The UNC path should be of the form \\server\share".
I'm running Windows 7, but I've just tried running a simplified version of the code on a Windows 2000 virtual PC and I get the same results. I've included the code below.
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
static class Program
{
private const int ErrorFileNotFound = 2;
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int GetFileAttributes(string fileName);
private static int SafeGetFileAttributes(string fileName)
{
if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName");
int result = GetFileAttributes(fileName);
if (-1 == result)
{
int errorCode = Marshal.GetLastWin32Error();
if (ErrorFileNotFound != errorCode) ThrowLastWin32Error();
}
return result;
}
private static void ThrowLastWin32Error()
{
int error = Marshal.GetLastWin32Error();
if (0 != error)
{
int hr = Marshal.GetHRForLastWin32Error();
if (0 > hr) Marshal.ThrowExceptionForHR(hr);
throw new Win32Exception(error);
}
}
static void Main()
{
try
{
int attributes = SafeGetFileAttributes("z:\\");
if (-1 == attributes)
{
Console.WriteLine("Drive not found");
}
else
{
Console.WriteLine("Drive found");
attributes = SafeGetFileAttributes("z:\\:xx");
if (-1 == attributes)
{
Console.WriteLine("ADS not found");
}
else
{
Console.WriteLine("ADS found");
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hi,
Thanks for your reply. I really appreciate it.
The problem that I was having was only when I tried to access the alternate stream of the network drive.
I figured out a work around for it by finding the UNC of the network drive and then using it. It works great now. Ref: http://www.wiredprairie.us/blog/index.php/archives/22[^]
--
Vinzy
|
|
|
|
|
Strange - it works for me whether I use the UNC path or the mapped network drive.
Glad you found a workaround.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
hi,
i am a newer,and i don't know how to use it.
how can i compile all the files in "Trinet.Core.IO.Ntfs" folder?
by the way i am using powershell
thanks
|
|
|
|
|
There is a pre-compiled version in the "bin" folder. Alternatively, you can build the solution using Visual Studio 2008, or from the command line using msbuild[^].
In PowerShell, make sure you're in the "Trinet.Core.IO.Ntfs" folder. To build the 32-bit version, run:
& $env:systemroot\Microsoft.NET\Framework\v3.5\msbuild.exe Trinet.Core.IO.Ntfs.sln
If you're on a 64-bit version of Windows, you can build a 64-bit version using:
& $env:systemroot\Microsoft.NET\Framework64\v3.5\msbuild.exe Trinet.Core.IO.Ntfs.sln
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
thanks a lot
some more
documentation in doc folder are not available
can you update?
thans again
|
|
|
|
|
The CHM file is probably blocked because you downloaded it from the Internet. To unblock it, right-click on the file and choose "Properties". At the bottom of the General tab, you will see "Security: This file came from another computer...", with an "Unblock" button next to it. Click the unblock button, and you should be able to open the file.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|