Building a C++ XPCOM Component in Windows

Jason was kind enough to translate this post to Belorussian.

I’ve been teaching myself to write Firefox extensions for the last few weeks, and became interested in XPCOM components. Unfortunately, I couldn’t find a good (and recent) summary of them, and had to spend 3 or 4 days cobbling together various tutorials, so I figured it’s time to write one.

What is XPCOM?

XPCOM is a language-agnostic communication platform used in Mozilla products (and some other random pieces of software) to allow code (specifically extensions) to be written in a wide variety of languages.

Why would I want to use XPCOM?

There are two ways to “use” XPCOM. First, you can call functions through XPCOM. For example, the Firefox bookmarks service uses an XPCOM interface. So in order to interact with this service from Javascript you would do something like:

var bmarks = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService();
bmarks.QueryInterface(Components.interfaces.nsIBookmarksService);
bmarks.addBookmarkImmediately("http://www.mozilla.org","Mozilla",0,null);

There are plenty of tutorials on doing this as it is the more common use for XPCOM, so I won’t go into any detail on it here.

The second way is to write an XPCOM service. That is what this tutorial covers. Sometimes you need extra functionality, speed, or just want to tie into some library that requires a different language. Most commonly this is C++, but there is also JavaXPCOM and PyXPCOM (and probably a few others). I’ll be talking about C++, since it’s what I needed for my project.

Warnings

  1. Before you trudge through this: you most likely are in the wrong place. Firefox extensions are usually all Javascript. If you can use JS to do what you want, stop now. There is no need to go through the complexity of an XPCOM component when you can just use JS. Go read a tutorial about writing extensions and get to work.
  2. There is something called ctypes coming to FF 3.7 that may make doing this a lot easier. I haven’t touched this at all, but it may be worth considering if you can wait for the functionality and only need to tie into a particular DLL for some functionality. Long story short, XPCOM may become the more difficult way to call C++ functions from FF extensions.

My Setup

  • Windows 7
  • Visual C++ Express 2008 (free from Microsoft’s website)
  • Firefox 3.6 (Gecko 1.9.2)
  • A UUID or GUID generator. This is a unique (read: random) ID that identifies your app to the world. Windows and Linux have tools to generate this (guidgen & uuidgen, respectively), or you can find various online generators (Mozilla links to several). I recommend this one since it gives you the C++ encoded form too, which you will need. You need two different UUIDs: one for your interface and one for your component.
  • Ability to read and understand C++

Sample Code

If you don’t want to go through the tutorial and just want everything to work, then download this sample code. Just follow step #1 of the tutorial, then make sure your Gecko SDK directory is set right in the build step, and you can breeze on by most of this article.

The Tutorial

This is mostly paraphrased from Alex Sirota’s great tutorial, but it hasn’t been updated since 2005 and is a bit outdated. This new one should work out of the box for FF 3.6.

