|
|
|
|
Open window on development at Xceed
|
|
|
 Wednesday, February 02, 2005
I've been working part time (translation: I should be working on something else) on a new sample: my own Command Prompt. I know, I'm reinventing the wheel, not to count that Microsoft will launch a new one called msh (codename Monad). But it was more a concept or proof around exposing AbstractFolder and AbstractFile within a command prompt.
E:\>dir
Directory of E:\
DATE TIME SIZE or TYPE NAME 27/12/2004 4:23 PM [FOLDER] Backup 03/11/2004 10:33 AM [FOLDER] Chart30 24/11/2004 10:09 AM [FOLDER] CLR Profiler 11/01/2005 5:24 PM [FOLDER] Config.Msi 24/11/2004 10:10 AM [FOLDER] My Music 12/01/2005 4:13 PM [FOLDER] My Pictures 10/09/2004 1:52 PM [FOLDER] RECYCLER 30/09/2004 8:40 PM [FOLDER] System Volume Information 02/02/2005 2:49 PM [FOLDER] temp 03/11/2004 10:33 AM [FOLDER] XceedProjectsNET 25/01/2005 7:06 AM 143 toto.txt
Files: 1 Folders: 14 Total file size: 143
E:\>copy toto.txt temp 100% E:\>cd temp E:\temp\>
As you can see, I can list the contents of folders, copy files, and change the working folder. The application simply manages a working "AbstractFolder", and enables commands to act on that folder (or an AbstractFolder obtained from an absolute path).
The sample quicky evolved into a prototype for upcoming features. Among other things, I needed a way to recognize a path like "E:\temp\test.zip\images" as a ZippedFolder within a zip file. Let's stop the talking, and show some traces:
E:\temp\>md test.zip E:\temp\>md test.zip\images E:\temp\>copy "..\My Pictures\Chalet\*" test.zip\images 100% E:\temp\>
What have I done here? Create a folder named "test.zip"? Well, the "md" command recognized the ".zip" extension as a request to create a new empty zip file. The second "md" command actually created a new folder within the zip file. And the paths can freely use the zip filename as a folder part for any command, as shown with the copy example. If we display the contents of "E:\temp", we see the two expected files:
E:\temp\>dir
Directory of E:\temp\
DATE TIME SIZE or TYPE NAME 02/02/2005 3:00 PM 40068736 test.zip 25/01/2005 7:06 AM 143 toto.txt
Files: 2 Folders: 0 Total file size: 40068879
E:\temp\>
As you can see, "test.zip" is really a file (DiskFile) within "E:\temp" (DiskFolder). What happens if I try changing the current folder into that zip file?
E:\temp\>cd test.zip E:\temp\test.zip\>dir
Directory of E:\temp\test.zip\
DATE TIME SIZE or TYPE NAME 02/02/2005 3:00 PM [FOLDER] images
Files: 0 Folders: 1 Total file size: 0
E:\temp\test.zip\>cd images E:\temp\test.zip\images\>dir
Directory of E:\temp\test.zip\images\
DATE TIME SIZE or TYPE NAME 06/08/2000 4:40 PM 6400006 Chaises.bmp 06/08/2000 4:35 PM 6348550 Chute.bmp 06/08/2000 4:29 PM 6337678 Ciel1.bmp 06/08/2000 4:30 PM 6396226 Ciel2.bmp 06/08/2000 4:33 PM 6414418 Ciel3.bmp 06/08/2000 4:38 PM 6524278 Couple.bmp 06/08/2000 4:37 PM 6388054 Martine.bmp 06/08/2000 4:32 PM 6405478 Ombre.bmp 06/08/2000 4:41 PM 6359254 Rochers.bmp
Files: 9 Folders: 0 Total file size: 57573942
E:\temp\test.zip\images\>
The zip file is exposed as a folder, because the path "E:\temp\test.zip" was recognized and mapped to a ZippedFolder around a DiskFile. And "images" is nothing more than a subfolder within that root ZippedFolder, actually something like:
new ZippedFolder( new DiskFile( @"E:\temp\test.zip" ), @"\images" );
Ok, let's get into serious things:
E:\temp\test.zip\images\>cd ..\.. E:\temp\>copy *.zip RAM:\ 100% E:\temp\>cd RAM:\test.zip\images RAM:\test.zip\images\>dir m*.bmp
Directory of RAM:\test.zip\images\
DATE TIME SIZE or TYPE NAME 06/08/2000 4:37 PM 6388054 Martine.bmp
Files: 1 Folders: 0 Total file size: 6388054
RAM:\test.zip\images\>
My Command Prompt exposes a root MemoryFolder called "RAM:\", which I can freely use. The commands act the same, no matter if I'm deeling with a ZippedFolder around a DiskFile or a MemoryFile. Want more?
RAM:\test.zip\images\>cd ftp://vermouth ftp://vermouth\>dir
Directory of ftp://vermouth\
DATE TIME SIZE or TYPE NAME
Files: 0 Folders: 0 Total file size: 0
ftp://vermouth\>md foobar.zip ftp://vermouth\>copy "E:\My Music\WMA\Mes Aieux" foobar.zip 100% ftp://vermouth\>dir
Directory of ftp://vermouth\
DATE TIME SIZE or TYPE NAME 02/02/2005 3:20 PM 62936759 foobar.zip
Files: 1 Folders: 0 Total file size: 62936759
ftp://vermouth\>dir c:\inetpub\ftproot
Directory of c:\inetpub\ftproot\
DATE TIME SIZE or TYPE NAME 02/02/2005 3:20 PM 62936759 foobar.zip
Files: 1 Folders: 0 Total file size: 62936759
ftp://vermouth\>
FTP servers are threated as any other kind of AbstractFolder. The application simply recognize the "FTP:" prefix as a signature for a root FtpFolder, as it did with "RAM:" exposed as a MemoryFolder. The command implementations don't care what kind of AbstractFolder or AbstractFile they are dealing with.
The engine behind this involves FileSystemMapper-derived classes. They mainly get asked two kinds of questions:
Question 1: Do you recognize this path as a root?
If so, they remove the part of the path they could recognize as a root folder, and return the matching AbstractFolder.
Examples of mappers and their responsability:
-
DiskMapper : Drive letters and UNC paths (yes, you can "cd" into a UNC path!)
-
FtpMapper : The "FTP:" prefix with server name, and optional username and password (e.g. ftp://user:pass@vermouth:9999)
-
IsolatedStorageMapper : A custom prefix name like "STORE:" (that's the one my sample app supports).
-
MemoryMapper : A custom prefix used to create the initial root MemoryFolder, like "RAM:" (that's the one my app supports). You can create more than one MemoryMapper to have more than one ram drive.
Question 2: Can you represent this AbstractFile as an AbstractFolder?
If so, they simply return the matching AbstractFolder.
An example of such a mapper:
-
ZipFileMapper : It simply checks if the provided AbstractFile exists, then tries to create a ZipArchive around that AbstractFile in a try/catch. If it succeeds, it returns this ZipArchive (which derives from ZippedFolder).
Curiously, today I came across a post on our forums asking how to detect if a file is really a zip file. I gave this man the "new ZipArchive within a try/catch" solution, and he came back, as I feared, with concerns with the time wasted catching an exception for all those non-zip files. It's actually one of the bottlenecks of my Command Prompt sample. A lot of time is wasted throwing an exception for all non-zip files my app comes across. Well, I guess I'll have to work sooner than later on a new "ZipArchive.IsZipFile" method! 
Now, you have to convince my boss I should put more time on this sample and these new FileSystem features! Does mapping absolute paths like shown above to their proper AbstractFolder or AbstractFile something that could be usefull for you?
|
|
 Wednesday, January 19, 2005
Scott Hanselman just posted about a case-{in}sensitivity problem he just went through. That reminded me I wanted to talk to you about case-sensitivity in Xceed Zip for .NET and the FileSystem. I remember back in the design days, we debated long and hard on if the FileSystem should be case-sensitive or not. Once we decided to support both, the debate continued about what should be the default behavior.
The conclusions were simple:
- Immitate by default.
- Uniformity within single product.
- Know the differences.
Since System.IO was case-insensitive (and moreover the whole Windows operating system), we had to be case-insensitive by default. Thus, if you have files "first.txt" and "second.TXT" in a folder, the code below will return you two files:
DiskFolder disk = new DiskFolder( @"t:\" ); AbstractFile[] files = disk.GetFiles( false, "*.txt" );
The same way, if you have both files in a zip file, the following code will return both:
ZipArchive zip = new ZipArchive( new DiskFile( @"t:\texts.zip" ) ); AbstractFile[] files = zip.GetFiles( false, "*.txt" );
Now, where it's getting tricky is that you will never have a folder on disk containing both "second.TXT" and "second.txt". The system won't let you create the second one. Thus the following code returns an existing file who's FullName is all lower case, even if the real file has an upper-case extension:
DiskFolder disk = new DiskFolder( @"t:\" ); AbstractFile file1 = disk.GetFile( "second.txt" );
You asked the "Disk" world for file "second.txt", and this world has recognized "second.TXT" as matching your request.
To obey to rule #2, the following code does exactly the same, even though the file stored in the zip file has its extension all upper case:
ZipArchive zip = new ZipArchive( new DiskFile( @"t:\texts.zip" ) ); AbstractFile file2 = zip.GetFile( "second.txt" );
But in a zip file, which can come from a different operating system, you potentially could end up with a zip file containing both. What would happen? I've created such a zip file for our tests, by adding "second.TXT" and "foobar.txt" to a zip file, and hex-editing "foobar" to "second":
second.zip (.23 KB)

When opening this file in WinZip, I can see both files. But when unzipping, it will unzip the first, then try to unzip the second over the first. You just can't unzip both in two separate files. Furthermore, trying to unzip any single one from within the classic view will always unzip both over the same file on disk.
How does Xceed Zip for .NET deal with such zip fles? Try the following code:
ZipArchive zip = new ZipArchive( new DiskFile( @"t:\second.zip" ) ); foreach( AbstractFile file in zip.GetFiles( false ) ) { Console.WriteLine( file.FullName ); }
The output is:
\second(1).txt \second.TXT
Any file that case-insensitively matches another file gets appended a number. This is not a perfect solution, as there is never a perfect solution. To support rule #2, DiskFolder and ZippedFolder instances had to behave the same. This post and the documentation tries to address rule #3 
Now, some of you want to always look for exact matches. You simply need to prepend the string mask with a ">", as in "I want a more precise match" (1). The following code will match a single file:
DiskFolder disk = new DiskFolder( @"t:\" ); AbstractFile[] files = disk.GetFiles( false, ">*.txt" );
The idea with System.String filter parameters is that we replace them with a NameFilter, which is the one responsible for that ">" trick. It only works with methods accepting filters (GetFiles, GetFolders, CopyFilesTo, MoveFilesTo). Methods like GetFile can only return a single instance (actually always returns an instance which may exist or not). Those methods return the single and unique AbstractFile matching your string, based on the world this AbstractFolder belongs to.
(1): We actually debated between using "<" as in "match less items" or ">" as in "a more precise match". I think we ended up tossing a coin! 
|
|
 Tuesday, January 18, 2005
I'm currently doing some tests on an alpha version of Xceed FTP for .NET, supporting the Xceed FileSystem. It's so wonderful to be able to manipulate files and folders no mather where they reside. Take this generic directory listing method:
private static void DisplayListing( AbstractFolder folder ) { if( !folder.Exists ) { Console.WriteLine( "\n Folder {0} does not exist.\n", folder.FullName ); } else { FileSystemItem[] items = folder.GetItems( false ); long totalSize = 0; int fileCount = 0; Console.WriteLine( "\n Folder listing of {0}\n", folder.FullName ); foreach( FileSystemItem item in items ) { Console.Write( "{0} {1} ", item.LastWriteDateTime.ToShortDateString(), item.LastWriteDateTime.ToShortTimeString() ); AbstractFile file = item as AbstractFile; if( file == null ) { Console.Write( " " ); } else { Console.Write( "{0,16} ", file.Size.ToString( "N0" ) ); totalSize += file.Size; ++fileCount; } Console.WriteLine( item.Name ); } int folderCount = items.Length - fileCount; Console.WriteLine( "\n {0} file{1}, {2} folder{3}, {4} bytes\n", fileCount.ToString(), ( fileCount == 1 ) ? string.Empty : "s", folderCount.ToString(), ( folderCount == 1 ) ? string.Empty : "s", totalSize.ToString() ); } }
As you can see, the code does not need to know what exactly is that AbstractFolder. People familiar with Xceed Zip for .NET already know we could call the above method like this:
DiskFolder folder = new DiskFolder( @"C:\Program Files\Microsoft SDKs\WinFX" ); DisplayListing( folder );
And obtain this kind of output:
Folder listing of C:\Program Files\Microsoft SDKs\WinFX\
24/11/2004 9:29 AM <DIR> bin 24/11/2004 9:27 AM <DIR> Help 24/11/2004 9:27 AM <DIR> License 24/11/2004 9:27 AM <DIR> misc 24/11/2004 9:29 AM <DIR> Setup 24/11/2004 9:29 AM <DIR> VS Install Directory 11/11/2004 6:03 PM 16,001 ReleaseNotes.htm 12/01/2005 2:58 PM 17,297 SetEnv.cmd 2 files, 6 folders, 33298 bytes
Or call the same method like this:
ZippedFolder folder = new ZippedFolder( new DiskFile( @"D:\sample.zip" ), "ContMenuExt" ); DisplayListing( folder );
To get this output:
Folder listing of \ContMenuExt\
08/01/2001 11:29 AM 8,091 ContextMenu.cpp 02/01/2001 4:15 PM 1,005 ContextMenu.h 08/01/2001 11:32 AM 7,820 ContextMenuExt.cpp 28/11/2000 11:23 AM 225 ContextMenuExt.def 02/01/2001 5:02 PM 5,301 ContextMenuExt.dsp 28/11/2000 11:23 AM 551 ContextMenuExt.dsw 28/11/2000 11:23 AM 742 ContextMenuExt.h 02/01/2001 4:12 PM 1,440 ContextMenuExt.rc 08/01/2001 3:29 PM 2 ReadMe.txt 02/01/2001 4:12 PM 1,195 resource.h 10 files, 0 folders, 26372 bytes
With the upcoming version of Xceed FTP for .NET, it won't be more difficult to display the contents of a folder located on an FTP server:
FtpConnectionInfo info = new FtpConnectionInfo( "ftp.microsoft.com" ); FtpFolder folder = new FtpFolder( info ); DisplayListing( folder );
Folder listing of \
25/11/2002 12:00 AM <DIR> bussys 21/05/2001 12:00 AM <DIR> deskapps 20/04/2001 12:00 AM <DIR> developr 18/11/2002 12:00 AM <DIR> KBHelp 02/07/2002 12:00 AM <DIR> MISC 16/12/2002 12:00 AM <DIR> MISC1 25/02/2000 12:00 AM <DIR> peropsys 02/01/2001 12:00 AM <DIR> Products 04/04/2003 12:00 AM <DIR> PSS 21/09/2000 12:00 AM <DIR> ResKit 25/02/2000 12:00 AM <DIR> Services 25/02/2000 12:00 AM <DIR> Softlib 0 files, 12 folders, 0 bytes
As a mather of fact, stuff like this already works fine on my machine:
FtpConnectionInfo info = new FtpConnectionInfo( "ftp.cam.org", "***", "***" ); FtpFile ftpFile = new FtpFile( info, @"\pub\Photos.zip" ); if( ftpFile.Exists ) ftpFile.Delete(); DiskFolder sourceFolder = new DiskFolder( @"E:\My Pictures\Clément\Petites" ); ZipArchive destFolder = new ZipArchive( ftpFile ); sourceFolder.CopyFilesTo( destFolder, true, true ); DisplayListing( destFolder ); DisplayListing( ftpFile.ParentFolder );
This is the output:
Folder listing of \
16/09/2004 9:39 AM 11,820 Buzz.jpg 16/09/2004 9:40 AM 11,143 Chalet - Bercé.jpg 16/09/2004 9:42 AM 15,749 Clément et Michel.jpg 16/09/2004 9:43 AM 18,473 Clément et Papa.jpg 16/09/2004 9:42 AM 15,004 Clément et Valérie.jpg 14/02/2003 4:17 PM 7,984 clément1.jpg 14/02/2003 4:18 PM 11,288 clément2.jpg 14/02/2003 4:18 PM 10,648 clément3.jpg 11/08/2004 3:25 PM 18,499 Famille.jpg 11/08/2004 3:23 PM 36,734 Fier.jpg 16/09/2004 9:40 AM 14,959 Grande discussion.jpg 16/09/2004 9:41 AM 13,426 Maman et Clément.jpg 11/08/2004 3:25 PM 24,594 Piscine.jpg 13 files, 0 folders, 210321 bytes Folder listing of \pub\
27/12/2004 11:06 AM 7,986 Builds du 2004-09-27.htm 12/01/2005 4:13 PM 208,439 Clement.zip 27/12/2004 11:06 AM 1,068 FileSystem.txt 18/01/2005 2:44 PM 208,423 Photos.zip 27/12/2004 11:06 AM <DIR> Second 27/12/2004 11:06 AM 11,723 VSSWarning.jpg 27/12/2004 11:06 AM 117,695 appnote.txt 27/12/2004 11:06 AM 96 vssver.scc 7 files, 1 folder, 555430 bytes
Am I the only one to find this cool? q
|
|
It's been a long time since my last post. I feel like I should post more often, even if the subject may not get your attention. But I have mixed emotions about blogging just for blogging.
Robert Scoble is an hardant promotor of corporate blogging. And prolific. But when he says stuff like "Talk is cheap. Doing is divine." just because he has nothing new to say about Longhorn, he's shooting his foot. How credible is that statement?
|
|
 Tuesday, December 14, 2004
I must agree 100% with Frans: While giants are busy fighting on the Desktop Search front, WinFS gets delayed, which should have been the real front. People here at Xceed know what I think about WinFS. While others here were all excited about Avalon and Indigo, I was the first to tell them the real innovation and revolution was WinFS. I'm sorry MS, but WinFS should have been the main Pillar of Longhorn! Instead, WinFS topics on MSDN all start with this remark:
UPDATE: In spite of what may be stated in this content, WinFS is not a feature that will come with the Longhorn Operating System. However, WinFS will be available on the Windows platform at some future date, which is why this content continues to be provided for your information.
Sure, desktop search tools help make my life easier. But it's a temporary solution... And temporary solutions tend to become permanent too easily...
On my TODO list: Post about the WinFS model and its implications on archives like zip files, which are not ready to store data, relationships, and metadata.
|
|
 Monday, December 13, 2004
I admit, I'm working in zip file compression, and I'm not even using something home made for unzipping zip files I run into. I'm using WinZip's context menu.
Well, I should talk to the past. I've made a man of myself and implemented my own "Unzip Here" context menu, which is using Xceed Zip ActiveX 5.x. Why reinvent the wheel when it works fine? Because it didn't work that fine for me.
How many times have I right-clicked on a zip file, went to the WinZip menu, stared at Extract to here and Extract to d:\someplace\somewhere\zipfilename just to find asking myself: "Does that zip file already contain paths?". If it does, I don't need to create a "zipfilename" subfolder, thus I should select the first menu item. But if it doesn't, I sure don't want all unzipped files to end up in the current folder, thus I want to select the second menu item. I end up opening the zip file just to view file paths.
That's what I just implemented. You right-click on a zip file, you click on Unzip Here, and it will automatically detect if it needs to create a subfolder (using the zip filename) or not, then unzip everything.
I won't go into the full details of how to create a Windows Shell Extension component, the sample is pretty self-explanatory, and the web is filled with tutorials. In short, you:
- Create a new ATL COM AppWizard project (VC++ 6).
- Add a new Simple Object with default names and attributes (make sure not to select "Free Threaded").
- Remove references to the newly created interface, you don't need it. (I left the IDL in there instead of copying the CLSID somewhere else... I'm lazy).
- Remove the type library from the resources and RGS file, you don't need it.
- Implement IShellExtInit and IContextMenu interfaces (see UnzipHereExtension.cpp).
- Add the required registry keys (see "DllRegisterServer" in UnzipHere.cpp).
The heart of the extension resides in IContextMenu::InvokeCommand. Don't forget more than one file can be selected when your context menu gets called.
While debugging, you'll often need to restart the explorer.exe in order to release usage of your DLL. Use the Task Manager's run menu to reload it. If you don't like ending a task via the Task Manager, try this: Start Menu -> Shutdown, press Ctrl-Alt-Shift and click Cancel. The explorer.exe process will end.
On my TODO list:
- Support zip files not ending with the ZIP extension (like self-extracting zip files).
- Implement a "Zip This" menu.
- Add a "File already exists. Do you want to overwrite?" prompt.
- Hide the "aborted" error on non-zip files.
Comments welcomed! Have fun! UnzipHere.zip (21 KB)
|
|
 Wednesday, December 08, 2004
When applications like KeyHole will become defacto cartographic applications, someone, somewhere will have the bright idea of putting huge ads on their building' roof.
How about ads on highways? They will have a T form, displaying their contents in front and above!
I can't wait to see the first guy to ask his girlfriend in marriage via KeyHole, spelling "Martha, will you marry me?" on some park's ground...
|
|
 Friday, December 03, 2004
I've been playing with KeyHole for a few days, and though such tools or web sites have been out there for years, I must admit KeyHole is simply amasing. Its smooth transitions, road map display abilities, sharp and precise images and placeholder sharing possibilities are incredible.
For example, once you have KeyHole installed, I can give you a link to my home, like this one:
Home.kml (.72 KB)
As you can see, both my neighbours have a pool (grin). And then, you could travel up to Xceed's headquarters by clicking on this:
Xceed.kml (.75 KB)
A very neet residential area, surrounded by two golf courses and a huge park, right in the middle of Longueuil. I often compare Longueuil to a mini-Redmond. And finally, how about a higher view of the greater Montreal's south shore:
HighView.kml (2.26 KB)
The next step is building KML files for your clients or invitees that smoothly shows them step by step instructions on how to get from the airport or their house, to your business headquarters or your open house party!
|
|
 Wednesday, November 24, 2004
For those playing with the newly released Avalon CTP and Chris Anderson's XamlPad application, and are using other Visual Styles than Windows XP (TGTSoft's StyleXP or patched UXTheme.dll), you can follow these instructions:
- Go in folder "C:\WINDOWS\Resources\Themes\Luna"
- Copy file "PresentationFramework.Luna.NormalColor.FxStyles"
- Go back one folder, then into your current theme's folder
- Paste the copied file
- Rename the file as follows:
- Replace "Luna" with the name of the "msstyles" file
- Replace "NormalColor" with the name of the folder under "Shell" which matches the active color scheme.
For example, I'm running with the CodeOpus theme with the Dusk color scheme:

Within "C:\WINDOWS\Resources\Themes", I copied "Luna\PresentationFramework.Luna.NormalColor.FxStyles" to "CodeOpus\PresentationFramework.CodeOpus.Dusk22.FxStyles". Notice that the color scheme part matches the subfolder name, not the color scheme name ("CodeOpus\Shell\Dusk22" in my case).
BTW, XamlPad's ClickOnce does not work within FireFox. Simply launch it from IE.
|
|
|
|
|
|
|
|
Copyright © 2008 Xceed Software Inc.
|
|
|
|
Calendar
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat | | 30 | 31 | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 10 | 11 | 12 | | 13 | 14 | 15 | 16 | 17 | 18 | 19 | | 20 | 21 | 22 | 23 | 24 | 25 | 26 | | 27 | 28 | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| June, 2006 (1) |
| April, 2006 (2) |
| March, 2006 (2) |
| February, 2006 (2) |
| January, 2006 (4) |
| November, 2005 (3) |
| October, 2005 (3) |
| September, 2005 (6) |
| August, 2005 (2) |
| July, 2005 (4) |
| June, 2005 (4) |
| May, 2005 (3) |
| April, 2005 (10) |
| March, 2005 (4) |
| February, 2005 (4) |
| January, 2005 (3) |
| December, 2004 (4) |
| November, 2004 (5) |
| October, 2004 (4) |
| September, 2004 (7) |
|