BPSoftware.com
Home   Utilities   Purchase   FAQ   Support   Contact        
Shareware Utilities
 APrintDirect
 AIconExtract
 AFile Attribute Manager
Freeware Utilities
 AddrMon
 AFileSync
 ASysIcon
 B&P Table Utilities
 BPACLer
 BPSNMPMon
 BPSNMPUtil
 CharCount
 Delphi® Components
 MacAddr
Miscellaneous
 BPSoftware Blog
 Purchase Shareware
 Support

 


Sunday, November 27, 2005
Delphi® and GetExplicitEntriesFromAcl

I have been doing quite a bit with the Windows® security (see BPACLer and an earlier post). At first glance it may seem complicated (what other unknowns don’t) but once you get the hang of it, it is rather ‘logical’. Each object has its own Access Control List (ACL) that either grants or denies access (via authorization) to that object.
ACLs are made up of Access Control Entries (ACE). Each ACE contains the authorization information for a Trustee. An ACE can either be an Effective or Explicit entry. In order to retrieve a list of Explicit Entries (EA) a call to GetExplicitEntriesFromAcl does the trick. I haven’t seen many examples (actually I haven’t seen a one) of using this from within Delphi® so I figured I’d post a sample use of this API. If you have any questions please let me know.

procedure GetEAList(filename: string; listbox: TListBox);
type
TEAArray = Array [0..0] of EXPLICIT_ACCESS;
PEAArray = ^TEAArray;

var
pSD: PSECURITY_DESCRIPTOR;
countofexplicitentries: Cardinal;
ListOfExplicitEntries: PEXPLICIT_ACCESS_A;
EAList: PEAArray;
pExplicitAccess: EXPLICIT_ACCESS;
pDACL: PACL;
i: integer;

begin
if not (GetNamedSecurityInfo(PChar(filename),SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION, Nil, Nil, @pDACL, Nil,
pSD)=0) then
Exit;

GetMem(ListOfExplicitEntries,SizeOf(ListOfExplicitEntries));

if GetExplicitEntriesFromAcl(pDACL^,countofexplicitentries,@EAList) <>
ERROR_SUCCESS then
begin
FreeMem(ListOfExplicitEntries);
Exit;
end;

if (countofexplicitentries > 0) then
for i:= 0 to countofexplicitentries - 1 do
begin
pExplicitAccess:= EAList[i];
listbox.Items.Add(GetName(pExplicitAccess.Trustee.ptstrName));
case pExplicitAccess.Trustee.trusteetype of
TRUSTEE_IS_USER: listbox.Items.Add('TRUSTEE_IS_USER');
TRUSTEE_IS_GROUP: listbox.Items.Add('TRUSTEE_IS_GROUP');
TRUSTEE_IS_DOMAIN: listbox.Items.Add('TRUSTEE_IS_DOMAIN');
TRUSTEE_IS_ALIAS: listbox.Items.Add('TRUSTEE_IS_ALIAS');
TRUSTEE_IS_DELETED: listbox.Items.Add('TRUSTEE_IS_DELETED');
TRUSTEE_IS_INVALID: listbox.Items.Add('TRUSTEE_IS_INVALID');
TRUSTEE_IS_WELL_KNOWN_GROUP: listbox.Items.Add('TRUSTEE_IS_WELL_KNOWN_GROUP');
TRUSTEE_IS_UNKNOWN: listbox.Items.Add('TRUSTEE_IS_UNKNOWN');
end;

case pExplicitAccess.grfAccessMode of
NOT_USED_ACCESS: listbox.Items.Add('NOT_USED_ACCESS');
GRANT_ACCESS: listbox.Items.Add('GRANT_ACCESS');
SET_ACCESS: listbox.Items.Add('SET_ACCESS');
DENY_ACCESS: listbox.Items.Add('DENY_ACCESS');
REVOKE_ACCESS: listbox.Items.Add('REVOKE_ACCESS');
SET_AUDIT_FAILURE: listbox.Items.Add('SET_AUDIT_FAILURE');
end;
end;

