Tuesday, February 17, 2009

Windows Installer (MSI) in VS 2008: How to Default to Everyone

When creating an installer packing in VS.NET 2008, the default option is to install as "Just Me" instead of "Everyone". This blog entry for VS.NET 2003 details this nicely -- but it is outdated. As that post details, you have to edit the MSI database to get it to work.

However, in VS.NET 2008, there is a property on the Setup project itself named "InstallAllUsers" that defaults to false. Simply toggle it to true and you're in business.

I just thought I'd add this so others who google for it don't go through the heart aches that used to be required in earlier versions of VS.NET.

Monday, October 13, 2008

Disable form submit on enter key press in (almost) one line of javascript

How to prevent auto-submission of forms with (almost) one line of javascript...

You make your web form and you test filling it out, and you find out hitting enter causes the form to automatically submit, when in fact you want a different behavior. Here's the simplest, cleanest, most compatible way to do this in browser-agnostic javascript without any dependencies of any kind:


<body onkeypress="var e = (event ? event : window.event); return (e.keyCode ? e.keyCode : e.charCode ? e.charCode : e.which) != 13;" >


Essentially that is saying grab the event, determine which key was pressed, and return false if it was the enter key. Returning false suppresses the form from auto-submitting.

Suppose you have a really super useful event handler like the following:


<input type='text' onkeypress='alert("Wow this sure is annoying");' />


The alert would still be shown even though you have the enter key-eating code at the body level thanks to the bubbling precedence of event handling. In essence, the onkeypress event handler is called for the textbox before the event handler code for the body is called. (If you return false from the textbox, it would never bubble up to the body's event handler)

This has been tested on the various current versions of IE (6,7,8), Firefox (2,3), and Safari (3).

Friday, January 11, 2008

2 - Not able to initialize admin object definitions against db ...

So I was tasked with automating the creation and deletion of BlackBerry users. According to RIM, the recommended way of doing this is by using their BlackBerry Resource Kit -- namely the BlackBerry User Admin Service and Tool.

I've never done anything with BlackBerry before, so needless to say I spent some time making noob errors when trying to install the Admin Service. First off, I was getting this funky error:

MAPILogonEx() failed (80040111) for profile:

Okay, that's easy enough to see what's going on. A missing or invalid MAPI profile. I needed a MAPI profile locally (dammit Jim, I'm a developer, not an admin!). I created one using a tool from Microsoft's website: http://support.microsoft.com/kb/228736

I eventually did get to a sticking point when google no longer helped:

"2 - Not able to initialize admin object definitions against db"

I ran SQL profiler to make sure the service was successfully connecting to the proper database (BESMgmt) and it had rights to do everything -- yep everything was cherry. Still no love though. It seemed to point at a database-related error. I fired up Query Analyzer and looked for a table relating to 'object definitions'. Sure enough, an ObjectDefn table existed. Here's a partial sample record:

<AdminObject name="ServerConfig" dbtable="ServerConfig"><Property clientname="Id" dbname="Id" type="56" length="4" isnullable="0" key="1" /><Property clientname="ServiceName" dbname="ServiceName" type="231" length="512" isnullable="1" /><Property clientnam...


Then it dawned on me: they're storing xml in the database. I didn't verify that msxml 4.0 was installed (per the Admin User Documentation from RIM). So I grabbed MSXML 4.0 from Microsoft's website and installed it. Sure enough, the service could finally start. Yay.

Moral of the story: verify your prereq's before banging your head against the wall.

Long story short:
2 - Not able to initialize admin object definitions against db == wrong version of MSXML installed

Hopefully somebody googles this and doesn't have to recreate my madness :-)

Tuesday, November 20, 2007

Calling Exchange COM objects from a .NET web service

Suppose we have the following sample code (assuming you're using System.DirectoryServices and your project has references to System.DirectoryServices and CDOEXM):

string ldapPath="ldap://{DN to user object}";
string homeMDBUrl = "{DN to private exchange store}";
using(DirectoryEntry de = new DirectoryEntry(ldapPath)){
IMailboxStore mbx = (IMailboxStore)de.NativeObject;
mbx.CreateMailbox(homeMDBUrl);
de.CommitChanges();
}


Works fine when run from a fat client. But when we call it from an IIS-based app, it errors with:

Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))

What gives?

This is a generic COM error saying "I couldn't reach a resource you need" (or something close to that). The best bet is that logging in to the Exchange server failed.

Exchange COM objects make requests under the identity associated with the process, not the thread. When trying to call into Exchange via COM (gotten from the NativeObject property on the AD object in .NET), the following do NOT work:
  • setting the user via impersonation in web.config
  • setting the user of all websites in IIS mmc
  • setting the user of the specific website in IIS mmc
  • setting the user of IIS Admin / W3SVC services
There is only one way to effectively get it to work: the ASP.NET worker process must itself run as the user who has rights to touch Exchange. This can be set in machine.config within the tag like this:

<processModel username="{domain\userid}" password="{password}" />


Setting that properly causes the ASP.NET worker process to run as that user at the process level. All other overrides are at the thread level and do not work.

With the machine.config file altered and IIS restarted (yes, you have to iisreset for changes to take effect), calling the code above within a web service / web application should work assuming your other parameters are correct.

Thursday, March 8, 2007

HOWTO: Change a cascaded style in IE / Firefox / Opera

So you want to alter an HTML element's style dynamically. Seems simple enough on the surface -- just alter the .style property appropriately, right?

<a href='javascript:showElement("div2")'>click me</a>
<div id='div2' style='display:none'>This will show up!</div>

<script type='text/javascript'>
function showElement(id)
var el = document.getElementById(id);
if (el){
el.style.display = 'block';
}
}

