13 September, 2007

Altering QueryString with Javascript

I'm making a grid in a legacy ASP application. That's right kids, ASP as in Active Server Pages. Old School stuff. None of the ultra-kewl .NET does it all for you stuff. Which, BTW, I really love because I can do so much more so much faster and better. But, alas, the client says "ASP only!" so, I go with ASP.

What they wanted to do was display their data in a grid. Yeah, right, ASP and datagrids, that is a rich one. So what I did was something almost, but not quite, completely different from a grid. I put their data in an HTML table with alternating row colors (hey, at least they think it's in a grid).

But of course, in any good datagrid, one must be able to click on the grid headers and VOILA! the grid sorts by the header clicked. This, I figured out, to do with JavaScript via the QueryString. The table is in an IFRAME, so the URL is never displayed to the end user. Doing this keeps the displayed URL fairly nice and neat. My original though, which seemed to work fairly well was just to cut the QueryString off at the desired parameter, and replace that parameter with the new one as such:

function reSortGrid(str)
{
var idx = location.href.indexOf('&sortby');
var loc = location.href.substring(0, idx);
loc = loc + '&sortby=' + str;
location.href = loc;
}
But then it occurred to me that if any parameters existed beyond the parameter I was replacing, they would be lost. No, what I really needed was to replace the existing sortby parameter within the actual string, while maintaining the rest of the string. This took a little thinking on my part, and what I came up with is:
function resortGrid(str)
{
var newQueryString = '';
if (location.href.indexOf('&sortby') > -1)
{
var locArray = new Array();
locArray = location.href.split('&');
for (i=0; iif (locArray[i].indexOf('sortby') > -1)
{
locArray[i] = "sortby=" + str;
// check to see if this is the start of the QueryString
if (locArray[i].indexOf('?') == -1)
newQueryString = newQueryString + '&' + locArray[i];
else
newQueryString = locArray[i];
}
}else{
newQueryString = location.href + '&sortby=' + str;
}
location.href = newQueryString;
}
So what I ended up with is a QueryString with the correct parameter changed, and the rest of the parameters intact.

14 August, 2007

Old School

This is becoming less of a blog about C# lately, and more of a blog about web programming in general. Hopefully I'll get back to C# sooner, rather than later.

Currently, my project is to enhance and expand an application written in ASP and...get this...Delphi!!!! And I have to tell you, after using C# and ASP.NET for the past 5 years, ASP SUCKS!! And not just a little, ASP sucks splendidly!

The current problem I fighting with is accessing an MS Access database via ASP. Getting there the first time isn't too bad, but when I try to get to it a second time, it blows up with that oh so informative error of:
Provider error '80004005'

Unspecified error

/Bridge/facilities/facmain.inc, line 13

Now, if Microsoft could any more ambiguous, I don't know how. It looks like I'm going to have make a separate DB connection for each RecordSet. This seems stupid and entirely inefficient, but as Duke Wayne said "A man's gotta do what a man's gotta do."

I am seriously not wanting to delve into that Delphi portion of the project, but that is coming soon enough.




Technorati Tags: , ,

01 August, 2007

Basic blog DB diagram

As promised, here is a basic diagram for my blog database. I still need to look at creating user role permissions (like can they post? can the delete comments? etc.). If you have any ideas, please through them my way!



Anyway, it is a start. I almost forgot to include a configuration table for the blog! This would have been a dire thing indeed without that!

Anyway, there you have it. If anyone is actually reading this stuff, please let me know what you think (I am no database guy), and offer suggestions!

Thanks.

Loading themes programmatically

I've made a little progress on the database diagram, but admittedly, that is not the most fun aspect of life. So I have been concentrating a little more the past couple of days on the front-end. I have been playing with themes, skins and styles.

One thing I wanted to be able to do was change the theme programmatically. I know this should be possible, but dog-gone if it wasn't a pain in the arse to figure out! After oodles of Google searches, I finally found this article at ASP Alliance. Good detail and explains a lot.

In a nutshell, the best way to programmatically change themes is to create a base class. I named mine BasePage.cs. This class inherits System.Web.UI.Page. Other pages (not the .master pages though) inherit this BasePage instead of System.Web.UI.Page. Wait, I think I'm getting a little ahead of myself. Let me back up a little.

One can programmatically set the page theme (this.Page.Theme), but only in the Page_PreInit(object sender,System.EventArgs e) method. However, this method is not valid in any .master file. Therefore, this method must be set in the actual .aspx files. There are two ways to do this, put the Page_PreInit method in each and every page file. This can cause issues if one wants to change the way they programmatically load themes (i.e., via cookies, session, use-select etc). The better way is to create a separate class which inherits from System.Web.UI.Page, and which all other .aspx classes inherit (instead of the System.Web.UI.Page class).

Okay, now that I have that out of the way, back to the meat. In the BasePage class I added a Page_PreInit method. In this method is where I set the theme.

void Page_PreInit(object sender, System.EventArgs e)
{
if (Session["Theme"] != null && Session["Theme"].ToString().Length > 0)
this.Page.Theme = Session["Theme"].ToString();
else
this.Page.Theme = "Default";
}

For testing, I created three themes, Default, LeftSidebar and RightSidebar. In the .master file I created a table with a 'header' row which contains only 1 column, a 'body' row which contains three columns and a 'footer' row which contains only 1 column. I assigned classes to each of the columns. In the stylesheet in the theme folders I defined how the table should appear. For instance, for easy identification in the Default theme's stylesheet, I put the header column background to aqua, the footer to lime the left side bar to read the main body to white and the right side bar to blue. This way, it is very easy to see what is displaying proper and what isn't.

For the LeftSidebar and RightSidebar themes' stylesheet, I simply made the column width 0px and the background color transparent (for which ever column was going to not be seen).

.leftSideBar
{
width:15%;
background-color:Red;
vertical-align:top;

}
.rightSideBar
{
width:0px;
background-color:Transparent;
vertical-align:top;
}

Am I making any sense here? Sometimes I think I start rambling, and then I don't very well explain myself. Anyway...

To select which theme to load, I placed a DropDownList in the header section of the table. The list contains four items "Select Theme","Default","Left Side Bar Theme" and "Right Side Bar Theme". Then I added an SelectedIndexChanged event handler and set a Session variable to the selected value of the DropDownList control.

protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.DropDownList1.SelectedIndex > 0)
{
Session["Theme"] = this.DropDownList1.SelectedItem.Value;
Response.Redirect(Request.Url.AbsoluteUri);
}
}