FreeMem(ListOfExplicitEntries);
end;


In my opinion, it is extremely important that an administrator reviews the ACLs for the objects within their directory. I have been often times surprised by some of the holes that are permission based. It is not always that a Trustee would intentionally cause damage, however accidents do occur. There are also those cases where a Trustee may be impersonated and well, we all know what happens from there. I am often criticized for being overly restrictive, however I feel it is better to be safe than sorry. After all, people don't typically tell you what then can do, rather what they can't.

Labels: , ,

posted by Brad Prendergast at 9:44:00 AM (5 comments)
Links to this post
Permalink
Saturday, November 26, 2005
Ode to Backup

Backing up your files is probably one of the most important things you could do. There is nothing worse than the feeling of loosing everything once your computer is on the fritz (I have received dozens of frantic phone calls asking how to, or if I can restore files). There are many commercial, shareware and freeware backup programs that are capable of accomplishing appropriate backups.

XCOPY has been included in the Microsoft® OS family since the DOS days. This little command prompt program copies file and folder information based upon the selection of a number of parameter options. This utility is lightweight and consists of a simple executable file. This is actually what I use for incremental backups of my personal system.

Open a command prompt and type xcopy /?. This displays a list of the xcopy.exe parameter options and their meaning. From the command prompt you can easily type in your xcopy command with the necessary parameters and you’ll be on your way. Personally I have a small batch file called backup.bat. This eliminates a lot of typing and the need to remember the options. This batch file is simple but extremely effective in giving me copies of the files I need. It consists of one line:

xcopy *.* c:\wutemp /S /C /I /M /F /R /H /K /Y /EXCLUDE:exclude.txt

My backup.bat file is located in the root of my profile directory, for this example we’ll say C:\Documents and Settings\BP. If you follow the parameters you’ll notice that I copy all the changed (archive bit set) files to C:\wutemp. This batch file will process the current folder and with the appropriate parameter set (/S) all subfolders as well. Once the backup batch is complete I them manually copy the files to DVD. If you are running a ‘full’ backup you would remove the /M parameter option.
I am fairly structured and organized when it comes to my files. I try to get everything that I need somewhere within the tree of my profile directory. Most applications these days store personal information in the Application Data of your profile directory or allow you to specify a ‘storage’ location. A lot of applications also store temporary or unimportant (unimportant in the sense of need to restore files in the event of a need to recover files) files in your profile directory as well. This is where the /EXCLUDE parameter comes into play. After all, it is really wasteful both time and space wise to back up all those temporary files.
My exclude.txt file looks something like this:

\DOCUME~1\BP\Templates
\DOCUME~1\BP\Start Menu
\DOCUME~1\BP\Recent
\DOCUME~1\BP\SENDTO
\DOCUME~1\BP\PrintHood
\DOCUME~1\BP\NetHood
\DOCUME~1\BP\Windows
\DOCUME~1\BP\Cookies
\DOCUME~1\BP\MY DOCUMENTS\MY MUSIC
\DOCUME~1\BP\LOCAL SETTINGS\TEMPORARY INTERNET FILES
\DOCUME~1\BP\LOCAL SETTINGS\TEMP
\DOCUME~1\BP\LOCAL SETTINGS\HISTORY

This is just an example on how I use XCOPY to back up my important files on my personal computer. In my opinion this is fast, lightweight and effective when it comes to storing those must have files. I hope this gives you some ideas on how you could possibly implement a simple backup solution.

Labels: , , , ,

posted by Brad Prendergast at 5:53:00 AM (0 comments)
Links to this post
Permalink
Friday, November 25, 2005
What's on my spreadsheet?

The ability for separate applications to ‘communicate and interact’ is well, neat. I have recently found and increasing need to create/manipulate some pretty involved Microsoft® Excel spreadsheets from a Delphi® application. The drive for this post is to show some simple sample code lines to accomplish tasks within Excel.

