Two extension approaches (add-in, packages), two APIs (automation model, services)… and four combinations:
It happens that add-ins not only can use its more natural API (the EnvDTE automation model), but can also use services provided by the Visual Studio shell and packages. The EnvDTE automation model doesn’t provide a direct way to get a service but you can do it as I explained long time ago in this article:
HOWTO: Get a Visual Studio service from an add-in
And packages not only can use its more natural API (services), but can also use the EnvDTE automation model. In fact, the EnvDTE*.* reference dlls are added by default to VS Package projects!. Getting the DTE instance from a package is not difficult except for a little detail: if the package is marked to load on startup (autoload), rather than using delayed loading, it can happen that the IDE is not initialized yet and the package can’t get the DTE instance. This IDE initialization problem happens also with add-ins, which have an OnStartupComplete method and therefore there is pattern to initialize correctly an add-in. Alas, packages need to use a more complicated pattern to get notified when the IDE is fully initialized, with a trick that Microsoft explained originally in this post:
Dr. eX: Why does GetService(typeof(EnvDTE.DTE)) return null?
Since I found the code of the package class somewhat dirtied with that approach, I have rewritten it with a more clean approach (using a separate class for the task):
HOWTO: Get an EnvDTE.DTE instance from a Visual Studio package