<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6955915</id><updated>2025-04-02T08:07:06.266-04:00</updated><category term="Installation General"/><category term="Windows Installer"/><category term="Diversions"/><category term="MSI Custom Actions"/><category term="Security"/><category term="C++"/><category term="Windows Internals"/><category term="WIX"/><category term="C#"/><category term="Design and Architecture"/><category term="Quality"/><category term="Visual Studio"/><category term="InstallShield"/><category term="XMLRPC"/><title type='text'>Boneman&#39;s Blog</title><subtitle type='html'>Ramblings about Software Architecture, .NET, Windows Installer, or any other topic that has my attention at the moment.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bonemanblog.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default?alt=atom&amp;redirect=false'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default?alt=atom&amp;start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>65</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6955915.post-8415977320942662407</id><published>2018-06-03T11:29:00.001-04:00</published><updated>2018-06-03T11:29:43.402-04:00</updated><title type='text'>Transacting business over unverified email addresses = Not Smart</title><content type='html'>On April 21, I recieved an email from &#39;SGW Payroll&#39; [sgwpayroll.com and paydashboard.com]:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Welcome to the SGW Payroll Portal [Redacted First Name]&lt;br /&gt;A new &lt;b&gt;employer admin account&lt;/b&gt; has been created for you, either by one of your colleagues or by us at SGW Payroll Ltd.&lt;br /&gt;Please click on the following link to choose a password and sign in to your account. [Redacted Link]&lt;br /&gt;You are receiving this email as your employer uses PayDashboard to deliver your payslip. Click here to manage your email preferences.&lt;/blockquote&gt;
This welcome email looked phishy, so I checked the headers, then the payroll site, and it looked legitimate. I&#39;m in the US, this company is in the UK. It is pretty obvious this was a mistake - likely bad entry of email address, but note the lack of any method for me to indicate there was an error. Therefore I ignored the email hoping that the intended recipient would realize they did not get the expected welcome email and correct the problem.&lt;br /&gt;
&lt;br /&gt;
On April 25, I get another email:&lt;br /&gt;
SGW Payroll Ltd has published new documents for [Redacted - Name of Business] within your document storage hub. Log in to view or download the documents.&lt;br /&gt;
&lt;br /&gt;
This was bad.&amp;nbsp; Looks like there is now an active account - mind you, an &#39;employer admin account&#39;, that somehow, without clicking the link, they started to use.&amp;nbsp; I also now have the name of the business.&amp;nbsp; At this point, I decide this could be a real problem, so I forward the email to support@paydashboard.com with the message &lt;i&gt;&quot;I did not request this, it is likely someone mistyping their email address.&quot;&lt;/i&gt;&amp;nbsp; I get an automated response, then a reply from &quot;Paul Gibbons&quot; from their support: &lt;i&gt;&quot;Thank you for your email. We have started an invesitigation [sic] to have this removed. Thank you for bringing this to our attention.&quot; &lt;/i&gt;Awesome. Case closed.&lt;br /&gt;
&lt;br /&gt;
On May 21, almost one month later, I get yet another email - same template as the prior one.&amp;nbsp; I respond back to the support request as follows:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
I received another email today. Please resolve this. I am not the intended recipient.&amp;nbsp; There is no &#39;investigation&#39; required. You are in the UK. I am in the USA. I do not work for this [Busness type redacted].&amp;nbsp; At this point I consider this to be:&lt;br /&gt;&lt;ol&gt;
&lt;li&gt;Horrendous security on your part.&amp;nbsp; Any email address entered into your system should have a &#39;confirm&#39; step where you send an email to test the ability of the recipient to receive an email. They click on a link and log into your system. Only then should you consider the email address &#39;worthy&#39; of use for anything sensitive.&lt;/li&gt;
&lt;li&gt;Even MORE Horrendous security on your part. Someone sending you a reply saying they are NOT the intended recipient should IMMEDIATELY result in removal of the address, flagging it as suspect.&lt;/li&gt;
&lt;/ol&gt;
Please have a supervisor/manager contact me immediately. I really do not wish to engage in a public humiliation campaign against your company. You are sending someone else payroll information!!!!!!!!!!!!!!!&amp;nbsp; This is crazy!!!!!!!!!!&lt;/blockquote&gt;
I get a response, again from Paul Gibbons: &lt;i&gt;&quot;You [sic] ticket has been escalated to myself to review. I will speak with the agent populating our software to establish what is happening and will revert back.&quot;&lt;/i&gt;&amp;nbsp; That&#39;s awesome. I ask for elevation, and the SAME GUY elevates the issue to HIMSELF.&lt;br /&gt;
&lt;br /&gt;
I decide to take a closer look at the company&#39;s website to find contact information for someone outside of support. No luck.&amp;nbsp; I now have a name of their CISO but am unable to find an email address at the corporate level or via linked in.&amp;nbsp; I reply to Paul: &lt;i&gt;&quot;Paul, you &#39;reviewed&#39; it the first time, and did not correct the issue. Please have Jeremy Lloyd contact me, ASAP.&quot;&lt;/i&gt; To which I received the reply from Paul: &lt;i&gt;&#39;We will respond once the investigation has been completed.&#39;&lt;/i&gt;&amp;nbsp;I give Paul one more chance to fix this.&lt;br /&gt;
&lt;br /&gt;
On June 1, I recieved another email:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
There are a couple of things you might want to take a look at&lt;br /&gt;Hi [Redacted - First Name]&lt;br /&gt;&lt;br /&gt;As we are able to compare data based on your employees payslips, we want to let you know when we spot something we think you should take a quick look at.&lt;br /&gt;&lt;br /&gt;When you log in to your Employer Dashboard you will see &quot;Notifications&quot; at the top right of your dashboard. Click on the link to view the latest notifications for your company. [Redacted - Name of Business, links, rest of email, etc]&lt;/blockquote&gt;
In response, I will send Mr. Gibbons a link to this post.&lt;br /&gt;
&lt;br /&gt;
Since this saga began, I have received several other emails obviously intended for a person in the UK with a similar email address to mine.&amp;nbsp; From the information in these emails, I can tell you full names, address, purchases, restaurant reservations, bed and breakfasts where they spent their holiday, etc. I can also tell you that no entry point into various systems required even a simple &#39;click to confirm this is you&#39; email address confirmation. I could have cancelled a dinner reservation with a simple click.&amp;nbsp; It sounds a bit scary, but I have to believe most people would do the right thing.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
Advice to developers and analysts, especially with&amp;nbsp;&lt;span style=&quot;color: #667278; font-family: Arial, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, sans-serif; font-size: 14px;&quot;&gt;GDPR&lt;/span&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;At a minimum, you need a business process to handle a &#39;I got this email by mistake, it is not intended for me&#39; response from a user. Always. It could be completely manual. But this DOES HAPPEN.&lt;/li&gt;
&lt;li&gt;At any entry point into your system - a welcome email or similar - provide a link to initiate that &#39;incorrect email&#39; business process. Every email from that first contact until the email address is determined to be valid should have a similar link, and NOT disclose personal information. Only consider an address to account valid when a link clicked from an email (or a code sent only via email) is authenticated as being from that user.&amp;nbsp; This does not need to be a login per-se, but a text message from a registered phone, etc. could all be valid.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;An unconfirmed email address should never be the sole means by which you reset a password or create an account. [NOTE: I did not try to see if I could do this with PayDashboard, as I consider that to be fraud. I would be surprised if I couldn&#39;t reset the password].&lt;/li&gt;
&lt;li&gt;Some email providers ignore punctuation, like gmail.&amp;nbsp; Others also allow automatic aliasing, such as gmail, where you can add a &quot;+&quot; with other text and the plus and text are ignored.&amp;nbsp; This is great for users, as I can filter emails based on this. However, accidental creation or intentional creation of multiple accounts is possible based on wont of a period.&amp;nbsp; This is something to be aware of, I&#39;m not sure how actionable it is.&lt;/li&gt;
&lt;li&gt;You should publish an email address or a method of contact for the executive team of your company. It should be monitored and legit messages forwarded. Sometimes through poor training, incompetence, or merely one person having a bad day, an interaction with your company WILL go poorly. You should give a method to reach the executive team so that these can get handled sooner rather than later.&lt;/li&gt;
&lt;/ol&gt;
</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/8415977320942662407?isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/8415977320942662407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/8415977320942662407'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2018/06/transacting-business-over-unverified.html' title='Transacting business over unverified email addresses = Not Smart'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-578787267219018146</id><published>2012-01-18T13:04:00.001-05:00</published><updated>2012-01-18T21:33:17.826-05:00</updated><title type='text'>SOPA, PROTECT-IP, and Legislative Position Statements</title><content type='html'>At the end of December 2011, I wrote to my federal representatives regarding my opposition to &lt;a href=&quot;http://en.wikipedia.org/wiki/Stop_Online_Piracy_Act&quot; target=&quot;_blank&quot;&gt;SOPA&lt;/a&gt; (US House) and &lt;a href=&quot;http://en.wikipedia.org/wiki/PROTECT_IP_Act&quot; target=&quot;_blank&quot;&gt;PROTECT-IP&lt;/a&gt; (US Senate)&amp;nbsp;legislation&amp;nbsp;that portends to reduce piracy and theft of intellectual property.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Today, many websites are protesting these bills. Wikipedia and others have decided to shut down for the day (or part of the day). &amp;nbsp;Google and others have decided to alter their homepages.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I encourage you to view the&amp;nbsp;legislation via the links above for a Wikipedia summary, then follow the &#39;External Links&#39; to the text of the bill&amp;nbsp;and develop your own&amp;nbsp;opinions. &amp;nbsp;If you wish to oppose it, &lt;a href=&quot;https://www.google.com/landing/takeaction/&quot; target=&quot;_blank&quot;&gt;Google has a petition online&lt;/a&gt; that you can sign. &amp;nbsp;In either case, you should also write your representatives directly. &amp;nbsp;Hopefully, you will get a useful response.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Senator &lt;a href=&quot;http://www.toomey.senate.gov/&quot; target=&quot;_blank&quot;&gt;Toomey&lt;/a&gt;&amp;nbsp;(R-PA) only responded to me with a confirmation that the&amp;nbsp;legislation&amp;nbsp;does, in fact, exist. &amp;nbsp;I did make specific references to provisions in the bill, so it is obvious that I already knew this. A follow-up did not result in an actual reply regarding the Senator&#39;s thoughts on this bill. &amp;nbsp;Senator &lt;a href=&quot;http://www.casey.senate.gov/&quot; target=&quot;_blank&quot;&gt;Casey&lt;/a&gt; (D-PA) and&amp;nbsp;Representative &lt;a href=&quot;http://www.altmire.house.gov/&quot; target=&quot;_blank&quot;&gt;Altmire&lt;/a&gt; (D-PA) both failed to respond to my email. &amp;nbsp;I only give minor props to Toomey for responding at all, however, all three of my representatives get failing grades for their lack of adequate response. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Even if my representative has opposing viewpoints or is projecting a vote contrary to my opinion, I believe it is their responsibility to have and disseminate intelligent position statements on each piece of&amp;nbsp;legislation pending in their chamber and inside any committee that they are a member of. &amp;nbsp;An intelligent position statement is one that is published within 48 hours of the bill successfully leaving the committee and would include all of the following elements:&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;A link to the full text of the bill, including chronological history of successful&amp;nbsp;amendments&amp;nbsp;with time stamps.&lt;/li&gt;
&lt;li&gt;If the vote were held today, based on version at timestamp ???, I would vote (yea/nay/uncommitted)&lt;/li&gt;
&lt;li&gt;In the words of the representative, a summary of the intention of the bill.&lt;/li&gt;
&lt;li&gt;List of elements that the representative supports and believes critical to the success of the bill.&lt;/li&gt;
&lt;li&gt;List of elements that the representative opposes.&lt;/li&gt;
&lt;li&gt;List of elements that require additional flushing out or personal research. &amp;nbsp;This list would be required if &#39;uncommitted&#39;.&lt;/li&gt;
&lt;li&gt;List of bills pending in committee or in the queue for a floor vote that portend to address the same concerns.&lt;/li&gt;
&lt;li&gt;List of existing legislation on this topic and&amp;nbsp;established case law that covers (or fails to cover) the subject matter of the new bill.&lt;/li&gt;
&lt;li&gt;Media reports, studies, corporate statements,&amp;nbsp;lobbyist&amp;nbsp;groups, etc. that advocate for the need of the new law.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Update 1/18/2012: Sen Toomey &lt;a href=&quot;http://toomey.senate.gov/?p=press_release&amp;amp;id=429&quot; target=&quot;_blank&quot;&gt;released a statement&lt;/a&gt; that he does not support SOPA or PROTECTIP &quot;in their current forms&quot;, yet he fails to make any statement as to what specific portions of it he takes issue with. &amp;nbsp;This statement is clouded in doublespeak. &amp;nbsp;Please keep the pressure up to get him to &lt;i&gt;explain&lt;/i&gt; his position.&lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/578787267219018146?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/578787267219018146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/578787267219018146'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2012/01/sopa-protect-ip-and-legislative.html' title='SOPA, PROTECT-IP, and Legislative Position Statements'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-309658212072884317</id><published>2011-03-15T10:21:00.000-04:00</published><updated>2011-03-15T10:21:42.822-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="WIX"/><title type='text'>Wix, Votive, and Semicolons...</title><content type='html'>If you are using the Wix in Visual Studio (known as &#39;Votive&#39;) and need to set one or more preprocessor variables, it is rather simple. If you right-click on the Wix project, select &#39;Properties&#39;, then the &#39;Build&#39; tab, you simply populate the &#39;Define preprocessor variables:&#39; text box like so:&lt;br /&gt;
&lt;blockquote&gt;Name1=Value1;Name2=Value2&lt;/blockquote&gt;If you are using MSBuild, or editing the .wixproj file itself, this translates to the contents of the &#39;DefineConstants&#39; element, which is where Votive stores what you put in that text box.&lt;br /&gt;
&lt;br /&gt;
Things, however, are not really clear (or documented) if you need to set Name1 in the example above equal to a semicolon delimited list - for example &quot;one;two;three&quot; - so lets try it this way:&lt;br /&gt;
&lt;blockquote&gt;Name1=one;two;three;Name2=Value2&lt;/blockquote&gt;Candle.exe is passed (which is obviously incorrect based on our intentions):&lt;br /&gt;
&lt;blockquote&gt;-dName1=one -dtwo -dthree&amp;nbsp;-dName2=Value2&lt;/blockquote&gt;The solution is NOT to put quotes around the list (my first guess), but to replace the semicolons that break up the list (and only the ones in the list) with &#39;%3b&#39;, like so:&lt;br /&gt;
&lt;blockquote&gt;Name1=one%3btwo%3bthree;Name2=Value2&lt;/blockquote&gt;&lt;br /&gt;
&lt;div style=&quot;margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;&quot;&gt;Candle.exe is now correctly passed:&lt;/div&gt;&lt;blockquote&gt;-dName1=one;two;three&amp;nbsp;-dName2=Value2&lt;/blockquote&gt;I do not know if this is way you would handle this situation in anything newer than Wix 3.0 - I haven&#39;t updated to 3.5 yet.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/309658212072884317?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/309658212072884317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/309658212072884317'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2011/03/wix-votive-and-semicolons.html' title='Wix, Votive, and Semicolons...'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-2544268968144724078</id><published>2011-03-04T21:11:00.000-05:00</published><updated>2011-03-04T21:11:11.176-05:00</updated><title type='text'>Electric Power Generation Choice in Pennsylvania</title><content type='html'>This post is centered around the deregulation of electricity generation in Pennsylvania, why you get to shop for an electricity generator (the history lesson), the factors to consider (the practical lesson), and ultimately how to save real money (the lesson in pragmatism). &amp;nbsp;I&#39;m sure that the general advice applies to other states that have started &#39;deregulation&#39; of the electricity market.&lt;br /&gt;
&lt;br /&gt;
Let me provide some quick background on the process by which electricity gets to your home. &amp;nbsp;There is, of course, a power plant that makes electricity - this is called a &#39;generator&#39;. There are several &#39;generators&#39; that are connected with each other at multiple points - this is known as the power &#39;grid&#39;. &amp;nbsp;The purpose of the grid is to assure service in the event that one or more of these &#39;generators&#39; is turned off or disconnected that power can still be supplied from other generators. &amp;nbsp;It made sense for generators on many levels to interconnect with other generators owned by different companies into the same grid. &amp;nbsp;Meters indicating how much power each generator supplied into the grid allowed for proper&amp;nbsp;bookkeeping. &amp;nbsp;Then you have the &#39;transmission and distribution&#39; part - which is the lines connecting the generators to the grid, and the grid to your home and meter how much power is consumed. &amp;nbsp;This is a very simple model of something much more complex.&lt;br /&gt;
&lt;br /&gt;
The way electric power was implemented, one company was responsible for dealing with all parts of the process - even if the company that billed you had no actual generation capabilities. &amp;nbsp;You were charged one rate based on how much power you consumed. &amp;nbsp;These companies are a &#39;natural monopoly&#39; - mostly because it doesn&#39;t make sense, economically, to run multiple power lines from several companies in parallel. Because of this competition does not exist. &amp;nbsp;This is a similar situation to gas and water companies today. &amp;nbsp;Over a decade ago cable and telephone companies were in the same boat, but technology advanced to the point that both wiring systems can carry signals that allow for a limited amount of competition.&lt;br /&gt;
&lt;br /&gt;
In 1997, a PA state law&amp;nbsp;&quot;&lt;a href=&quot;http://www.legis.state.pa.us/WU01/LI/LI/CT/HTM/66/00.028..HTM&quot;&gt;Electricity Generation&amp;nbsp;Choice and Competition Act&lt;/a&gt;&quot; was passed (Regulations covering this law at&amp;nbsp;&lt;a href=&quot;http://www.pacode.com/secure/data/052/chapter54/chap54toc.html&quot;&gt;52 Pa. Code&amp;nbsp;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-small;&quot;&gt;§&amp;nbsp;&lt;/span&gt;54&lt;/a&gt; - &lt;a href=&quot;http://www.pacode.com/secure/data/052/chapter54/052_0054.pdf&quot;&gt;pdf&lt;/a&gt;). &amp;nbsp;The links provided are to the text of the &lt;b&gt;current&lt;/b&gt; law and rules and regulations pertaining to it, as&amp;nbsp;amended. &amp;nbsp;The intent of the law was to deregulate the electricity &lt;b&gt;generation&lt;/b&gt; market. &amp;nbsp;Since any generator in close physical proximity to a consumer can apply power to the grid, and the amount each one applies can be controlled, why should the consumer be stuck with buying power from one specific generator? &amp;nbsp;The direct impact of the law to the consumer consists of a few major points: (1) The costs of generating power were&amp;nbsp;separated&amp;nbsp;from the costs of delivering the power from the grid and itemized on your bill. &amp;nbsp;(2) All of these rates were&amp;nbsp;temporarily&amp;nbsp;capped and tightly controlled, until (3)&amp;nbsp;These&amp;nbsp;caps expired on Dec. 31, 2010. The very first part of the law, &lt;a href=&quot;http://www.legis.state.pa.us/WU01/LI/LI/CT/HTM/66/00.028..HTM&quot;&gt;Declaration of policy&lt;/a&gt;, describing its complete intent, is a good read that is easy to understand.&lt;br /&gt;
&lt;br /&gt;
The law attempted to take into account consumer protections and industry protections during the transition period to an unregulated market. If we look at the old regulated market, the supplier either engaged in long term contracts with a generator or generated the power themselves. &amp;nbsp;Unless you (the consumer) were willing to build your own power grid, you were at the mercy of their business decisions. These decisions were based on the monopolistic system of the time, industrial/residential growth projections, understated nuclear power costs and growth, and should be viewed in that light.&amp;nbsp;Some were good, some not so good. &amp;nbsp;The not-so-good decisions resulted in what was called &#39;stranded costs&#39; in that if the market was opened up and the utilities were forced to sell power for their cost of generation no one would buy power at that price. &amp;nbsp;After the law took effect, the generators were able to recoup these &#39;stranded costs&#39; but under a capped price system - essentially meant to prepare the generators to compete in a free market. A whole book could be written explaining the theory of how this works. In the end, it sort of worked out - the 2008 national cost of electricity was 9.83 cents per kWh and PA was 9.60 so the &#39;capped&#39; numbers were not far off in the end.&lt;br /&gt;
&lt;br /&gt;
What the law didn&#39;t take into consideration is that deregulation do&lt;b&gt;es not and can not do anything&lt;/b&gt; for supply capacity and demand in this particular market. There has been very little supply capacity added to the market since the death of domestic nuclear power. &amp;nbsp;This law did nothing to make it easier to add generating capacity. Government projections show little growth in capacity. Power generated by your former monopolistic provider can now be sold to other distributors both in and out of state at competitive rates further reducing local supply by filling demand elsewhere.&lt;br /&gt;
&lt;br /&gt;
Electricity, like oil, is a &#39;source unknown&#39; commodity. &amp;nbsp;An electron is an electron regardless if it came from solar, wind, oil, coal, gas, or some guy on a treadmill. &amp;nbsp;The true difficulty in the marketplace now is figuring out who owes whom what... The whole concept of the electric grid is that it gets fed (hopefully) at the same rate it is drained - figuring out who owes who for the times you over or underfed the grid sounds like an added task &amp;nbsp;(and challenge) for your local utility.&lt;br /&gt;
&lt;br /&gt;
By making your selection of generator, you are telling your utility to buy the same number of kWh that you use from the generator you selected at the price you are contracted for. &amp;nbsp;There are several selection criteria that you may want to consider aside from the current price: (1) Environmental reasons such as how that generator produces power, (2) Rate terms - how long you wish to &#39;lock in&#39; a specific rate, (3) Usage patterns - can you or are you willing to juggle your energy use to minimize peak load or consumption based on time of day. &amp;nbsp;Make sure the &#39;price to compare&#39; you are quoted from various suppliers includes all fees and taxes. The Gross Receipts tax is complex to calculate yourself if not included, and a really stupid tax for more than just that reason.&lt;br /&gt;
&lt;br /&gt;
Environmental&amp;nbsp;concerns is a tough one to compare. &amp;nbsp;If this is a concern for you I&#39;d suggest rating each generator on a four star system based on their byproducts of generation (coal, gas, oil, nuclear, or renewable) and your personal belief system. I&#39;d personally favor nuclear and renewable equally - but to each his or her own. This information is difficult to obtain from most providers. &amp;nbsp;Be aware that at times your generator may need to buy extra power on the spot market - exactly how much and from whom and how are all something to concern yourself with. &amp;nbsp;They could &#39;trade&#39; power (best) by taking some extra now and paying it back when production peaks - wind and solar is very inconsistent. &amp;nbsp;They could buy on the spot market for demand peaks (medium). They could be oversubscribed (bad) where they are never producing as much as their customers are buying so a portion of the &#39;clean&#39; energy you are purchasing is really quite dirty but you are paying much more for it - if those profits are 100%&amp;nbsp;dedicated&amp;nbsp;to expanding their production capabilities and are NOT considered profit then that may be OK. &amp;nbsp;Environmentally sensitive folks are likely better off conserving and selecting the cheapest provider while investing the difference in for-profit companies researching commercially viable clean energy production. &amp;nbsp;Funneling money into the current non-viable technologies only slows the progress in developing truly groundbreaking and cost-effective solutions that will truly benefit us all. If you really do the math on what goes into manufacturing (inputs and byproducts) and transporting solar, wind, and battery systems required to support them and&amp;nbsp;amortize&amp;nbsp;that environmental cost over its lifespan they are not as attractive as many believe them to be compared with other options - especially modern nuclear technologies with near-zero waste.&lt;br /&gt;
&lt;br /&gt;
Some suppliers have a contract term where the rate is held constant for a period of 1, 2, or 3 years. These providers may or may not have incentives for signing (gift card, airline miles, etc.) and termination fees if you quit early. &amp;nbsp;Be sure to add in the costs and benefits from those deals as well in your calculations. &amp;nbsp;Should you do a 3 year lock in? &amp;nbsp;&lt;a href=&quot;http://www.eia.doe.gov/oiaf/aeo/tablebrowser/#release=AEO2011&amp;amp;subject=0-AEO2011&amp;amp;table=8-AEO2011&amp;amp;region=0-0&amp;amp;cases=ref2011-d120810c&quot;&gt;The government is predicting generation prices drop from current levels and bottom out in 2012 then begins to rise again&lt;/a&gt;, yet &lt;a href=&quot;http://www.eia.doe.gov/emeu/aer/txt/ptb0810.html&quot;&gt;historical price data shows steady climb in the &#39;real&#39; column&lt;/a&gt;&amp;nbsp;so you can bet either way here.&lt;br /&gt;
&lt;br /&gt;
There are other ways that you can change your usage behaviors to save more money. Can you go to &lt;a href=&quot;http://www.firstenergycorp.com/Get_Help_With/Saving_Energy/Load_Management.html&quot;&gt;load based pricing&lt;/a&gt; to save money (running dryer, dishwasher, A/C, and stove/oven at&amp;nbsp;separate&amp;nbsp;times to minimize the simultaneous current draw)? &amp;nbsp;Is there a time based rate plan where you concentrate your power use to off-peak times and pay less for it?&lt;br /&gt;
&lt;br /&gt;
All that stuff mentioned above is rather difficult for the average person to understand and digest. &amp;nbsp;What happens if you DON&#39;T&amp;nbsp;choose a specific generator? If you don&#39;t you will be the sucker that will end up subsidizing the prices that allows your default generator to sell power cheaper to other utilities. &amp;nbsp;The &#39;default&#39; generator is assigned based on contract with your utility - when your contract expires with your current generator, they close or get shut down, or you go into financial default you get that one. &amp;nbsp;There is no incentive for them to charge the lowest rates, since a good number of people &lt;b&gt;won&#39;t&lt;/b&gt; choose and they are allowed to recoup fees and costs associated with being a default generator. &amp;nbsp;Based on a brief survey of default vs. cheapest alternative the &#39;idiot tax&#39; on people that don&#39;t pick a provider is 5-10%. &amp;nbsp;Oh, and the default generator is not allowed to provide a usage based discount but other generators can so not picking can cost you even more than that.&lt;br /&gt;
&lt;br /&gt;
There is a case where you may not want to switch... at least today... so just skip the next three paragraphs. If you have an all-electric home and are under &#39;Residential Heating&#39; billing codes (stated on your bill as &#39;RH&#39; - &#39;Rate RH&#39; and &#39;Penn Power RH&#39;) you are probably charged different rates at different times in the year. This rate system was developed years ago to encourage all-electric homes. &amp;nbsp;Electric only homes are interesting, because their usage is much greater in the winter than summer - the exact opposite demand cycle of a gas heated home. Electricity generators need to have the capacity to handle peak loads or you have brownouts/blackouts and since the all-electric home is in the minority that occurs in the summer. &amp;nbsp;In this peak usage time, smaller power plants get taken online or offline based on demand (which&amp;nbsp;correlates&amp;nbsp;with temperature) - these are generally the most inefficient and costliest ones to run like coal, oil, and gas fired plants. &amp;nbsp;When they are turned off or down you save a bundle as you are burning less fuel. When you have a nuclear plant, it &lt;b&gt;does not&lt;/b&gt; saves much money when production is less than the&amp;nbsp;maximum&amp;nbsp;capacity. &amp;nbsp;With the advent of cheap excess power in the winter, you now want to encourage usage spikes in the winter to get the best rate of return on your nuclear power plant investment. &amp;nbsp;Enter the all-electric home, which is only cost effective comparable to gas (in our climate) if the cost of electric power is cheaper in the winter. &amp;nbsp;Deals made with developers to offer a special rate plan where this dream can be fulfilled. &amp;nbsp;This was a mutual win for homeowners and power generation plants. &amp;nbsp;People built their homes, chose their appliances, and chose their heating system based on these rate promises. &amp;nbsp;The history lesson is now over - lets look at what this means.&lt;br /&gt;
&lt;br /&gt;
As of&amp;nbsp;Feb 28, 2011,&amp;nbsp;&lt;a href=&quot;http://www.oca.state.pa.us/Industry/Electric/elecomp/pprh.pdf&quot;&gt;Penn Power&#39;s Residential Heating rates for June-Sept were 6.44 cents/kWh and for Oct-May were 4.50 cents/kWh&lt;/a&gt; straight up. &amp;nbsp;This means&amp;nbsp;no kWh&amp;nbsp;minimums before the discount was applied - just a seasonally adjusted generation rate.&amp;nbsp;According to a phone call I had with Penn Power on 3/4/2011 there is no plans to change their existing program, although no new subscribers can be added to it. &amp;nbsp;Also of this date there is no competitor for this pricing plan. &amp;nbsp;If you fall into this boat, as I do, you shouldn&#39;t do anything.&lt;br /&gt;
&lt;br /&gt;
PECO Energy has a slightly different program &lt;a href=&quot;http://articles.philly.com/2010-11-14/business/24955555_1_electric-heat-cathy-engel-peco-energy&quot;&gt;which is being phased out&lt;/a&gt;&amp;nbsp;(&lt;a href=&quot;http://hvac-talk.com/vbb/showthread.php?t=166955&quot;&gt;more discussion on that topic here&lt;/a&gt;). &amp;nbsp;I&#39;d call your provider to get the details and figure out your own cost structure. &amp;nbsp;&lt;a href=&quot;http://www.oca.state.pa.us/Industry/Electric/elecomp/pecrh.pdf&quot;&gt;Through 3/31/2011 their prices were 9.74 cents/kWh for the first 600 kWh then 5.35&amp;nbsp;cents/kWh for any usage above that with no mention of summer prices&lt;/a&gt;. &amp;nbsp;Pennsylvania&#39;s consumer advocate,&amp;nbsp;Irwin A. &quot;Sonny&quot; Popowski, states:&amp;nbsp;&quot;&lt;a href=&quot;http://articles.philly.com/2010-11-14/business/24955555_1_electric-heat-cathy-engel-peco-energy&quot;&gt;The commission regulations essentially require the elimination of the special winter heating rates, though we have tried to do this over a multi-year period.&lt;/a&gt;&quot; I fail to find supporting evidence of this statement in the Rules and Regulations. Since the context of the quote is PECO specific, and their RH rates are discounted only after a kWh minimum is reached, the reference may be to that specific implementation of the winter heating rates by a default provider. &amp;nbsp;Personally, I call bullshit on this rate change being from the&amp;nbsp;legislation&amp;nbsp;or Rules and Regulations since PECO&#39;s price to compare for non RH codes varies in June 2011 (&lt;a href=&quot;http://www.oca.state.pa.us/Industry/Electric/elecomp/pecor.pdf&quot;&gt;9.99 cents/kWh for the first 500 and 11.20 thereafter&lt;/a&gt;). I&#39;d call your&amp;nbsp;legislator and &#39;Sonny&#39; and get the specific portions of the law/regs I linked to above that he is using to support his assertion. It&#39;s PECO&#39;s game, anyhow, and yeah - you folks in Philly are probably screwed regardless of what the law says.&lt;br /&gt;
&lt;br /&gt;
After looking at the options across the Commonwealth, I conclude that you could probably save more money by conserving than switching. &amp;nbsp;Remember that if you save 10% by switching that is only saving the generation charge - not 10% of your total bill. &amp;nbsp;To learn how to conserve, you need to know your baseline usages, and understand where the power is being consumed then reduce it. &lt;a href=&quot;http://michaelbluejay.com/electricity/cost.html&quot;&gt;This site explains what a kWh is and how to save energy&lt;/a&gt;. There are also some simple techniques to check basic insulation effectiveness - like seeing if the snow melts off your roof faster than all your neighbors (that would be bad). Other such tips can be found in the two books I recommend at the end of this post. &amp;nbsp;If you have electric heat the book on insulation is excellent and a must read.&lt;br /&gt;
&lt;br /&gt;
Good luck in selecting a&amp;nbsp;provider&amp;nbsp;- you will need it. &amp;nbsp;I found that trying to figure out all the nuances of selecting a provider is way too specific to your particular needs and usage patterns to offer any sort of general advice.&lt;br /&gt;
&lt;br /&gt;
More Information:&lt;br /&gt;
&lt;a href=&quot;http://www.oca.state.pa.us/Industry/Electric/elecomp/ElectricGuides.htm&quot;&gt;PA Office of Consumer Advocate Shopping Guide&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://www.papowerswitch.com/&quot;&gt;PA Public Utility Commission &#39;PAPowerSwitch&#39; Site&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Recommended conservation books:&lt;br /&gt;
&lt;iframe align=&quot;left&quot; frameborder=&quot;0&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://rcm.amazon.com/e/cm?lt1=_blank&amp;amp;bc1=FFFFFF&amp;amp;IS2=1&amp;amp;nou=1&amp;amp;bg1=FFFFFF&amp;amp;fc1=000000&amp;amp;lc1=0000FF&amp;amp;t=bonemansblog-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as4&amp;amp;m=amazon&amp;amp;f=ifr&amp;amp;asins=1561585548&quot; style=&quot;height: 240px; width: 120px;&quot;&gt;&lt;/iframe&gt;&lt;iframe align=&quot;left&quot; frameborder=&quot;0&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://rcm.amazon.com/e/cm?lt1=_blank&amp;amp;bc1=FFFFFF&amp;amp;IS2=1&amp;amp;nou=1&amp;amp;bg1=FFFFFF&amp;amp;fc1=000000&amp;amp;lc1=0000FF&amp;amp;t=bonemansblog-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as4&amp;amp;m=amazon&amp;amp;f=ifr&amp;amp;asins=1880120186&quot; style=&quot;height: 240px; width: 120px;&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/2544268968144724078?isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/2544268968144724078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/2544268968144724078'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2011/03/electric-power-generation-choice-in.html' title='Electric Power Generation Choice in Pennsylvania'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-5665242233123339931</id><published>2009-08-27T01:12:00.000-04:00</published><updated>2009-08-27T01:12:36.375-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><category scheme="http://www.blogger.com/atom/ns#" term="Windows Installer"/><category scheme="http://www.blogger.com/atom/ns#" term="WIX"/><title type='text'>Wix way should you go?</title><content type='html'>Now that I know Rob still reads my mostly stagnant blog, I guess it is the appropriate time to write a long-overdue post.&lt;br /&gt;
&lt;br /&gt;
When I started working with Windows Installer technology, back when it was first introduced with Office 2000, I played around with customizing the package for the IT department to push out a customized version.  The tools were quite primitive, the technology was new and largely unknown, and the concept of having blogs, yet alone Microsoft folks blogging, seemed completely foreign.  Support was pretty much nonexistent, and much of the documentation was unintelligible.  Fast-forward 10 years, and what a difference that makes!  Today there are several free and low-cost repackaging tools for transitioning non-Windows Installer based setups to the MSI format, authoring tools, and lots of community support.&lt;br /&gt;
&lt;br /&gt;
Most setup authoring tools have significant issues. Non-MSI or script based installations have issues because they encourage hacks - I can&#39;t tell you how many installations I encountered that install services by writing keys to the CurrentControlSet hive and forcing you to reboot merely so the Service Control Manager can pick up that addition.  Furthermore, if you are targeting any sort of enterprise where more than one of your setups will be installed IT departments want MSI deployments for very good reasons.  GUI based Windows Installer tools fail to do a good job of grouping related things into the same component, and dynamically adding a directory of files at build time breaks patching semantics horribly.  Another big disadvantage to these tools lie in the setup author because he or she does not need to understand the underlying technology and can get away with &quot;programming by coincidence&quot; (as described in The Pragmatic Programmer).   &lt;br /&gt;
&lt;br /&gt;
I remember several paradigm shifts throughout my experiences with setup technology - nested MSIs, merge module distribution, and chaining installations.  During this time the stock price of Rolaids likely skyrocketed.  The biggest challenge was attempting to get developers to take a more proactive approach to deployment considerations as they were writing their code.  One approach that I took was the use of merge modules - developers of feature-units would package their build output in an MSM that was consumed when building the final product.  Using Visual Studio 2005+ with their deployment projects was not only difficult, but downright impossible because of how limited, shortsighted, and buggy deployment projects are.  Adding custom actions to these modules involved a complex and convoluted post-build scripting process that nobody understood, but it DID move teams towards the direction of thinking of deployment while coding.&lt;br /&gt;
&lt;br /&gt;
These days, the tag-team of MSBuild plus Wix 3.0 is THE enabler to accomplishing those goals and largely eliminating the disadvantages of the GUI-based tools.  Since there is close to a one-to-one correlation of XML elements to the Windows Installer tables, it is quite simple to follow if you understand the underlying Windows Installer engine.  To use WiX to author a complete installation, you MUST have an understanding of the Windows Installer engine. To make a few tweaks or additions once the basic skeleton of the installer is laid out, just about any developer can do it provided access to the WiX documentation.  I have team members that are NOT setup developers add services, event log sources, and more with no official training.  &lt;br /&gt;
&lt;br /&gt;
Some of the more compelling points in favor of WiX is how you can use it to easily and properly make multiple product editions which share components, separate units of related components into their own WXS file(s) for easier understanding and maintenance, and integrate it easily as a first-class citizen into an MSBuild project.  No other product is available to my knowledge that  accomplishes those goals.  Best of all - WiX is free, fast, and easily installable onto any developer machine.&lt;br /&gt;
&lt;br /&gt;
If you are looking to switch authoring tools, take WiX for a test run by using the dark.exe decompiler to convert your existing MSIs and play around with it a bit.  Subscribe to the WiX mailing list and ask a few questions.  You just might like it.&lt;br /&gt;
&lt;br /&gt;
Congratulations to Rob and the entire team and individuals who have contributed to it, as well as the community of developers who support it via the mailing list on a daily basis.  If you are ever in Pittsburgh, let me know.  I&#39;ll buy you a beer.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/5665242233123339931?isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/5665242233123339931'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/5665242233123339931'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2009/08/wix-way-should-you-go.html' title='Wix way should you go?'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-2757817073879618703</id><published>2009-08-04T01:01:00.007-04:00</published><updated>2009-08-22T08:32:14.630-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><category scheme="http://www.blogger.com/atom/ns#" term="WIX"/><title type='text'>Visual Studio 2008 GenerateBootstrapper task and UAC</title><content type='html'>I am extremely disappointed in the Visual Studio 2008 bootstrapper to say the least.  While the concept of a bootstrapper is great, it is obvious that the designers did not look at real-world deployment scenarios.&lt;br /&gt;&lt;br /&gt;Let me give one example scenario: Installing prerequisite packages and interacting with Vista&#39;s User Access Control (UAC).  Most of the time, the author of the bootstrapper (by the way that packages are authored and selected) is pretty confident as to how things are required (and I use the word required here very specifically) to be installed: per-user or per-machine.  In some cases, such as the .NET framework and many third party redistributables, this is a per-machine only situation.  Perhaps some components &lt;span style=&quot;font-weight:bold;&quot;&gt;could&lt;/span&gt; be installed per-user, but generally if &lt;span style=&quot;font-weight:bold;&quot;&gt;one&lt;/span&gt; part is installed per-user, then &lt;span style=&quot;font-weight:bold;&quot;&gt;all&lt;/span&gt; parts capable of being installed per-user should be.&lt;br /&gt;&lt;br /&gt;The above understanding should certainly be incorporated as part of a bootstrapper from the start.  If some components require a per-machine installation, UAC should be prompted for once, start the elevated process, and this elevated process could be &#39;commanded&#39; by the non-elevated bootstrapper main process.  If per-user components exist, the option to install all of them could be given to the user, and appropriate ALLUSERS=x or command line arguments could be passed to the chained installations capable of doing per-user installations, and the elevated or non-elevated boostrapper  process is used based on the requirements of the package and user selection.&lt;br /&gt;&lt;br /&gt;Instead, the VS 2008 bootstrapper prompts for elevation of each chained installation, even though the entire POINT of a bootstrapper is a single entry point and interaction for the user to get a piece of software installed onto a users machine.    We as developers consider setups a massively coordinated symphony of prerequisites and third party components. The user considers software they purchase or wish to install as a single piece and a single process, and it should seem this way to them.&lt;br /&gt;&lt;br /&gt;For setups that are rather long and/or chained-installation heavy, prompting after each chained installation is aggravating to the user and is completely unnecessary from their perspective.  This design flaw requires the user to babysit the entire process.  We as developers hope that the user chooses to elevate each one - if not the entire setup fails with a rather hopeless error message requiring a support call.  &lt;br /&gt;&lt;br /&gt;Corporate users are the only ones who generally care about per-user installations, and generally can follow a dependency chain of installations to push apps to the users.  Most of them will NOT use our generated bootstrapper, anyway.&lt;br /&gt;&lt;br /&gt;The point of this post is to tell you how to work around the &#39;elevate for each&#39; behavior of the generated setup.exe by hacking the setup bootstrapper used by the MSBuild GenerateBootstrapper task so that it prompts for elevation once at startup and installs everything elevated.  This is not ideal because sometimes it is not necessary to elevate, but it certainly makes the end-user result better for the &#39;happy path&#39; for 99% of all users.&lt;br /&gt;&lt;br /&gt;Normal caveats apply - if you break something, its not my fault.  Keep a backup of all modified files.  The solution presented here will change the behavior for ALL GenerateBootstrapper build tasks unless as part of your msbuild script you replace the appropriate file with the version that gives you the appropriate results.&lt;br /&gt;&lt;br /&gt;The Windows SDK actually contains this bootstrapper, and it can be located here on a default installation on a &#39;normal&#39; PC: C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Engine\setup.bin.  Copy this somewhere and rename it to setup.exe. In Visual Studio 2008, do a File-&gt;Open-&gt;File and find your setup.exe. This will open up a resource viewer/editor.  The RT_MANIFEST &#39;folder&#39; is what you want to expand. Right-click on &quot;1&quot; and export it, saving it as a text file.  Open the text file and find the requestedExecutionLevel node - change the level attribute&#39;s value to &#39;requireAdministrator&#39;.  Use CTRL-A and CTRL-C to copy the contents of this file to the clipboard. Back at the resource editor, double-click the &#39;1&#39; entry to get it in the hex editor.  Select all but the first three bytes and paste your clipboard contents in there.  Save and exit.  Now rename the setup.exe back to setup.bin and replace the original file, saving a backup, of course.  The next time your build happens, the setup.exe will now use the new manifest, giving you one prompt and doing EVERYTHING elevated.&lt;br /&gt;&lt;br /&gt;I won&#39;t get into the other issues with this bootstrapper - no autogeneration of MSI logs, no timestamps in its own log, immediate run and exit that hinders seld-extracting archive packages for single-file download, no MSI caching for patch scenarios like the Office team uses, spanning over multiple CDs/DVDs etc.  I will wait for &lt;a href=&quot;http://robmensching.com/blog/posts/2009/7/14/Lets-talk-about-Burn&quot;&gt;Wix 3.5&#39;s Burn&lt;/a&gt; to hopefully resolve these issues and the ones not mentioned.  Rob Mensching, are you listening? I&#39;ll even volunteer to help write it.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/2757817073879618703?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/2757817073879618703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/2757817073879618703'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2009/08/visual-studio-2008-generatebootstrapper.html' title='Visual Studio 2008 GenerateBootstrapper task and UAC'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-5013749487427977969</id><published>2008-12-11T11:45:00.003-05:00</published><updated>2008-12-11T12:04:14.573-05:00</updated><title type='text'>Turning off Data Execution Prevention (DEP) for IE7 on Vista x64</title><content type='html'>&lt;span class=&quot;Apple-style-span&quot; style=&quot;border-collapse: collapse; font-family: arial; font-size: 13px; &quot;&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Time flies so fast that you dont even realize you have not written a new blog entry in a very long time...  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In January of 2007, &lt;a href=&quot;http://blogs.msdn.com/michael_howard/archive/2006/12/12/update-on-internet-explorer-7-dep-and-adobe-software.aspx&quot;&gt;I posted a comment to a security blog&lt;/a&gt; relating to the difficulty in turning off DEP in the 32-bit version of Internet Explorer 7 on a Vista x64 OS.  As to why you would want to do this, I will offer no opinion and would not recommend it in general for long term use.  However, lets say that your 32 bit IE (on Vista x64) opens and immediately crashes for some reason.  This means you need to tweak settings (like removing an add-in) for the 32-bit IE, yet the control panel only offers the 64-bit IE settings. Normally, you can get to the 32-bit settings from inside a 32-bit browser instance but due to the immediate crash this is not possible.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A very astute individual came across my comment and since comments were closed on the initial post, sent me a how-to guide for accessing the 32-bit IE settings in Vista x64.  I offered to re-post the details here with attribution.  Kudos to Razvan Socol, a SQL Server MVP, for providing this solution after &lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: Arial; &quot;&gt;encountering the same problem (after &quot;blindingly following the advice in the &lt;a href=&quot;http://www.microsoft.com/technet/security/advisory/961051.mspx&quot;&gt;Security Advisory 961051&lt;/a&gt;&quot;)&lt;/span&gt;, which makes this post fairly timely in case someone else falls into this trap:&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family:Arial;font-size:85%;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;span style=&quot;font-family:Arial;font-size:85%;&quot;&gt;To access the options for the 32-bit Internet Explorer, I started an elevated command prompt and executed C:\WINDOWS\SYSWOW64\EXPLORER.&lt;wbr&gt;EXE /SEPARATE to execute Windows Explorer in 32-bit mode. In this window, I navigated to the Internet Explorer icon on the desktop and by right-clicking it, I accessed the Internet Options for the Internet Explorer 32-bit, where I could finally uncheck that box.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family:Arial;font-size:85%;&quot;&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family:Arial;font-size:85%;&quot;&gt;I&#39;d like to post a comment on Michael&#39;s blog to share this info with the world, but the comments are closed for that post. Anyway, I thought at least I should let you know on how to solve that problem, although I&#39;m sure you have solved it in another way by now... [ed: I actually did not find an easy way to do it previously outside of several registry tweaks]&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;span style=&quot;font-family:Arial;font-size:85%;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/5013749487427977969?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/5013749487427977969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/5013749487427977969'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2008/12/turning-off-data-execution-prevention.html' title='Turning off Data Execution Prevention (DEP) for IE7 on Vista x64'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-8712224332871407337</id><published>2008-02-14T12:11:00.001-05:00</published><updated>2008-02-14T12:13:11.844-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design and Architecture"/><title type='text'>Software Product Lines</title><content type='html'>This is the first of a series of articles on an approach to systems architecture called “Software Product Lines.”  To introduce the concept, let’s ignore the word “software” for now and focus on what a product line is in a more general and tangible fashion.&lt;br /&gt;&lt;br /&gt;If you are in the market for a laptop and go to Dell’s website, you have a choice of several different base models that can be configured to your specification.  Once you select the base model, you can configure things such as the size of RAM, size of the hard drive, type of display, etc.  What makes this possible is a hardware architecture of various components that can fit into the same slot – compatible electrically as well as physically.  This allows Dell to create and mass produce (or purchase) several standardized components and simply plug them together to create a customized product at a reasonable cost in a short timeframe.  When new technologies come out that require changes to the base product, like ATA laptop hard drives, Dell spins up a new product line with that one modification reusing the old technology and components that is still valid, tried, and true.  This sounds like a great idea and solid common sense, doesn’t it?&lt;br /&gt;&lt;br /&gt;As with anything, there are challenges to this approach.  Take for instance a design flaw in one of the more heavily shared components, such as the motherboard, where the power connector solder joints go bad due to heat and constant plugging/unplugging.  This one design flaw could end up costing dearly in repair and possibly recall expenses, as a significantly higher number of devices are impacted then if the system was not as modular.  It is also significantly more difficult and time consuming up-front to design, build, and test a system that shares components.  In a product line, tight adherence to specifications needs to be considered for future compatibility.  Take for example the wireless network/Bluetooth riser cards found in many laptops.  Let’s assume the bus timings are just a hair off spec, but the current crop of hardware is tolerant of it.  Newer technologies coming out six months later may not be tolerant.  A non-product line approach would be to make a revision of the entire system – which is required anyway; however, the product line approach requires significant expense to revise and test one or both of the components to assure compatibility.&lt;br /&gt;&lt;br /&gt;Software is not very different than hardware when it comes to product lines.  Most high-end software packages are heavily customizable so it can integrate into a variety of environments.  Microsoft’s SharePoint product is one example, SAP is another.  These two products are off-the-shelf packages intended to appeal to a very large market.  There was heavy up-front investment by the vendor in design, documentation, interfaces, components, external training and certification programs, and more.  But what if your product is very specialized with a small market?  Even worse, what if your product is designed for small to medium sized businesses that typically can’t afford or will not have the resources to self-customize such a vast system?  To top it all off, certainly, you cannot afford to invest in the amount of up-front work required to build, test, and debug a heavily customizable system given the small size of your target market.&lt;br /&gt;&lt;br /&gt;Enter the concept of Software Product Lines.  It is important to note that this is an engineering approach to solve a specific business problem.  Using the Software Product Line methodology in a business environment that is not controlled or the problem domain of the customer is not well understood will cause you to paint yourself into a corner with no easy or cheap way out.  Another salient comment is choosing this approach should not significantly change your development methodology (assuming, of course, that you have adopted a legitimate methodology in the first place).  Finally, this approach is NOT a method of implementation, merely a way of thinking from a design perspective of what needs componentized.  The decision to use a Service Oriented Architecture to implement a Software Product Line is off-topic – these are two different animals entirely.&lt;br /&gt;&lt;br /&gt;Part II of this series will delve into some of these concepts deeper.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/8712224332871407337?isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/8712224332871407337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/8712224332871407337'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2008/02/software-product-lines.html' title='Software Product Lines'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-113799292547409844</id><published>2006-01-22T22:39:00.001-05:00</published><updated>2008-02-14T01:30:29.851-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Diversions"/><title type='text'>Go Steelers Redux</title><content type='html'>Not only has it been a long time since my last post, but it has been &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/01/go-steelers.html&quot;&gt;exactly a year since my last post on my hometown NFL football team&lt;/a&gt;, the &lt;a href=&quot;http://www.steelers.com/&quot;&gt;Pittsburgh Steelers&lt;/a&gt;. Sadly, last year we lost the AFC championship. This year we won it against the Denver Bronco&#39;s. This means we are off to the Super Bowl against the &lt;a href=&quot;http://www.seahawks.com/&quot;&gt;Seattle Seahawks&lt;/a&gt;. Superbowl XL is on February 5th, 2006. In preparation, my blog banner has been updated to &quot;All Steelers, all the time&quot; from the revolving banner of the past.&lt;br /&gt;&lt;br /&gt;One of the great things about living in (or coming from) the Pittsburgh area is the friendliness of the people in and around the city. Last year, I blogged a bit about Myron Cope and the &quot;Terrible Towel.&quot; One thing I neglected to mention is that all proceeds on the sale of the towel go to the &lt;a href=&quot;http://www.avs.net/terribletowel.cfm&quot;&gt;Allegheny Valley School&lt;/a&gt; that provides care for children, adults, and seniors with mental retardation and physical disabilities. If you would like to support the Steelers, and the school, please use the above link (instead of the Google AdSense ones) so that the school gets additional credit.&lt;br /&gt;&lt;br /&gt;I believe the Steelers have been playing for the &#39;Bus&#39; - a.k.a. running back Jerome Bettis. The Bus has been a Pittsburgh fixture for years, not only as a dependable player, but as an &lt;a href=&quot;http://i.tsn.com/features/goodguys/2002/bettis.html&quot;&gt;excellent example of what a celebrity should be like&lt;/a&gt;. To illustrate my point, Hines Ward (a teammate and Steelers Wide Receiver) was in tears last year after the AFC Championship loss because he believed the team let Jerome down in his potentially last season. This year is different. &lt;a href=&quot;http://www.detnews.com/apps/pbcs.dll/article?AID=/20060122/UPDATE/601220414/1003&quot;&gt;Jerome will have the opportunity to play, and in his home city, Detroit&lt;/a&gt;. Jerome is active in the community, and is currently &lt;a href=&quot;http://www.chp.edu/clinical/03a_asthma.php&quot;&gt;raising awareness about Asthma&lt;/a&gt;, a disease he is afflicted with, in addition to a program called &quot;&lt;a href=&quot;http://www.thebus36.com/&quot;&gt;The Bus Stops Here&lt;/a&gt;,&quot; benefiting children from both Pittsburgh and Detroit. Jerome&#39;s parents, &lt;a href=&quot;http://www.post-gazette.com/pg/06019/640511.stm&quot;&gt;Gladys and John&lt;/a&gt;, have every reason to proud of their son, and the values they instilled in him.&lt;br /&gt;&lt;br /&gt;Other team members have had tremendous impact on the Pittsburgh region. I don&#39;t want to leave out any players, but &lt;a href=&quot;http://pittsburghlive.com/x/tribune-review/trib/pittsburgh/s_390966.html&quot;&gt;Charlie Batch&lt;/a&gt;, a backup quarterback, is also famous for not only coming from the Pittsburgh area, but for his contributions to it. There are several other examples of how the team and its players gives back to the community.&lt;br /&gt;&lt;br /&gt;I believe much of the reason for the positive impact of the players of the Steelers goes back to the team&#39;s ownership and philosophy. The most famous owner is &lt;a href=&quot;http://www.steelergridiron.com/history/artrooney.html&quot;&gt;Art Rooney&lt;/a&gt;, a.k.a. &quot;The Chief&quot;, who bought the franchise in 1933. If any Pittsburgher&#39;s have not yet seen the play &quot;The Chief&quot; at the Pittsburgh Public Theatre, you don&#39;t know what you missed! This play ran in 2003, 2004, and earlier this month - keep checking for it to return (possibly) next fall at the &lt;a href=&quot;http://www.ppt.org/upcoming.htm&quot;&gt;PPT website.&lt;/a&gt; Franco Harris (star player in the 70&#39;s) was talking about Art Rooney when he said, &quot;He was always there to help and to give. And this feeling filtered down to the players. I think the Steelers&#39; players give more to their community than any other team in professional sport.&quot;&lt;br /&gt;&lt;br /&gt;I&#39;m hoping that the Super Bowl in a few weeks does not bring on any additional &lt;a href=&quot;http://pittsburghlive.com/x/tribune-review/trib/regional/s_414565.html&quot;&gt;heart attacks&lt;/a&gt; among the fans, but that you watch it with the understanding that many of the athletes on both sides of the ball are using their celebrity status and monies to better their communities. I think we can all learn from this.&lt;br /&gt;&lt;br /&gt;To my readers in the Seattle/Redmond area - I didn&#39;t mean to leave out the players of your team, I merely am not familiar with their good works. I am, however, familiar with &lt;a href=&quot;http://edb.seattletimes.nwsource.com/ae/scr/edb_vd.cfm?ven=1750&amp;amp;s=st&quot;&gt;Victor&#39;s Celtic Coffee Company&lt;/a&gt;, and am currently out of the &quot;Redmond Slough&quot; blend as of this morning. I&#39;d be willing to trade for some of that in exchange for posting Seattle Seahawks charity links and an equivalent amount of coffee from one of our local coffee houses (&lt;a href=&quot;http://pittsburgh.citysearch.com/review/8612151&quot;&gt;Coffee Tree Roasters&lt;/a&gt;)...&lt;br /&gt;&lt;br /&gt;Go Steelers!</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/113799292547409844?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113799292547409844'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113799292547409844'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2006/01/go-steelers-redux.html' title='Go Steelers Redux'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-113799478389447847</id><published>2006-01-22T09:00:00.001-05:00</published><updated>2008-02-14T01:30:44.343-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Diversions"/><title type='text'>Silence is a virtue...</title><content type='html'>I was looking for some way of explaining my recent blog silence. As one who likes to use the words of others to explain things that are difficult to explain in my own words, I hit the quote book and encountered this gem by Sally Berger: &quot;You never saw a fish on the wall with its mouth shut.&quot; That doesn&#39;t really fit, but I found it kinda funny and that will have to do.&lt;br /&gt;&lt;br /&gt;In addition to the typical excuse of the holidays, I also recently changed employers. This has the side effect of changing the type of development I do. Keeping with my philosophy that I speak only for myself and not my employer, I will not divulge the name of my current employer. I will also not divulge anything that could be considered something other than generally available developer knowledge. So in reality, the only thing that could possibly change is the type of content.&lt;br /&gt;&lt;br /&gt;Most likely I will be delving into topics such as .NET Development, the Compact Framework, and native Windows Mobile (Windows CE) development/debugging. Some of these postings may be about deployment concerns in the Windows Mobile environment. Content suggestions related to this area of development are welcome. I also plan on finishing up a few posts on MSI related subjects I started a while back after some editing and reviewing.&lt;br /&gt;&lt;br /&gt;If you have questions regarding MSI of other installer technologies, please keep them coming - many of the privately asked (and answered) questions I have saved as blog post drafts to be completed later, and I will continue to attempt to answer them time permitting.&lt;br /&gt;&lt;br /&gt;In the meantime, constant reader, please bear with me while I adapt to my new environment and gather together some new (and old) content.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/113799478389447847?isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113799478389447847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113799478389447847'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2006/01/silence-is-virtue.html' title='Silence is a virtue...'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-113374124812695071</id><published>2005-12-04T17:11:00.003-05:00</published><updated>2008-02-14T00:51:21.775-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="Quality"/><title type='text'>Writing Clear Code</title><content type='html'>This topic is a bit of a divergence from Custom Actions, but useful for some general coding skills. I&#39;m going to highlight the C# language in this post, although you don&#39;t need to know C# to benefit from this discussion - I&#39;ll cover the essentials in the post. The topic is how to write clear code that makes it easier to defer the understand the intention of the code rather than the behavior of the code. The subject came up about a few months ago in a discussion with a coworker who initially didn&#39;t quite agree with me. I&#39;ll lay out the argument here as I did with him.&lt;br /&gt;&lt;br /&gt;Take for instance the following code snippet:&lt;br /&gt;&lt;pre&gt;string a = &quot;hello&quot;;&lt;br /&gt;string b = &quot;hello&quot;;&lt;br /&gt;System.Console.WriteLine(a == b);&lt;br /&gt;System.Console.WriteLine((object)a == (object)b);&lt;/pre&gt;Lets explore the last two lines. The a == b equivalence operator is working on strings, and by &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_7_9_7.asp&quot;&gt;C# rule&lt;/a&gt;, two strings are equivalent if they are both null, or if both values are non-null references to string instances that have identical lengths and identical characters in each character position. Translation - two nulls are identical, and any two strings that match, case sensitive, will result in a true expression. In the latter comparison, when we cast both of the strings to objects, we are not comparing the values of the strings, but the objects themselves.&lt;br /&gt;&lt;br /&gt;What do you think it will print? The answer is True and True. Why? &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_2_4_4_5.asp&quot;&gt;String literals that are identical within the same assembly refer to the same instance.&lt;/a&gt; Translated, &#39;a&#39; and &#39;b&#39; are variables that refer to the same underlying object.&lt;br /&gt;&lt;br /&gt;Now lets replace the declaration and assignment for string b with:&lt;pre&gt;string b = &quot;he&quot;+&quot;llo&quot;;&lt;/pre&gt;Now what do you think it will print? The answer is again True and True. Why? Because the Lexical analysis (lexer) in the compiler stripped out the needless additive operator in the literal. The same would have happened if we changed the string to be &quot;hell\u006f&quot; - since &#39;\u006f&#39; is the Unicode escape sequence of the lower-case letter &#39;o&#39;, and this expansion takes place (most likely) prior to the lexer during the transformation phase in the compilation process.&lt;br /&gt;&lt;br /&gt;Now lets replace the same line as above with the following two lines:&lt;br /&gt;&lt;pre&gt;string b = &quot;he&quot;;&lt;br /&gt;b += &quot;llo&quot;;&lt;/pre&gt;Now what do you think it will print? The answer is now True and False. Why? The strings are equivalent, but since the preprocessing of the source file before the compilation did not identify the strings as being the same literal, they are in different objects. In fact, because a string is immutable (once created cannot be changed), there was the construction of the string &quot;b&quot; during declaration and initial assignment that was later garbage collected when a new instance of &quot;b&quot; was created during the string concatenation.&lt;br /&gt;&lt;br /&gt;Now that the necessary background information is understood, can you tell me the intention of the following code snippet?&lt;br /&gt;&lt;pre&gt;string a,b;&lt;br /&gt;//code here that assigns and manipulates a and b&lt;br /&gt;...&lt;br /&gt;if (a==b)&lt;br /&gt;    compareOK();&lt;br /&gt;else&lt;br /&gt;    compareBad();&lt;/pre&gt;I&#39;m hoping you are going to tell me that you have no clue as to the intent of the programmer. The intent could have been to compare the two strings in a case sensitive manner, ignoring cultural rules (which is what actually is happening in the above snippet). It also could have been to compare object equivalence, just that the programmer forgot to cast the strings to object first. The intention could also have been a case-insensitive comparison. Another possible intent was to compare strings using cultural rules. The intent of the programmer simply is not clear!&lt;br /&gt;&lt;br /&gt;Rewriting the comparison line in the above snippet correctly, assuming the actual behavior of the code was the intent should look like this:&lt;pre&gt;if ( String.Compare(a, b, false,&lt;br /&gt;System.Globalization.CultureInfo.InvariantCulture ) == 0 )&lt;/pre&gt;This tells me that the intent of the programmer was to compare the two strings in a case sensitive manner, ignoring cultural rules . There is no other possible intent given the above line of code. This is not saying that the code is correct - just that the intent of the code is clear to those lucky enough to read it, and that the developer thought it through.&lt;br /&gt;&lt;br /&gt;If I am skimming through some code and see a non-String.Compare() string comparison, I will sprinkle a &lt;a href=&quot;http://blogs.msdn.com/larryosterman/archive/2005/03/29/403439.aspx&quot;&gt;//BUGBUG: comment&lt;/a&gt; above it. Why? The simple string comparison syntax has been a rather common cause of bugs in C# code - much like the C/C++ switch statement. When investigating an issue in a piece of code, I will first look for these BUGBUG&#39;s and the TODO&#39;s as a starting point - often with better-than-random chance results.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/113374124812695071?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113374124812695071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113374124812695071'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/12/writing-clear-code.html' title='Writing Clear Code'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-113068882306714843</id><published>2005-11-15T09:04:00.001-05:00</published><updated>2008-02-14T00:00:45.036-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="MSI Custom Actions"/><title type='text'>Custom Action Tutorial Part III – What we did in Part II</title><content type='html'>&lt;p&gt;This is the third in a series of articles about building Custom Actions. If you are coming in late to the party, check out &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/10/custom-action-tutorial-part-i-custom.html&quot;&gt;Part I&lt;/a&gt; and &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/11/custom-action-tutorial-part-ii.html&quot;&gt;Part II&lt;/a&gt; first. If I tried to explain what we were doing and why in the last installment of this tutorial, you would be even more lost now than you probably are. This article is an attempt to explain exactly what it was we did in Part II and why. Let’s start by going over each project in our solution. &lt;p&gt;&lt;strong&gt;The HelloWorld project&lt;/strong&gt; &lt;p&gt;We needed something to install. This happens to be it. I could have picked notepad.exe or something, but it is nicer to see how Visual Studio could grab project outputs and put them into an MSI fairly easily. &lt;p&gt;&lt;strong&gt;The CustomAction project &lt;/strong&gt;&lt;p&gt;This one is really the meat of what we are trying to learn. The other projects are sort of extraneous if you are using a tool other than Visual Studio to build your MSI. Microsoft did a pretty good job of explaining why you may want to use &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_actions.asp&quot;&gt;Custom Actions&lt;/a&gt; in the first place, so I’m not going to rehash that. &lt;p&gt;We created a Custom Action DLL. The reason I chose a DLL is because it is the most flexible means of tightly integrating a Custom Action with the installation engine. Additionally, there is not much you cannot do in a Win32 C++ DLL. An executable Custom Action cannot interact with the MSI engine. A VBScript or JScript action does have this capability, but if you are not careful, &lt;a href=&quot;http://blogs.msdn.com/robmen/archive/2004/05/20/136530.aspx&quot;&gt;these&lt;/a&gt; &lt;a href=&quot;http://blogs.msdn.com/astebner/archive/2005/02/07/368917.aspx&quot;&gt;are not&lt;/a&gt; &lt;a href=&quot;http://bonemanblog.blogspot.com/2004/06/vbscript-and-jscript-msi-custom.html&quot;&gt;bulletproof&lt;/a&gt;, and usually won&#39;t allow you to do much anyway. &lt;p&gt;We added a &quot;Module Definition File&quot; (.def) file to the project. This .def file is read by the linker and is used (for our purposes, anyway) to prevent mangling of function names (to be technically correct, specify the calling convention to be used). Mangling is entirely normal when a DLL is created without using the extern &quot;c&quot; function declaration – in fact, it is called by the more pleasant name &quot;Decorating&quot; the function names. If you run &quot;Dependency Walker&quot; (depends.exe in the Bin folder of the Platform SDK), and open a DLL in it, the listing of function exports appears. Pick FrameDyD.Dll from the same Bin directory and look at it. You will notice some ? and @ symbols around the names of functions. The .def file allows us to not create these mangled exports – you can verify this by opening the custom action dll we built in our project and see this for yourself. Ultimately, this makes the process less prone to error when entering data in the custom action table of the MSI. When adding the Custom Action via Visual Studio, it will handle the mangling automatically, so for this specific project this was not strictly necessary. If you are adding this DLL using another installation designer, you will thank me for not mangling the function names. Anyway, the .def file must contain the function names of any function you wish to make callable from outside the DLL. In our case, we wanted all four of the functions we added to be callable by the MSI engine, which is why they were added to the DEF file. There are tons of other ways to accomplish this goal &lt;p&gt;The function signatures we added to the CustomAction.cpp file are based on the requirements of the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/dynamic_link_libraries.asp&quot;&gt;DLL Custom Action type&lt;/a&gt;. We return an unsigned integer (UINT) and accept a single parameter that is the handle to the installation (MSIHANDLE). We must use this handle to interact with the MSI Engine. The UINT return type indicates &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_return_values.asp&quot;&gt;our status to the MSI engine.&lt;/a&gt; &lt;p&gt;The file &quot;msi.lib&quot; is the link library for the MSI engine. All custom action DLL’s must link to this file in order to call any MSI functions. The instructions in Part I explained an alternate way to inform the linker what to link with. One way is not necessarily better or worse than the other. The functions defined in the header files msi.h and msiquery.h (that we included in the stdafx.h file) all require linking to this library. &lt;p&gt;We made additional solution configurations to give us a UNICODE project type. While this is not intended to be a primer on UNICODE, all NT kernel OS’s are UNICODE under the hood. Windows 95 through ME are not UNICODE, although they can support some UNICODE extensions with the Unicode Library. For dll&#39;s and executables that only will run under Windows NT, 2000, XP, or 2003, the only option you should use is UNICODE. Therefore, if you are targeting only NT based systems, build and use the UNICODE projects. If you are supporting Windows 9x based machines use the non Unicode versions. To make the Unicode transition easier, Microsoft provided the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_generic.2d.text_mappings_in_tchar..h.asp&quot;&gt;tchar.h header&lt;/a&gt; that we include in the stdafx.h file that deals with strings based on compiler settings (actually preprocessor macros), so you may write code that will compile and work as native UNICODE or ANSI based on compiler settings. The TEXT() macro used in the &quot;Hello MSI!&quot; exercise is an example of one such tchar.h macro. Future Custom Action examples presented here will use the tchar.h macros when the compatibility of the Custom Action spans both Windows 9x and NT based platforms. Actions targeting Windows NT based systems will only compile in UNICODE, and will not use the tchar.h macros. &lt;p&gt;The final thing we added to the CustomAction project was the header include for &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/strings/usingstrsafefunctions.asp&quot;&gt;strsafe.h&lt;/a&gt;. This header file describes the safe string functions intended to replace the C/C++ standard library string functions as well as specific Windows string handling implementations. These functions always start with the word &quot;String&quot; followed by the type of count provided as a parameter to the function, either the &quot;Cb&quot; which is a count of bytes, or &quot;Cch&quot; which is a count of characters. Remember that Unicode implementations use 2 bytes per character as opposed to the ANSI 1 byte per character – so the Cb and Cch designations are important. The last important fact about this header is by default it will deprecate the unsafe string handling functions – if you use sprintf(), you will get a compiler warning. &lt;p&gt;&lt;strong&gt;The Setup1 Project &lt;/strong&gt;&lt;p&gt;Hopefully we are pretty clear on the basics for the first part of this one – an EXE is generated, Visual Studio generates a basic MSI and adds entries in the Feature, Component, File, Shortcut, and other tables to install the HelloWorld exe and the shortcut for it. &lt;p&gt;When we added the Custom Actions, we did a few things that affected the compiled MSI. Use Orca (which you installed in Part I) to view the generated MSI and follow along - try to remember back to the Custom Action theory presented in Part I: &lt;ol&gt;&lt;li&gt;Added a new Component and File table entry that installs the CustomAction.dll file. &lt;li&gt;Added four entries to the CustomAction table, referencing the DLL that we installed with the product. Notice the Source column of the CustomAction table is a reference to the key of File table for our Custom Action DLL. The CustomAction table&#39;s Target column is the DLL entry point (remember this is nothing more magical than the function names in our .def file). The action name is just a randomly generated GUID. The type column starts its life as a &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_type_17.asp&quot;&gt;Type 17&lt;/a&gt; which indicates &lt;span style=&quot;font-family:Courier;&quot;&gt;msidbCustomActionTypeDll + msidbCustomActionTypeSourceFile&lt;/span&gt;, and to this number is added the following: &lt;table border=&quot;1&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Action Type &lt;/th&gt;&lt;th&gt;Decimal Value&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Uninstall &lt;/td&gt;&lt;td&gt;1041&lt;/td&gt;&lt;td&gt;= 17 + msidbCustomActionTypeInScript&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Install &lt;/td&gt;&lt;td&gt;1041 &lt;/td&gt;&lt;td&gt;= 17 + msidbCustomActionTypeInScript&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rollback &lt;/td&gt;&lt;td&gt;1297 &lt;/td&gt;&lt;td&gt;= 17 + msidbCustomActionTypeInScript + msidbCustomActionTypeRollback &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Commit &lt;/td&gt;&lt;td style=&quot;WIDTH: 35px&quot;&gt;1553 &lt;/td&gt;&lt;td&gt;= 17 + msidbCustomActionTypeInScript + msidbCustomActionTypeCommit&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;li&gt;Looking at the InstallExecuteSequence table, note that all but the uninstall custom action is scheduled before the RegisterUser Standard action, and the uninstall custom action is scheduled before the UnpublishComponents action. Note that these actions are &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/conditional_statement_syntax.asp&quot;&gt;conditionalized on the action state of the component, denoted by the dollar-sign&lt;/a&gt; - you can see this clearly in the InstallExecuteSequence table. In a nutshell, this condition will cause the custom actions to run only when the component it is tied to (the Custom Action DLL) is being added or removed from the system. &lt;/li&gt;&lt;/ol&gt;Visual Studio does not give us a bunch of options when dealing with Custom Actions, so we are pretty much stuck with what they offer – which is the ability to add an additional condition (other than the component install state), stuff what goes into the CustomActionData property fairly easy, and sequence the custom actions relative to each other. You cannot change sequencing of the custom action (like making it occur relative to another standard action other than RegisterUser or UnpublishComponents, embedding the custom action in the binary table, or add immediate custom actions. Other authoring tools offer the ability to be tons more flexible, but we are stuck with these limitations for now. &lt;p&gt;That concludes Part III of the Custom Action Tutorial. In the next part, we will learn how to debug the Custom Action. &lt;/p&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/113068882306714843?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113068882306714843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113068882306714843'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/11/custom-action-tutorial-part-iii-what.html' title='Custom Action Tutorial Part III – What we did in Part II'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-113068823399361865</id><published>2005-11-03T08:30:00.003-05:00</published><updated>2009-03-10T14:50:31.152-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="MSI Custom Actions"/><title type='text'>Custom Action Tutorial Part II – Creating the Project</title><content type='html'>&lt;p&gt;[Update 3/10/2009: Updated download link.  Thanks for hosting, Aaron!]&lt;/p&gt;&lt;p&gt;In &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/10/custom-action-tutorial-part-i-custom.html&quot;&gt;Part I we covered the dry material&lt;/a&gt; - what custom actions are, and how to use them. In this part of the tutorial, we will configure the development environment and create a baseline project to start from when developing future Custom Actions. Please make sure your system is configured as described in the first part before continuing. &lt;/p&gt;&lt;p&gt;I am providing a download link for the final project described in this part for convenience (see the end of this post for the link), but I recommend first time custom action developers follow the steps so they understand what is going on. For my more experienced readers, please bear with the simplicity here, but review the project settings before downloading or using the template in future parts.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Create a Basic Installation Project&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;To create our solution, open &lt;strong&gt;Visual Studio 2003&lt;/strong&gt;. We are going to create two projects to start with – one a Win32 Project called &quot;HelloWorld&quot;, and a deployment project that will create an MSI to install it. Go to the &lt;em&gt;File-&gt;New-&gt;Project&lt;/em&gt; menu. Select the &lt;em&gt;Visual C++ Projects-&gt;Win32-&gt;Win32 Project&lt;/em&gt;. Call the name of the project &quot;Hello World&quot; and place it into a directory such as &lt;strong&gt;C:\Projects\CATutorial&lt;/strong&gt;. Select &lt;strong&gt;Finish&lt;/strong&gt; in the Wizard dialog. When the IDE initializes, you should see the &lt;strong&gt;Solution Explorer&lt;/strong&gt; window – Right-click on the &lt;strong&gt;Solution &#39;HelloWorld&#39;&lt;/strong&gt; line at the top of this box and select &lt;em&gt;Add-&gt;New Project&lt;/em&gt;. Find the &lt;strong&gt;Setup and Deployment Projects&lt;/strong&gt; category and select &lt;strong&gt;Setup Project&lt;/strong&gt;, and select the &lt;strong&gt;Setup Project Wizard&lt;/strong&gt; – you can accept the default name of &lt;strong&gt;Setup1&lt;/strong&gt;. Hit &lt;strong&gt;next&lt;/strong&gt;, and accept the defaults for page 2. On page 3, check the &lt;strong&gt;Primary Output from HelloWorld&lt;/strong&gt; option and hit &lt;strong&gt;Finish&lt;/strong&gt;. When the project opens, select the &lt;strong&gt;Users Programs Menu&lt;/strong&gt; and right-click in the pane to the right. Select &lt;strong&gt;Create New Shortcut&lt;/strong&gt;. Double-click &lt;strong&gt;Application Folder&lt;/strong&gt; and double-click the &lt;strong&gt;Primary Output from HelloWorld (Active)&lt;/strong&gt;. Rename the shortcut if you wish. &lt;/p&gt;&lt;p&gt;Now we have both an EXE and an MSI project that installs the EXE plus a shortcut to it. At this point, we can build and test our MSI by right-clicking the &lt;strong&gt;Setup1&lt;/strong&gt; project and selecting &lt;strong&gt;Build&lt;/strong&gt;. Next, right-click the project and select &lt;strong&gt;Install&lt;/strong&gt;. You will be walked through the standard MSI project dialog sequence – just accept the defaults and finish the install. A shortcut is installed to &lt;strong&gt;Program Files&lt;/strong&gt; in the &lt;strong&gt;Start Menu&lt;/strong&gt;, which will open a window that pretty much does nothing. If you get this far, you are on the right track. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Add a DLL Project&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;To make a Custom Action DLL, we want to now add a new project – a Win32 Project that is a DLL. Follow the add project instructions for &quot;HelloWorld&quot; above, but call it &quot;CustomAction&quot; and in the Wizard, select the &lt;strong&gt;Application Settings&lt;/strong&gt; and pick &lt;strong&gt;DLL&lt;/strong&gt; for the Application Type. Press &lt;strong&gt;Finish&lt;/strong&gt;. We now need to add some items to this project – please follow this closely. &lt;/p&gt;&lt;ol&gt;&lt;li&gt;Right-click on the &lt;strong&gt;CustomAction&lt;/strong&gt; project and select &lt;em&gt;Add-&gt;New Item&lt;/em&gt;. Find the &lt;strong&gt;Module-Definition File (.def)&lt;/strong&gt; and select it. Call the file &lt;strong&gt;CustomAction&lt;/strong&gt; and press &lt;strong&gt;Open&lt;/strong&gt;. We&#39;ll add some content to it in a later step. &lt;/li&gt;&lt;li&gt;Right-click on the &lt;strong&gt;CustomAction&lt;/strong&gt; project and select &lt;strong&gt;properties&lt;/strong&gt;. &lt;/li&gt;&lt;li&gt;In the &lt;strong&gt;Configuration&lt;/strong&gt; dropdown, select &lt;strong&gt;All Configurations&lt;/strong&gt; &lt;/li&gt;&lt;li&gt;Expand the &lt;strong&gt;Linker&lt;/strong&gt; folder, and select &lt;strong&gt;input&lt;/strong&gt;. In the &lt;strong&gt;Additional Dependencies&lt;/strong&gt; edit box, enter &lt;strong&gt;msi.lib&lt;/strong&gt; and hit &lt;strong&gt;Apply.&lt;/strong&gt; NOTE: As an alternate to this and the previous 2 steps, you can use the following directive in the stdafx.h or any other source file to tell the linker to link with msi.lib: #pragma comment(lib, &quot;msi.lib&quot;) &lt;/li&gt;&lt;li&gt;In the upper right hand corner of the &lt;strong&gt;Property Pages dialog box&lt;/strong&gt;, click &lt;strong&gt;Configuration Manager&lt;/strong&gt;. &lt;/li&gt;&lt;li&gt;Under the &lt;strong&gt;Active Solution Configuration&lt;/strong&gt; dropdown, select &lt;strong&gt;New&lt;/strong&gt;. Enter the name &lt;strong&gt;Release Unicode&lt;/strong&gt;, select &lt;strong&gt;Release&lt;/strong&gt; from the &lt;strong&gt;Copy Settings from&lt;/strong&gt; dropdown, and leave the checkbox checked. Press &lt;strong&gt;OK&lt;/strong&gt;. &lt;/li&gt;&lt;li&gt;Repeat the previous step, calling the Configuration &lt;strong&gt;Debug Unicode&lt;/strong&gt; and select &lt;strong&gt;Debug&lt;/strong&gt; under the &lt;strong&gt;Copy Settings From&lt;/strong&gt; dropdown. Close the &lt;strong&gt;Configuration Manager&lt;/strong&gt;. &lt;/li&gt;&lt;li&gt;We are now back at the CustomAction property pages. Select &lt;strong&gt;Debug Unicode&lt;/strong&gt; from the &lt;strong&gt;Configuration&lt;/strong&gt; dropdown. Under the &lt;strong&gt;General Configuration Properties&lt;/strong&gt;, find the &lt;strong&gt;Character Set&lt;/strong&gt; setting and select &lt;strong&gt;Use Unicode Character Set&lt;/strong&gt;. Press &lt;strong&gt;Apply&lt;/strong&gt;. &lt;/li&gt;&lt;li&gt;Repeat the previous step for &lt;strong&gt;Release Unicode&lt;/strong&gt;. Press &lt;strong&gt;OK&lt;/strong&gt; to close the CustomAction property pages. &lt;/li&gt;&lt;li&gt;Repeat the previous two steps for the &quot;HelloWorld&quot; project by opening the property pages for the HelloWorld project. &lt;/li&gt;&lt;li&gt;In the CustomAction project, under the &quot;Header Files&quot; folder, double-click the file stdafx.h file to edit it. Under the &lt;span style=&quot;color:#008000;&quot;&gt;// TODO:&lt;/span&gt; line, add the following lines: &lt;div style=&quot;MARGIN-LEFT: 10pt; MARGIN-RIGHT: 2pt&quot;&gt;&lt;div   style=&quot;OVERFLOW: auto;font-family:&#39;Courier New&#39;;font-size:15px;&quot;&gt;&lt;span style=&quot;color:#0000ff;&quot;&gt;#include &lt;/span&gt;&amp;lt;tchar.h&amp;gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;MARGIN-LEFT: 10pt; MARGIN-RIGHT: 2pt&quot;&gt;&lt;div   style=&quot;OVERFLOW: auto;font-family:&#39;Courier New&#39;;font-size:15px;&quot;&gt;&lt;span style=&quot;color:#0000ff;&quot;&gt;#include &lt;/span&gt;&amp;lt;msi.h&amp;gt;&lt;span style=&quot;color:#0000ff;&quot;&gt;&lt;br /&gt;#include &lt;/span&gt;&amp;lt;msiquery.h&amp;gt;&lt;span style=&quot;color:#0000ff;&quot;&gt;&lt;br /&gt;#include &lt;/span&gt;&amp;lt;strsafe.h&amp;gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;It is always a good idea to add versioning information to all the compiler outputs you build, so right-click on the CustomAction project and select &lt;em&gt;Add-&gt;Resource&lt;/em&gt;. Pick &lt;strong&gt;Version&lt;/strong&gt; from the list by double clicking on it. Edit the information here if you so desire. &lt;/li&gt;&lt;li&gt;In the CustomAction.cpp file, add four functions based on the following template named &lt;strong&gt;Install, Commit, Rollback, &lt;/strong&gt;and &lt;strong&gt;Uninstall&lt;/strong&gt; – function names are case sensitive. &lt;div style=&quot;MARGIN-LEFT: 10pt; MARGIN-RIGHT: 2pt&quot;&gt;&lt;div   style=&quot;OVERFLOW: auto;font-family:&#39;Courier New&#39;;font-size:15px;&quot;&gt;&lt;span style=&quot;color:#0000ff;&quot;&gt;extern&lt;/span&gt; &quot;C&quot; UINT&lt;span style=&quot;color:#0000ff;&quot;&gt; __stdcall&lt;/span&gt; Install(MSIHANDLE hInstall)&lt;br /&gt;{&lt;br /&gt; &lt;span style=&quot;color:#0000ff;&quot;&gt;return &lt;/span&gt;ERROR_SUCCESS;&lt;br /&gt;}&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;Edit the CustomAction.def file and make it look like this: &lt;div style=&quot;MARGIN-LEFT: 10pt; MARGIN-RIGHT: 2pt&quot;&gt;&lt;div style=&quot;OVERFLOW: auto&quot; size=&quot;15px&quot; face=&quot;&#39;Courier New&#39;&quot;&gt;LIBRARY CustomAction&lt;br /&gt;EXPORTS&lt;br /&gt;Install&lt;br /&gt;Commit&lt;br /&gt;Rollback&lt;br /&gt;Uninstall&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;As a final step, we want to add the Custom Action DLL to the setup project. Right-click on &lt;strong&gt;Setup1&lt;/strong&gt; and select &lt;em&gt;View-&gt;Custom Actions&lt;/em&gt;. Right-click on &lt;strong&gt;Custom Actions&lt;/strong&gt; in the pane that opens up and select &lt;strong&gt;Add Custom Action&lt;/strong&gt;. Double-click on &lt;strong&gt;Application Folder&lt;/strong&gt;, and then click &lt;strong&gt;Add Output&lt;/strong&gt;. Select &lt;strong&gt;CustomAction&lt;/strong&gt; from the &lt;strong&gt;Project&lt;/strong&gt; dropdown and accept the default &lt;em&gt;Primary Output&lt;/em&gt; and &lt;em&gt;Active Configuration&lt;/em&gt; settings. Click OK, then click OK on the &lt;strong&gt;Select Item in Project&lt;/strong&gt; dialog. You will notice that four custom actions are added, and in viewing the properties for each of them the EntryPoint corresponds to the function names we created in the &lt;em&gt;CustomAction.cpp&lt;/em&gt; file. &lt;p&gt;At this point, lets right-click on the &lt;strong&gt;Setup1&lt;/strong&gt; project and build it. You will notice that the file &quot;msi.dll&quot; is detected as a dependency for the &lt;strong&gt;Setup1&lt;/strong&gt; project – as a matter of fact, it will create a warning during compilation. Right-click on the msi.dll file under dependencies and check the &lt;strong&gt;Exclude&lt;/strong&gt; option. Rebuilding the &lt;strong&gt;Setup1&lt;/strong&gt; project should now generate no warnings. &lt;/p&gt;&lt;p&gt;If you have not uninstalled the setup project from the earlier test, do so now, and run the &lt;strong&gt;Setup1&lt;/strong&gt; installation. You should see no difference from the previous time you ran the setup. If you get errors, you may want to download the zip file that contains the projects and steps we have performed up to here, and use WinDiff from the Bin directory of the Platform SDK to see if you can figure out the issue by comparing the directories of my project against yours. &lt;/p&gt;&lt;p&gt;Save your workspace and exit Visual Studio. Create a .zip file of the project directories – if you used my directory structure, you want to compress the &quot;C:\Projects\CATutorial&quot; directory. We will use this as a template for any future C++ custom action tutorials. &lt;/p&gt;&lt;p&gt;Initially, I was going to end this part here, but I thought people would be cursing me, saying that this whole exercise was pointless and no fun! After all this work, we created a Custom Action that did absolutely nothing! Let’s rectify that! Open your solution and replace the Install function in the CustomAction.cpp file with the following code (Don&#39;t worry if you don&#39;t understand it, we will cover this later): &lt;/p&gt;&lt;div style=&quot;MARGIN-LEFT: 10pt; MARGIN-RIGHT: 2pt&quot;&gt;&lt;div   style=&quot; OVERFLOW: auto; font-family:&#39;Courier New&#39;;font-size:15px;&quot;&gt;&lt;span style=&quot;color:#0000ff;&quot;&gt;extern&lt;/span&gt; &quot;C&quot; UINT&lt;span style=&quot;color:#0000ff;&quot;&gt; __stdcall&lt;/span&gt; Install(MSIHANDLE hInstall)&lt;br /&gt;{&lt;br /&gt; PMSIHANDLE hRecord = MsiCreateRecord(0);&lt;br /&gt; MsiRecordSetString(hRecord, 0, TEXT(&quot;Hello MSI!&quot;));&lt;br /&gt; MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER + MB_OK), hRecord);&lt;br /&gt; &lt;span style=&quot;color:#0000ff;&quot;&gt;return &lt;/span&gt;ERROR_SUCCESS;&lt;br /&gt;}&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When you rebuild the project and install it, you will see a &quot;Hello MSI!&quot; message when the Install Custom action runs. &lt;/p&gt;&lt;p&gt;You can download the Visual Studio 2003 Project Template here:&lt;iframe scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; frameborder=&quot;0&quot; style=&quot;width:240px;height:66px;margin:3px;padding:0;border:1px solid #dde5e9;background-color:#ffffff;&quot; src=&quot;http://cid-27e6a35d1a492af7.skydrive.live.com/embedrowdetail.aspx/Blog%7C_Tools/CATutorialCodePart2.zip&quot;&gt;&lt;/iframe&gt; &lt;p&gt;(Special thanks to &lt;a href=&quot;http://blogs.msdn.com/astebner&quot;&gt;Aaron Stebner&lt;/a&gt; for hosting this for me). The &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/11/custom-action-tutorial-part-iii-what.html&quot;&gt;next installment in this series&lt;/a&gt; will explain what we did (and why we did it)! &lt;/p&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/113068823399361865?isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113068823399361865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113068823399361865'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/11/custom-action-tutorial-part-ii.html' title='Custom Action Tutorial Part II – Creating the Project'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-113039101340059075</id><published>2005-10-27T00:28:00.002-04:00</published><updated>2010-01-07T10:11:08.659-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="MSI Custom Actions"/><title type='text'>Custom Action Tutorial Part I – Custom Action Types and Sequences</title><content type='html'>&lt;p&gt;This is the first part of a multi-part series on Custom Actions in the MSI world. The articles are designed to be read in order, as each one will build from knowledge gained in the previous ones. To give you a short roadmap, the series will start with some dry and boring theory, and the subsequent articles will start us down a path of writing Custom Actions in unmanaged C++, C#, and script. &lt;p&gt;Custom Actions are used when the power of the Windows Installer engine&#39;s Standard Actions are not enough to accomplish a given installation task. For instance, installing or controlling a service is trivial using &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp&quot;&gt;MSI Standard Actions&lt;/a&gt;, but creating a virtual directory in IIS is not. To create that virtual directory, you will need to write a custom action. Other examples of installation tasks that require Custom Actions can be found &lt;a href=&quot;http://blogs.msdn.com/astebner/archive/2004/06/06/149713.aspx&quot;&gt;here&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;Before beginning, I&#39;d like to make sure you have the proper environment to work under. I&#39;m going to use Microsoft&#39;s Visual Studio 2003 as the environment of choice for this series. The writing level is intended to be a walkthrough for someone who may be academically familiar with C/C++. I am going to handwave much of the Win32 enumerated types and definitions - the reader who is completely unfamiliar with these topics can find additional resources in books, the MSDN library, or around the web.&lt;br /&gt;
&lt;br /&gt;
This part covers some necessary background on the different Custom Action types and Custom Action scheduling. Mostly this is dry and boring theoretical stuff, but essential to understand. Hopefully, I summarized it fairly well and won&#39;t cause anyone to drift off to sleep during their lunch hour.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Setting up your environment&lt;/b&gt;&lt;/p&gt;&lt;p&gt;For this first part, you only need the Platform SDK and a free MSI editor called Orca (distributed with the Platform SDK) installed. Future parts will require the following setup, so prepare yourself by setting this up. The development environment should be set up as follows: Install Visual Studio 2003, the current MSDN Library, and the most current &lt;a href=&quot;http://www.microsoft.com/msdownload/platformsdk/sdkupdate/&quot;&gt;Platform SDK&lt;/a&gt;. The Platform SDK contains new and updated header files and link libraries to support the latest OS platforms, components, and service packs. The installation of the Platform SDK installs a shortcut &lt;strong&gt;Microsoft Platform SDK&lt;/strong&gt; in the &lt;strong&gt;Program Files&lt;/strong&gt; menu. Open this and find the option to &lt;strong&gt;register the platform SDK directories with Visual Studio &lt;/strong&gt;and run it. Finally, in the installation directory for the SDK, there is a directory &quot;bin&quot; and inside this you will find &lt;strong&gt;orca.msi&lt;/strong&gt;. I recommend installing Orca, as my instructions are based on this. You may also substitute the MSI editor of your choice, just be aware of the differences.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Let&#39;s get started&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;We are going to use Orca to investigate the Orca.msi file. So, find the Orca.msi file, right-click on it, and select &lt;strong&gt;Edit With Orca&lt;/strong&gt;, and play along. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;The Two &quot;categories&quot; of Custom Actions&lt;/strong&gt; &lt;p&gt;There are only two major categories of custom actions. The category of a custom action is indicated by a number in the type column of the CustomAction table (You should be finding this table in Orca as you read along). This number is actually a bitmask - for folks new to programming, if you looked at the number in binary form, the presence of a 1 or 0 in a particular location is how all the flags and options are interpreted by the MSI engine. I will give the hex (or &lt;a href=&quot;http://en.wikipedia.org/wiki/Hexidecimal&quot;&gt;Hexidecimal&lt;/a&gt;) representations of these flags throughout (all hex digits begin with a 0x to indicate they are hex). You can use the Windows &quot;calc&quot; program in the scientific view to enter the hex and convert it to binary to see which flag toggles which bit. Using hex (or even binary) is perhaps the best way to reverse engineer the decimal value from the table into the flags it represents. The two major categories of custom actions are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Deferred&lt;/strong&gt; - This is a modifier of (in hex) 0x400 or 0x4000 in the Type column of the CustomAction table, which corresponds to what MSDN calls (and you do not need to understand what these are yet) &lt;em&gt;msidbCustomActionTypeInScript&lt;/em&gt; or &lt;em&gt;msidbCustomActionTypeTSAware&lt;/em&gt;, respectively. What is important is &lt;strong&gt;&lt;em&gt;Deferred actions are actions that modify the state of the system in some way.&lt;/em&gt;&lt;/strong&gt; There are also some notable restrictions on deferred actions - you cannot change the value of a property, you can only read the value of a single property, and you cannot interact with the MSI tables. These can only be sequenced between the InstallInitialize and InstallFinalize actions in the Sequence table, lest you encounter the &lt;a href=&quot;http://blogs.msdn.com/windows_installer_team/archive/2005/09/04/454366.aspx&quot;&gt;dreaded 2762 error &quot;Cannot write script record. Transaction not started.&quot; error&lt;/a&gt;. You will find out the reasons behind this error later in this article. Additionally, there are options to specify a special kind of deferred action of &quot;Rollback&quot; or &quot;Commit.&quot; These will also be covered later in this article.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Immediate&lt;/strong&gt; - This is a modifier of zero in the Type column of the CustomAction table - in other words it is any entry in the Type column that does not contain the bitmasks described in the Deferred section above. Immediate actions are used for UI tasks, evaluating the state of the system, or preparing data or the system itself for later modification from a Deferred action. &lt;strong&gt;&lt;em&gt;Immediate actions should never &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/changing_the_system_state_using_a_custom_action.asp&quot;&gt;modify the state of the system&lt;/a&gt;. &lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The Orca.msi I am looking at has two different custom action types - 0x23 and 0x33. Neither of these numbers has the 0x400 or 0x4000 bits set, so this must mean they are &lt;em&gt;Immediate Actions&lt;/em&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;The Type column of the CustomAction table&lt;/strong&gt; &lt;p&gt;Custom Actions have a Type - this is the base number (or bitmask) that gets entered in the Type column. This Type column tells the MSI engine both what they do and how to interpret the Source and Target columns of the CustomAction table. The major Types are:&lt;br /&gt;
&lt;table border=&quot;1&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Action&lt;/th&gt;&lt;th&gt;Hex Code&lt;/th&gt;&lt;th&gt;Description / Notes&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Call a DLL function&lt;/td&gt;&lt;td&gt;0x01, 0x11&lt;/td&gt;&lt;td&gt;The DLL function that is called must be specifically written for the MSI engine.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Run an EXE&lt;/td&gt;&lt;td&gt;0x02, 0x12, 0x22, 0x32&lt;/td&gt;&lt;td&gt;Any EXE will run. By default, if the EXE returns a code other than zero, the action will fail. There is a flag available to turn off this behavior.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Abort the Installation&lt;/td&gt;&lt;td&gt;0x13&lt;/td&gt;&lt;td&gt;Aborts installation and displays a Formatted message from the target column.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Set a directory&lt;/td&gt;&lt;td&gt;0x23&lt;/td&gt;&lt;td&gt;Uses formatted text from the Target column to set a Directory.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Set a property&lt;/td&gt;&lt;td&gt;0x33&lt;/td&gt;&lt;td&gt;Uses formatted text from the Target column to set a Property.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Run some Jscript&lt;/td&gt;&lt;td&gt;0x05,0x15,0x25,0x35&lt;/td&gt;&lt;td&gt;Bear in mind the normally present &lt;a href=&quot;http://bonemanblog.blogspot.com/2004/08/why-cant-i-use-wscript-in-my-msi.html&quot;&gt;WScript object is not available in these scripts.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;tr&gt;&lt;td&gt;Run some VBScript&lt;/td&gt;&lt;td&gt;0x06,0x16,0x26,0x36&lt;/td&gt;&lt;td&gt;Bear in mind the normally present &lt;a href=&quot;http://bonemanblog.blogspot.com/2004/08/why-cant-i-use-wscript-in-my-msi.html&quot;&gt;WScript object is not available in these scripts.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Run a nested installation&lt;/td&gt;&lt;td&gt;0x07, 0x17, 0x27&lt;/td&gt;&lt;td&gt;Nested installations are evil. Don&#39;t do it. Use a setup launcher or AppSearch to enforce setup order.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Hopefully you see the pattern here - all the EXE actions contain 0x2, all the JScript ones contain a 0x5, etc. &lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The Source Column of the CustomAction Table&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The second hex digit from the right in the CustomAction Type column tells us how to interpret the Source column. This is a bit more of a stretch then above, but you should see the trend:&lt;/p&gt;&lt;table border=&quot;1&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Hex Code&lt;/th&gt;&lt;th&gt;Description / Notes&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x00&lt;/td&gt;&lt;td&gt;The Source column is a key into the Binary Table, where the the file needed to run this action is stored.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x10&lt;/td&gt;&lt;td&gt;The file needed for this action is installed as part of this installation, the Source column is the key into the File table. This fact places restrictions on where this type of action can be placed in the installation sequence. In the case of the nested installation, the Source column points to the msi path inside this msi&#39;s source tree. For the abort action, the source column is ignored.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x20&lt;/td&gt;&lt;td&gt;Source column is the key into the Directory table for setting the directory or indicating the EXE&#39;s working directory,Source column is the ProductCode for the nested installations,Source is null for the VBScript and Jscript actions. Note that you &lt;a href=&quot;http://blogs.msdn.com/windows_installer_team/archive/2005/07/23/442331.aspx&quot;&gt;cannot return anything other than a success code with actions of this type&lt;/a&gt;. Actual script is stored in the Target column of the CustomAction table.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x30&lt;/td&gt;&lt;td&gt;Source is the Property name where the value of the property will be set by the set property action, Source column contains the script code for script actions, or for the EXE action the Source column is the property that contains the path of the EXE to run.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The Target column of the CustomAction table&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The Target column is dependent on the major type of the CustomAction, reading the MSDN &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp&quot;&gt;Summary List of All Custom Action Types&lt;/a&gt; will explain this column in more appropriate detail. Looking back to the Orca.msi file - the actions listed translate to the following: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;0x23 - Set the directory listed in the Source column to the Formatted Text in the Target column. &lt;/li&gt;
&lt;li&gt;0x33 - Set the property listed in the Source column to the Formatted Text in the Target column.&lt;/li&gt;
&lt;/ul&gt;&lt;strong&gt;&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;How and When is a Custom Action run?&lt;/strong&gt;&lt;br /&gt;
Now that we know a Custom Action is either &lt;strong&gt;Immediate&lt;/strong&gt; or &lt;strong&gt;Deferred&lt;/strong&gt;, and can be or do just about anything, we need to answer the question &quot;How does a Custom Action get fired off?&quot; Entering a Custom Action into the CustomAction table really only makes it available to be called - so just adding a line in the CustomAction table effectively does nothing. There are three methods of firing off a Custom Action:&lt;br /&gt;
&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;In the sequence tables&lt;/strong&gt;. This is probably the most common way of running a custom action, and the focal point of our discussion for the remainder of this article series.&lt;br /&gt;
&lt;li&gt;&lt;strong&gt;In the ControlEvent tables&lt;/strong&gt;. Essentially this is the method to launch a Custom Action in response to a UI event such as a button click. On Windows Server 2003 or better, custom actions run this way cannot send messages with MsiProcessMessage() calls, however Session.Message() calls from the automation interface work just fine (technically, prior to Windows Server 2003 neither message API officially works). Obviously, the installation must be running in full UI mode to fire these off. The Custom Action type launched by this method must be an &lt;strong&gt;Immediate &lt;/strong&gt;type - remember we should never alter system state in the UI phase. I&#39;m not going to cover how to run an Immediate Action from a dialog here using ControlEvent, perhaps in a later article.&lt;br /&gt;
&lt;li&gt;&lt;strong&gt;By a call to &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/session_doaction.asp&quot;&gt;Session.DoAction&lt;/a&gt;() or &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msidoaction.asp&quot;&gt;MsiDoAction()&lt;/a&gt;&lt;/strong&gt;. This is a way of calling one Custom Action from another. Although this won&#39;t make sense until it is explained later on, you can not call a Deferred Custom Action in this manner unless it is called while the &quot;installation script&quot; is being written - but it should be OK to call it from a custom action scheduled between InstallInitialize and InstallFinalize. More on the &quot;installation script&quot; concept later. &lt;/li&gt;&lt;br /&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;The Sequence tables&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;Before we can discuss the primary way Custom Actions are launched, you need a better understanding on how MSI sequences work. We will get into this in more detail later in this article so I am handwaving some important details here for the sake of getting a basic understanding.