Besides searching newsgroups you can also search MSDN for additional commands. I have found the simplest way is to record a macro and the use the Visual Basic Editor to see the code commands created in the macro.

Delphi® includes Microsoft® Office® server controls. Keep in mind that these controls work with a specific version of Office. If you need to target additional versions, I have found variants to work well for manually creating the right version, for example:

uses
ComObj;

var
ExcelApplication: Variant;

ExcelApplication := CreateOleObject('Excel.Application.7');
ExcelApplication := CreateOleObject('Excel.Application.8');
ExcelApplication := CreateOleObject('Excel.Application.9');

For the most part I generally drop a TExcelApplication control onto a form and go from there. I guess you could also use TExcelWorkbook and TExcelworksheet components as well, but I prefer to manually create those. To create a new Workbook and Worksheet:

var
ExcelWorkBook1: TExcelWorkbook;
ExcelWorkSheet1: TExcelWorksheet;
lcid: integer;

begin
lcid := GetUserDefaultLCID;
ExcelApplication1.Visible[lcid] := true;
ExcelWorkBook1 := TExcelWorkBook.Create(ExcelApplication1);
ExcelWorkBook1.ConnectTo(ExcelApplication1.Workbooks.Add(EmptyParam, lcid));
ExcelWorksheet1 := TExcelWorksheet.Create(ExcelApplication1);

{The next line creates a new worksheet in a workbook}
ExcelWorkbook1.Sheets.Add(EmptyParam, EmptyParam, EmptyParam, EmptyParam, LCID);

{The next line attaches to the active worksheet in the workbook}
ExcelWorkSheet1.ConnectTo(ExcelWorkbook1.ActiveSheet as _Worksheet);

ExcelWorkSheet1.Name := 'My Sheet';
end;

The following are additional syntax examples:

var
{r and c are the row and column indexes}
r, c: Integer;
begin
{Freeze panes}
ExcelApplication1.ActiveWindow.FreezePanes:= true;

{Select range and merge cells}
ExcelWorkSheet1.Range[Excelworksheet1.Cells.Item[r-1,c-2], Excelworksheet1.Cells.Item[r,c]].Merge(true);

{Autofit a column}
Excelworksheet1.Cells.Item[r,c].EntireColumn.AutoFit;

{Hide column}
Excelworksheet1.Cells.Item[r,c].EntireColumn.Hidden:= True;

{Format cells}
{I typically never use with statements, in fact I despise them. I generally only use them when I am working with Excel cells. The following all don’t need to be used together, they are meant to be used as examples to pick and choose from.}

with Excelworksheet1.Cells do
begin
Item[r,c].HorizontalAlignment:= xlRight;
Item[r,c].Value:= 'somevalue';
Item[r,c].Font.Bold:= False;
Item[r,c].Font.Underline:= False;
Item[r,c].Font.Size:= 8;
Item[r,c].Font.ColorIndex:= 2;
Item[r,c].Interior.ColorIndex:= 1;
Item[r,c].NumberFormat:= '#,##0.00_);[Red](#,##0.00);-';
end;

{Add a formula to a cell, the cell ranges are offset from the current cell }
Excelworksheet1.Cells.Item[r,c].FormulaR1C1:= ‘=SUM(R[-12]C:R[-1]C)';

{Open a preexisting file}
ExcelApplication1.Workbooks.Add(filename,0);

ExcelWorksheet1.Free;
ExcelWorkBook1.Free;
end;

This sample was merely intended to give some general ideas and hopefully spark interest in working with Excel worksheets from within a Delphi® application. There seems to be an endless array of commands that you can use to communicate with a spreadsheet.

Labels: , ,

posted by Brad Prendergast at 6:43:00 AM (0 comments)
Links to this post
Permalink
Saturday, November 19, 2005
Free Gems Do Exist (Part 2)