Basically, this will cause two postbacks. The first when the DropDownList control selection is changed, and the second after the Session["Theme"] value is set. It is neccessary to do another post back at this time because, in the hierarchy of how things happen in ASP.NET, the Page_PreInit method happens before the Page_PostBack and anything that follows that. Since the Session variable is not set until after the Page_PreInit is executed, it is necessary to post back again after setting the Session["Theme"] variable.

This will programmatically change the themes based on what the user selects. Other ways of loading themes I will be investigating are via cookies, and database entries.

Thanks for stopping by, you can wake up now!

31 July, 2007

Templates and themes and skins...OH MY

Templates, templates, templates!

One of the great new features of ASP.NET in the 2.x/3.x framework are templates. Short story, templates are just what they imply, the create a basic framework for you ASP.NET application, into which you stick your content.

We all like sites that are intuitive and easy to navigate. Before now, the way we achieved this was either through frames, user controls and/or simply copying code to all the pages. This would get the breadcrumb trail and the left margin links and the header an all that great stuff. The most efficient, IMO were user controls. You create a control, and stick it on each page, then if you needed a change, you just change the control, not having to bother with the other pages.

Templates work in much the same way, but they go beyond that. With ASP.NET 2.x you can apply themes and skins to your site by simply identifying which one to use. You can have blue skins, and red skins and purple skins. And you can allow the visitor to select which skin they like best, and load that one for them.

Themes are a little more in depth. Anyone familiar with blogger or wordpress or other blogging formats will understand the themes idea. A theme defines the layout and module content of the page. What I am referring to as module content in this case are the little applet type things one finds on pages. Like the aforementioned breadcrumb trail, the header, the footer the sidebars, the ad space, the blog rolls or other links. Basically, everything that is not your main content, that you want viewable on every page.

