Compact that Virtual PCLabels: How-To, Information, Tech
Is it Hammer Time?Labels: Information, Misc
DSOFile and Summary PropertiesYesterday I posted about using ShellExecuteEx to display the document properties dialog for a file or folder. Displaying the properties dialog is nice, however I also need to access the OLE document properties (Document Summary Properties), the information that is stored on the Summary and Custom tabs of the properties dialog. I did quite a bit of searching, reading and testing in an attempt to find a simple solution. I did get to learn a lot about the Document Summary Properties and it looked like my only option was to head down the road using the IPropertyStorage Interface. As I was testing some code I came across the best little ActiveX component that can quickly access the property information. The Dsofile.dll is an ActiveX component is available from Microsoft. They even include a sample application (although the samples are in VB6 and VB7 they are useable). This component simplifies (I like things simple) setting and getting the document summary properties. The KB article and download mention that this is meant for Office files, however I have found that it works fine on other files as well (the exception being that the Office documents seem to be the only types of files with Custom Properties). The other great thing about this component is that "You have a royalty-free right to use, to modify, to reproduce, and to distribute the Dsofile.dll sample file component and the C++ source code files in any way you find useful." That quote is taken directly from the DSOfile.dll Article ID: 224351 that is listed on Microsoft's Support site. My thoughts, why reinvent the wheel when you really don't need to. I highly recommend using this component if you need to access the extended properties. As usual, I whipped up a sample application testing this components functionality. The meager sample code is listed here (image of application screen is also included in this post):
public partial class Form1 : Form
...{
public Form1()
...{
InitializeComponent();
}
private void btnSelectFile_Click(object sender, EventArgs e)
...{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
...{
textBox1.Text = openFileDialog1.FileName;
toolTip1.SetToolTip(textBox1, textBox1.Text);
lstSummary.Items.Clear();
OleDocumentPropertiesClass odpc = new OleDocumentPropertiesClass();
odpc.Open(
@openFileDialog1.FileName,
false,
dsoFileOpenOptions.dsoOptionOpenReadOnlyIfNoWriteAccess
);
// Summary "Standard" Properties
ListViewItem lvTitle = new ListViewItem();
lvTitle.Group = lstSummary.Groups[0];
lvTitle.Text = "Title";
lvTitle.SubItems.Add(odpc.SummaryProperties.Title);
lstSummary.Items.Add(lvTitle);
ListViewItem lvSubject = new ListViewItem();
lvSubject.Group = lstSummary.Groups[0];
lvSubject.Text = "Subject";
lvSubject.SubItems.Add(odpc.SummaryProperties.Subject);
lstSummary.Items.Add(lvSubject);
ListViewItem lvCategory = new ListViewItem();
lvCategory.Group = lstSummary.Groups[0];
lvCategory.Text = "Category";
lvCategory.SubItems.Add(odpc.SummaryProperties.Category);
lstSummary.Items.Add(lvCategory);
ListViewItem lvKeyword = new ListViewItem();
lvKeyword.Group = lstSummary.Groups[0];
lvKeyword.Text = "Keywords";
lvKeyword.SubItems.Add(odpc.SummaryProperties.Keywords);
lstSummary.Items.Add(lvKeyword);
ListViewItem lvComments = new ListViewItem();
lvComments.Group = lstSummary.Groups[0];
lvComments.Text = "Comments";
lvComments.SubItems.Add(odpc.SummaryProperties.Comments);
lstSummary.Items.Add(lvComments);
ListViewItem lvAuthor = new ListViewItem();
lvAuthor.Group = lstSummary.Groups[1];
lvAuthor.Text = "Author";
lvAuthor.SubItems.Add(odpc.SummaryProperties.Author);
lstSummary.Items.Add(lvAuthor);
ListViewItem lvRevision = new ListViewItem();
lvRevision.Group = lstSummary.Groups[1];
lvRevision.Text = "Revision Number";
lvRevision.SubItems.Add(odpc.SummaryProperties.RevisionNumber);
lstSummary.Items.Add(lvRevision);
// Custom Properties
foreach (DSOFile.CustomProperty cp in odpc.CustomProperties)
...{
ListViewItem lvc = new ListViewItem();
lvc.Group = lstSummary.Groups[2];
lvc.Text=cp.Name;
lvc.SubItems.Add(cp.get_Value().ToString());
lstSummary.Items.Add(lvc);
}
odpc.Close(false);
}
}
}This is something I'd also like to port to Delphi for use in those applications. Maybe this week sometime......
ShellExecuteEX and ShellExecuteInfo RevisitedWho says history doesn't repeat itself? I am working on a project that needs to display and access file properties. A quick search through the .NET 2.0 Framework didn't yield anything that popped out. I did however, remember our wonderful friend - the SHELL32 API. Previously, I had posted on using ShellExecuteEX with Delphi (I am a huge Delphi fan; I may not be posting much about it recently, but I did not forget it). Here is that same sample in using C#.
public const uint SW_SHOW =0x5;
public const uint SEE_MASK_INVOKEIDLIST = 0xC;
public enum verbs
...{
properties,
open,
edit,
explore,
print
}
[StructLayout(LayoutKind.Sequential)]
public struct SHELLEXECUTEINFO
...{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
public String lpVerb;
public String lpFile;
public String lpParameters;
public String lpDirectory;
public uint nShow;
public int hInstApp;
public int lpIDList;
public String lpClass;
public int hkeyClass;
public uint dwHotKey;
public int hIcon;
public int hProcess;
}
[DllImport("shell32.dll")]
static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
public void MyShellExecuteInfo(string filename, verbs verb)
...{
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
switch (verb)
...{
case verbs.properties:
info.lpVerb = "properties";
break;
case verbs.print:
info.lpVerb = "print";
break;
case verbs.open:
info.lpVerb = "open";
break;
case verbs.explore:
info.lpVerb = "explore";
break;
case verbs.edit:
info.lpVerb = "edit";
break;
}
info.lpFile = filename;
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
ShellExecuteEx(ref info);
}
A ToolStripMenuItem and a CheckOne nice addition to the .NET 2.0 framework is the ToolStripMenuItem Class. The ToolStripMenuItem Class replaces the MenuItem Class. The ToolStripMenuItem has a Checked property that gets or sets a value indicating if the item is checked or not. Coupled with the ToolStripMenuItem.CheckOnClick property, this can be useful for identifying which item has been selected or activated. Unfortunately, there isn't some sort of 'allow only one checked' property which allows for only one item in a menu to be 'Checked' at a time. This functionality can be easily added in code (or you could derive your own class that has this functionality; a nice addition to a personal control library). Create an OnClick and OnCheckChanged method that are referenced by each of the ToolStripMenuItems. Basically, you check to see if the current item is checked or not and then toggle the check for the other menu items. You could also check another property of the sender (such as the Tag property) to perform some other action.
Here is an example:
private void CheckedIconsToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
...{
if (sender is ToolStripMenuItem)
...{
if (!((ToolStripMenuItem)sender).Checked) return;
foreach (ToolStripMenuItem item in (((ToolStripMenuItem)sender).GetCurrentParent().Items))
...{
if (item != null && item != sender && item.Checked)
...{
item.Checked = false;
return;
}
}
}
}
private void CheckedToolStripMenuItem_Click(object sender, EventArgs e)
...{
if (sender is System.Windows.Forms.ToolStripMenuItem)
...{
if (!((ToolStripMenuItem)sender).Checked)
...{
((ToolStripMenuItem)sender).Checked = !((ToolStripMenuItem)sender).Checked;
};
switch (((ToolStripMenuItem)sender).Tag.ToString())
...{
case "1":
break;
case "2":
break;
case "3":
break;
case "4":
break;
case "5":
break;
default:
break;
};
}
}
Who is copying your web site?In the academic world (and various other worlds as well) there are a number of tools that are used to detect plagiarism. Plagiarism comes in many forms and if you have a published web site it could be easy for some of your content to show up on another page. The vast number of web sites published makes it difficult to search them all. I came across this cool web site the other day. CopyScape is a web site that allows you to search for copies of your web site on the Internet. I have only experimented with the free CopyScape service, and it seems pretty cool.
Labels: Information, Internet, Misc
A little FileIconInit for the System Image listI can hear the theme for Welcome Back Kotter as I write this. It seems that things have been quiet on here for quite sometime, however those that know me can attest to the fact that life away from here has been anything but that. Thankfully, things have returned to a point where a lot more content should find its way onto this world.
As things progress, I am doing a substantial amount of development using C# and the .NET Framework. I still continue to develop new utilities that satisfy my specific needs. I also try to update some of the available utilities. The most recent update has been to ASysIcon (this application was redeveloped using the .NET Framework). I've done quite a bit of work with icons in the past (also take a look at AIconExtract) and ASysIcon is a utility that displays the images found in the system image list. The system image list contains the icons that are associated with the different registered file types on a computer.
The retrieval of the system image list is a straight forward process. There are a few unmanaged API calls that work quite well for this task. One thing that may not be so apparent is that the the images retrieved will be those images that are cached in the system image list. If the system hasn't had a need to load them, well, they won't be in the list. This could pose some concern if you would like to retrieve the entire list. The trick is to get all the system images cached. Actually, this isn't really a trick. With little searching the FileIconInit function surfaces. This function will initialize or reinitialize this image list. FileIconInit has a boolean parameter determines if it should restore the image cache from disk. Passing true as this parameter will load all of the images, not just the recently cached ones. The following sample code shows how to display the system image list in a ListView:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices; // http://support.microsoft.com/kb/319350
namespace SysIcons1
...{
public partial class Form1 : Form
...{
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
...{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
//Retrieve the handle to the icon that represents the file and the index of the icon within the system image list
public const uint SHGFI_ICON = 0x100;
// Retrieve the index of a system image list icon.
public const uint SHGFI_SYSICONINDEX = 0x4000;
// Large icon
public const uint SHGFI_LARGEICON = 0x0;
// Small icon
public const uint SHGFI_SMALLICON = 0x1;
public const uint LVM_FIRST = 4096;
public const uint LVSIL_NORMAL = 0;
public const uint LVSIL_SMALL = 1;
public const uint LVM_SETIMAGELIST = LVM_FIRST + 3;
// need this style on Windows 9x
public const uint LVS_SHAREIMAGELISTS = 0x0040;
// Retrieves information about an object in the file system, such as a file, folder, directory, or drive root.