As time progresses I am becoming far more comfortable and impressed with Microsoft’s Log Parser. For one thing, I am awed at how it processes so fast. I processed 404 logs (over 4.6GB) in seconds, not minutes or hours as I had first thought. The command line syntax is also quite powerful. This tool allows you to precisely and quickly analyze data without a lot of overhead.

I had mentioned in the first post that another bonus is the COM interface. All the power of this command-line tool can be wrapped in a nice GUI interface, which is exactly what I had done. After reading about the interfaces in the accompanying help file I imported the Type Library for Log Parser into Delphi® (I have Delphi® 5 and Delphi® 2005 installed on my development machine and I worked with Log Parser in both versions) and installed the new components. One thing to note is that the TLogQueryClass is the only class that seemed to work properly after importing the library. Each of the Input and Output format components kept on reporting invalid log formats when I passed them to the TLogQueryClass method. I tried numerous ways to get them working. This was/is more of a nuisance more than anything else. The help file clearly lists (I have also included them in this source) the classes which can easily be created as variants.

To get started with a simple example of how to use the Log Parser interfaces I started a new application and placed a TFileEdit, TStaticText, TListBox and three (3) TButtons on the TForm. Rather than go into a verbose explanation I figured I’d just post some sample code and let the code explain what is going on. Here is a wonderful byproduct of living in the 21st century; there is no guarantee or warranty, expressed or implied, concerning the applicability of code and techniques included in this example. This example code is supplied AS IS. If you wish to use this code or technique, it is your responsibility to test and certify the code in your project.

Button1 (caption:= ‘Output’) shows how to use an InputContextClass and OutputContextClass.
Button2 (caption:= ‘LogParser Version’) shows how to retrieve the version information from TLogQueryClass.
Button3 (caption:= ’Record’) shows how to retrieve the information into a RecordSetClass from an InputContextClass and iterate through each of the records. In this example I add each entry to a list box.


uses
ComObj;

{
Input Formats:
ADS : MSUtil.LogQuery.ADSInputFormat
BIN : MSUtil.LogQuery.IISBINInputFormat
CSV : MSUtil.LogQuery.CSVInputFormat
ETW : MSUtil.LogQuery.ETWInputFormat
EVT : MSUtil.LogQuery.EventLogInputFormat
FS : MSUtil.LogQuery.FileSystemInputFormat
HTTPERR : MSUtil.LogQuery.HttpErrorInputFormat
IIS : MSUtil.LogQuery.IISIISInputFormat
IISODBC : MSUtil.LogQuery.IISODBCInputFormat
IISW3C : MSUtil.LogQuery.IISW3CInputFormat
NCSA : MSUtil.LogQuery.IISNCSAInputFormat
NETMON : MSUtil.LogQuery.NetMonInputFormat
REG : MSUtil.LogQuery.RegistryInputFormat
TEXTLINE : MSUtil.LogQuery.TextLineInputFormat
TEXTWORD : MSUtil.LogQuery.TextWordInputFormat
TSV : MSUtil.LogQuery.TSVInputFormat
URLSCAN : MSUtil.LogQuery.URLScanLogInputFormat
W3C : MSUtil.LogQuery.W3CInputFormat
XML : MSUtil.LogQuery.XMLInputFormat

Output Formats:
CHART : MSUtil.LogQuery.ChartOutputFormat
CSV : MSUtil.LogQuery.CSVOutputFormat
DATAGRID : MSUtil.LogQuery.DataGridOutputFormat
IIS : MSUtil.LogQuery.IISOutputFormat
NAT : MSUtil.LogQuery.NativeOutputFormat
SQL : MSUtil.LogQuery.SQLOutputFormat
SYSLOG : MSUtil.LogQuery.SYSLOGOutputFormat
TPL : MSUtil.LogQuery.TemplateOutputFormat
TSV : MSUtil.LogQuery.TSVOutputFormat
W3C : MSUtil.LogQuery.W3COutputFormat
XML : MSUtil.LogQuery.XMLOutputFormat
}