</script>

You go test it, it works fantastic. You develop the rest of your page and complete it. So to do things properly, you move most of the style attributes into a CSS file instead of doing inline style definitions. So now you have:

css file:
.toggleme {
display:none;
}


page file:

<div id='div2' class='toggleme'>This will show up!</div>

You go back and test your page, but it doesn't work any more! No error, but the div doesn't show up either. What gives?

It turns out that most browsers handle the .style attribute of an element as meaning the attribute value directly from the html itself, instead of the cascaded value. IE uses .currentStyle and .runtimeStyle to renote the read/write properties for cascaded styles, while Firefox/Opera/etc uses the DOM standard of document.defaultView.getComputedStyle(). But we want something we can call that will just set the silly style regardless of browser and regardless of where the style is actually declared.

I wrote an open source framework for doing a fair amount of client-side manipulation that is browser independent. I call this Interactive Website Framework -- you can check it out at IWF on sourceforge. But for our needs, we really only need a single function (which I modified slightly to eliminate calls to other functions in the framework):

function iwfStyle(id, styleName, newStyle){
var el = document.getElementById(id);
if (!el) { return false; }
var ret = '';
if (typeof(newStyle) != 'undefined'){
// assigning new style
if (el.runtimeStyle){
ret = el.runtimeStyle[styleName] = newStyle;
} else {
ret = el.style[styleName] = newStyle;
}
} else if (el.currentStyle) {
// reading IE style
ret = el.currentStyle[styleName];
} else {
// reading DOM style
try {
var cs = document.defaultView.getComputedStyle(el,null);
ret = cs.getPropertyValue(styleName);
} catch(e) {
}
}
return ret;
}


Now if we change our showElement function to simply call iwfStyle, we're set:

<script type='text/javascript'>
function showElement(id)
iwfStyle(id, 'display', 'block');
}

</script>

So now the call will work regardless of browser or where the style is defined. Check out the full framework on sourceforge. It includes a client-side Xml parser, an Ajax abstraction layer, and some rudimentary gui enhancements, like animating elements, docking elements, drag-n-drop, etc.

HOWTO: Temporarily bypass connection limit - terminal server has exceeded max number of allowed connections

So you need to remote desktop into a Windows 2000/2003/XP/Vista box, but when you do you get the dreaded "terminal server has exceeded max number of allowed connections" error causing you to flail and try to contact all those you think *might* be connected. Which is a major hassle, especially when all you really need to do is touch one little thing and jump right out.

To see who's currently connected (from the command line of your local box):

C:\>qwinsta /server:[server name or ip here]
SESSIONNAME USERNAME ID STATE TYPE
console 0 Conn wdcon
rdp-tcp 65536 Listen rdpwd
jane.doe 2 Conn rdpwd
john.doe 3 Disc rdpwd


So you can see jane.doe and john.doe are both logged into the box. But john.doe is disconnected, so let's pick him to boot:


C:\>rwinsta /server:[server name or ip here] 3


Where the "3" is john.doe's ID. That should boot him off. If it doesn't, the alternative is to login to the console session. We can do this using a command line switch for the Remote Desktop client. Open a command prompt and run:


mstsc.exe /console


This tells Remote Desktop to try to connect to the console instance when you click Connect. This should allow you one more concurrent connection over the limit imposed by windows (usually 2). Happy Remote Desktopping!

HOWTO: Ubuntu Edgy Eft (6.10) + Ruby 1.8.5 + Gem 0.9.2 + Rails 1.2.2

There have been several excellent articles / blogs / etc explaining how to get the latest version (as of 2007-03-08) of ruby + gem + rails running on Ubuntu 6.10 Edge Eft, but no one resource met my needs to get me completely up and running.

So here's the various excellent resources I used to finally get ruby + gem + rails working on my ubuntu system:

Install ruby:
http://www.rubywizards.com/viewtopic.php?pid=19

Grab the gem source
http://rubyforge.org/frs/?group_id=126&release_id=9501

To Install gem:
tar -zxvf rubygems-0.9.2.tgz
cd rubygems-0.9.2
ruby setup.rb

If you get the zlib error, you probably need to get additional libs:
apt-get install zlibc zlib1g zlib1g-dev
(see http://www.nabble.com/Gems-zlib-problem-t992896.html for more info)

Re-compile and re-install ruby now that the new libs are there
http://www.rubywizards.com/viewtopic.php?pid=19

Make sure you're running the latest version:
ruby -v
ruby 1.8.5 (2006-08-25) [i686-linux]

if you still get the zlib error, copy the zlib over (this should be one line):
sudo cp /usr/lib/ruby/1.8/i486-linux
/zlib.so /usr/local/lib/ruby/site_ruby/1.8/i686-linux/

Great info about the zlib error in Ubuntu:
http://www.penlug.org/twiki/bin/view/Main/LinuxBookReviewsAgileWebDevelopmentwithRails

Then re-install gem:
ruby setup.rb

make sure gem works:
gem -v
0.9.2

Then grab rails:
gem install rails --include-dependencies --remote

make sure you're good to go:
rails -v
Rails 1.2.2


I hope this helps someone!