The typical installation is kicked off by double-clicking the MSI. This causes the installation engine to begin processing the InstallUISequence table (The processing of this table is skipped entirely if the UI mode is set to &quot;basic&quot; or &quot;no UI&quot; - both Full and Reduced UI modes actually do process this table). There are other sequences such as the AdminUISequence and AdvtUISequence that all follow the same logic for administrative and advertised installations. Hopefully you still have Orca open, so look at the tables as I describe them and follow along - sort the table by ascending sequence number. &lt;/p&gt;&lt;p&gt;The InstallUISequence table is processed in the order identified by the Sequence column (starting at sequence number 1), executing each action listed(Standard, Custom, and Dialog), as long as the Condition column for that action evaluates to true or is null (empty). The negative values in this table are &quot;jumped&quot; to on termination of the setup based on the result code of the installation. Once the &quot;ExecuteAction&quot; action is encountered, the InstallExecuteSequence is run. When the InstallExecuteSequence is complete, control will return to process the remaining UISequence table or it will jump to the corresponding negatively numbered sequence based on the result code of the installation process. Every action in the *UISequence tables are run in the currently logged on user&#39;s process for security reasons, and this is known throughout MSDN&#39;s documentation as the &quot;client&quot; portion of the installation. &lt;p&gt;The InstallExecuteSequence is where things get interesting, and differs based on platform. &lt;ul&gt;&lt;li&gt;On Windows NT based systems, this table is evaluated and run in a separate process from that of the User Interface. See &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/06/all-about-properties.html&quot;&gt;my earlier article on Properties&lt;/a&gt; for information as to what is passed from the &quot;client&quot; (UI portion) to the &quot;server&quot; or &quot;service&quot; portion of the installation. To recap, the admin user has two processes owned by him/her, one is the UI sequence processing, and the second is the Execute sequence processing. The non admin user installing with elevated privlidges can have three processes - the same two as above, plus a third process, owned by the System, which executes the deferred &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_security.asp&quot;&gt;&lt;br /&gt;
msidbCustomActionTypeNoImpersonate&lt;/a&gt;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_in_script_execution_options.asp&quot;&gt;&lt;/a&gt; actions. &lt;/li&gt;
&lt;li&gt;On Windows 9x based systems, both the *UISequence and *ExecuteSequence and all actions contained therein are run in the same process. &lt;/li&gt;