procedure TForm1.Button1Click(Sender: TObject);
var
szQuery: WideString;
pObjectInput,
pObjectOutput: variant;

begin
szQuery:= Format('SELECT c-ip FROM %s GROUP BY c-ip',[FileEdit1.Text]);

pObjectInput:= CreateOleObject('MSUtil.LogQuery.W3CInputFormat');
pObjectOutput:= CreateOleObject('MSUtil.LogQuery.DataGridOutPutFormat');

LogQueryClass1.ExecuteBatch(szQuery,pObjectInput,pObjectOutput);

pObjectInput:= Unassigned;
pObjectOutput:= Unassigned;
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
StaticText1.Caption:= Format('LogParser Version %d.%d',[LogQueryClass1.versionMaj,LogQueryClass1.versionMin]);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
szQuery: WideString;
pObjectInput,
LogRecordSet,
oRecord: variant;

begin
szQuery:= Format('SELECT c-ip FROM %s GROUP BY c-ip',[FileEdit1.Text]);

pObjectInput:= CreateOleObject('MSUtil.LogQuery.W3CInputFormat');

LogRecordSet:= LogQueryClass1.Execute(szQuery,pObjectInput);

while not LogRecordSet.atEnd do
begin
oRecord:= LogRecordSet.GetRecord;
ListBox1.Items.Add(oRecord.GetValue(0));
{ ListBox1.Items.Add(oRecord.ToNativeString(',')); }
{ The ToNativeString method retrieves the entire record with each value separated by the specified delimiter. }

LogRecordSet.MoveNext;
end;

pObjectInput:= Unassigned;
oRecord:= Unassigned;
LogRecordSet:= Unassigned;
end;


If anyone else does anything with this interface I welcome the posting of examples. This is it for now, time to go off and expand upon the BPACLer application.

Labels: , , , , ,

posted by Brad Prendergast at 8:17:00 AM (1 comments)
Links to this post
Permalink
Thursday, November 17, 2005
Free Gems Do Exist (Part 1)

This is another post that I write with much enthusiasm and excitement. Recently, I was in search of a versatile log analyzer. In the past, I have used AWSTATS for analysis if IIS logs. In this instance, my needs involved the analysis of ISA log files. AWSTATS is an excellent open-source Web and E-Mail log analyzer (that I highly recommend, and often do), but it just didn’t give me what I was looking for when it came to ISA logs.

My journey through the Internet, via Google, in search of an ISA log analyzer ensued. I read through many feature lists and articles, viewed a number of screenshots, for various programs that were capable of processing ISA logs. Then it happened. I stumbled across one of the most flexible, free forming analyzers I had come across. This program also has the feature of being absolutely FREE. The program I am talking about is Microsoft’s Log Parser. Yes, Microsoft does have those hidden GEMS that are free.

At first glance, this command-line utility does appear overwhelming and complicated. Once I dug into its functionality and discovered its wide range of use, the only overwhelming feeling I had was excitement. This command-line utility can extract information from many common log ‘file’ types. Log Parser also includes a variety of output options (NAT, DATAGRID and CHART are my favorite). The data that is outputted is determined through a SQL like syntax, which is very powerful and FAST. I was able to process inquiries across large (individual) log files, in a directory, in a matter of seconds (600K records in multiple files took under 5 seconds to process). This program handles such a range of log ‘file’ types that I find myself using it constantly throughout the day.

A couple basic examples of Log Parser’s use are:







Log Parser is also accessible via COM interface. Once I came across this information in the help file my excitement level raised to a new high. The power of this command-line utility can be easily ‘wrapped’ in a GUI interface.
I am a Delphi fanatic, as many know, and love to incorporate it into everything. After reading up on the Log Parser classes I quickly imported the Type Library and threw together a sample application. Unfortunately, this is long enough as it is and the sample Delphi code will have to wait for Part 2 of this posting…