There are a plethora (gosh I love that word - plethora - it just kind of rolls of the tongue, doesn't it?) of pages out there with information on how to use templates, and with some digging, one can find instructions on how to make templates. Please note, templates,skins and themes make extensive use of CSS. If you don't understand CSS, it would be a really good idea to learn more about that first, because it will only frustrate you in the end!

One of the best sources I have found for theme information and instruction is Microsoft's ASP.NET Quickstart Tutorials. This is setup so, if you know the basics, and are familiar with ASP.NET 1.1, you can jump right into what you are looking for.

If creating your own template seems to daunting a task, you can download several templates from the MSDN site, then customize as you see fit! The templates are available in both VB and C# source, so which ever you use, you can use these templates.

Over the next few weeks (hopefully not that long, but you never know). I'm going to create a template from scratch (hopefully it will be visually appealing, but again, you never know), and use this site to document my efforts. The template I am going to create will be for a blog. I have been (as stated in other posts I think) lamentful that there is really good, easily usable and easily findable blog software written in ASP.NET (especially C#). So I am going to create blog software to do that. First, I must create the database though, and for that I am going to borrow heavily from the mySQL database used for WordPress blogs. That database has pretty much everything I can think to need, but there are something I think I would do differently, so I'm going to use the schema as base point to start from. The database will be created using Microsoft SQL Server 2005.

Hopefully, but the end of the week, I'll have a database diagram that I can post here and (if anyone is actually reading this) can get some feedback on.

Until then, take care, and thanks for visiting! (seriously, you can leave comments and everything!)

11 July, 2007

Spex

Something that we didn't really do at my previous job was functional
specifications for projects or applications. Well, that is not entirely
accurate. What I should say is that we didn't do these before we started coding the application, but once it was all done and complete, then we would write out the specs. Amazing how all of our code matched perfectly to the specs and design docs.



This puts me at somewhat of a disadvantage as now I am supposed to
write up a functional spec for a project. Up pops the trusty ol' Google
search engine. This is what I have found regarding functional
specifications for software. There is a whole lot of stuff out there,
but it is very difficult to find the one that really describes things
in a way that 1) is understandable by me and b) somewhat almost fits
the type of project I'm working on.

So far, one of the ones I like the best is the PDF doc "Template for Functional Specifications".

A rather humorous template can be found a Joel on Software.

A rather nice essay titled "Writing Software Requirements Specifications" by Donn LeVie, Jr is informative, but not really much of a sample. I'm much better with looking at how something is put together, then altering it to fit my needs, than I am at reading on how to do it from scratch.

Another template in PDF format is the "Software Functional Specification Template". It is sometimes interesting to see the different methods and styles used to write basically the same information.

Well, I hope these resources help you out some if you need to look at doing a functional spec. If not, well at least it was a short post, and you didn't waste too much time on it!

Have Fun!


Technorati Tags: , , , ,

21 June, 2007

New Directions

Okay, so I have changed jobs. Where I was working I was doing C# desktop development. Now, I am doing C# ASP.NET development. This is quite a change. I have not done any web development for about four years. I have quite a bit of catching up to do.

I am working with a mental picture of web development that is four years old. In internet years, that is something like 75.6 years old to you and me. So I will probably be posting here a lot more as I run across what I think are new and exciting things in the world of C# ASP.NET development.

Stay tuned, and hang on. It looks like it is going to be a bumpy ride!

22 February, 2007

Resolving strange label behavior

I ran into a problem recently where I have something very much like map in my Windows Desktop application. The map-type thingy is bound to a dataset. The app draws items in a picture box based on the data in the dataset. What I needed was a label that would display information when the user moved the mouse over one of the items drawn on the form (like a tool tip).

Naturally, I used a dynamically created System.Windows.Forms.Label object. The user has the ability to determine what is displayed in the mouse over label. They could go to a settings form and set to display static text, values from data columns, or a combination of static text and column values (i.e. ID=234 - ID being static, 243 being the value from the dataset).

The problem I ran into was that the label generated and displayed properly when a data value was declared, but would only display the label border when only static text was to be displayed. The label has a border of BorderStyle.FixedSingle and the BackColor of Color.FromARGB(255,255,225) - this is the color of the standard ToolTip in Windows. but what was being displayed was just the outline of the label. No BackColor, no text. The label color would be whatever color was under where the label was supposed to be.