This tutorial will create a component called MyComponent with one function: Add, which will take 2 numbers and, surprisingly, return the sum.

  1. Download the Gecko SDK for your version of Firefox. I used 1.9.2 for FF 3.6.
  2. Create an idl file – IMyComponent.idl, with the following (replacing ***IUID*** with your interface UUID):
    #include "nsISupports.idl"
    
    [scriptable, uuid(***IUID***)]
    interface IMyComponent : nsISupports
    {
      long Add(in long a, in long b);
    };
    

    This file is a language-agnostic interface definition which you can read more about here.

  3. Generate the interface header and typelib files out of the interface definition file. Assuming you extracted the Gecko SDK to C:\xulrunner-sdk\, run the following commands (from the directory you saved IMyComponent.idl to):
    C:\xulrunner-sdk\sdk\bin\xpidl.exe -m header -I C:\xulrunner-sdk\idl .\IMyComponent.idl
    C:\xulrunner-sdk\sdk\bin\xpidl.exe -m typelib -I C:\xulrunner-sdk\idl .\IMyComponent.idl
    

    These will create IMyComponent.h and IMyComponent.xpt, respectively.

  4. IMyComponent.h has two snippits of code that you can use for the next two files. Everything between /* Header file */ and /* Implementation file */ can be used for MyComponent.h:
    1. Start by inserting double inclusion protection code and the right include:
      #ifndef _MY_COMPONENT_H_
      #define _MY_COMPONENT_H_
      
      #include "IMyComponent.h"
    2. Add the following lines, which define your component name, contract ID, and CUID (where ***CUID*** is the C++-style component UUID, of the form { 0×12345678, 0x9abc, 0xdef0, { 0×12, 0×34, 0×56, 0×78, 0x9a, 0xbc, 0xde, 0xf0 } }):
      #define MY_COMPONENT_CONTRACTID "@example.com/XPCOMSample/MyComponent;1"
      #define MY_COMPONENT_CLASSNAME "A Simple XPCOM Sample"
      #define MY_COMPONENT_CID ***CUID***
    3. Copy in the snippet from IMyComponent.h, replacing all the instances of _MYCLASS_ with the name of your component (MyComponent).
    4. Finish off the double inclusion protection code with #endif //_MY_COMPONENT_H_
  5. Everything between /* Implementation file */ and /* End of implementation class template. */ can be used for MyComponent.cpp:
    1. Start by inserting the right include:
      #include "MyComponent.h"
    2. Copy in the snippet from IMyComponent.h, replacing all the instances of _MYCLASS_ with the name of your component (MyComponent).
    3. Add some implementation to the Add method. I replaced return NS_ERROR_NOT_IMPLEMENTED; with
      	*_retval = a + b;
      return NS_OK;
  6. Create your module definitions files:
    #include "nsIGenericFactory.h"
    #include "MyComponent.h"
    
    NS_GENERIC_FACTORY_CONSTRUCTOR(MyComponent)
    
    static nsModuleComponentInfo components[] =
    {
        {
           MY_COMPONENT_CLASSNAME, 
           MY_COMPONENT_CID,
           MY_COMPONENT_CONTRACTID,
           MyComponentConstructor,
        }
    };
    
    NS_IMPL_NSGETMODULE("MyComponentsModule", components) 

You now have all of the files needed to build an XPCOM component:

IMyComponent.h
IMyComponent.idl
IMyComponent.xpt
MyComponent.cpp
MyComponent.h
MyComponentModule.cpp

Now comes the hard part: getting the damn thing to build.

Building the code

Ok, it’s actually not hard since I’ve done most of the legwork for you. Assuming you’re using Visual C++ 2008 here are the settings you need (again assuming C:\xulrunner-sdk is where your Gecko SDK is). In Project->Properties:

Configuration Properties
  General
    Configuration Type: .dll
  C/C++
    General
      Additional Include Directories: C:\xulrunner-sdk\include
    Preprocessor
      Preprocessor Definitions: XP_WIN;XP_WIN32;XPCOM_GLUE_USE_NSPR
  Linker
    General
      Additional Library Directories: C:\xulrunner-sdk\lib
    Input
      Additional Dependencies: nspr4.lib xpcom.lib xpcomglue_s.lib

If you put the idl file into your project, be sure to mark it “Excluded from Build” in its properties…we don’t want VS touching it.

Cross your fingers, pray to whatever deity you believe in, and hit the build button. If it didn’t work let me know why in the comments and I’ll try to build a troubleshooting section.

Installing/Testing the Code

  1. Copy two files to C:\Program Files\Mozilla Firefox\components:
    • The DLL the build generated
    • IMyComponent.xpt
  2. Normally, if this was installed as part of an extension, it would automatically search this directory and find these files. But now we have to force a refresh. Delete xpti.dat. and compreg.dat from your profile directory (FF will regenerate them on next restart)
  3. Close Firefox and open it with this test file (MyComponentTest.html in the sample code):
    <html>
    <script type="text/javascript">
    function MyComponentTestGo() {
    	try {
    		// normally Firefox extensions implicitly have XPCOM privileges, but since this is a file we have to request it.
    		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    		const cid = "@example.com/XPCOMSample/MyComponent;1";
    		obj = Components.classes[cid].createInstance();
    		// bind the instance we just created to our interface
    		obj = obj.QueryInterface(Components.interfaces.IMyComponent);
    	} catch (err) {
    		alert(err);
    		return;
    	}
    	var res = obj.Add(3, 4);
    	alert('Performing 3+4. Returned ' + res + '.');
    }
    </script>
    <body>
    <button onclick="MyComponentTestGo();">Go</button>
    </body>
    </html>
  4. One last time: cross your fingers, pray to whatever deity you believe in, and hit the Go button. If it didn’t work let me know why in the comments and I’ll try to build a troubleshooting section.