In the meantime I highly recommend downloading Microsoft’s Log Parser and becoming familiar with it. Once you do I am sure you will use it often.

Labels: , ,

posted by Brad Prendergast at 8:29:00 PM (0 comments)
Links to this post
Permalink
Monday, November 14, 2005
What’s in a word?

Language is truly a fascinating form of communication. The way individual words have meaning and that those same words can be put together to form sentences is ingenious. Have you ever thought about the origin of language? Who came up with the different words and their meanings?

A few years ago I started browsing through the dictionary for additional words to add to my vocabulary. The English language is comprised of an abundance of words, some of my favorites include: malodorous, pusillanimous, recreant, fustigate, nescience and panjandrum. (There are many more, however, that would force this to display well beyond physical limits.) Through my journeys I was elated when I had come across the Merriam-Webster Online Dictionary. Not only does this site contain a searchable dictionary and Thesaurus, but it also includes a Word of the Day. What better way is there to increase your vocabulary?

The Merriam-Webster Word of the Day is available online and you can also subscribe to the Word of the Day list and have the word delivered to your e-mail daily. The listing includes the word, its definition and a sample use of the word. I have entries in my Word of the Day email folder that date back to 2003. I am one that actually tries to use the word of the day in my normal course of action. I look forward to seeing what word appears each morning.

I highly recommend subscribing to the Merriam-Webster Word of the Day. It is a great way to pick up some of the neat words within the English language.

Labels: , ,

posted by Brad Prendergast at 7:39:00 PM (0 comments)
Links to this post
Permalink
Friday, November 11, 2005
Delphi™ Components and Freeware Updates

I often reflect back on a business trip I had made to Mexico. I was ‘fortunate’ enough to not be in a so called ‘tourist area’, but the actual inner-sanctum of the country. One evening on the way to a local dining delight I asked our host if we could drive around and see authentic scenery and culture. By US standards, most people had nothing. There were cinder block houses with open window and doorways. I even thought I saw a few cardboard box expansions. One thing that I did notice was a sense of family and content. Most seemed happy with just that and their few material possessions. Most of the residents I had come across were very relaxed and happy to enjoy their family and health. There was a definite lack of pressure and 'rat' race to constantly get ahead (and obtain more). The older one gets, the faster the perception of time moves along. Seasons come and go in the blink of an eye, without most evening noticing. Which is culturally correct the appreciation of what you have or the constant pursuit and wanting of more?
-=-=-=-

Today’s updates include two Delphi™ Components and one of the Freeware utilities listed on this website.

TDBDateTimePicker has been updated to correct an issue that would cause the value to not be updated when you scrolled through a recordset. The correct value was stored; it was just the value not being displayed unless you were in a state of dsEdit.

TDBSpinEdit has also been updated. This update includes a change to the display (not stored) value on a new record. Also corrected was an issue that forced the need to click the up or down button twice to move the value. This is because the first would put the State to dsEdit and the second changed the value. This is all done on once click now.

BPACLer is the Freeware utility that has been updated. A number of minor enhancements and adjustments were made. The most notable enhancement is the ability to print the ACL information. A Check Version option has also been added to the Help menu. This allows you to check if you have the most current version of the software without the need to constantly check this website. This version checking does not send any information. It simply queries (GET) a web document containing product version information and compares it to your version. It is basically the equivalent of browsing and reading a web page. I am pleased with the feedback I have received regarding this utility.

If you have any comments or suggestions regarding this, or anything listed on this website please let me know.

Labels: , , ,

posted by Brad Prendergast at 12:48:00 PM (0 comments)
Links to this post
Permalink
Sunday, November 06, 2005
FASTMM Saves the Day

Back in October, Borland® announced a new version of Delphi®. ‘Delphi 2006’, codename Dexter, looks like it is going to contain a number of significant fixes and enhancements. These enhancements look like they will greatly enhance Delphi®’s rapid application development (RAD) environment.