On a last ditch effort, I set the Label.Font property to this.Font. That fixed the problem. I don't know why it fixed the problem. I don't know what happened to the default font for the label, nor why the label BackColor was displaying as either Color.Transparent, or Color.Empty or whatever it was doing. But I do know that this fixed the problem.

I hope this helps someone out there.

Thanks for visiting!

06 January, 2007

A new direction

Ok, while at work I'm still kinda-sorta working with the Enterprise Library, that has - for a while anyway - been put on hold while I tossed onto a product to be delivered in January 07. This is something I haven't worked on, and must now get caught up in what it is and what it does, so the EntLib has to be put on hold.

On another note. I have a couple of other blogs, most hosted by places like this (blogger.com). One is hosted on my own. I mean, I host it on space I rent - it's not like I actually have access to the server guts or anything. For that blog (http://www.bigsibling.com), I have been using DotNetNuke. DNN is a great portal application. I use it several other places as well. And for what it is designed for, it does an exceptional job, and I wouldn't really want to use anything else for a portal application.

But (there is always a but - isn't there), for the blog I am using a blog module for DNN. While this works for the most part. Posting to it is a bit slow, and something just don't work as smoothly as I would imagine they should. I want to emphatically state that the product is a great and feature rich product. I have no real complaints about DNN or the blog plugin for DNN. The only thing is, the whole thing is a little bloated because it was not designed to be a blog application, but a porta application. So DNN has oodles of ultra-kewl features that I don't use on the blog itself.

With that in mind, I'm toying with the idear of creating a blog web appliction in .NET. I know, I know, there is dasBlog and probably some others. But hey, how can I learn new and exciting stuff if all I do is use other peoples stuff? Also, how can you laugh at all of my stupid mistakes if I don't jump into adventures like this? I mean, seriously, how can I provide your needed comic releif in any other way?

In preliminary research I did find this fairly (I think) comprehensive list of what a good blog application should have.

Anyway, this is what I'm thinking of. If you care, let me know if you think it is a worthy endeavour, or if you think I'm nutz.

Thanks for visiting!

21 December, 2006

EntLib starting to come around

We are focusing on three blocks in the Enterprise Library; Logging, Configuration and Exception Handling. I'm looking at Logging first because everthing else will touch on the logging. What we need is to force three attributes into each log. These three are already provided in by the LogEntry object, these are; Application Name, Application Version and Event ID. Okay, so application version is not supplied by the LogEntry, but we can get that easy enough.

The other thing we want is that the custom logger or tracelistener or whatever it is we create must be inherited and cannot be instantiated on its own. While this sounds easy enough via an abstract class, it turned out not to be so practical. It seems that, while I can create an abstract class that inherits the ILogFormatter, that class can't be used in the Enterprise Library Configuration tool.

Hmmmm...I wonder if we can extend the Enterprise Library Configuration tool to force the required attributes and formatters. Now I have something else to look at.

I've been able to find some information on using Enterprise Library. One place which has links to tutorials (or at least is supposed to) is Channel9 Wiki. However, most the tutorials that would actually do me any good are by that Hisham Baz fellow, and I can't seem to get to his site from work. I'll have to try from home, perhaps it is just that exceptionally poor quality of internet service we experience here:

Channel9 Wiki
EnterpriseLibrary Home
EnterpriseLibrary MSDN blogs
MSDN EnterpriseLibrary page
Hands on Labs - EnterpriseLibrary 2

15 December, 2006

Oh the confustion is causes!

Trying to find information on how to use the Enterprise Library is like looking for information on how to use the Enterprise Library! This is worse than needle in a haystack scenario. I see lots of how EntLib works, and why it works, but very little on practical applications of EntLib. I should probably look at the documentation that came with the EntLib download. I haven't yet - but mostly because Microsoft has a history of piss-poor documentation (when there is any at all).

More soon!

07 December, 2006

Enterprise Library

We're going to start using Enterprise Library for .NET Framework 2.0 to handle our configuration, exception handling, and logging processes. This is going to be fairly new because we've never been ones to actually follow recommended practices on most of anything. Maybe we'll end up with some readable code that actually works. :)

Anyway, I'll be posting in the near future the experience about a first time user with this library and it's implications.

Thanks for visiting!

01 December, 2006

Other places of interest

I have several other blogs, they are updated as my schedule permits, but at least a couple of times a week.  You can get there via the following links:

My Home Town
Bigsibling
At The Top Of My Lungs
C# Follies

Using CACLS in an MSI install

What I have discovered is that if you use the Setup and Deployment project from the Visual Studio 2003 IDE, it doesn't always work the way one might expect it to work.

For instance, when the user installs the program, they are presented with the option to select to install the program for them only, or for anyone on that machine. If the user selects for them only, it would be expected that the files and components of that program's permissions are set so as to only allow that person to run the program.

However, if the user selects to allow all users to run the program, my prevailing expectation was that it would set permissions on the files and components to allow any user to run the program. WRONG. As I should have expected, with Microsoft making so many assumptions and decisions for us (and giving us very little means to adjust that easily), the files and components are set to read only for all but admin account holders.

In our programs, there are many files that are read and written to. For this, since we have dropped InstallShield as our primary install creating agent, I had to create yet another small program that runs on install (that makes two now). I do have these installed as "hidden" files so hopefully it won't clutter up the install directory too badly.

At any rate, I use CACLS.EXE to set the directory permissions. It's a fairly simple command line program. As I didn't want to pop open an command line window, I created a form that is 1px by 1px, located at 0,0 and has an opacity of 0. This should keep it from being seen at all. What we needed was for the local "Users" group to have read/change permissions to all files that get installed. For that I used the command line switch "/G BUILTIN\Users:C(hange)". This table lists the permission codes:




















NNone
RRead
WWrite
CChange
FFull

The codes really are (surprisingly) intuitive. There are other switches involved. These are




























/TRecursive through current and all sub-directories
/EEdits permissions (doesn't replace them)
/CIgnores "Access Denied" errors
/GGrant access rights
/RRevoke access rights
/PReplace access rights
/DDeny access rights


So I end up with the following example:

string response;
string userPerm = @"BUILTIN\Users:C";
string fileDirectory = @"C:\Program Files\Program";
string caclsSwitches = @" /T /E /C /G ";
string caclsLoc = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "CACLS.EXE");
Process p;
if (Directory.Exists(fileDirectory)
{
p = new Process();
p.StartInfo.FileName = calsLoc;
p.StartInfo.Arguments = fileDirectory + caclsSwitches + userPerm;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.StartInfo.CreateNoWindow = true;
p.Start();
response = p.StandardOutput.ReadToEnd();
if (response == null || response.Lenghth == 0)
{
// handle errors here
}
if (p != null) p.Dispose();
response = null;
}



Resources:
Undocumented CACLS: Group Permissions Capabilities
How to Use CACLS.EXE in a Batch File
Information about the cacls command.

Technorati

I found this site where blogs are listed and indexed and thought I'd sign up.


Technorati Profile

06 October, 2006

Creating an Add-In for VS.NET 2003

Eh, nevermind (25 October, 2006)

So, as Mathieu so kindly pointed out in the comments section, PushOK does a VS.NET 2003 add-in for CVS rather inexpensively. While I was looking forward to actually creating an add-in for VS.NET, the time I have to play with it just doesn't exist, so I'm scrapping this one. I may play with it some in VS.NET 2005 at home (I don't have 2003 installed at home anymore), so this might actually go somewhere, but who knows where and when.

Starting Out (06 October, 2006)


My company is switching is source control from the woefully in adequate Visual SourceSafe to CVS. I don't know why they refuse to go to Microsoft Team Server, but I suspect that it has a great deal to do with CVS being free, and TeamServer being expen$ive.

Specifically, we are instructed to use the WinCVS client to access the source code in CVS. After plenty of reading, I realized there is currently no way to access WinCVS through the VS.NET IDE like there is for VSS. I have never created an Add-In for VS before, and thought it sounded somewhat interesting task to tackle. I know it is possible to create one because I see them for sale online for anywhere from $10 to over $200.

I did some queries on Windows Live search looking for add-in information. I got a lot of places selling add-ins, but nothing in the way of how to create one. Then I got the bright idea thinking that if any place would have information on creating an add-in for VS, it would be VisualStudio Managzine.

29 September, 2006

Crystal Reports in VS 2003 IDE

Fixing Data Issues in Crystal Reports

I was tasked with making alterations to a set of Crystal Reoprts created by an off-shore company. I had never used Crystal Reports, either in VS.NET or anywhere else.

The first task was to change the length of a data field. I made the appropriate alterations to the data table, changing the allowable number of characters from 15 to 50. This worked just as expected in the C# application. But it was not working in the Crystal Reports report. The generated report still only showed a max of 15 characters.

So I fired up the IDE and opened up the proper .RPT file. Looking at this thing was like something there are no words to describe. I loathed working in it. I haven't yet been able to find a way into the guts of the report, to edit things manually, sans the GUI. I don't believe there is a way to do this. It's all done through the GUI, which, in my book, is a huge minus. The whole reason I never ever bought a Mac was because there was no command line interface. Okay, I know that now there is a command line in the Mac OS, but too little too late.

Back to the problem at hand. I found the field in question in the report, right clicked it and was presenting with a menu I didn't fully understand. But hey, it says Format..., that's go to be where to change the field length. WRONG. The only thing format does is format the text, you know, font, color, weight, size, position, all that jazz.

I hit "Browse Field Data..." not really knowing what would happen. I sent me into a wizard to determine which database to use, I selected the proper database, but it showed only 15 of the 50 characters I had in the database for that field. Strike Two.

Select Expert... did nothing for me. It sent me into another wizard interface that didn't have anything to do with field size. And, still, after two days of messing with this thing, I don't know what an expert is.

To the left of the main window, Crystal Reports has a "Field Explorer" which is kind of like the Server Explorer VS has. Right clicking on the database brings up a different context menu. Oooh, what's this? I see there is an item named "Verify Database". This should fix it. I hit that sucker and the thing starts moving, things are happening, I know this because I'm getting tons of dialog boxes talking about what is being updated.

After it finally finishes, I right click on the report field again, and do the "Browse Field Data..." to see if anything has changed. VOILA! It displays the whole 50 characters. Excellent. That was easy! Rebuild the application and run the report. Doesn't work. I am now getting the error:"Error in formula. 'TableName.FieldName' This field name is not known."While I know good and well that field is in that table which is in the database.

Next thing to try was to re-create the report. I copy the report to another file, and remove the data connection and re-connect. But this method ended up giving me 26 prompts for data. After entering the proper data in all 26 prompts, the reports generated properly. However, the user should not have to enter the same piece of data 26 times in order to see the report. Espcially since the probability of the user knowing the correct data to enter is very, very slim.
Finally, I clicked enough menu items to obtain a solution. I selected the Database Expert, the Set Location option and created a new connection, selected the proper database. Finally after 2 1/2 days of messing around with all of this, it worked, and I was able to put this horrid experience behind me.

27 September, 2006

Getting Disk Information in C#

Using System.Management and Win32_LogicalDisk


In my last post, I briefly spoke about finding drive information in a C# application. The problem I've been trying to solve is that I have a windows form that needs to run at the end of an install. The form allows the user to copy files from a specific location on the install DVD to the users machine. The files are about 1.3Gb, and I didn't really want to package all that data into one MSI file. A problem arose when the install was finished, and the little windows application was copied to the user's machine, how can I find the DVD drive? And after I found a DVD drive, how can I make certain it is the correct drive with the correct disk?

I started out by importing the kernel32.dll and using the external GetDriveType(string drive). This worked, but I was really wanting to find a C# way to do this without having to import anything else. Enter System.Management - which allows a user to get all of this information natively in C# using Win32_LogicalDisk. Win32_LogicalDisk give you access to a plethora of information on all physical and mapped network drives. You can get the drive name, device id, volume name, serial number and on and on. In addition, you can run SQL type queries against the drive to get just the desired information from the desired drives or drive types or drive sizes and so forth.

Since I am interested in finding CD/DVD drives, I had to find how to tell what kind of drive I was looking at. The following (from the above linked MSDN page) table was a great help:
























Value
Meaning
0
Unknown
1
No Root Directory
2
Removeable Disk
3
Local Disk
4
Network Drive
5
Compact Disc
6
RAM Disk

Using System.Management.ManagementObjectCollection and System.Management.ManagementObjectSearcher I was able to create a collection of all drives with a device type of 5.

ManagementScope ms = new ManagementScope();
ObjectQuery oq = new ObjectQuery("SELECT DeviceID, VolumeName FROM Win32_LogicalDisk WHERE DriveType= 5");
ManagementObjectSearcher mos = new ManagementObjectSearcher(ms,oq);
ManagementObjectCollection moc = mos.Get();


Then I loop through the collection to check out each object's information:

foreach (ManagementObject mo in moc)
{
if (string.Compare(mo["VolumeName"].ToString(),"myString",true) == 0)
DirectoryInfo newDI = new DirectoryInfo(System.IO.Path.Combine(mo["DeviceID"].ToString(), "myPath"));
}

(thanks Shawn, for pointing out the error where I tried to get the volume name from the ManagementObjectCollection, instead of the ManagementObject. While here, I thought I'd go ahead and do the string comparison correctly too. :) )

So now I have what should be the proper path to the files I want to install.

Another route to this information is via the ManagementClass class. It is a little cleaner, and a bit quicker to code, but does give you more than you might need for your particular purpose.

ManagementClass mc = new ManagementClass("Win32_LogicalDisk");
ManagementObjectCollection moc = mc.getInstances();
foreach (ManagementObject mo in moc)
{
// Your code here
}


Few less lines of code, but a much more generic return on the drive information.

Windows Installer Woes

Launching a windows application after install completes in Windows Installer


I was tasked at work to become the "install czar" so to speak. For this, I needed to learn some things about the Windows Installer. While there is a pretty good guide for the Windows Installer at Microsoft's MSDN site, much of the information was elusive. For instance, I needed information about custom actions. For one installation I created a windows forms application which would copy a boat-load of images from the install DVD to the client machine. The images were about 3Gb in total size, so I didn't want to package them in the install. The install contained a small sampling of the images, and it was bulging at 300+Mb - this takes such a long time to process, checkout, build etc.

MS has a pretty good CustomAction Reference page, but it didn't cover everything I was looking at. I went into the setup project I had created in VS 2003, added the .EXE I had created and added it to the "Install" custom action. It seems to me, this should have worked, but it didn't. It appeared the EXE was never called. So I downloaded the Windows Platform SDK which comes with an ultra-cool tool called Orca which allows editing of the MSI tables. Looking at the tables, the EXE being called had a type of 1024. However, there was not 1024 type in the reference page.

Long story short, I found this blog from some guy calling himself Boneman, which is an excellent tutorial for Custom Actions in the Windows Installer.

I tried everything I could think of to get this stupid application to launch at the end of the install - nothing seemed to work. Then I went and revisited the Custom Actions section in the VS IDE. What I found was a property settings named "InstallerClass". I set that to "False" and voila! It worked!

But my application was flawed. I didn't know exactly what the working directory would be, I thought it would be the directory from which the install was launched. I was wrong, it was the directory in which the application file was placed. Now I needed to find the correct DVD drive in order to get the images off of it. To accomplish this goal, I imported the kernel32.dll to access the GetDriveType and GetVolumeInformation methods. These two methods return to me the type of drive, and all of the drive information you think you might ever want. Well, actually, the GetDriveType returns a System.Long value, and you have to know what each value represents. Here is how I figured drive type:


[DllImport("kernel32.dll")]
public static extern long GetDriveType(string driveLetter);

private int
DriveType(string asDrive)
{
if ((GetDriveType(asDrive) & 5) == 5)
return 5;
if ((GetDriveType(asDrive) & 3) == 3)
return 3;
if ((GetDriveType(asDrive) & 2) == 2)
return 2;
if ((GetDriveType(asDrive) & 4) == 4)
return 4;
if ((GetDriveType(asDrive) & 6) == 6)
return 6;

return 0;
}


Now, figuring out that 5 is the CD drive took quite a bit of searching, but having the Platform SDK mentioned above sure helps. At any rate, I got the CD drive, then looked for the volume name that I named the DVD during creation. If it locates that and the proper directory is on that disc, then it copies the files. If not, I pop up a folder browse window to allow the user to determine where the image files are.