Hopefully this clears up what looked like a lot of confusion to me. I will keep this updated to the best of my abilities and hopefully it will continue to be useful for a long time.

Job Hunt Post Mortem

I’ve been meaning to write about this for a while, but I never really got around to it until just now. I wanted to share some experiences from my big job hunt.  It was definitely a fun experience: during last November alone I was on over 20 different airplanes.  I had at least preliminary interviews with over a dozen companies, and ended up with a really tough decision between two amazing options.  But more on that later. There are many different types of companies that I ran into in my interview process, and here are just a few experiences from them.  Note that whatever I say about these companies, I have the utmost of respect for them all.  They all treated me very well, and I had a great time interviewing with them (and I’m not just saying that on the off chance that they read this).

Job Fairs / Standing Out

The only place I looked for a job was Carnegie Mellon’s job fair.  It’s a really amazing experience: over 200 companies show up, most in the technology realm.  Thousands of students run around in suits trying to convince companies that they’re special: different than all of the other students running around in suits.  I had the opportunity to help a big company I was interning at a few years ago with manning their booth.  I collected resumes for a few hours, and honestly it was incredibly boring.  Most students slipped me their one page resume, gave me the same spiel about the courses they’re taking and the random programming job they did, ask me what I do, and wandered off.  Very few people stood out.  The ones who left a lasting impression on me had one or more of the following traits:

  • A really interesting story to tell about a problem they solved. I mean like get-a-rocket-into-space interesting.  No, not how you figured out how to multiply matrices on CUDA architecture, I want to hear something even a fifth-grader would say “awesome!” in response to
  • A unique background. I was more interested in a freshman who had been consulting for 3 years than a senior who had done the same.  Or even just a freshman who had the guts to show up at a job fair after only being in school for a month.  That’s exactly what I did, and I got tons of raised eyebrows and kudos (and an internship).
  • Personality! One guy was so incredibly energetic and excited to meet me that I couldn’t forget him.  Note that I really don’t recommend trying this unless you have the charisma to back it up, because faking that is incredibly difficult to get away with.  For a more toned down example: I ran into a company this last job fair that I really wasn’t looking to talk to (we had one of those you-made-eye-contact-now-you-have-to-talk-to-me moments).  We started talking and they admitted that this was their first job fair, as they used to not target recent grads so heavily.  My response was “Job fairs suck, don’t they?” and launched into my stories from working one.  The discussion had nothing to do with getting a job, but the friendly banter ensured they wouldn’t forget me.  My recruiter instantly remembered me by first name when I called her a month later.

Other advice for job fairs:

  • Learn to love people. One of my greatest assets at job fairs is that I genuinely love meeting new people.  Each booth I stop by is not only a chance to hear about a potentially interesting product (and get free swag), but also to meet someone and find out what they do.  It’s great practice for real life too: if you make an ass of yourself to a recruiter, odds are you’ll never see them again, whereas doing the same for a coworker or classmate would have longer-lasting implications.
  • Talk to as many people as possible. I’d much rather talk to 10 companies I’ve never heard of than wait an hour on line to talk to someone from Google (they require everyone to apply online anyway).  I had literally never heard of the company I ended up accepting an offer from.  Also, you can always decline interviews later on, but this is your best shot to initiate contact with these companies.
  • Relax. No one likes talking to someone who’s nervous.  It’s awkward, and shaking a sweaty hand is gross.  I would say for those who are always nervous that they should take a shot or have a beer (just one!) before hitting the job fair, but I’ve never tried this personally and can’t be held liable for the consequences.
Bad Interviewers

I had my fair share of botched interviews, but about halfway into my job hunt I realized that it wasn’t always my fault.  Sure, sometimes you walk into the room and instantly become stupid, and sometimes you’re just in the wrong place at the wrong time, either literally (I slept through an interview once) or figuratively (I once commented that I hate SML, only to immediately hear from my interviewer that “SML is my favorite language”). But other times the interviewers themselves suck, and there’s nothing you can do about it.  I’ve had awkward interviewers who didn’t seem to get how the process was supposed to work: they gave no feedback.  This wouldn’t be a problem for easy questions that everyone should be expected to answer, but some of their questions were the type that people aren’t supposed to know the answer to.  The goal is to start a discussion and evaluate process, eventually working your way to an answer.  I was told in a phone interview to “think about it and email me”, which is usually code for “I want to stop wasting my time with this call”.  I’m not going to eliminate the possibility that I just suck, but everyone I spoke to about the problem agreed that I couldn’t be expected to solve it without prodding.  Some people just don’t know how to run interviews, it’s the luck of the draw.