&lt;/ul&gt;&lt;p&gt;Actions in the *ExecuteSequence should only use UI interactions that use the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/session_message.asp&quot;&gt;Session.Message()&lt;/a&gt; or &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiprocessmessage.asp&quot;&gt;MsiProcessMessage()&lt;/a&gt; API&#39;s and not reference the Dialog table or contain UI&#39;s (such as MessageBoxes, status bars, shell progress indicators, dialogs from 3rd party libraries, etc.) in order to respect the UILevel wishes of the user.
&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Now or Later&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;Let&#39;s continue the discussion by distinguishing the &lt;strong&gt;Immediate&lt;/strong&gt; vs. &lt;strong&gt;Deferred&lt;/strong&gt; custom action categories a bit more. An immediate action is one that is &quot;run&quot; or &quot;executed&quot; immediately when the action is encountered in the sequence. A Deferred action is one that, when encountered in one of the Sequence Tables, is written to a sequential &quot;installation script&quot; with some metadata - you can think of this as a &quot;To-Do list&quot;. Wherever you see the words &quot;script&quot; or &quot;written to the script&quot; in MSDN documentation, this is what I am referring to. More on deferred actions and scripts later - let&#39;s get the Immediate Custom Actions out of the way first. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Immediate&lt;/strong&gt; actions have a method of assuring they will only be executed a set number of times - &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_execution_scheduling_options.asp?frame=true&quot;&gt;this is perhaps the most confusing page of documentation in the MSDN library.&lt;/a&gt; The Custom Action Type &lt;em&gt;msidbCustomActionTypeFirstSequence&lt;/em&gt; will cause the action to not run in the Execute sequence if it already ran in the User Interface sequence. The &lt;em&gt;msidbCustomActionTypeOncePerProcess&lt;/em&gt; flag is similar to the &lt;em&gt;msidbCustomActionTypeFirstSequence&lt;/em&gt; flag, with the exception that it will run an action again in the Execute sequence if the Execute Sequence is being run in a different process than the User Interface process (In English - it will run in both sequences on NT machines, and only once on 9x based machines). The &lt;em&gt;msidbCustomActionTypeClientRepeat&lt;/em&gt; flag means if you run the MSI in silent mode (and therefore don&#39;t have the UI sequence table processed) , the Custom Action wouldn&#39;t execute, but if you ran it in full UI mode it would execute. The lack of any of these flags causes the action to be run always. Remember that Immediate Actions should not modify the state of the system in any way. &lt;em&gt;Deferred actions cannot have the properties described in this paragraph.&lt;/em&gt;

