Copyright © 2007 Chris Bensen. All rights reserved.
I will be taking a bit of a break this week to spend with my family. I have updated the photo for this month with a photograph I took at Monterey Bay Aquarium of the Jellyfish. If you ever get a chance to visit Monterey the Monterey Bay Aquarium is a destination worth checking out.
Friday, June 29, 2007
Taking a Break this Week
Posted by Chris Bensen at 5:00 PM 0 comments
Labels: photo
Good Practices When using Version Control
I have addopted a practice of only committing one bug fix at a time into the version control system (in my case Tortoise SVN). This can take a bit more time but it solves a lot of problems and saves a lot of time later. I find if the code ever needs to be merged it is a lot easier, especially being able to test the bug, merge the code and test that the bug was fixed. If the code ever has problems one can identify exactly what code was changed for a particular bug fix. It is also the cleanest approach because when you checkin your changes you know exactly what you are changing and you won't forget any changes breaking the rest of the team. The trick is human error happens so I like to reduce it when possible.
What practices have you addoped when working with version control systems?
Posted by Chris Bensen at 8:00 AM 7 comments
Thursday, June 28, 2007
Plastic in Our Oceans
I came across this video at Patagonia's blog. I really don't have any more to add on the subject that hasn't already been said, I just want to get the information out. So please click here and read the original post "When Plastic is Not So Fantastic".
Posted by Chris Bensen at 8:00 AM 0 comments
Wednesday, June 27, 2007
Einstein's Rule Of 72 - Easy Calculation of Compound Interest
The other day I was doing a little bit of investing and needed to do some quick compound interest calculations. As usual when this happened my brain gears came to a halt, but then I remembered something about Einstein and 72 so I looked it up and voila, Einstein's Rule of 72 to calculate compound interest. It is a great little thing to know about and can make you the center of attention at parties.
The rule of 72 says that in order to find the number of years required to double your money at a given interest rate, you can divide the interest rate into 72. For example, if you want to know how long it will take to double your money at twelve percent interest, divide 12 into 72 and get 6 years.
You can also find the interest rate required to double your money in a given number of years. For example, if you want to double your money in nine years, just divide 9 into 72 to find that it will require an interest rate of about 8 percent.
As long as the interest rate is less than twenty percent, the rule of 72 is accurate.
Update: Fix typo in second example when doubling money in nine years.
Posted by Chris Bensen at 7:00 AM 4 comments
Tuesday, June 26, 2007
Delphi Tips And Tricks: CoInitialize/CoUninitialize Part III
Previously I talked about using CoInitialize and CoUninitialize in very simple situations, a single threaded console application. If you havn't read those posts you can read Part I here, Part II here and the adendum to Part II here. So for this post I'm going to demonstrate the use of CoInitializeEx and CoUninitialize in a multithreaded environment.
This demo consists of two files, two instances of a thread and a couple loops to give your system enough time to actually allow some processing to happen. Below find the code for this demo:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
ActiveX,
MyThread in 'MyThread.pas';
var
Thread1, Thread2: TMyThread;
Success: HResult;
begin
Success := CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
try
Thread1 := TMyThread.Create(False, 'thread1');
Thread2 := TMyThread.Create(False, 'thread2');
try
// Note my implementation could have a
// problem if DoStuff completes too quicly.
DoStuff('main');
Thread1.Wait;
Thread2.Wait;
finally
Thread1.Free;
Thread2.Free;
end;
finally
CoUninit(Success, 'main');
end;
end.
unit MyThread;
interface
uses
Classes, SyncObjs;
type
TMyThread = class(TThread)
private
FLock: TCriticalSection;
FText: string;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean; const Text: string);
destructor Destroy; override;
procedure Wait;
end;
procedure CoInitStatus(Success: HResult);
procedure CoUninit(Success: HResult; const Text: string);
procedure DoStuff(const Text: string);
implementation
uses
SysUtils, ActiveX, Windows;
procedure CoInitStatus(Success: HResult);
begin
case Success of
S_OK, S_FALSE: WriteLn('CoInitialize success');
RPC_E_CHANGED_MODE, E_INVALIDARG,
E_OUTOFMEMORY, E_UNEXPECTED: WriteLn('CoInitialize failed');
end;
end;
procedure CoUninit(Success: HResult; const Text: string);
begin
case Success of
S_OK, S_FALSE: CoUninitialize;
end;
end;
procedure DoStuff(const Text: string);
var
Index: Integer;
begin
for Index := 0 to 10000 do
WriteLn(Text + ' ' + IntToStr(Index));
end;
{ TMyThread }
constructor TMyThread.Create(CreateSuspended: Boolean; const Text: string);
begin
FLock := TCriticalSection.Create;
inherited Create(CreateSuspended);
FText := Text;
UniqueString(FText);
end;
destructor TMyThread.Destroy;
begin
FLock.Free;
inherited;
end;
procedure TMyThread.Execute;
var
Success: HResult;
begin
FLock.Acquire;
Success := CoInitializeEx(nil, COINIT_MULTITHREADED);
try
DoStuff(FText);
finally
CoUninit(Success, FText);
FLock.Release;
end;
end;
procedure TMyThread.Wait;
begin
FLock.Acquire;
FLock.Release;
end;
end.
I want to bring your attention to two things. First, notice my use of the return value from CoInitializeEx. And second, the first thread to call CoInitializeEx (located in the main function) is also the last one to call CoUninitialize by use of the the function TMyThread.Wait.
I would talk more about it but I think the code explains everything. So read the source and if you have questions ask.
Update: I have moved the critical section to occur before the constructor to TThread to avoid a race condition and called UniqueString on the string passed to the thread as pointed out by Anders Melander.
Posted by Chris Bensen at 8:00 PM 13 comments
Thursday, June 21, 2007
Delphi Tips And Tricks: CoInitialize/CoUninitialize Part IIb
I forgot to mention in Part II that a very simple call stack containing code like this:
CoInitializeEx(nil, COINIT_APARTMENTTHREADED)
CoInitializeEx(nil, COINIT_MULTITHREADED)
CoUninitialize;
CoUninitialize;
will cause one too many calls to CoUninitialize. The second call to CoInitializeEx will return RPC_E_CHANGED_MODE and does not require another call to CoUninitialize because the internal reference counting of CoInitialize didn't increment. The example I provided in Part II demonstrated this but I neglected to mention it specifically.
I just wanted to mention this before I forgot. Next post about CoInitialize will be as requested a multithreaded example.
Posted by Chris Bensen at 4:00 PM 0 comments
Photographing At the Zoo
Copyright © 2007 Chris Bensen. All rights reserved.
Last week I took the family to the zoo. Our daughter loves animals and we figured she should see the monkeys in real life and not just stuffed animals and pictures in her books. I figured I'd bring my camera along. I don't typically photograph animals in captivity (it was a hard decision because I don't like seeing the animals in zoos, they don't look very happy and I'd prefer to see them in the wild), but it turns out you can do some pretty good photography at the zoo if you are careful and patient. The one problem is all the man made things like rocks and fences can really get in the way. In the photograph above you can see the fence behind the giraf and the man made rock that the meerkat is standing on below.
Copyright © 2007 Chris Bensen. All rights reserved.
At any rate my wife is a Science Illustrator and she can always use examples of animals to draw so I tried to capture the animals in good poses and ignore the man-mande contraptions. But the important thing is our daughter had a blast!
Posted by Chris Bensen at 8:45 AM 3 comments
Changes for COM in Delphi 2007 Update 1
One change everyone might not be aware of in Delphi 2007 Update 1 is that the Transactional Object and Transactional Data Module wizards no longer require a registry entry to enable the gallery items. Note that these wizards are only available in the Enterprise and Architect SKUs.
Posted by Chris Bensen at 8:00 AM 1 comments
Wednesday, June 20, 2007
Delphi Tips And Tricks: CoInitialize/CoUninitialize Part II
In Part I of CoInitialize/Uninitialize of Delphi Tips and Tricks I talked about encountering problems with CoUninitialize and how the Delphi compiler generates code to IUnknown._Release after a function block. In this part I want to discuss another common problem having to do with making multiple calls to CoInitialize and CoUninitialize.
Here are the rules to calling CoIninitialize and CoUninitialize:
- Each thread must initialize the COM library by calling CoInitialize
- Each call to CoInitialize with a return value of S_OK and S_FALSE must be paired with a call to CoUninitialize
- Each thread can only be initialized with one threading model (remember GUI code is appartment threaded)
- The first thread that calls CoInitialize must be the last thread to call CoUninitialize.
- Do not call CoInitialize in DllMain
If you do not follow these five basic rules your application will not function properly. You can find these the documentation with these five basic rules on MSDN here. There is more information but the simple list above is much easier to read.
Below is a simple example that demonstrates correct usage and handling of the return value from CoInitialize and CoInitializeEx in a single threaded application. If there is any interested I will expand this to a multithreaded application?
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, ActiveX, Windows;
procedure CoInitStatus(Success: HResult);
begin
case Success of
S_OK, S_FALSE: WriteLn('success');
RPC_E_CHANGED_MODE, E_INVALIDARG,
E_OUTOFMEMORY, E_UNEXPECTED: WriteLn('failed');
end;
end;
procedure CoUninit(Success: HResult);
begin
case Success of
S_OK, S_FALSE: CoUninitialize;
end;
end;
var
Success1, Success2, Success3: HResult;
begin
Success1 := CoInitialize(nil);
CoInitStatus(Success1);
Success2 := CoInitialize(nil);
CoInitStatus(Success2);
Success3 := CoInitializeEx(nil, COINIT_MULTITHREADED);
CoInitStatus(Success3);
CoUninit(Success3);
CoUninit(Success2);
CoUninit(Success1);
end.
The output from running this program should be:
success
success
failed
Posted by Chris Bensen at 10:00 AM 4 comments
Tuesday, June 19, 2007
Video of Transformers Costumes
I guess everyone is celebrating the Transformers Movie in their own way. I was just planning on watching it but making a transforming costume works too.
Posted by Chris Bensen at 8:00 AM 0 comments
Monday, June 18, 2007
Windows Vista UI Guidelines
I was looking around for some Vista UI Guidelines and I ran across the Windows Vista Icon Development Guidelines on MSDN. I figured everyone could benefit from knowing this exists and where it is.
http://msdn2.microsoft.com/en-us/library/aa511280.aspx
Posted by Chris Bensen at 8:00 PM 2 comments
Wednesday, June 13, 2007
Customer Feedback About Delphi COM Features by Blog Readers
I wanted to thank everyone for all their feedback two weeks ago when I asked for feedback on the Delphi COM features. I received a lot of useful input, some of which I knew about and some that I did not, but I will take all of the input and incorporate as much as I can into our future plans. It really helps to get prioritization on what bugs to fix and features to do. So thank you again!
Posted by Chris Bensen at 8:00 PM 2 comments
Useful Applications As Suggested by Blog Readers
A few weeks ago I listed the programs that I use on my computer and there were a bunch of comments with additional useful tools. Thanks to all of those who left comments. I thought I'd bring them to the top level of the blog for others to read in case they haven't read all the comments of that other post. So here are links to all of those tools:
File management
Version Control
File comparison
File Editing
Launch Tools
Web Authoring
Chat
Virtual Machine
RSS Reader
Other
Update: Added TextPad to the list of text editors.
Posted by Chris Bensen at 10:00 AM 3 comments
Experience of Installing Microsoft XNA
Last week I installed Microsoft XNA on my computer and the experience was so horrible I had to blog about it. If you don't know what XNA is, XAN is Microsoft's bold, and from my first look, very good attempt at creating a cross platform gaming framework and toolset for .NET. Cross platform between Windows and XBox 360, but pretty cool none-the less and free. You download XNA here and Wikipedia has a good description of that it is here.
So on to my experience. I downloaded the setup for XNA from Microsoft and ran it. It required Visual C# Express SP1. So I downloaded and installed Visual C# Express. It wasn't SP1 so I went to the help menu and ran update. Well my default web browser is Firefox and update doesn't work in Firefox. Since the page was a redirected page with the error updated couldn't run in Firefox, I had to go the history copy and paste the URL and run IE. It turns out the update was just the Windows update and I wasn't even provided with a Visual C# Express update 1. So I went to Google and searched for "Visual C# Express update". The first link had the updates but try and find the correct one from this list.
Why the author of the article can't put another column in the table with the produce name but instead requires the user cross reference between two tables is beyond me. I can't believe the user experience from the largest software manufacturer is this bad. Well actually I can, I just don't want to. You get what you pay for.
Posted by Chris Bensen at 8:00 AM 2 comments
Tuesday, June 12, 2007
Apple Safari on Windows
Yesterday at Apple WWDC 2007 Steve Jobs announced that Safari is now available as a public beta on Windows. Since I like using Safari on my home Mac I figured I'd give it a try so I've been using it for the better part of a day now.
Subjectively Safari on Windows appears to be snappier than Firefox. I prefer the interface to IE and for the most part Firefox, but that is just my preference. I've had a few crashes, one I reported to Microsoft, but overall it is a good experience. I find it a little chunky over Remote Desktop.
In-line finding is a pretty sweet feature. I wish Mac apps on Windows would addopt the Windows shortcuts or at least give you the option to choose the Mac or Windows shortcuts. When I'm on Windows I want to use CTRL + F4 to close a window, but I don't want to use the Command + W (or on Windows it's CTRL + W).
You might like it, you might not, but I'd say it is worth the download to at least have a look.
Update: A review of Safari on Windows from ars technica was just published. So head over there to read what they think.
Posted by Chris Bensen at 8:00 AM 6 comments
Wednesday, June 6, 2007
Adding Google Search To Your Website
I just added Google search to my blog and website but there were a few tweaks I had to make to the default HTML generated by the Google wizard. The default width was too wide for the sidebar so I figured I'd let others know what I did to save anyone the 20 minutes it took to make it look the way I wanted.
First you need to create a Google AdSense account. Once you have an AddSense account go to AdSense Setup and choose AdSense for Search. Here is a screen capture of the my defaults:
The default search will look pretty close to this depending on what options you provide. From there the default HTML generated by the Google wizard was:
<form method="get" action="http://www.google.com/custom" target="_top">
<table border="0" bgcolor="#ffffff">
<tr><td nowrap="nowrap" valign="top" align="left" height="32">
<a href="http://www.google.com/">
<img src="http://www.google.com/logos/Logo_25wht.gif" border="0" alt="Google" align="middle"></img></a>
<br/>
<input type="hidden" name="domains" value="chrisbensen.blogspot.com"></input>
<label for="sbi" style="display: none">Enter your search terms</label>
<input type="text" name="q" size="22" maxlength="255" value="" id="sbi"></input>
<label for="sbb" style="display: none">Submit search form</label>
<input type="submit" name="sa" value="Search" id="sbb"></input>
</td></tr>
<tr>
<td nowrap="nowrap">
<table>
<tr>
<td>
<input type="radio" name="sitesearch" value="" checked id="ss0"></input>
<label for="ss0" title="Search the Web"><font size="-1" color="#000000">Web</font></label></td>
<td>
<input type="radio" name="sitesearch" value="chrisbensen.blogspot.com" id="ss1"></input>
<label for="ss1" title="Search chrisbensen.blogspot.com">
<font size="-1" color="#000000">chrisbensen.blogspot.com</font></label></td>
</tr>
</table>
<input type="hidden" name="client" value="pub-4398465119811197"></input>
<input type="hidden" name="forid" value="1"></input>
<input type="hidden" name="ie" value="ISO-8859-1"></input>
<input type="hidden" name="oe" value="ISO-8859-1"></input>
<input type="hidden" name="cof" value="GALT:#008000;GL:1;DIV:#336699;VLC:663399;AH:center;BGC:FFFFFF;LBGC:336699;ALC:0000FF;LC:0000FF;T:000000;GFNT:0000FF;GIMP:0000FF;FORID:1"></input>
<input type="hidden" name="hl" value="en"></input>
</td></tr></table>
</form>
And here is how I tweaked it. I removed the image at the top and removed the options to search the web or my site because this was too wide.
<form action="http://www.google.com/custom" target="_top" method="get">
<table border="0" bgcolor="#ffffff">
<tr><td nowrap="nowrap" valign="top" height="31" align="left">
<br/>
<input value="chrisbensen.blogspot.com" name="domains" type="hidden"/>
<label for="sbi" style="display: none">Enter your search terms</label>
<input maxlength="255" id="sbi" value="" name="q" size="20" type="text"/>
<label for="sbb" style="display: none">Submit search form</label>
<input id="sbb" value="Search" name="sa" type="submit"/>
</td></tr>
<tr>
<td nowrap="nowrap">
<input id="ss1" checked value="chrisbensen.blogspot.com" name="sitesearch" type="hidden"/>
<input value="pub-4398465119811197" name="client" type="hidden"/>
<input value="1" name="forid" type="hidden"/>
<input value="ISO-8859-1" name="ie" type="hidden"/>
<input value="ISO-8859-1" name="oe" type="hidden"/>
<input value="GALT:#008000;GL:1;DIV:#336699;VLC:663399;AH:center;BGC:FFFFFF;LBGC:336699;ALC:0000FF;LC:0000FF;T:000000;GFNT:0000FF;GIMP:0000FF;FORID:1" name="cof" type="hidden"/>
<input value="en" name="hl" type="hidden"/>
</td></tr></table>
</form>
Here is a screen capture of the final result that you can now find on the sidebar of this site:
Posted by Chris Bensen at 4:00 PM 0 comments
Tuesday, June 5, 2007
What do Delphi and Ninjas Have in Common?
Nothing!
But Ninjas are way cool and I never get a chance to post a photo of a Ninja let alone a video with lots of Ninjas. So here is a cool video of photographer Chase Jarvis doing a photo shoot with lots of Ninjas!
Posted by Chris Bensen at 8:00 PM 1 comments
Monday, June 4, 2007
Delphi Tips And Tricks: CoInitialize/CoUninitialize Part I
Have you ever been thinking about something and started noticing it all over the place? I find this happens when looking to buy a car. This phenomenon started happening for me lately with CoInitialize. CoInitialize has come up a dozen or more times in the last few months and then again last week, so I decided to start a little series on CoInitialize and CoUninitialize. Since this is a common source of problems for COM applications, even people who know better, I think everyone will benefit from reading this.
In this post I want to go over a bit more than just CoInitialize and CoUniniitalize, I want to show you how Delphi compiler magic can cause you problems when initializing the COM Library. Take the following program, that does just about nothing but cause an error:
program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, ActiveX, XMLintf, XMLDoc;
var
Document: IXMLDocument;
begin
CoInitialize(nil);
Document := TXMLDocument.Create('foo.xml');
CoUninitialize;
end.
This very simple program, calls CoInitialize, creates a TXMLDocument and then calls CoUninitialize. The failure happens after the CoUninitialize. Do you know why?
Delphi manages the lifetime of interfaces calling IUnknown._AddRef on an interface assignment and IUnknown._Release on cleanup of the method. In this case, the COM Library has already been cleaned up when the destructor of TXMLDocument occurs. So really this very simple program, calls CoInitialize, creates a TXMLDocument, increments the reference count of IXMLDocument, calls CoUninitialize, decrements the reference count and destroys the object. We need to change the program so the call to IUnknown_Release happens before CoUninitialize. The simplest way to solve this problem is to change the program by adding a method:
program Project2;
{$APPTYPE CONSOLE}
uses SysUtils, ActiveX, XMLintf, XMLDoc;
procedure DoSomething;
var
Document: IXMLDocument;
begin
Document := TXMLDocument.Create('foo.xml');
end;
begin
CoInitialize(nil);
DoSomething;
CoUninitialize;
end.
Now this very simple program, calls CoInitialize, creates a TXMLDocument, increments the reference count of IXMLDocument, decrements the reference count and destroys the object, calls CoUninitialize.
Posted by Chris Bensen at 8:00 AM 31 comments
Friday, June 1, 2007
Customer Feedback About Delphi COM Features
I'm going to take a stab in the dark here and follow David Lock's lead and solicit feedback from all of you! I am responsible for quite a few more areas of Delphi and C++Builder than David since I've been with the company for over 12 years, but what I'm specifically looking for feedback about is our COM/ActiveX features. COM isn't going away anytime soon, in fact it is my belief that more of you are looking at COM when you are moving your applications to .NET. I want to know what your pain points are, what features you'd like to see, and what bugs are blocking you. Please respond to this post or email me at codegearalias@gmail.com with the word "blog" anywhere in the message (I've taken extra precautions as to avoid spam unlike David).
Posted by Chris Bensen at 8:00 AM 6 comments