How much I cared in my classes

GPAs

I’ve never cared much about my GPA.  I would rather have an interesting consulting project than focus more on my grades hands down.  My mentality is that if a company values a high GPA over actual experience, I have no desire to work for them.  It turned out this was the right call, since only two companies that I was interested in asked for my GPA.  One still asked me for an interview, and the other only asked on their web form they made everyone fill out.  The other 20+ companies I handed resumes to didn’t even mention grades.

Warning: I am not advocating ignoring your classes.  Everyone has an average grade they would get if they put in their “I feel obligated to do at least this much” level of effort.  I was comfortable with what mine was, so I didn’t feel the need to do more.  Yours may not be in the same place: I recommend evaluating this carefully before making a decision on level of effort in classes.  Also, Carnegie Mellon has a reputation behind it that I’ve been told companies value more than GPAs.  If you go to a different school, your mileage may vary.

Wine-and-Dine Companies

These companies pull out all the stops for you, either to try and buy your love or just to give the impression that they’re doing really well.  One company flew me out twice, and both times sent a town car for me and put me in a $500/night hotel.  Then they took me out to really nice restaurants on interview day, including some really good (and I’m sure expensive) sake.  Granted, I think this particular company operated like this on a day-to-day basis, and all companies do it to an extent (one rented out an upscale lounge and had an invite-only event with an open bar for potential candidates), but my point is don’t let it affect you.  I let myself get sucked into the magic, and it clouded my ability to properly evaluate the company. It also made me more nervous during interviews.  Some companies provide lots of perks, some spend tons of money on little things, and some don’t.  While it’s important to evaluate these things when considering a company, be sure it’s weighted rationally.

Offer Negotiation

No, I’m not going to talk about salary negotiations, you’re on your own for that.  One thing that was important to me when deciding on an offer was my non-compete.  I’ve put a lot of time into building my relationships with my clients, and I don’t want to have to destroy that for a full-time job.  I also get bored easily, and like being able to work on projects without having to worry about whether or not I can open source them.  Also, if I get bored enough I could always tend bar on weekends, and I want to feel free to do that (surprisingly enough most of the non-competes I saw from outside of California barred me from doing that).  Having a paralegal for a mother means that I tend to assume the worst when it comes to legal stuff: I hear too many horror stories to think otherwise.  That’s why when I saw a non-compete that prevented me from doing any of the above items, I knew I couldn’t accept the offer as-is.  I was pretty upset, since that company was one of my top choices.

About a day after I saw the non-compete and knew it was a deal killer, I got a call from the CEO of the company (a really kind gesture that I didn’t expect) asking if I had any questions, so I mentioned this to him.  He told me that of course the company wasn’t looking to stop me from doing reasonable things, only not run to their competitors.  I mentioned that I had a family member who was in law, and he said that I was more than welcome to have her change it.  A little legal back-and-forth later and we had a new non-compete that everyone was happy with.

The moral of the story?  Negotiate!  If a company makes you an offer and you’re not happy with a part of it, whether it be paperwork, benefits, or plain old salary, say something.  Keep in mind that the company has decided they want you: at this point you’re both on the same level in the negotiation, since they need you as much as you need them (give or take).  The worst that could happen is they say “no”, in which case you’re no worse off than you were before.

My Story / Final Advice

So having learned all of this, I finally reached the end of my job hunt.  I had narrowed things down to two offers, and for the life of me I couldn’t decide between them. The salaries were comparable, both had great projects to work on, and both teams were awesome.  So I made a list. And I stayed up at night thinking. And I asked everyone I saw “Silicon Valley or Boston”, with mixes responses. In the end, it basically came down to two things. First, I had been in Silicon Valley for the last two summers: I knew it very well, and I considered that a bad thing. I wanted to try something different. I knew that I could always go to the valley and I would be comfortable there, but I worried that once I went there I would be too comfortable and settled to leave and regret it. At least if I went to Boston I would know enough about both places to decide which one I’d prefer. Second, Boston was closer to home. The though of being able to be home for Rosh Hashanah, something I haven’t done in 3 years, was comforting. There’s a lot to be said for being 4 hours away from family instead of 24.