FASTMM is an opensource replacement memory manager for Borland® Delphi® (Thanks Pierre for an excellent project!). Not only does it noticeably increase speed, but it also assists with the debugging and logging of memory leaks. FASTMM discussions have been circulating for a while. Discussions about and attention to FASTMM have recently increased with the indications that FASTMM will be included in the next Delphi® release.

My curiosity was peaked, and I started working with FASTMM over the past week. I must admit that I am impressed. I am also excited to see how it will integrate with the newest release of Delphi®. I started working with some small applications to get the feel for its usage and functionality. In one of my tests I dropped a TShellComboBox, TShellListView and TShellListView from the Samples palette onto a TForm and ran the application. Much to my surprise, I received FASTMM notification of memory leaks when I closed the application. Considering I hadn’t written one line of code, I must admit I was skeptical of the leak. In order to give myself piece of mind (and boost confidence in FASTMM) I decided to look through ShellCtrls.pas to see if anything stood out. I also searched the web and QualityCentral (QC) to see if there were any fixes or posts discussing memory leaks with theses sample controls. I did find some old posts and fixes, however none related to my situation.

In reviewing ShellCtrls.pas I did notice a few things that potentially validated the memory leaks noted by FASTMM. I changed some code (listed below), recompiled and reran my sample application. I was pleasantly surprised to see that the dialog box (that causes your heart to drop) displaying memory leak information no longer appeared. I consider this my indoctrination to using FASTMM. Thus far I am very pleased with it and look forward to the continued use of this excellent memory manager.

The changes I made to ShellCtrls.pas:

In TCustomShellTreeView add:
public
destructor Destroy; override;

destructor TCustomShellTreeView.Destroy;
begin
if
Assigned(FRootFolder) then
FRootFolder.Free;
inherited;
end;



In TCustomShellListView change:
destructor TCustomShellListView.Destroy;
begin
ClearItems;
FFolders.Free;
inherited;
end;


to

destructor TCustomShellListView.Destroy;
begin
ClearItems;
FFolders.Free;
if Assigned(FRootFolder) then
FRootFolder.Free;
inherited;
end;



In TCustomShellComboBox change:

destructor TCustomShellComboBox.Destroy;
begin
inherited Destroy;
if Assigned(FImageList) then FImageList.Free;
end;

to

destructor TCustomShellComboBox.Destroy;
var
i: integer;
begin
for i := 0 to Items.Count - 1 do
if Assigned(Folders[i]) then Folders[I].Free;
if assigned(FRootFolder) then FRootFolder.Free;

inherited Destroy;
if Assigned(FImageList) then FImageList.Free;
end;

Labels: , , ,

posted by Brad Prendergast at 9:11:00 PM (2 comments)
Links to this post
Permalink
Saturday, November 05, 2005
Shareware: APrintDirect version 5.1.2.7 available

An update to the popular shareware application APrintDirect is now available for download. APrintDirect is a feature rich 32-bit utility that allows you to easily print or save a customizable listing of the files contained in any folder (directory) on your computer. A number of enhancements and internal adjustments have been included in this latest release (see the history section of the help file). Notable enhancements include:

The addition of a parameter option was intended to allow for APrintDirect to be used as a SendTo option from Windows® Explorer (for information on setting up a SendTo shortcut visit My Send To).
APrintDirect now makes it easier to ensure that you’re using the current version through a Check Version option from the Help Menu.
A sortable file Owner column has been added to the list of available output columns.
Total file, size and folder count option has been added back to the listing.

Existing users should take advantage of the latest APrintDirect enhancements by upgrading to the latest version. If you have not yet tried APrintDirect download it today and try a fully functional version free for 30 days.

As always if you have any questions, comments or suggestions about APrintDirect please let me know. User feedback is vital to the continued development of quality software.

Labels: , ,

posted by Brad Prendergast at 9:57:00 PM (0 comments)
Links to this post
Permalink
Tuesday, November 01, 2005
Conditional Format