&lt;strong&gt;Deferred&lt;/strong&gt; Actions are &quot;recorded&quot; in what is called the &quot;installation script&quot; before anything is actually run/executed. The script is &lt;strong&gt;created&lt;/strong&gt; in the *ExecuteSequence when the InstallInitialize action is encountered, therefore deferred actions can only be sequenced after this action. The only time an item is &lt;strong&gt;written to the script is if the Condition is true at the time it is to be written to the script&lt;/strong&gt; (as indicated in the Condition column of the Sequence table). In reality, there are two scripts being generated, the &quot;installation script&quot; and the &quot;rollback script&quot;. I&#39;m going to treat it as if there is only one script for simplified understanding. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Deferred Actions and the script&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Think of this example: The MSI Engine is going through each of the actions in the InstallExecuteSequence in order of the sequence numbers, when - blammo - it hits the InstallInitialize action. At this point, it creates a data structure known as the &quot;script&quot; and starts recording all actions that meet the condition specified in the Condition column and are marked as &lt;em&gt;msidbCustomActionTypeInScript&lt;/em&gt; along with some &lt;strong&gt;magical metadata&lt;/strong&gt; (In other words it only records &lt;strong&gt;deferred&lt;/strong&gt; actions in the script). If an &lt;strong&gt;immediate&lt;/strong&gt; action with a proper condition (that also is compliant with the multiple execution flag setting) is encountered, it runs immediately. Otherwise, it keeps adding stuff to the script until it hits an InstallExecute, InstallExecuteAgain, or InstallFinalize action is encountered. Ignoring InstallExecute and InstallExecuteAgain for now, when InstallFinalize is encountered, it completes writing to the script and seals it from further additions. Then it begins processing the script by executing the actions, top down. The details of how the list is processed and the effect of InstallExecute and InstallExecuteAgain is covered in a bit. &lt;/p&gt;&lt;p&gt;So, what exactly is this &lt;strong&gt;magical metadata&lt;/strong&gt; mentioned above? We already know that the condition column was evaluated before the decision was made to write the action to the &quot;script&quot; - if the condition was false, the task was not written. As near as I can figure, this metadata holds three major things: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;The &lt;strong&gt;value of a property&lt;/strong&gt; that having the same name as the CustomAction - the so-called &lt;strong&gt;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/obtaining_context_information_for_deferred_execution_custom_actions.asp&quot;&gt;CustomActionData&lt;/a&gt;&lt;/strong&gt; property you have undoubtably heard so much about. Additionally, the ProductCode and UserSID properties are available in a Deferred action. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to Run it flags&lt;/strong&gt; (derived from the Type column of the CustomAction table)&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;The type of action (DLL, EXE, Script, etc.) and the path to the CustomAction code.&lt;br /&gt;
&lt;li&gt;Impersonation type - The flag &lt;em&gt;msidbCustomActionTypeNoImpersonate&lt;/em&gt; will cause the action to run in the system context vs. the otherwise usual currently logged-on user context. The exception to this rule is a server with Terminal Services which normally runs actions in the system context unless the &lt;em&gt;msidbCustomActionTypeTSAware&lt;/em&gt; flag is set (flag valid on Windows 2003 Terminal Server only).&lt;br /&gt;
&lt;li&gt;&lt;em&gt;msidbCustomActionTypeAsync&lt;/em&gt; indicates that once the action is kicked off there is no need for the MSI engine to wait for it to complete before executing the next action. However, the MSI engine does want to know the return code, and as such will wait around at the end of the sequence until it finally returns if the action takes that long. All actions are synchronous unless this flag is set. Only EXE type custom actions can also combine the &lt;em&gt;msidbCustomActionTypeContinue&lt;/em&gt; flag so that the installation can exit even while the Custom Action EXE is still running asynchronously.&lt;br /&gt;
&lt;li&gt;&lt;em&gt;msidbCustomActionTypeContinue&lt;/em&gt; indicates that the exit code of the custom action is ignored - therefore the sequence will progress even if the return code indicates failure.&lt;/li&gt;&lt;br /&gt;
&lt;/ul&gt;&lt;li&gt;&lt;strong&gt;When to run it flags&lt;/strong&gt; indicates three possible things (again derived from the Type column of the CustomAction table). As briefly mentioned earlier there are actually two different scripts written - Commit and Rollback actions are both written to a separate &quot;Rollback Script&quot;, but ignore this for now. &lt;/li&gt;
&lt;ul&gt;&lt;li&gt;&quot;Normal Deferred&quot; assuming nothing has failed and the list is being processed top to bottom - &lt;em&gt;msidbCustomActionTypeTSAware&lt;/em&gt; or &lt;em&gt;msidbCustomActionTypeInScript&lt;/em&gt;&lt;br /&gt;
options without &lt;em&gt;msidbCustomActionTypeRollback&lt;/em&gt; or &lt;em&gt;msidbCustomActionTypeCommit &lt;/em&gt;flags &lt;/li&gt;
&lt;li&gt;&quot;Commit&quot; - Run after the entire (sealed) script is processed once successfully (After InstallFinalize is encountered and the complete script has been processed once successfully). Actions in this category have the&lt;em&gt;msidbCustomActionTypeTSAware&lt;/em&gt; or &lt;em&gt;msidbCustomActionTypeInScript&lt;/em&gt; combined with &lt;em&gt;msidbCustomActionTypeCommit&lt;/em&gt; flags in the Type column. This is only written if Rollback is not disabled - more on this later..&lt;br /&gt;
&lt;li&gt;&quot;Rollback&quot; - if another deferred action failed, this will be run. This is only written for the &lt;em&gt;msidbCustomActionTypeTSAware&lt;/em&gt; or &lt;em&gt;msidbCustomActionTypeInScript&lt;/em&gt;&lt;br /&gt;
when combined with &lt;em&gt;msidbCustomActionTypeRollback&lt;/em&gt; actions, and only if Rollback is not disabled - more on this later..&lt;/li&gt;&lt;br /&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;How the Script is Processed&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;To recap, we have a &quot;script&quot; containing deferred actions along with some metadata that we started collecting when InstallInitialize was encountered in the sequence. We can potentially start processing the list before it is completely written (assuming a InstallExecute or InstallExecuteAgain was encountered prior to InstallFinalize in the sequence tables).&lt;/p&gt;&lt;p&gt;Processing always &lt;em&gt;begins&lt;/em&gt; top-down, and there are up to three &quot;phases&quot; in the script processing based on the &quot;&lt;strong&gt;when to run it&lt;/strong&gt;&quot; metadata described above.&lt;/p&gt;&lt;p&gt;First, only &quot;normal deferred&quot; actions are processed. Each deferred action is executed only once - if an InstallExecute or InstallExecuteAgain action was encountered in the sequence tables causing a portion of the script to be executed prior to the script being completely written, &quot;normal deferred&quot; actions already executed are skipped.&lt;/p&gt;&lt;p&gt;If an error is thrown during the execution of a &quot;normal deferred&quot; action, the processing of the list reverses and starts moving backwards from where it currently is processing, executing the &quot;On Rollback&quot; actions in the list. Rollback can be &quot;turned off&quot; at a system level, if this is the case, Rollback actions will not be written to the script, and will not be executed.&lt;/p&gt;&lt;p&gt;Assuming all &quot;normal deferred&quot; actions are successful, processing of the script begins executing at the top again, this time executing the &quot;On Commit&quot; actions. Retuening a failure from a Commit Custom Action will cause any previously written Rollback actions to be executed. Because this causes some interesting and practically impossible to test scenarios, it is important to write solid Commit actions that do not fail or also specify the &lt;em&gt;msidbCustomActionTypeContinue&lt;/em&gt; flag in the CustomAction table. Rollback can be &quot;turned off&quot; at a system level, if this is the case, Commit actions will also not be written to the script, and will not be executed.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Dealing with Rollback and Commit&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If you are familiar with database concepts, Windows Installer is &lt;a href=&quot;http://en.wikipedia.org/wiki/Transactions&quot;&gt;transactional&lt;/a&gt;. In other words, a failure to install a MSI package should leave the system in the exact state it was prior to beginning the installation. As such, Rollback and Commit actions are a part of the lexicon of Windows Installer, and understanding this when writing a Custom Action is essential. Also essential is understanding that Rollback can be disabled on a system level, thus, Rollback and Commit actions are not executed.&lt;/p&gt;&lt;p&gt;To adhere to the Windows Installer &quot;Best Practices,&quot; all changes that are made to a system are in a &quot;deferred&quot; action - as an example, the &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installfiles_action.asp&quot;&gt;InstallFiles&lt;/a&gt;&quot; standard action is actually a deferred action under the hood. Although not apparent in this manner (nor is apparrant by looking at the Sequence tables), the InstallFiles &quot;normal deferred&quot; action creates a backup of all the files it replaces if Rollback is enabled. This way, if an error occurs, all the replaced files can be restored to their previous state if an error occurs in a later deferred action through the Rollback process. The Commit actions are only run after all the &quot;normal deferred&quot; actions are run successfully - and the MSI engine uses the Commit action to delete the Rollback files.&lt;/p&gt;&lt;p&gt;Because of their nature, Rollback and Commit actions cannot be asynchronous.&lt;/p&gt;&lt;p&gt;A &quot;normal deferred&quot; Custom Action should only save rollback information if the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/rollbackdisabled.asp&quot;&gt;RollbackDisabled&lt;/a&gt; property is not set. If it is set, Rollback and Commit actions will not be run, and this will cause your saved rollback data to remain after the installation is completed. You will need to pass this data into your &quot;normal deferred&quot; CustomActionData property in order to check if you should save rollback information. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Other Considerations&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Inside a CustomAction, you may need to evaluate if a patch or repair is occurring. Since deferred CustomActions do not have the ability to check properties (other than the few properties mentioned earlier), you need to use an Immediate action to check (and pass on to the deferred action via CustomActionData) the value(s) of REINSTALL, PATCH, and/or MsiPatchRemovalList properties. For more details, see the &lt;a href=&quot;http://blogs.msdn.com/windows_installer_team/archive/2005/08/09/451471.aspx&quot;&gt;Windows Installer Team Blog&#39;s comments on the subject&lt;/a&gt; or &lt;a href=&quot;http://blogs.msdn.com/heaths/archive/2005/08/12/451037.aspx&quot;&gt;Heath Stewart&#39;s post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To determine if you should save Rollback information in your deferred action, you will need to do the same technique as above to pass the value of the RollbackDisabled property. Other properties of interest for deferred CustomActions (depending on what they do) is &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/allusers.asp&quot;&gt;ALLUSERS&lt;/a&gt;, and possibly &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/uilevel.asp&quot;&gt;UILevel&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To handle user requests to cancel the installation in any custom action, make sure that you check the return value from MsiProcessMessage() (or Session.Message()) calls to handle the IDCANCEL return value. I will be showing examples of handling progress messages in future posts on this topic. In the meantime check out the &lt;a href=&quot;http://blogs.msdn.com/windows_installer_team/archive/2005/09/12/458939.aspx&quot;&gt;Windows Installer Team blog entry on the subject.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Another tricky concept is scheduling a reboot, if required, from a deferred Custom Action. Normally, you can&#39;t! To work around this oversight you have several options. The one I use is creating a file on the filesystem that the user running the installation can have delete access to. This is where the ProductCode and UserSID properties available to a CustomAction come in handy. Find the temp directory of the user using their SID and write a file with the name &quot;ProductCode_rebootme&quot; there. In an immediate action scheduled after InstallFinalize, check for the existance of this file and if present, delete it and call &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msisetmode.asp&quot;&gt;MsiSetMode()&lt;/a&gt; to indicate a reboot is required.&lt;/p&gt;&lt;p&gt;MSDN provides a few pointers on security &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/guidelines_for_securing_custom_actions.asp&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_security.asp&quot;&gt;here&lt;/a&gt;. In my opinion, it is extremely difficult to author a &quot;data secure&quot; MSI. If you need to pass internally sensitive data to a deferred custom action, use encryption on the CustomActionData in addition to adding sensitive properities (and the name of the deferred custom action) to the MsiHiddenProperties and using the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_hidden_target_option.asp&quot;&gt;msidbCustomActionTypeHideTarget&lt;/a&gt; CustomAction type flag. It is possible to &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/digital_signatures_and_windows_installer.asp&quot;&gt;digitally sign&lt;/a&gt; an installer package and its cabinet files. It is trivial to circumvent these measures if someone wanted to - and in a typical administrative installation, the msi signing is removed anyway. Think about data security in an MSI as glass in a jewelry store - it only keeps the honest people honest.&lt;/p&gt;&lt;p&gt;If a Custom Action needs to use disk space (to install a database, for example), you need to author (or modify at run time) the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/reservecost_table.asp&quot;&gt;ReserveCost&lt;/a&gt; table. This is keyed on the installation state of a component.&lt;/p&gt;&lt;p&gt;The use of COM in a CustomAction is fine - when a dll-based Custom Action runs, it is created on its own thread. There is some conflicting documentation on this, however. &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/dynamic_link_libraries.asp&quot;&gt;MSDN&lt;/a&gt; claims CoInitialize is called in a per-machine installation and is not called in a per-user installation. A post here &lt;a href=&quot;http://blogs.msdn.com/astebner/archive/2005/03/02/384088.aspx&quot;&gt;(scroll through to the comments)&lt;/a&gt; indicates that CoInitialize and/or its brethren CoInitializeEx is not called on this thread. I like MSDN&#39;s advice to not quit if it is determined the thread is already COM initialized.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://blogs.msdn.com/windows_installer_team/archive/2005/10/23/484044.aspx&quot;&gt;Terminal Server is special&lt;/a&gt;. Throughout this article I have mentioned the *TSAware flags. In a per-machine installation on a Terminal Server, actions not marked with this flag are run as LocalSystem, where on a non-terminal server system they will normally impersonate the calling user. &lt;/p&gt;&lt;p&gt;The way patching and upgrading is handled, and its effect on Custom Actions, is the subject of a future article in this series.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The Ten Cent Summary&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Custom Actions can be very powerful, and can be run in a variety of different ways and at a variety of times. The important things to remember when deciding on how to design them are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Immediate actions are normally used to &quot;set up&quot; deferred actions (by stuffing CustomActionData) or to simply evaluate the state of a machine. Immediate actions are NEVER to be used to alter the state of a machine.&lt;/li&gt;
&lt;li&gt;Deferred actions come in three different flavors, and where you have one, you should have all of them - the &quot;normal deferred&quot;, rollback, and commit. The &quot;normal deferred&quot; should save undo information on the system somewhere if rollback is enabled. The rollback action will use this undo information to restore the initial state of the machine. The commit actions will clean up the rollback&#39;s undo information. Deferred actions can only be included in the execute sequences between InstallInitialize and InstallFinalize.&lt;/li&gt;
&lt;li&gt;Rollback only occurs if an error is encountered while processing deferred actions (including Commit actions) between InstallInitialize and InstallFinalize in the *Execute sequences. &lt;/li&gt;
&lt;li&gt;Because the conditions of deferred actions (including commit and rollback actions) are evaluated at the time the script is written, the same condition should be used for the sister actions.&lt;/li&gt;
&lt;li&gt;Actions should be written to run correctly regardless of the UI mode and respect the wishes of the user when it comes to UI levels.&lt;/li&gt;
&lt;li&gt;Never use a nested installation custom action type.&lt;/li&gt;
&lt;li&gt;Understand the flags and options for scheduling and running actions as presented above.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;a href=&quot;http://bonemanblog.blogspot.com/2005/11/custom-action-tutorial-part-ii.html&quot;&gt;Part II of this series&lt;/a&gt; will cover writing our first Custom Action in C++!&lt;/p&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/113039101340059075?isPopup=true' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113039101340059075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/113039101340059075'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/10/custom-action-tutorial-part-i-custom.html' title='Custom Action Tutorial Part I – Custom Action Types and Sequences'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-112847804138920061</id><published>2005-10-04T21:12:00.002-04:00</published><updated>2008-02-14T00:16:15.865-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="InstallShield"/><title type='text'>InstallScript and Enumerators</title><content type='html'>I was helping a coworker out today trying to figure out why a simple VB Script using WMI could not be &quot;ported&quot; to InstallShield&#39;s InstallScript. The reason turned out to be that InstallScript does not support handling enumerators - In VBScript, you can enumerate a collection using the &quot;For Each&quot; construct. In JScript, you have the Enumerator object. In InstallScript - sadly - you have nothing.&lt;br /&gt;&lt;br /&gt;I started thinking that I may be able to create a DLL to handle this. I then thought that someone else likely has done the same. My Google-foo came in handy and I found a post by __EDITED___ that covers the topic quite well. I was able to easily compile his C++ code into a DLL and use it immediately.&lt;br /&gt;&lt;br /&gt;UPDATE: Turns out that the link previously referenced above was pretty much a blatent copy of &lt;a href=&quot;http://www.installsite.org/files/GetObject.zip&quot;&gt;code previously submitted&lt;/a&gt; to &lt;a href=&quot;http://www.installsite.org/&quot;&gt;Installsite.org&lt;/a&gt;. Thank you to the commenter for pointing this out.&lt;br /&gt;&lt;br /&gt;In the deleted link above, the poster was complaining about certain InstallShield limitations. I agree with most of the comments in his post - InstallShield&#39;s help system is at least second-rate. The &lt;a href=&quot;http://www.amazon.com/exec/obidos/redirect?link_code=ur2&amp;amp;camp=1789&amp;amp;tag=bonemansblog-20&amp;amp;amp;creative=9325&amp;amp;path=http://www.amazon.com/gp/product/0764547232?v=glance%26n=283155%26s=books%26v=glance&quot;&gt;InstallShield&lt;/a&gt; &lt;a href=&quot;http://www.amazon.com/exec/obidos/redirect?link_code=ur2&amp;amp;amp;amp;amp;camp=1789&amp;amp;tag=bonemansblog-20&amp;amp;amp;amp;amp;amp;amp;amp;creative=9325&amp;amp;path=http://www.amazon.com/gp/product/0971570833?v=glance%26n=283155%26s=books%26v=glance&quot;&gt;books&lt;/a&gt; by &lt;a href=&quot;http://www.amazon.com/exec/obidos/redirect?link_code=ur2&amp;amp;amp;amp;amp;camp=1789&amp;amp;tag=bonemansblog-20&amp;amp;amp;amp;amp;amp;amp;creative=9325&amp;amp;path=http://www.amazon.com/gp/product/0971570809?v=glance%26n=283155%26s=books%26v=glance&quot;&gt;Baker&lt;/a&gt; are nice to have around, but not everyone has this luxury.&lt;br /&gt;&lt;br /&gt;I disagree with his comments that MSI is &quot;limited.&quot; It certainly is not the &quot;holy grail&quot; of installation technology - and I doubt that this can even exist. MSI technology is extremely extensible, but this comes at a cost of understanding the design patterns inherent in it - which are vastly different than the &quot;other&quot; installation technologies. I know I mentioned this before, but using MSI technology could practically eliminate vendor dependence and ease the switch between authoring tools.&lt;br /&gt;&lt;br /&gt;Unfortunately, the documentation for MSI technology is equally as poor as InstallShield&#39;s. Rumor has it (plus the Amazon ratings) that &lt;a href=&quot;http://www.amazon.com/exec/obidos/redirect?link_code=ur2&amp;amp;camp=1789&amp;amp;tag=bonemansblog-20&amp;amp;amp;amp;amp;creative=9325&amp;amp;path=http://www.amazon.com/gp/product/1590592972?v=glance%26n=283155%26%5Fencoding=UTF8%26v=glance&quot;&gt;Phil Wilson&#39;s book&lt;/a&gt; on the subject is pretty good - if &lt;a href=&quot;http://dictionary.reference.com/search?q=coworker&quot;&gt;Brian&lt;/a&gt; will ever let me borrow his copy I can give it an official review. I will attempt to help out with this by (finally) publishing my series on Custom Actions over the next several weeks.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/112847804138920061?isPopup=true' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/112847804138920061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/112847804138920061'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/10/installscript-and-enumerations.html' title='InstallScript and Enumerators'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-112767043303425617</id><published>2005-09-25T10:38:00.001-04:00</published><updated>2008-02-14T00:17:51.878-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Diversions"/><title type='text'>Avoiding Disaster</title><content type='html'>This post is a bit off the usual technical topic - I will be talking about the duo of hurricanes, impact on the victims, emergency response, pre/post emergency evacuation of large populated areas, your individual family emergency plan, and how to help. Although I am not currently active, I was a volunteer paramedic and firefighter, serving poor and middle-class neighborhoods for several years, so I do have some background in this area - but far from an expert on the subject. To my loyal readers, I have some technical content in the queue for later...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Impact on People&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;It looks like Hurricane Rita was not as devastating as was feared. Katrina, however, was a nightmare. Ignoring the evacuation problems, lets look at the issues someone who evacuated early will encounter:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Money. If you used a local bank, forget about getting cash from an ATM machine. As the banks were knocked offline, so was the ability of their patrons to access their money. Perhaps some distributed systems can be used in the future to alleviate this issue. In the meantime, I&#39;d start depositing a good portion of my money in a nationwide bank or split assets between two banks housed on opposite coasts. Fortunately, lenders are claiming they are not going to report victims to the credit breuraus. Also fortunately, any cash you had in a banking institution should eventually be available.&lt;/li&gt;&lt;li&gt;Housing. I&#39;d venture to say this is perhaps the worst - you come back to a slab where your house once stood. Jewelry, important documents, photos of your great-grandparents, etc. are now all gone. Even worse is the potential backups of this information stored at the bank or a family member&#39;s house are gone, too. Is insurance going to cover any part of the replaceable damaged items? &lt;/li&gt;&lt;li&gt;Job. Unless you work in construction, it could be a while before you can get your old job back. Hopefully, the owners of the damaged or destroyed companies will be able to rebuild at all. Without money, housing, or a job - I can&#39;t imagine surviving too well in today&#39;s society outside a log cabin house in the forest.&lt;/li&gt;&lt;li&gt;Attitude. While there are several personality types out there, the two I am looking at are the &quot;society owes me&quot; and &quot;I&#39;d never take a handout&quot; type of people. Both of these extreme personality types will require a make-over. If the &quot;society owes me&quot; folks continue with this attitude (or possibly get it more ingrained), they will never recover - assistance programs will decrease as time goes on. The &quot;never take a handout&quot; folks will not be able to recover until they &lt;em&gt;do&lt;/em&gt; get some external assistance. Unless you are in that log cabin house in the woods, you live in a society, and participation is a two way street. This reminds me about a old and now not-so-funny anecdote about a man stuck on his roof during a flood. Three rescue boats floated past and the man refused the assistance saying &quot;God will save me.&quot; Eventually he drowns, goes to heaven, and asks God why he wasn&#39;t saved. God&#39;s response - &quot;I sent three boats...&quot;&lt;/li&gt;&lt;li&gt;Fixed income. These folks are the ones who tear at my heart-strings the most. In the previous paragraph I stated that &quot;assistance programs will decrease as time goes on.&quot; These people are retired, and can&#39;t possibly rebuild their home, get a well-paying job (either because of age/health related issues or the fact that no company will hire them), or rely on long-term financial assistance. It would be nice to see a specific charity set up to help these people long-term who will soon be forgotten. Please leave a comment if you know of one.&lt;/li&gt;&lt;li&gt;Social Structure. Coming back from complete devastation, it is likely a significant portion of what used to be society is no longer there. Neighbors, friends, church members, etc. could have either died or given up on their old lives. The remaining social structure will be slow to recover and ineffective. Small communal groups will likely form inside the shattered society, and their establishment should be encouraged. I see faith-based church groups as the foundation of these mini-communes, as many of these organizations typically group together people of different skills to repair an elderly member&#39;s house in &quot;normal times&quot;. These groups of people can utilize each member to their best potential to serve the needs of the group. This was the old &quot;American Western Frontier&quot; mentality, which is exactly what is needed in these areas today.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The other subset of people, who did not evacuate prior to the devastation have all the above issues, plus some others:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Mental scaring. I couldn&#39;t imagine being stuck in an attic with a gallon of water and a stale loaf of bread for a week - not knowing if I would eventually get rescued or not. Witnessing looting, fighting, and gunfire would put a damper on my ability to trust society when I do rebuild. Seeing bodies stacked on the sidewalk - unthinkable.&lt;/li&gt;&lt;li&gt;Mental boost. The word &quot;news&quot; was formed from the first letters of &quot;North East West South&quot;. I have been thinking that the &quot;N&quot; now stands for &quot;Negative&quot;. I have heard many stories about positive things occurring in the aftermath, but stories without images have a way of being forgotten - images of people shooting at rescue choppers don&#39;t. I&#39;m sure that many (but hopefully most) people in the midst of this disaster had positive interactions with others of their own species.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;There is also an impact on the part of society that was not affected at all by the hurricanes.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&quot;How can I help&quot; - I&#39;m sure at least everyone in America has tossed at least a quarter in a fireman&#39;s boot for the relief effort. Unfortunately, this is not a very personal way of dealing with it. If this is as far as you have gone in the relief effort, shame on you. The money is needed later. Early on, the need was/is for water, housing, clothing, food, toiletries, sanitization supplies, ice, etc. The most effective assistance was immediate and non-financial. A local radio show got corporate and individual sponsors to get trucks full of various goods (from a specific list, donated by listeners) and convoy it to the affected areas. This was immediate assistance of exactly what was needed. Listening to the stories of the volunteers drivers in this effort brought me to tears - literally.&lt;/li&gt;&lt;li&gt;&quot;I&#39;m not helping those looters&quot; - I can&#39;t believe I actually heard this one (while dropping off a donation of goods for the convoy...). Repeat after me - &quot;Not everyone in this world is evil&quot;. Taking it one step further, that attitude is not going to help society become less evil.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Emergency Response&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Without getting overly technical, the response to any emergency is the same. It requires knowledge (as early as possible in the emergency) such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Where is the emergency? In cases of mass casualty incidents, where is the &lt;em&gt;worst&lt;/em&gt; emergency so we can attack that first. &lt;/li&gt;&lt;li&gt;What is the emergency? Each response team has a different one (the engineers&#39; emergency was to plug up the levees, as an example), but all efforts are focused on saving lives first.&lt;/li&gt;&lt;li&gt;What are my resources? This is people (rescue workers), equipment (trucks, boats, choppers), and supplies (water, medications, etc) - just to name a few.&lt;/li&gt;&lt;li&gt;Access. How close can I get to the scene? Can I get other, possibly more critical, resources in later? Can I get victims out?&lt;/li&gt;&lt;li&gt;Mitigation. If there is a chemical spill, do I have to worry about keeping it out of the creek? For a fire, we would want to protect the building housing the explosive materials more than other surrounding buildings. How do we stop an already bad situation from getting worse?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is where the concept of disaster planning comes in. Every area has one. In Pennsylvania, we have a bunch of coal mines, so dealing with fires, subsidence, and rescue is in our plans, and it may not be in your community&#39;s plan. To create the plan, you take a risk analysis of different disaster types, and attempt to answer all the questions posed above for the highest risks in advance. &lt;/p&gt;For example, if there is a poly-methyl-bad-stuff chemical manufacturing plant, we would know, in advance, of where to go, what to do, and who to call. This is easy. Keeping it up to date is hard. When a parking lot next to the plant gets converted to an office building, you have additional concerns of more victims and less access. Was the plan reviewed and updated with this new development? Usually, the answer is no.&lt;br /&gt;&lt;br /&gt;Planning for disaster response on a large scale - like an entire city - is impossible. You just can&#39;t answer questions like the above in advance. For things like this, we rely on a list of resources. In my community&#39;s plan, we have several phone numbers for school bus and public transportation companies, heavy equipment companies, etc. If we need a drilling rig on Christmas Eve, we should be able to get one. Are these numbers and resources kept up to date? Likely - no. In a mass disaster situation, you do not have the resources to hunt down the current foreman of &quot;Bob the Builder, Inc&quot; to get a bulldozer to prevent one tragedy when there are 100 other tragedies going on at the same time.&lt;br /&gt;&lt;br /&gt;This is where you, as an individual or business owner, can help. Contact your local fire department, and let them know what resources you have available, where they are located, and how to contact you or others that also have access to them. Notify them when this changes. There were cases where I wish I had an ATV or a snowmobile I can get quickly where it was rather difficult to get access or equipment to it. Ask what you can do in the event of an emergency to help out. Volunteer as a fund raiser or associate member of the fire and/or rescue squads to make sure these items are kept up to date and you and your community are protected. You don&#39;t have to go into a burning building to help save a life. Remember, after an incident starts, it is too late for planning.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Mass Evacuation&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The infrastructure of all cities makes a &lt;a href=&quot;http://www.nytimes.com/2005/09/25/national/nationalspecial/25evacuation.html?ei=5065&amp;amp;en=c8b2aa6b15383046&amp;amp;ex=1128225600&amp;amp;partner=MYWAY&amp;amp;pagewanted=print&quot;&gt;mass evacuation impossible&lt;/a&gt;. Roads are designed (and in Pittsburgh, especially poorly) to manage the traffic of under 10% of the population using a major roadway on a daily basis. There is no way to manage this or mitigate it. Mass evacuations of cities will never go well. If you do have to evacuate, understand why you are evacuating - for a flood, if you can&#39;t get out, seek the highest natural ground or a flotation device. Have emergency packs where you keep items like water, non-email-based Spam, first aid products, sanitizers, blankets, maps, camping gear, and medical information/contact information tags for your family that they can wear, put in their pocket, or tape to their arms. You can grab this kit and go. Don&#39;t try to move that plasma TV to the attic and barricade the house from looters. Most people will try to get this stuff together before they leave. The earlier you leave the better.&lt;br /&gt;&lt;br /&gt;Head for any area that is safe where you think other people won&#39;t think of to go. Going to the next major city where friends and relatives are is the first thing people think of. You can get to the friends and relatives later - for now get out of the danger zone and go where the route will be less crowded.&lt;br /&gt;&lt;br /&gt;For businesses with large vehicles (trucks, buses, etc.) - make sure you know what to do before you get out in a mass evacuation. Where do you fit in the evacuation plan? Ask this ahead of time - you can be given a specific location to go to to pick up people and route to take when an evacuation is called. You are more likely to get assistance if you run out of gas if you are hauling maximum vehicle capacity of random people, than a car with one or two people full of useless stuff.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Your Family Emergency Plan&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;As a small kid in an amusement park, there was nothing more thrilling than being old enough where your parents let you go off on your own. Typically, you met at different times throughout the day at the same location so they knew everything was OK. But before you were old enough, I am sure your parents gave instructions as to what to do if you were separated - meet at the Merry-Go-Round, find an employee or security guard, etc. How many people have a plan if their house catches on fire? For something like Katrina? An earthquake?&lt;br /&gt;&lt;br /&gt;There are plenty of sites that will help you create one. I&#39;ll leave it up to your Google-foo to find them. Here are some ideas to start you off:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Sinking Titanic, Flooding, injured by a large explosion, etc. - Lets say the worst has happened. You are with your family now - but you may get separated. If not by the incident itself, rescue workers will need to separate people based on the criticality of the injury. Make sure everyone has ID - this could be as simple as putting a drivers license or credit card in everyone&#39;s pocket, diaper, whatever. Better yet is a Sharpie marker or something indelible where you can write directly on the skin - name, DOB, SSN, important medical information, and a few phone numbers of people you know. Trust me - in this situation scrawling &quot;Diabetic&quot; on someone&#39;s forehead is a REALLY good idea. Remember - you may get separated from your ID, clothing, medical bracelet, etc - and in the worst case, you may not be able to communicate. Make sure everything a rescuer could need is available, not only to medically assist, but to reunite you.&lt;/li&gt;&lt;li&gt;Grabbing on to others. Same goes for tying a bunch of people together. In a water emergency, you may lessen the chances of both for survival. There are several techniques you can use, if you are in flood prone areas or like to boat, this is something you should research. I like the &quot;The Worst-Case Scenario&quot; series of books - they are humorous to an extent, but contain real and practical information. They are great coffee table type books to have around and page through, as an added bonus.&lt;/li&gt;&lt;li&gt;Have a series of phone numbers handy, and on your kids at all times - not just in an emergency. Have not only the numbers of the local relatives, but the long-distance ones too. Explain that you are to call as many of these people as you can with your location to make reuniting easier. Cell phone numbers, although nice, are useless in a mass emergency.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Helping Out&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Before donating, see if your company, like mine does, matches donations - then pick the best one from the list. Organizations that rate charities such as &lt;a href=&quot;http://www.charitynavigator.org/&quot;&gt;Charity Navigator&lt;/a&gt; are helpful. Remember that there is always overhead with these organizations, so try to get the most bang for the buck with your donation. Even better is to call a few local church groups and see if they are tied into a group in the affected areas with specific needs. Go out and buy these needs, and ship them down there. This way, 100% of your donation is being utilized.&lt;/p&gt;&lt;p&gt;My heart goes out to all the victims of this disaster, and to the families who lost loved ones. God bless them, and protect them.&lt;/p&gt;</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/112767043303425617?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/112767043303425617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/112767043303425617'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/09/avoiding-disaster.html' title='Avoiding Disaster'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-112286494872757736</id><published>2005-07-31T22:50:00.002-04:00</published><updated>2008-02-14T01:15:48.207-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><category scheme="http://www.blogger.com/atom/ns#" term="Windows Internals"/><title type='text'>Trouble ahead for time computations?</title><content type='html'>I was just reading an article about the new U.S. Energy Bill that intends to &lt;a href=&quot;http://www.baltimoresun.com/news/nationworld/bal-te.nat31jul31,1,4403766.story?coll=bal-nationworld-headlines&quot;&gt;extend Daylight Savings Time an additional month&lt;/a&gt; (starting in 2007). I wonder what the impact of this is on the Computer Science front. There has not been any chatter on my normally read blogs, but perhaps there will be soon. I have no facts to back up my theory here, but lets play along with my conspiracy theory and possibly learn something about static and dynamic linking of DLLs.&lt;br /&gt;&lt;br /&gt;Think about this from the Microsoft C/C++ developer perspective: the date functions are implemented in the C standard library, MSVCRT.DLL (or its post VC6 brethren MSVC*.dll). Lets assume (although I am guessing) that the &quot;rules&quot; for the current daylight savings time are implemented in this same DLL (NOTE: Please see comments). One of these functions is called &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_mktime.asp&quot;&gt;mktime&lt;/a&gt;(). Let me quote from MSDN:&lt;br /&gt;&lt;blockquote&gt;When specifying a tm structure time, set the tm_isdst field to 0 to indicate that standard time is in effect, or to a value greater than 0 to indicate that daylight savings time is in effect, or to a value less than zero to have the C run-time library code compute whether standard time or daylight savings time is in effect. (The C run-time library assumes the United States&#39;s rules for implementing the calculation of Daylight Saving Time). &lt;/blockquote&gt;So if we developers assumed that the runtime would take care of conversions for us, &lt;em&gt;and&lt;/em&gt; we statically linked to the library, we may have to recompile against a newer link library (with the revised code) for our applications to continue working if this change actually happens. On the other hand, had the developer dynamically linked to this DLL &lt;em&gt;and &lt;/em&gt;not copied the pre-dst-updated MSVCRT.DLL to the application folder or application current directory (Remember that in Windows Server 2003 and possibly Windows XP &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/dynamic-link_library_search_order.asp&quot;&gt;the rules have changed&lt;/a&gt;), the DLL can be updated once in the system32 directory (perhaps through Windows Update) and our preexisting applications will be none the wiser and work just fine with the change.&lt;br /&gt;&lt;br /&gt;This is more food for thought on the debate of &lt;a href=&quot;http://blogs.msdn.com/larryosterman/archive/2004/04/29/123090.aspx&quot;&gt;static vs. dynamic linking&lt;/a&gt; that could help sway some developers into a different line of thought - I prefer dynamic linking, but in some cases for some applications I do not practice what I preach. For the installation folks, please read the previous link (and the comments - good stuff there on merge modules) - this is why you need to be sure that you are following proper version replacement and marking shared DLL&#39;s as shared - otherwise you can cause grief for other applications or your own.&lt;br /&gt;&lt;br /&gt;[UPDATE: 10/4/2005 - fix formatting and errant character]</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/112286494872757736?isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/112286494872757736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/112286494872757736'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/07/trouble-ahead-for-time-computations.html' title='Trouble ahead for time computations?'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111985217993652021</id><published>2005-06-27T01:17:00.001-04:00</published><updated>2008-02-14T01:16:12.882-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Windows Installer"/><title type='text'>All About Properties</title><content type='html'>Late last week an anonymous poster commented it has been a while since my last blog post. I&#39;m going to attempt to make up for it with this rather long post as the first in a short series on MSI Properties. Newbies to MSI technology have a difficult time understanding them, and even experienced setup developers get burned by forgetting something. Let&#39;s start by looking at some of the basic properties of, well, Properties.&lt;br /&gt;&lt;br /&gt;Properties are sort of like variables. Properties cannot have spaces in their name and must begin with a letter or underscore. All properties can be defined in the Property table or set by other mechanisms – via AppSearch, Custom Actions, dialog control elements, etc. They have scope: Public, Private, and Restricted. Cosmetically, Private properties have lower case letters, Public properties are all uppercase. Under the hood, there are plenty of differences. I am going to ignore the Win9x and Terminal Server aware differences and concentrate on the NT based MSI engine.&lt;br /&gt;&lt;br /&gt;We first need to cover something I should have covered in an earlier blog post – the MSI engine&#39;s &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installation_mechanism.asp&quot;&gt;UI phase and execution phase&lt;/a&gt;. The UI phase (sometimes also referred to as the &#39;acquisition phase&#39;) is carried out in the current logged on user&#39;s context. The execution phase (sometimes also referred to as the &#39;server process&#39; or &#39;server side&#39;, or simply &#39;service&#39; – lets hear it for MSDN documentation consistency!) is carried out in a completely different process (or processes). This is pretty easy to see for yourself – write a custom action that pops up a message box with the current process ID and &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/custom_action_in_script_execution_options.asp&quot;&gt;run it in three different contexts&lt;/a&gt; – UI (Immediate), Execute (Immediate), and Execute (msidbCustomActionTypeNoImpersonate + msidbCustomActionTypeInScript). If you are an administrator, you will notice that the UI action runs in one process and the execute actions run in a different process – all owned by the interactive user. If you are a non-admin user, you will note that each of the three actions run in their own separate process, with the difference being the &quot;NoImpersonate&quot; action, which runs in the SYSTEM context.&lt;br /&gt;&lt;br /&gt;Back to the property discussion – &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/private_properties.asp?frame=true&quot;&gt;Private properties&lt;/a&gt; have lower case letters as stated earlier. They cannot be passed into an installation via the command line method or modified by transforms. Predefined Private properties may be set by the MSI engine – see &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/property_reference.asp&quot;&gt;Property Reference&lt;/a&gt;&quot; in the MSI docs for a list of properties set by the MSI engine automatically. Private properties can be changed by Custom Actions, although you likely do not want to change one of the predefined private properties. One last important distinction is that changes to private properties are NEVER passed from the UI phase to the Execution phase. Although there may be a reason to use a Private Property for something, I have never created a private property for use in an installation.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/public_properties.asp?frame=true&quot;&gt;Public Properties&lt;/a&gt; are a bit more useful. There is a slight difference between Public and Restricted which I will cover next. For the purposes of this paragraph, treat them as synonymous. Public Properties must contain all uppercase letters. They can be set via the command line and modified by transforms. Public properties CAN be passed into the execution phase, and the key word here is CAN (more on this later). Also, values of properties are NEVER passed from the execution phase back to the UI phase when the execution phase is done processing – so if the value of a property is changed in the execution phase, it is not reflected in the UI phase afterwards.&lt;br /&gt;&lt;br /&gt;Restricted, or &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/restricted_public_properties.asp?frame=true&quot;&gt;Restricted Public Properties&lt;/a&gt;, are Public properties with the distinction being purely based on system or user state. Looking at an out-of-the-box Windows installation and a generic template MSI installation the following statements apply: If you are an administrator running an installation, properties WILL be passed to the execution phase (Restricted public property behavior). If you are not an admin user, most public properties WILL NOT be passed to the execution phase (normal public property behavior). If you want ALL properties set in the UI phase to pass to the execute phase (i.e. you want all public properties to be restricted), enter a property into the property table &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/enableusercontrol.asp&quot;&gt;EnableUserControl&lt;/a&gt;&quot; and set it to &quot;1&quot;. This setting can also be set as a system group policy by a network administrator. Alternatively, each property that you want to allow to be passed into the execution phase (made Restricted) can be specified in another entry in the property table called &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/securecustomproperties.asp&quot;&gt;SecureCustomProperties&lt;/a&gt;&quot; which is a semicolon delimited list of public properties. By default, some properties are set to be restricted, see the &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/restricted_public_properties.asp?frame=true&quot;&gt;Restricted Public Properties&lt;/a&gt;&quot; list in MSDN. This concept is a bit confusing, as the &quot;Restricted&quot; properties are the ones allowed to transition from UI to execute phases, even though you may think the opposite when first hearing the term.&lt;br /&gt;&lt;br /&gt;The concept of &quot;Restricted Public Properties&quot; is rather important for your installation to work if installed by a non-admin user via group policy style deployments. It is highly advisable to test your installation for this deployment type by creating a local admin user and setting both HKLM and HKCU Software\Policies\Microsoft\Windows\Installer &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/alwaysinstallelevated.asp&quot;&gt;AlwaysInstallElevated&lt;/a&gt;&quot; (DWORD) to 1. Then take the user out of the admin group. This will have the effect described above. Test your installation in this configuration to make sure it functions correctly.&lt;br /&gt;&lt;br /&gt;There are more fun tidbits about properties…&lt;br /&gt;&lt;br /&gt;First, all &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/using_a_directory_property_in_a_path.asp?frame=true&quot;&gt;entries in the directory table are accessible as properties&lt;/a&gt;. Therefore, be sure not to run into naming clashes when determining your property names. Be sure not to rely on the directory properties until after CostFinalize has been run.&lt;br /&gt;&lt;br /&gt;Some properties, if authored into the Property table, result in an ICE validation warning. The documentation for &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice87.asp&quot;&gt;ICE 87&lt;/a&gt; explains some of them. Other properties in this table are validated with &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice05.asp&quot;&gt;ICE 05&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice16.asp&quot;&gt;ICE 16&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice24.asp&quot;&gt;ICE 24&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice74.asp&quot;&gt;ICE 74&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice80.asp&quot;&gt;ICE 80&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice86.asp&quot;&gt;ICE 86&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice90.asp&quot;&gt;ICE 90&lt;/a&gt;, and &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/ice99.asp&quot;&gt;ICE 99&lt;/a&gt;. I found it useful to review all the ICE&#39;s when learning MSI, since it locks in the &quot;don&#39;t do this&quot; mentality before you try something and it later fails...&lt;br /&gt;&lt;br /&gt;You can set the &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msihiddenproperties.asp&quot;&gt;MsiHiddenProperties&lt;/a&gt;&quot; Property to a list of semicolon delimited properties that should not be placed into the install log. This only works on MSI 2.0 or greater schemas, and is really not a good means to secure vital information, such as passwords or product codes.&lt;br /&gt;&lt;br /&gt;Many Properties (and several Windows Installer API&#39;s) are not available to a &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/obtaining_context_information_for_deferred_execution_custom_actions.asp&quot;&gt;custom action running deferred&lt;/a&gt;. To pass a property to a deferred custom action, use a type 51 custom action to set a property with the name of the deferred custom action you want to pass data into, sequenced prior to the deferred action in the execute sequence. In the deferred custom action, you can access this property by getting a property called &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsintro7/html/vxgrfcustomactiondataproperty.asp&quot;&gt;CustomActionData&lt;/a&gt;&quot;. Since you typically need to pass more than one property to a custom action, the technique of stuffing a bunch of properties together and later separating them is commonly used.&lt;br /&gt;&lt;br /&gt;Any property value that is changed during the installation process is not preserved or persisted – thus, properties that were set during installation are not present when uninstallation occurs. IMHO, not providing a built-in persistence mechanism is a serious oversight made by the MSI team.&lt;br /&gt;&lt;br /&gt;Speaking of persistence, the &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/adminproperties.asp&quot;&gt;AdminProperties&lt;/a&gt;&quot; property is one way to persist a property – albeit only during an &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/administrative_installation.asp?frame=true&quot;&gt;administrative installation&lt;/a&gt;. To allow this behavior, add the property you wish to set into a property called &quot;AdminProperties&quot; in the usual semicolon delimited list manner. When a user runs an installation after it was deployed to an administrative installation point, these properties are loaded with their saved values – a good easy way to provide default values specific to an enterprise without developing a transform.&lt;br /&gt;&lt;br /&gt;The last important thing to realize about properties is their &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/order_of_property_precedence.asp&quot;&gt;Order of Precedence&lt;/a&gt;&quot;. Essentially, this describes when a property is set. I&#39;m going to list these in the reverse order as MSDN does, as it makes more sense to do so. A property can potentially start its life as an entry in the Property table. If a transform was applied to the MSI, this will change what was described in the Property table. Next, the saved properties from an administrative installation that were listed in the &quot;AdminProperties&quot; property are restored. If anything was specified on the command line, those properties are used. Finally, operating system properties are set. To illustrate by example, if you declare property FOO to be &quot;prop table&quot; in the property table, then run the installation from the command line by &quot;msiexec /I test.msi FOO=cmdline&quot; FOO will be set to &quot;cmdline&quot; since the property is set by the command line AFTER it was set by reading the property table.&lt;br /&gt;&lt;br /&gt;My next blog entry will discuss some techniques to persist properties and their data across installation sessions.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111985217993652021?isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111985217993652021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111985217993652021'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/06/all-about-properties.html' title='All About Properties'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111440982679284698</id><published>2005-04-25T00:49:00.001-04:00</published><updated>2008-02-14T01:23:13.178-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><category scheme="http://www.blogger.com/atom/ns#" term="MSI Custom Actions"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><category scheme="http://www.blogger.com/atom/ns#" term="Windows Installer"/><title type='text'>Installing an Internet Explorer ActiveX Control Part II</title><content type='html'>&lt;a href=&quot;http://bonemanblog.blogspot.com/2005/04/installing-internet-explorer-activex.html&quot;&gt;In part I of this article&lt;/a&gt;, I expressed concern over the various methods of installing an ActiveX control normally distributed and/or updated in a CAB file via Internet Explorer. This follow-up post addresses some concerns of a commenter to that last post, and describes what my solution was.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://bonemanblog.blogspot.com/2005/04/installing-internet-explorer-activex.html#comments&quot;&gt;The comment made by Troy&lt;/a&gt; was from the perspective of a repackaging solution. Troy is quite correct - using pre and post installation snapshots will grab the changes to the ModuleUsage subkey. However, this solution fails to achieve the other goals for this particular installation. We still have the issue of a later update coupled with the MSI repair (or self-repair) operation. A point I failed to mention but Troy unintentionally reminded me of is compatibility across versions of IE. With the type of software this is, it may have a shelf life that would include IE7. Are the changes made to the system under IE6 applicable to all versions of IE under all OS&#39;s? I don&#39;t know. Using the API to install the control alleviates most (but not all) of the potential future incompatibility concerns.&lt;br /&gt;&lt;br /&gt;I especially like how he disliked option #3 - changing the IE security settings to assure a successful installation, calling the API, and resetting them. I laughed as I wrote this option, as I could not imagine anyone doing this. In the middle of last week, I came across the &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwebteam/html/webteam03052001.asp&quot;&gt;Get In the Zone&lt;/a&gt;&quot; section of an issue of &quot;Web Team Talking.&quot; They actually suggest and provide code for doing just that - although in a different context for a slightly different reason. I happen to agree with the KISS directive - &quot;Keep it Simple, Stupid&quot; - but sometimes to assure a successful operation you need to handle the corner cases.&lt;br /&gt;&lt;br /&gt;The last part of his comment - &quot;[this is] why many enterprise customers choose to repackage installations from ISVs&quot; is a completely different blog post. I cannot tell you how true this statement is - and it is usually caused by setup developers, either intentionally or unintentionally. I&#39;ll add this one to the list of future topics for sure.&lt;br /&gt;&lt;br /&gt;For the sake of discussion we were using the Crystal Reports Web Viewer which can generically be replaced with any properly signed cab file that works in the normal deployment mechanism of IE using the OBJECT tag. I have not tried this with unsigned controls, but I tested the approach using Macromedia&#39;s Flash player. You can play along by visiting the &lt;a href=&quot;http://www.macromedia.com/shockwave/download/triggerpages_mmcom/flash.html&quot;&gt;Macromedia&#39;s Flash download page&lt;/a&gt; and getting the CLSID from the OBJECT tag in the page&#39;s source. I then downloaded the swflash.cab file from the codebase attribute of the object tag and stored it on my local disk. The example below is going to use this flash cab file and the flash Class ID. Additionally, I am going to assume the reader is familiar with COM, C++, and MFC. The code is a bit lengthy to post here, but using the steps below a reasonable Windows developer should be able to complete this exercise in an hour or two.&lt;br /&gt;&lt;br /&gt;I chose MFC since we need to implement IBindStatusCallback. This is easily accomplished by deriving a class from CCmdTarget. Make sure the implementations of QueryInterface(), AddRef(), and Release() call the External...() base class implementations. GetPriority() and OnLowResource() can simply return E_NOTIMPL. All the other methods can return S_OK. Since this is an async callback we need to have an event handle member variable, initialized in the Constructor, and have the event set in the OnObjectAvailable() method. That&#39;s the essential plumbing for the callback class. The callbacks for download progress can be implemented for the progress bars in the MSI.&lt;br /&gt;&lt;br /&gt;Before we continue, let&#39;s make sure we have all the data we need to install the control.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The class id of the control is {D27CDB6E-AE6D-11cf-96B8-444553540000}. It needs to be in this format as a string (with the braces) so we can call CLSIDFromString to convert it to a CLSID type.&lt;/li&gt;&lt;li&gt;We are going to use major and minor versions of 0xFFFFFFFF to assure it is always installed.&lt;/li&gt;&lt;li&gt;Finally, we need a codebase - or place to &quot;download&quot; the cab file from. Since we are installing the cab file locally, the CA needs to be appropriately place in the sequence and we need to get the local path to the file. The path needs to be in appropriate format - for a local disk where the file is at &lt;strong&gt;C:\swflash.cab&lt;/strong&gt;, we need to make it &lt;strong&gt;file://c:\\swflash.cab &lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The entry point code needs to hook up the plumbing - instantiate the IBindStatusCallback implementation class, call ExternalQueryInterface() on it to get the interface pointer. Then we need to create and register the binding context by calling CreateBindCtx() and RegisterBindStatusCallback().&lt;/p&gt;&lt;p&gt;The meat of what we are doing is a call to CoGetClassObjectFromURL() with the parameters described above. The two common returns from this call will be&lt;br /&gt;MK_S_ASYNCHRONOUS, S_OK, or an error. If S_OK is returned, the object is already installed. If the async value is returned, we need to loop until the event in the callback class is set or some predefined timeout elapses. After the event is set, check to see that the object was created based on what was set in the OnObjectAvailable() method.&lt;/p&gt;Based on testing, this method appears to work all of the time, regardless of the various IE security zone settings. This is likely due to using the file:// protocol. Remember that we are dealing with a signed control - with unsigned controls, your mileage may vary. This method was not tested in IE with Server 2003 in Enhanced Security Configuration, so all bets are off there as well.&lt;br /&gt;&lt;br /&gt;If you wish to have the installation always grab the &quot;latest&quot; version of the code from a URL, you will need to implement the code to change and restore the IE security settings from the article linked to above and also implement the download callbacks to move the MSI progress bar. Be sure to alert the user that you will be temporarily altering the IE security settings, and make sure when handling a cancel button press from the MSI that you restore the settings. This particular corollary to the problem can be useful if you want to refresh the clients using a simple repair/maintainence/reinstall option after the server-side control was updated without having Bob the network admin do much extra work (or having to release client side patches).</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111440982679284698?isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111440982679284698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111440982679284698'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/04/installing-internet-explorer-activex_25.html' title='Installing an Internet Explorer ActiveX Control Part II'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111380578289544223</id><published>2005-04-17T02:30:00.001-04:00</published><updated>2008-02-14T01:23:40.923-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><category scheme="http://www.blogger.com/atom/ns#" term="MSI Custom Actions"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><category scheme="http://www.blogger.com/atom/ns#" term="Windows Installer"/><title type='text'>Installing an Internet Explorer ActiveX Control</title><content type='html'>This post is different from my usual posts, mainly because I have no idea what I am talking about! Well, at least I am admitting it this time!&lt;br /&gt;&lt;br /&gt;I am tasked with creating an installation for an intranet type application. I need to create a shortcut to a URL and install a client side ActiveX control. For the sake of argument lets make it a fairly common one, such as the Crystal ActiveX viewer control. Sounds pretty simple, eh?&lt;br /&gt;&lt;br /&gt;Let&#39;s go over a typical usage scenario. Hopefully, when designing applications and their deployment packaging you have some made-up profiles of typical users. Lets make up a few for our purposes. &quot;Bob the system admin,&quot; &quot;Mary the power user,&quot; and &quot;Joe the restricted user&quot; should be good enough. We can be a bit more specific and cover XP SP2 users, users with severely restricted ActiveX Browser settings, and non-IE browser users as well, but lets ignore these for now.&lt;br /&gt;&lt;br /&gt;Essentially, Joe has a problem. We can give him the URL to the web application or even create a shortcut and/or favorite for him. The problem is, the first time he tries to run a Crystal Report, he gets a security warning, and the Crystal ActiveX control does not install. Of course, it is because he has no permissions to install software, even if everything else we ignored above is in good shape. Incidentally, if we can&#39;t figure out that Joe&#39;s problem is that he is a limited user, you can try the &lt;a href=&quot;http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/samples/internet/browsertools/cdllogvw/default.asp?frame=true&quot;&gt;CDLLogViewer&lt;/a&gt; application to find out why.&lt;br /&gt;&lt;br /&gt;Bob and Mary never have issues with the system. In fact, if Bob and/or Mary perform the same steps as Joe everything works, and Joe logs in later on, even Joe can view and print reports. The problem is that Bob and Mary would never run reports on the system. Heck, Bob doesn&#39;t even launch the application. Bob may even deploy the install package using SMS or Group Policy.&lt;br /&gt;&lt;br /&gt;In the corporate network world, pretty much everyone is a Joe. There are a few &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/privileged.asp&quot;&gt;Privileged&lt;/a&gt; Mary&#39;s, and even less Bob&#39;s. To make all this work, Bob needs to install applications for the likes of Joe. This behavior is not only acceptable, but practically expected.&lt;br /&gt;&lt;br /&gt;So how would Steve, our installation guru, make all this work? He would not only need it to work, but be practically bulletproof.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Option 1&lt;/em&gt;: The easiest approach. 99% of the time, this is synonymous with the wrong approach. We can extract the Crystal Reports ActiveX viewer cab file, read the .inf file to figure out what it is doing, add the files to a MSI project selecting the proper registration options, create the shortcut file, and be done with the project in under 20 minutes.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Why it doesn&#39;t work&lt;/em&gt;: Since Joe is not the only user of the machine (remember Mary and Bob?), any updates to the control will cause the MSI-based installation to screw up at some point. If the control is updated by Mary, and Bob uninstalls the application, the component/reference count then causes havoc with Mary by possibly removing the file on uninstall. Effectively, we have precluded the normal installation/updating/removal of the ActiveX Viewer by repackaging it in an MSI. More information on the hazards of this approach can be found on MSDN in the &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/workshop/delivery/download/overview/infregdetails.asp?frame=true&quot;&gt;Registry Details&lt;/a&gt;&quot; page. Bad stuff.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Option 2&lt;/em&gt;: Install the shortcuts as in Option 1. Call the appropriate API&#39;s to install the CAB file from the web server. In this case, (after a bit of digging through MSDN I see this process is called &quot;&lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/workshop/delivery/download/overview/implementation.asp?frame=true&quot;&gt;Internet Component Download&lt;/a&gt;&quot; or ICD for short) we need to call &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/workshop/networking/moniker/reference/functions/cogetclassobjectfromurl.asp&quot;&gt;CoGetClassObjectFromURL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Why it doesn&#39;t work&lt;/em&gt;: Referring to the ICD link above, we assume the web server is up and running at installation time - #1 in the &quot;Download and install process&quot;. We also assume that WinVerifyTrust returns a value that is compatible with the setting for ActiveX controls for the Zone in which the server we are pulling the cab file from resides - #2 in the &quot;Download and install process&quot;. Last, but not least, we are assuming that good old Bob didn&#39;t muck with the CODEBASE setting of the Internet Search Path.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Option 3&lt;/em&gt;: Completely decimate the user&#39;s machine by &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/workshop/security/szone/overview/sec_zone_elevation_overview.asp&quot;&gt;querying the IE Security Settings&lt;/a&gt;, saving them, adding the server to a trusted zone, modifying the zone parameters to set the ActiveX settings appropriately, call &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/workshop/networking/moniker/reference/functions/cogetclassobjectfromurl.asp&quot;&gt;CoGetClassObjectFromURL&lt;/a&gt;, after it succeeds, set everything back to the way it was.&lt;br /&gt;&lt;br /&gt;This seems like a heck of a lot of work, but it appears to be the only way to bulletproof an installation of an ActiveX component. Plus, we ignored several issues. If the default browser was (or will be) Firefox or even Lynx, a shortcut to this URL will always fail to display the reports. To solve this problem, an EXE wrapper would need to be written to host the IE control to assure that IE is actually the browser engine used to access the page. &lt;a href=&quot;http://www.joelonsoftware.com/articles/APIWar.html&quot;&gt;Joel&lt;/a&gt;, the API war is far from over - it just made things more complicated!&lt;br /&gt;&lt;br /&gt;Of course, I&#39;ll get to play around with these API&#39;s and see if installing the cab file from a file:// UNC path changes anything. I&#39;ll report back what I find. If anyone else has already &quot;solved&quot; this problem using an approach I haven&#39;t considered, please let me know. In all likelihood, I&#39;ll end up calling &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/workshop/networking/moniker/reference/functions/cogetclassobjectfromurl.asp&quot;&gt;CoGetClassObjectFromURL&lt;/a&gt;, and if it fails, try to provide some meaningful feedback to the user as to why the installation can not succeed and let Bob worry about the corner cases. After all, he ultimately created them.&lt;br /&gt;&lt;br /&gt;UPDATE: Please see &lt;a href=&quot;http://bonemanblog.blogspot.com/2005/04/installing-internet-explorer-activex_25.html&quot;&gt;Part II of this post&lt;/a&gt;!</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111380578289544223?isPopup=true' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111380578289544223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111380578289544223'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/04/installing-internet-explorer-activex.html' title='Installing an Internet Explorer ActiveX Control'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111268345743807499</id><published>2005-04-05T02:37:00.001-04:00</published><updated>2008-02-14T01:23:50.893-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Diversions"/><title type='text'>A look in the keyhole</title><content type='html'>Google just added &lt;a href=&quot;http://maps.google.com/maps?q=white+house&amp;amp;ll=38.898865,-77.036144&amp;amp;sll=38.894048,-77.035618&amp;amp;spn=0.006652,0.009634&amp;amp;sspn=0.026608,0.038538&amp;amp;t=k&amp;amp;hl=en&quot;&gt;satellite imagery&lt;/a&gt; to their maps using their Keyhole technology. I&#39;m wondering how long it will be before someone sells ads mowed into their front lawns on &lt;a href=&quot;http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&amp;amp;category=317&amp;amp;amp;item=5967114770&amp;amp;rd=1&amp;amp;ssPageName=WDVW&quot;&gt;ebay&lt;/a&gt;. Perhaps I should &lt;a href=&quot;http://news.bbc.co.uk/1/hi/world/asia-pacific/1418165.stm&quot;&gt;patent&lt;/a&gt; the idea...</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111268345743807499?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111268345743807499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111268345743807499'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/04/look-in-keyhole.html' title='A look in the keyhole'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111267408984217596</id><published>2005-04-04T23:01:00.001-04:00</published><updated>2008-02-14T01:24:08.744-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="XMLRPC"/><title type='text'>New version of XML-RPC Library Released!</title><content type='html'>&lt;a href=&quot;http://www.xmlrpc.com/&quot;&gt;XML-RPC&lt;/a&gt; is a spec that allows software running on different operating systems to make remote procedure calls over the internet using XML over HTTP. It was inspired by a more simplistic RPC protocol and an early draft of the SOAP protocol. &lt;a href=&quot;http://sourceforge.net/projects/xmlrpc-c/&quot;&gt;xml-rpc-c&lt;/a&gt; is one such implementation of XML-RPC.&lt;br /&gt;&lt;br /&gt;I used this library in the past for multiple reasons. First, I needed to (essentially) perform a remote procedure call. Secondly, I needed it to be cross platform (*nix and Windows). Third was the library must not run under some compatibility layer for Windows such as MinGW or CygWin (too many headaches in the past for my taste). On the wishlist was a single library that can be used on both platforms so I could concentrate on core application functionality and not worry about writing interface classes to several different implementations of a protocol. At the time, SOAP was out, and this library was in.&lt;br /&gt;&lt;br /&gt;Granted, there was a bit of work needed on the Windows side. I wanted to use the WinInet API&#39;s so I wouldn&#39;t have to deal with configuring proxy settings in my application - if IE worked, I wanted my app to work, too. WinInet support wasn&#39;t working out of the box - in fact the original maintainer sort of dropped out of sight. Thanks to the community at the time (especially Alex), I was able to share a few patches and get it working.&lt;br /&gt;&lt;br /&gt;When I saw that Bryan Henderson had taken over the library as the official admin and began active development, I saw it as an opportunity to contribute something back to the community. I suppose my role is maintainer of all things Windows-related.&lt;br /&gt;&lt;br /&gt;The latest release features a new possibility for Windows users. I created an XML-RPC server using the http.sys dll shipped with Windows Server 2003 and Windows XP SP2. Applications using this dll can share the same port as other applications using this dll. Windows Server 2003&#39;s IIS 6 happens to use it, so you can even share port 80 and/or 443 with it seamlessly - no more ISAPI dll&#39;s to accomplish that feat!&lt;br /&gt;&lt;br /&gt;For developers looking to use http.sys in your own code, as well as using it with SSL, this code may serve as an example for your own purposes - such as dealing with basic authentication or SSL connections which is not provided for in the http.sys API&#39;s or documentation. There is a dearth of example code out there for this particular API for some reason.&lt;br /&gt;&lt;br /&gt;For making client-to-server calls on Windows, the only out-of-the-box solution was the WinInet transport. The problem with this API is it can&#39;t be used in a service. Although the xml-rpc-c library always supported using &lt;a href=&quot;http://curl.haxx.se/&quot;&gt;libcurl&lt;/a&gt; out of the box, the compilation and linking of this was difficult to figure out and required a few creative changes. With this release, I hope to have made it easier.&lt;br /&gt;&lt;br /&gt;Finally, the last major change was tightening up the WinInet transport a bit. Previously, communication over SSL would succeed even if the server certificate was invalid. That functionality was great when developing using a test certificate, but probably not what you want to use in production. By default, the old behavior is now only active if set by a specific transport option at runtime.&lt;br /&gt;&lt;br /&gt;If anyone is using this library, on Windows or otherwise, please feel free to give me a shout. If you have any feature requests, let me know. One note to downloaders - for some reason the tarball does not extract properly using WinRar. If you rename the download to have a .tgz extension and use WinZip to decompress it, you will be able to open the project files in Visual C++ 6 without problems. Visual Studio 2003 will also open, convert, and compile the project without problems.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111267408984217596?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111267408984217596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111267408984217596'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/04/new-version-of-xml-rpc-library.html' title='New version of XML-RPC Library Released!'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111256806705618963</id><published>2005-04-03T17:42:00.001-04:00</published><updated>2008-02-14T01:24:56.923-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><title type='text'>Integrate the latest MSDN Library Help with VC++ 6</title><content type='html'>*See update at bottom of this entry*&lt;br /&gt;&lt;br /&gt;There is an awesome article just published on &lt;a href=&quot;http://www.codeproject.com/&quot;&gt;Code Project&lt;/a&gt; that shows how to &lt;a href=&quot;http://www.codeproject.com/macro/VSNetHelp.asp&quot;&gt;Integrate the latest MSDN Library Help with VC++ 6&lt;/a&gt;. Although you can simply download the demo project and follow the instructions after &quot;How to set the default help collection?&quot; at the end of the article, the article itself is a great primer on reverse engineering.&lt;br /&gt;&lt;br /&gt;Of course, the newer-than-October-2001 MSDN libraries do not have the help files for the actual Visual C++ 6 development environment and/or compiler, if you are still using VC6, I would hope you don&#39;t need this documentation anymore.&lt;br /&gt;&lt;br /&gt;I am curious why Microsoft did not include this integration ability (with the normal caveats) with the newer MSDN libraries, but I guess they sold a couple of extra copies of Studio .NET this way...&lt;br /&gt;&lt;br /&gt;UPDATE: With this plugin enabled, I get a sharing violation on the .opt file if opening the project from explorer - a double-click or &quot;Open With&quot;. Opening Visual Studio, then opening the workspace seems to not have this problem. Please let me know if you have similar problems.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111256806705618963?isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111256806705618963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111256806705618963'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/04/integrate-latest-msdn-library-help.html' title='Integrate the latest MSDN Library Help with VC++ 6'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111246891177313661</id><published>2005-04-02T13:15:00.001-05:00</published><updated>2008-02-14T01:25:23.778-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><title type='text'>More Setup Pet Peeves</title><content type='html'>Everywhere you look there are more &lt;a href=&quot;http://bonemanblog.blogspot.com/2004/09/top-20-installation-pet-peeves-and-how.html&quot;&gt;setup pet peeves&lt;/a&gt; that keep popping up. Many of these new ones are in my top 20 list - some of them aren&#39;t.&lt;br /&gt;&lt;br /&gt;Microsoft&#39;s &lt;a href=&quot;http://blogs.msdn.com/jeffdav/archive/2004/10/04/237816.aspx&quot;&gt;JeffDav writes one &lt;/a&gt;about two installation issues - a 3rd party application and a Microsoft application. The second one was probably a MSI based installation, and I blame the MSI engine&#39;s design on this one - what ever happened to &quot;Couldn&#39;t copy file from source media, retry&quot; dialogs?&lt;br /&gt;&lt;br /&gt;Raymond Chen chimed in with &lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2005/03/28/402889.aspx&quot;&gt;his own story&lt;/a&gt;, and the usual 2 billion responses to it - some of the catchy ones captured below.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2005/03/28/402889.aspx#402905&quot;&gt;Janus&lt;/a&gt; points out that several Java based apps have issues - write once, run anywhere (as long as there are no spaces in the path). Other commenters chimed in issues with *nix application ports having similar path issues. (My peeve #19).&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2005/03/28/402889.aspx#403180&quot;&gt;Foxyshadis&lt;/a&gt; points out a few more: &lt;em&gt;&quot;are you sure you want to create the folder&quot; AND &quot;are you sure you want to install in an existing folder&quot;&lt;/em&gt; - I don&#39;t get this one either (My peeve #10). &lt;em&gt;&quot;Once you install with custom options and later upgrade, and try to use typical, not only will it always forget which options you chose, it&#39;ll install into a completely new default folder.&quot;&lt;/em&gt; Why is that? This is a nice addition to my peeve #11.&lt;br /&gt;&lt;br /&gt;&lt;a id=&quot;Comments.ascx_CommentList__ctl22_NameLink&quot; href=&quot;http://blogs.msdn.com/oldnewthing/archive/2005/03/28/402889.aspx#403261&quot; target=&quot;_blank&quot;&gt;Gryphonvere&lt;/a&gt; points out some applications &lt;em&gt;&quot;ask you rather forcefully to install some older version of DirectX&quot;&lt;/em&gt; - some of this is the fault of the dependent application not providing a mechanism to determine the current version of itself, and some on the setup developer for not doing the smart thing and saying &quot;This application was not tested with Acrobat Reader 12, if you have issues, try using Acrobat Reader 4.0.&quot; This is a corollary to my peeve&#39;s #17 and #13.&lt;br /&gt;&lt;br /&gt;&lt;a id=&quot;Comments.ascx_CommentList__ctl27_NameLink&quot; href=&quot;http://blogs.msdn.com/oldnewthing/archive/2005/03/28/402889.aspx#403542&quot; target=&quot;_blank&quot;&gt;David Walker&lt;/a&gt; complains about the &quot;Company Name\Product Name&quot; default directory convention (My peeve #14). He also hits the &quot;Common Files&quot; directory issue. This latter point is somewhat valid, but for keeping the redistribution, duplicate files, and simplified patching for a company&#39;s product line shared components down to a minimum, I can live with that. We all have to understand that x-copy deployment of applications or product lines is not always possible. While on the subject (and expanding on my earlier peeve #20), my belief is that &quot;Application Data&quot; folders are underused. If I want to back up my system or use OS features such as roaming profiles, I want to have a one-stop method of doing so. Backing up the per-user application data folder, the per-system application data folder, and the &quot;My documents&quot; folders should be the only backup philosophy I ever have to use.&lt;br /&gt;&lt;br /&gt;Doesn&#39;t anyone test the key features of an installation for both correctness and usability anymore?</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111246891177313661?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111246891177313661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111246891177313661'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/04/more-setup-pet-peeves.html' title='More Setup Pet Peeves'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6955915.post-111069175485794919</id><published>2005-03-12T23:54:00.001-05:00</published><updated>2008-02-14T01:26:32.287-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><category scheme="http://www.blogger.com/atom/ns#" term="Installation General"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><title type='text'>Application Preloading at System Startup</title><content type='html'>Sometimes I wonder if we (as software developers) should have some form of a code of ethics, where violations can get you barred from writing software in the future. Mainly, this would apply to the spyware/malware developers (or perhaps the ethical violation is more directable to those who bundle the spyware).&lt;br /&gt;&lt;br /&gt;If this ethical code existed, I would add a violation for any application that did not inform and offer a choice to the user at install time that some startup or background task will always be running. Some typical offenders are QuickTime, Real, Adobe, Microsoft Office, Microsoft Messenger, printer driver &quot;status monitors&quot;, and more. These startup applications increase boot time and memory/pagefile requirements. Most of the time, I don&#39;t use these applications each time I boot the PC.&lt;br /&gt;&lt;br /&gt;If the goal is to &lt;a href=&quot;http://blogs.msdn.com/oldnewthing/archive/2005/03/11/394249.aspx&quot;&gt;make your application start up faster &lt;/a&gt;(or appear to start up faster) there are ways of accomplishing this. Rebasing your DLL&#39;s, background/on demand loading of plugins, delay loading dependent DLL&#39;s, etc. are a few common tricks that can be used. The utility &quot;&lt;a href=&quot;http://www.tnk-bootblock.co.uk/prods/misc/index.php&quot;&gt;Adobe Reader SpeedUp&lt;/a&gt;&quot; accomplishes a magnificent improvement in load times, and this doesn&#39;t involve rewriting any of Adobe&#39;s code. If you wrote one of these startup applications anyway, at least offer the user the option at install time if they wish to use it.</content><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6955915/111069175485794919?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111069175485794919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6955915/posts/default/111069175485794919'/><link rel='alternate' type='text/html' href='http://bonemanblog.blogspot.com/2005/03/application-preloading-at-system.html' title='Application Preloading at System Startup'/><author><name>Steven Bone</name><uri>http://www.blogger.com/profile/16752950454936948886</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5p-OBUS5azSCcB0ooh3kV7aJd0LiBWg-XqXGgWca5ZutU9hUKcAVzKb06Mia6Osm7byswlj7yDj_BLIUF0v6Jjm_J55SNM6FJDnRUm-Toc_dAHkP8hPrsne2_1ynwzVE/s220/JanHike.png'/></author><thr:total>0</thr:total></entry></feed>