So there you have it: in the end my decision came down to location, having nothing to do with the actual companies (with a touch of irony in that being in the valley drove me away from it, even though I loved, and still love, the area). It’s with that that I am thrilled to announce I will be joining TripAdvisor’s New Initiatives Team in May. My other option was Mozilla’s Webdev team, which I also would have been thrilled to join (having interned there, I can safely say that it is a wonderful place to work with an amazing group of people).

What final advice do I give? Try something new. I’ve never lived in Boston before, and while it’s scary to think of going to a new place when I could have had something I already know, I take comfort in knowing that I’m putting myself out there and trying different things. Challenge yourself to break out of whatever mold you’re in: challenge your assumptions and decisions every step of the way.

Also, if you’re looking for a job I’d highly recommend checking out TripAdvisor or Mozilla, they both have great people, and I feel privileged that they both felt highly enough of me to make me offers.

Download Day is Awesome

<rant>

Today I ran into an interesting response to Paul‘s post on those who dislike the awesome bar (which I decided not to link to).  The post pulls the following quote from the end of Paul’s well thought-out and harmless article:

So just give it a shot and quit complaining. Yes, it is a complete paradigm shift. But it’s not called the Awesome Bar for nothing; it really is awesome once you give it a chance

And proceeded to call Paul an “asshat” and a “discredit to the Mozilla community”.  Really?  One sentence saying “give it a chance” and all of a sudden Paul’s an asshat?

Normally I would ignore this and chalk it up to people seeking out drama for the sake of snagging pageviews, but it made me realize that most media does this.  The Firefox 3 launch was absolutely amazing: we had 8 million people download our software in 24 hours, 12 million in 48.  It’s unreal!  Yet I’ve seen dozens of posts and articles talking about a minor outage and “howls of derision across the blogosphere”.  Are people that desperate for a negative angle to a story?  I’ve yet to hear one negative comment from people in person (granted, I see a lot of Mozilla employees, but I still see/talk to other non-Moz folk).  The execution had a few hiccups, but the overall project went amazingly well.

This is why I like sites like Hacker News.  The community discourages blowing things out of proportion.  For example, I recently commented on a post entitled Firefox 3 smart bar is just too smart, and it led to an intelligent discussion about the merits of the bar.  My post said basically the same thing as Paul’s, but there’s less hostility towards differing opinions, and it results in the ability to actually discuss things instead of pointless name-calling.

So the next time you’re writing a blog post focusing on a tiny negative aspect of a project, think about the big picture, and if it really matters, or is just a rant to spread negative views that only benefit your pageview count.

</rant>

Life Update

Inevitably over each summer I develop a strong desire to blog, and conveniently enough I have this blog to do so. So as a windup to this blogging spree, I figured I should give an update to my life:

  • I’m interning at Mozilla this summer, specifically the WebDev team. They seem like an great bunch, and I’m really looking forward to it. Obviously this means I’ll be in Silicon Valley again.
  • I’ll be at the 2008 Firefox Summit, and it’s going to be awesome!
  • I’m officially a senior, which means that I’ll begin my big full-time job hunt during/after this summer
  • For various reasons, I’ll shortly be moving off of SoftLayer to VPSLink for hosting. I love SoftLayer and would never consider anyone else for dedicated hosting, but since it’s now just me and my projects I can’t justify the cost. VPSLink was my first non-shared webhost (I was one of their first customers, they used my testimonial for a few years on their homepage). Plus they have a half-off deal if you prepay 2 years, which makes them even better of a bargain than they usually are (which is saying a lot).

So, this means a few things for this blog (and the 4 and a half readers):

  • More frequent posts!
    • More startupy posts!
    • More Brian-y posts!
    • More Mozilla-y posts!
  • I’m considering moving the blog back to nerdlife.net or over to briankrausz.com (haven’t decided yet but don’t worry, all the current links will redirect). The idea behind using startupy.net was to make a theme for the blog, and I decided that this limits me too much, and I’d rather call this a personal blog and post businessy things than vice-versa.

So stay tuned, I’m out in California Saturday, and start at Mozilla Tuesday. I also have my VPS allocated, so I’ll be swapping things over within the next week or 2 (depending on whether or not I have to wait for internet in my apartment).