Spreadsheets are a great tool for analyzing, manipulating and 'testing' data. Over the years there have been significant advances in spreadsheets that allow for a lot more functionality. Based upon my interactions, I feel comfortable in saying that most people are unaware of and/or greatly underutilize many of the functions and features found in most popular spreadsheet applications.

I often find myself importing thousands of records from databases into a Microsoft Excel spreadsheet for some random ‘quick and dirty’ analysis or output. Once in Excel the possibilities are almost limitless when it comes to the representation of this data, whether it’s via pivot table, charts or just a plain old on the fly filtered list.

When viewing thousands of records it is more often palatable and easier on the eyes with some sort of visual record cues. For example, let’s say we’re looking at a list of customer invoice records, which are sorted by customer. Ata quick glance, by default, all the rows generally look the same. Without intense scrutiny the data sort of, blends all together. What about some sort of visual separation by customer? Excel does have Grouping and AutoFormat, however sometimes the grouping can be overkill. All those '+' and '-' can become overwhelming. Don’t get me wrong grouping is great for adding fast subtotals. The AutoFormat is nice for shading; however it shades on alternating records. What if you wanted to shade groups of records? One answer.. Conditional Formatting.

Conditional Formatting allows you to specify cell properties based upon certain value or formula information. After playing with it a bit, one can see that it is really quite useful. For my scenario:


  1. Sort your list
  2. Insert what I call tend to call a 'Helper Column'. This column is typically hidden, and used to indicate when there is a change in a specific value, i.e. Customer. I prefer to put this column as the first or last column in the data set. For this example we will use column A.

  3. Type the following formula in the first data row. We’ll start on row 2 because row 1 is our 'header': '=MOD(OFFSET($A2,-1,0)+ OR($B2<>OFFSET($B2,-1,0),0),2)'. Basically what this formula is saying is alternate 1 or 0 in this cell A2, based upon B2 being different than B1. (The $ before a cell RC value indicates that value is constant (will not change) when it is copied to another cell.)

  4. Copy (or use Fill Down; Ctrl + D) the formula down the entire length of the data set. There are a number of shortcuts that make this easier (Mental note: discuss some quick keyboard navigation another time).

  5. Select the entire data set including the 'Helper Column', making sure you have the upper rightmost column active, A2 in this example.

  6. Select Conditional Formatting… from the Format menu item.

  7. Change condition 1 to ‘Formula is’ and enter ‘=$A2=1’. We’re only going to apply the format if the cell value is 1. The 'Helper Column' should be alternating 0 and 1 on a change in Customer.

  8. Click the Format button

  9. Select the Pattern Tab

  10. Choose which color to fill the cell with when the condition is met

  11. Click Ok to close the Format dialog

  12. Click Ok to close the Conditional Format dialog.



Your spreadsheet should now be shaded by Customer.
This is just a simple sample of using the Conditional Formatting feature of Micrsoft Excel. There are a number of other properties that can be set based upon value or calculated formula that greatly enhances the representation of a data set in Excel.

Labels: , , ,

posted by Brad Prendergast at 6:51:00 PM (1 comments)
Links to this post
Permalink
Recent Posts
 Command Line: Visual Source Safe
 SQL: Remove / Delete Orphan Users
 SQL Delete/Drop a User from each Database
 Is there an 'I' in phone?
 SQL Optimization: Am I missing any indexes? - Part...
 Meaningful Signature File Quotes
 My Shared RSS Items
 Edit those XML files
 A little System.Diagnostics
 Wi-Fi Detector Shirt


Labels



Archives
 October 2005
 November 2005
 December 2005
 January 2006
 February 2006
 March 2006
 April 2006
 May 2006
 June 2006
 July 2006
 August 2006
 September 2006
 December 2006
 January 2007
 February 2007
 March 2007
 September 2007
 October 2007
 November 2007
 July 2008
 November 2008
Powered by Blogger