My day job is a software consultant. In the past I have built a career around the client side software development under Microsoft Windows. I have worked on games, internet software, client/server code, you name it I have probably have tried a little of that at some point in my long career. I specialize now in Browser plugins and IOS development for the iPhone and iPad devices. I guess I could do Android development as well, but my Java skills are not current.
This page is meant to discuss all things software related in the areas where I am focused at the moment.
This post is about how to take the npruntime code sample that I have blogged about and install it as a chrome extension.
In this series of articles I have provided the code to build a simple NPAPI plugin. The next steps are to register the plugin with Firefox and Google chrome so we can test the code.
Firefox install
To install a plugin on Firefox under windows you need to use the registry. That is the most reliable method to install the plugin.
The registry keys used to install the plugin are located in the HKey Local Machine (HKLM) registry hive, so administrative permissions will be required to install the plugin.
Windows Registry Editor Version 5.00
; for 32 bit installations
[HKEY_LOCAL_MACHINE\SOFTWARE\MozillaPlugins\@conedogers.wordpress.com/npruntime,version=1.0.0.3]
"Path"="npruntime.dll"
"ProductName"="npruntime"
"Vendor"="conedogers.wordpress.com"
"Version"="1.0.0.3"
; for 64 bit OS installation
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MozillaPlugins\@conedogers.wordpress.com/npruntime,version=1.0.0.3]
"Path"="npruntime.dll"
"ProductName"="npruntime"
"Vendor"="conedogers.wordpress.com"
"Version"="1.0.0.3"
The path should be the absolute path to the npruntime dll.
Google Chrome installation
Installation on chrome is almost as easy. Google has added a few steps to package the NPAPI control as an extension. This extension can either display the plugin publicly, which could expose the client browser to security threats, or you can make the plugin private which only allows your script to access the plugin.
To begin, we need to create a manifest file manifest.json in a sub-folder that is named for the extension. For this sample, there is a sub-folder installers\Chrome. Inside this sub-folder we have a folder named npruntime. Inside this folder we place all the files needed for the chrome extension.
The first step of installing the chrome extension is to create the CRX package. To do this you need to navigate to the Chrome://extensions page and select the developer mode by selecting the checkbox in the upper right hand corner. Once you have enabled developer mode, select the pack extension button.
Select the folder where the we have the nanifest.json file.
For this sample, navigate to the sub-folder npruntime inside installers\chrome.
Now we package the control and create a crx package that we can use to deploy to chrome. The second parameter is a path to your private key file used to provide a certified package. For this sample, leave it blank
When we succeed with the packaging, we will have the npruntime.crx and npruntime.pem file. DO NOT lose the pem file! this is a key assigned by chrome that is a key assigned to the control.
Retrieving the Extension ID
Now that you have the packaged extension you need to retrieve the extension ID. To do this, you open the Chrome://extensions page and in the developer section select the “Load unpacked extension…” button. You will be prompted to select the control to load, so navigate to the installers\chrome folder and select the npruntime folder to load it into chrome.
After you load the unpacked extension, you will see a screen similar to this. Highlighted is the extension ID assigned to this control.
Now that you have the extension ID, you can complete the registration of the packaged NPAPI control by adding a pair of registry keys to the windows registry.
Windows Registry Editor Version 5.00
; 32 bit operating systems.
[HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\Extensions\ooaihagiflmbfpeiggpknanhplcngmld]
"Path"="C:\\dev\\mozilla\\npruntime\\installers\\chrome\\npruntime\\npruntime.dll"
"Version"="1.0.0.3"
; 64 bit operating systems
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Google\Chrome\Extensions\ooaihagiflmbfpeiggpknanhplcngmld]
"Path"="C:\\dev\\mozilla\\npruntime\\installers\\chrome\\npruntime\\npruntime.dll"
"Version"="1.0.0.3"
And that should do it. You should be able to view the chrome NPAPI extension in the test.html file provided with the sample.
Source code for this project is available on github: https://github.com/chrisDwarner/npruntimeSampleCode
I recently had to refer to that project for a new piece of work that I am researching. the problem is the code sample uploaded to github was the original sample, and if you have downloaded it, the project won’t build without some work.
The project on github.com has been updated to contain a building project.
Fixed the Visual studio project settings
Fixed the obsolete datatypes.
Fixed the obsolete headers.
The source code on github has been updated with the latest changes.
From the build steps listed in the article from Mozilla,
Create a new project in Visual Studio. Make sure that you create a generic Win32 Project.
In the application setting, select a DLL project and make sure to check the “empty Project” check-box.
Steps 4& 5 – If you have created an empty project, you will not have to delete any files. Add the npruntime source files that you download from Mozilla.
steps 6 & 7 – You will need the xulrunner SDK to build the project. After downloading and installing the SDK you will need to point the include paths to build the NPAPI plugin.
step 8 you will need to add the correct preprocessor defines to build properly.
step 9 – if you have created an empty project, the pre-compiler headers should be disabled.
An omitted step is the addition of the library paths for the linker to find the xulrunner-sdk libraries.
step 10 – add the module definition file to the project. This is important to export the proper NPAPI functions needed to load the plugin in the browser.
that should be enough to build the control at this point. From here you would want to continue with the Mozilla link to configure the plugin MIME type string so you can define your own data type for the control to use.
If you try to build the project at this point, it will fail with errors. As the Mozilla and Gecko SDK’s have evolved, this sample has not been kept up to date which is disappointing.
To fix the problems you will need to do the following:
The control should be compiled with UNICODE enabled. Otherwise older browsers such as Firefox will not properly load the plugin if the absolute path to the plugin contains spaces.
Visual Studio does not properly handle the C99 standards about the int32_t and other C99 types. You will have to manually update the code to get around this issue. Change integer types (int16, int32, uint16, uint32) to use the new modern prototypes (int16_t, int32_t, uint16_t, and uint32_t). As Gecko evolved, the data types changed. The sample code was never updated and won’t compile without these changes.
change headers. The old header “npupp.h” has been replaced by “npfunctions.h”. Mozilla – Bug 455458 covers the change to these headers. This was done back in 2008 with Gecko 2.0 (Firefox 4).
fix property setter method. There is a bug with the sample code when you process a property and set a value.
debugging with Firefox: attach the debugger to the app “plugin_container.exe” and this will let you debug your code within visual studio. Be careful! if you take too long, Firefox will think the plugin crashed and kill the app and end your session.
Debugging with Chrome: This is a lot like debugging with IE. Chrome launches a new Chrome process to contain the plugin, so you need to figure out which process belongs to the plugin and attach the debugger to that process. Typically this is the Chrome process with the highest process ID as reported by Visual Studio.
If you get to this point, you should have a Visual studio project that you can build.
I recently got started on a project that had a NPAPI/XPCOM component that implements a custom control to be used in a web page to demonstrate the companies technology. I rewrote the control to get rid of the XPCOM api which opened up the compatibility of the control to include Google chrome and other NPAPI supported browsers. In rewriting the control I wanted to start from a clean foundation for the new pure NPAPI plugin. I decided to write this blog entry because it is really hard to find a NPAPI code sample that is up to date, and complete enough so you can build it and continue the job at hand. I found an old code sample from Mozilla, npruntime. I choose this sample because it was the most similar in structure to the original plugin that I was rewriting.
I found this blog entry that covers what you need to do, but no complete source code sample is provided that provides a complete sample that you can compile and run. Despite that deficiency the blog is really quite good at explaining the basic concepts behind the NPAPI run time and I highly recommend that you read it. building-a-Firefox-plugin. Once you understand the concepts behind NPAPI and the requirements for the plugin, we can get started building the control.
setup your build environment. This blog entry used Visual Studio 2008 for the build environment. You can learn about setting up that environment here: Visual Studio project settings for NPAPI projects
the NPRuntime sample
np_entry.cpp
A NPAPI plugin exposes 3 functions from the component that used by the browser to load the plugin.
NP_Initialize() – When a plugin is loaded by the browser, NP_Initialize() is called by the browser. A pointer to an array of function pointers is passed to the plugin. This array of function pointers contain all of the functions that your plugin can call to interact with the browser and the loaded webpage.
NP_GetEntryPoints() – Like NP_Initialize(), this function initializes a table of function pointers that the browser uses to call into your plugin. These functions are used to identify the exposed properties and methods used by the script in the loaded page.
NP_Shutdown() – This function is called when a browser unloads a plugin. This is your last chance to clean up your resources before the plugin is shutdown.
npn_gate.cpp
This source file implements the plugins implementation of the NPAPI entrypoints exposed from the browser. This file contains helper functions that wrap the function pointers passed in by the browser for your use. There is no hard requirement to use these functions, but calling these methods is recommended becasue they do some error checking and parameter validation that helps to build robust plugin code with out relying on exception processing.
npp_gate.cpp
This source file contains the implmentation of the NPAPI entrypoints exposed by the plugin. NPAPI defines a set of functions that each plugin needs to implment. This is the main entry point to the scriptable plugin. From this point in the source code you create you main plugin object that in turn manages the scriptable object passed back to the calling browser. The Npruntime sample code leaves many of these functions empty with no implementation. Enough code has been implemented within this source file to manage the main plugin object along with the creation of objects, windows and event handlers.
plugin.cpp/h
The Plugin.* source files contain the class definitions for the plugin object at the native OS layer through the scriptable object code that is exposed within the DOM. These sources contain several classes used to construct the scriptable JavaScript object that you use within your script.
NPIdentifier’s description and purpose
A NPIdentifier is a unique ID assigned by the browser and should be unique for the life of the plugin instance. These identifiers are used for everything that is represented in the DOM. In otherwords, you can request specific DOM elements by type or “id” attribute. NPIdentifiers are also used to declare methods and properties exposed by the scriptable object. NPIdentifiers can also be used to access the DOM by requesting an ID for a known HTML tag or by using the id or name attribute of the HTML tag you want to examine. The lifetime of these identifiers is open to debate and you should expect to encounter different lifetime behavior on different browsers.
CPlugin
The CPlugin class is the parent class in the NPAPI plugin. The first step in creating a new plugin instance is to create a new instance of the CPlugin class. this class instance manages the scriptable interface between the browser and the natvie code implmented within the plugin. This class also contains any native platform support for the plugin.
class CPlugin
{
private:
// this is the NPAPI instance of the plugin loaded in the browser
NPP m_pNPInstance;
#ifdef XP_WIN
HWND m_hWnd;
#endif
// the main window for the browser
NPWindow * m_Window;
// a stream object for sending data streams to and from scipt running in the browser.
NPStream * m_pNPStream;
NPBool m_bInitialized;
// a pointer to the scriptable object exposed via NPAPI to the browser.
NPObject *m_pScriptableObject;
public:
char m_String[128];
public:
CPlugin(NPP pNPInstance);
~CPlugin();
NPBool init(NPWindow* pNPWindow);
void shut();
NPBool isInitialized();
int16 handleEvent(void* event);
void showVersion();
void clear();
void getVersion(char* *aVersion);
NPObject *GetScriptableObject();
};
ScriptablePluginObjectBase
The ScriptablePluginObjectBase class acts as the proxy between your plugin implementation and the calling browser. The class has static and non-static methods. The scriptable classes derived off this class implement the virtual functions while the static functions call into the scriptable plugin class passed into the method by the caller.
The npruntime sample provides several samples embedded within the source code. One part of the sample involves returning an object as a property of the plugin. The object returned to the script has no implementation, so you can’t do much with it without playing around with the code. To get this returned object functional within JavaScript, you need to implement the rest of the ScriptablePlubinObjectBase interface to define properties and methods for the object as we do with the ScriptablePluginObject class.
The scriptable plugin object class contains the implementation of the NPAPI plugin. A NPAPI plugin can contain tain properties and methods, each identified by a NPIdentifier assigned by the browser. Each time a piece of script running in the browser accesses a property or method of a plugin, the browser will query the plugin to see if it has this property or method. If the plugin contains the method, Invoke() is called. If the plugin contains the property, GetProperty() or SetProperty() is called depending on the operation in the script. If the scriptable object represents a method, such as a callback or constructor call, then InvokeDefault() is called.
The implementation of your plugin will be contained with the ScriptablePluginObject or a similar class. Within this class we use NPIdentifiers to retrieve unique IDs for each of the tag elements we want to access in the DOM.
// Declare NPIdentifiers to be used as id tags for the elements we need to access.
static NPIdentifier sFoo_id; // the sample method
static NPIdentifier sBar_id; // a simple property
static NPIdentifier sPluginType_id; // an object property.
static NPIdentifier sDocument_id; // the id for the DOM object
static NPIdentifier sBody_id; // the id of the tag
static NPIdentifier sCreateElement_id; // An id used to create the new div tag elements.
static NPIdentifier sCreateTextNode_id; // The id for the text in the new tag
static NPIdentifier sAppendChild_id; // the id to identify the insert location of the new div tag.
static NPObject *sWindowObj; // the main browser window object.
ScriptablePluginObject::ScriptablePluginObject(NPP npp) : ScriptablePluginObjectBase(npp)
{
// Get the instance of the browser window object
NPN_GetValue(npp, NPNVWindowNPObject, &sWindowObj);
// initialize a temporary npid.
NPIdentifier n = NPN_GetStringIdentifier("foof");
// setup the properties for the plugin. These id's are unique to the plugin and assigned
// by the browser. npids can be eiter properties or methods.
sFoo_id = NPN_GetStringIdentifier("foo");
sBar_id = NPN_GetStringIdentifier("bar");
// define some npids for HTML elements that we commonly use.
sDocument_id = NPN_GetStringIdentifier("document");
sBody_id = NPN_GetStringIdentifier("body");
// these are the element npid used to create new elements to be appended to the dom
sCreateElement_id = NPN_GetStringIdentifier("createElement");
sCreateTextNode_id = NPN_GetStringIdentifier("createTextNode");
// used to append to the loaded dom
sAppendChild_id = NPN_GetStringIdentifier("appendChild");
// required: used in the construction of the plugin
sPluginType_id = NPN_GetStringIdentifier("PluginType");
}
Each time a plugin is accessed by the browser, HasMethod() and HasProperty() are invoked to determine if the method or property are supported by the plugin. Within your plugin scriptable code you need to implement each of these methods in order for scriptable access to your plugin. Using the NPIdentifiers you created earlier, you compare the NPIdentifer sent into your assigned identifers. If there is a match, you return true, otherwise return false to indicate to the browser that you do not support the identifier sent into the methods.
bool ScriptablePluginObject::HasMethod(NPIdentifier name)
{
return name == sFoo_id;
}
bool ScriptablePluginObject::HasProperty(NPIdentifier name)
{
return (name == sBar_id || name == sPluginType_id);
}
bool ScriptablePluginObject::GetProperty(NPIdentifier name, NPVariant *result)
{
VOID_TO_NPVARIANT(*result);
if (name == sBar_id)
{
static int a = 17;
INT32_TO_NPVARIANT(a, *result);
a += 5;
return true;
}
if (name == sPluginType_id) {
NPObject *myobj = NPN_CreateObject(mNpp, GET_NPOBJECT_CLASS(ConstructablePluginObject));
if (!myobj) {
return false;
}
OBJECT_TO_NPVARIANT(myobj, *result);
return true;
}
return true;
}
The only part that is notable about GetProperty() and SetProperty() in this sample is how you implement a read only variable. By omitting the sPluginType_id identifier, we can control the access rights to the property. In this case, we setup the sPluginType_id as a read only variable.
If the browser calls a method on your scriptable object, then internally the browser invokes the function through a call to Invoke(). When calling a methods it will be nessesary to extract all the command line arguments into their proper data types. Unlike XPCOM, the programmer has no control over how arguments are converted and passed to the method. This forces the developer to query to the type of argument wrapped in the NPVariant before extracting the real values.
bool ScriptablePluginObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
if (name == sFoo_id) {
printf ("foo called!\n");
// get the main document from the window object.
NPVariant docv;
NPN_GetProperty(mNpp, sWindowObj, sDocument_id, &docv);
// convert the variant into a NPObject
NPObject *doc = NPVARIANT_TO_OBJECT(docv);
// insert a DIV tag into the document
NPVariant strv;
// create the element.
NPUTF8 *putf8Str = (NPUTF8 *)NPN_MemAlloc(strlen("div")+1);
strncpy(putf8Str, "div", strlen("div"));
STRINGZ_TO_NPVARIANT(putf8Str, strv);
// Create the div tag object that will be inserted into the DOM.
NPVariant divv;
NPN_Invoke(mNpp, doc, sCreateElement_id, &strv, 1, &divv);
// now put some text into the div tag
// first create the string.
putf8Str = (NPUTF8 *)NPN_MemAlloc(strlen("I'm created by a plugin!")+1);
strncpy(putf8Str, "I'm created by a plugin!", strlen("I'm created by a plugin!"));
STRINGZ_TO_NPVARIANT(putf8Str, strv);
// now insert it into the DIV tag we just added.
NPVariant textv;
NPN_Invoke(mNpp, doc, sCreateTextNode_id, &strv, 1, &textv);
NPVariant v;
NPN_Invoke(mNpp, NPVARIANT_TO_OBJECT(divv), sAppendChild_id, &textv, 1, &v);
NPN_ReleaseVariantValue(&v);
NPN_ReleaseVariantValue(&textv);
// now insert the DIV tag into the body of the loaded HTML document.
NPVariant bodyv;
// get the body element as a NPObject
NPN_GetProperty(mNpp, doc, sBody_id, &bodyv);
// Append the new div tag into the DOM
NPN_Invoke(mNpp, NPVARIANT_TO_OBJECT(bodyv), sAppendChild_id, &divv, 1, &v);
NPN_ReleaseVariantValue(&v);
NPN_ReleaseVariantValue(&divv);
NPN_ReleaseVariantValue(&bodyv);
NPN_ReleaseVariantValue(&docv);
// allocate a return value as a string.
NPUTF8 *putf8RetStr = (NPUTF8 *)NPN_MemAlloc(strlen("foo return val")+1);
strncpy(putf8RetStr, "foo return val", strlen("foo return val"));
STRINGZ_TO_NPVARIANT(putf8RetStr, *result);
return PR_TRUE;
}
return PR_FALSE;
}
The InvokeDefault() method is badly named. This method is invoked if the object represented by the NPObject is a function. This allows you to expose a method to JavaScript via NPAPI. By calling InvokeDefault() on any NPObject will either invoke the object as a function or as the class constructor for the object.
bool ScriptablePluginObject::InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result)
{
printf ("ScriptablePluginObject default method called!\n");
// this is crashing because we are not using the correct memory heap.
// Any string or object allocated for a return value MUST be allocated off
// the browser's heap.
//STRINGZ_TO_NPVARIANT(strdup("default method return val"), *result);
NPUTF8 *putf8RetStr = (NPUTF8 *)NPN_MemAlloc(strlen("default method return val")+1);
strncpy(putf8RetStr, "default method return val", strlen("default method return val"));
STRINGZ_TO_NPVARIANT(putf8RetStr, *result);
return PR_TRUE;
}
other files
nprt.def - a standard windows definition file used to define the exported functions from the DLL.
nprt.rc – the windows resource file for this sample.
resource.h – the windows resource file for this sample.
test.html – a test page for the sample.
makefile.in – the Mozilla makefile for the project if you build it within the Mozilla projecct.
building the project
On the Mozilla developer site you can find references to a very old sample npruntime which will serve as a basis for this code sample. You can find the NPRuntime code here, and the documentation to setup a visual studio project is here. Follow these steps exactly and you will have a project ready to build.
If you try to build the project at this point, it will fail with errors. As the Mozilla and Gecko SDK’s have evolved, this sample has not been kept up to date which is disapointing.
To fix the problems you will need to do the following:
change integer types (int16, int32, uint16, uint32) to use the new modern prototypes (int16_t, int32_t, uint16_t, and uint32_t). As Gecko eveolved, the data types changed. The sample code was never updated and won’t compile without these changes.
change headers. The old header “npupp.h” has been replaced by “npfunctions.h”. Mozilla – Bug 455458 covers the change to these headers. This was done back in 2008 with Gecko 2.0 (FireFox 4).
fix property setter method. There is a bug with the sample code when you process a property and set a value.
debugging with Firefox: attach the debugger to the app “plugin_container.exe” and this will let you debug your code within visual studio. Be careful! if you take too long, firefox will think the plugin crashed and kill the app and end your session.
Debugging with Chrome: This is alot like debugging with IE. Chrome lauches a new Chrome process to contain the plugin, so you need to figure out which process belongs to the plugin and attach the debugger to that process.
installing the plugin
Historically, plugins have been loaded by placing the NPAPI dll into a known location on the path. If you placed the DLL within a sub-folder called “plugins” the calling browser will load the plugin from that location. The key is the plugins folder must be located in the same folder where the calling process was launched. You can load this sample by creating a folder called “plugins” in the installed location for Firefox, and then it will be loaded when you re-launch the browser. This was ok with older operating systems, but a modern operating system should require you to install to a unique location. For Firefox and Chrome, this requires an external installer since we have to modify the registry.
Installing a NPAPI plugin on Firefox
If you want to install a plugin to a custom folder path, we need to add some registry keys that the browser will use to locate the plugin. For more information on how the browser finds plugins, you can read that here: How Mozilla finds plugins.
If you look at the loaded plugins like QuickTime, Silverlight, and flash, all of those plugins use the registry to install the plugin location. This allows you to place the plugin in a custom path for the browser to search when looking for plugins to load The documentation on Mozilla is a little out of date, for 64 bit windows, you need to use the following registry key path
In the HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MozillaPlugins registry path, you need to create a new key that contains a specific format @<vendor>/<application name>,version=<app version number>. Inside this key we need 4 string values
Path – The absolute path to the NPAPI plugin installed location. Beware of some limitations. With Firefox, if the control is not built with Unicode enabled, then the path cannot contain spaces in the path to the control or it will not load. If you build the control with multi-byte character support the path variable cannot contain spaces and should conform to the obsolete 8.3 file naming standards to insure correct operation. Chrome is not affected by this issue.
ProductName – the name of the product
Vendor – the company or individual who authored the control.
Version – installed version number.
that’s it! your plugin is now registered with Firefox. Below is a sample of what the registry keys should look like. There is a problem with this approach in that the registry key is located in the HKEY_LOCAL_MACHINE key. This means that you will need administrative privlidges to install the plugin. For this reason, you can’t download a plugin and you will need an external installer like NSIS to create a proper windows installer executable.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MozillaPlugins\@conedogers.com/npruntime,version=1.0.0.2] "Path"="C:\\dev\\mozilla\\npruntime\\Debug\\npruntime.dll" "ProductName"="npruntime" "Vendor"="conedogers" "Version"="1.0.0.2"
When you get to this point, you should be able to open Firefox and on the installed plugins page under tools|Add-ons – Plugins tab you should see the plugin in the list of installed plugins. If you see the plugin listed, then you can open the test page to see the control run.
Conclusion
When you get the code running you will have everything you need to implement a modern NPAPI control. A copy of the source code that I have used to create this blog is available for download off of gitHub. NPAPI is an old technology that has found new life in the browser world. It can be cumbersome and there are many runtime issues that I have run into while devloping plugins for Firefox using NPAPI. XPCom was almost easier, but then again, I know how COM objects work in Windows, so XPCOM is easy for me to work with.
The code is released under the original Mozilla license.
wordpress has updated their apps on the IPhone and IPad. The new code is much easier to use, I almost don’t have to use a browser to post to my blog now.
For years, Microsoft was regarded as the elephant in the computer room. It was big, it was boring, and it was impossible to go one conversation without begrudgingly mentioning the computer behemoth. But now, with the meteoric rise of Apple, things look a little different - and possibly more favorable, for the world's largest software company and its decisive push into hardware.
With Surface, Microsoft has finally figured out how important it is to control the hardware platform. Apple was the first company that successfully make the personal computer a consumer device. Apple owns every aspect of their design. That has been the key to their success. My MacBook Pro is one of the best laptops that I have run windows on. why? because the quality of the hardware and the quality of the driver packages is better than anything available in the PC laptop market because Apple controls every aspect of the design. While Microsoft has historically had to negotiate with their vendors, Apple just worked through the problem internally.
Will Surface succeed? time will tell. Microsoft has to get rid of their current CEO, but that's a topic for another post.
It's hard to blame Microsoft for making bold decisions with its upcoming desktop operating system. But the renamed Windows 8-style UI (or Modern UI) instead of Metro might be too great a departure from known and trusted interactions found in the previous versions of Windows.
Microsoft is certainly taking a big risk here. combining elements of the mobile platform and the desktop is big step for their user base.
I don't know if this is a good idea, time will tell.
Reading the version information from a windows binary is a non trivial task. The version information is stored in the resource file in a bit-wise fashion. this means that all the information as you read it in the resource file is laid out the same way in binary form when it is compiled into the application resources. I wrote this sample because I didn’t find any sample code that correctly handles the windows Multi-byte and Unicode character sets without significant modifications.
Below is a typical version block as you see it in the resource file. The version block is divided into two parts. the fixed version info, the information in the resource version structure that is language independent, and the string block information that we are interested in retrieving.
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "conedogers"
VALUE "FileDescription", "Sample to demonstrate Version information"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "vsVersionInfo.exe"
VALUE "LegalCopyright", "2012 (c) Chris Warner. All rights reserved."
VALUE "OriginalFilename", "vsVersionInfo.exe"
VALUE "ProductName", "VSVersionSample"
VALUE "ProductVersion", "1.0.0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
The fixed version info is this part of the version information. In this section we have numerical representations of the file and product version along with a bit mask that identifies the properties of the binary. the file mask values tell you what kind of binary you have (DLL, EXE, lib, etc…) and which operating system is supported, and other information that can be view off of MSDN here: Resource VERSION block
The string block section is defined below. Each string block will have a unique entry for each language supported. The string block is a variable length block whos length is determined by the length of the strings in the block, and by how many languages are supported for the binary.
The string block itself is divided into block sections identified by each langauge supported. The languages are identified by the language identifier string
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "conedogers"
VALUE "FileDescription", "Sample to demonstrate Version information"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "vsVersionInfo.exe"
VALUE "LegalCopyright", "2012 (c) Chris Warner. All rights reserved."
VALUE "OriginalFilename", "vsVersionInfo.exe"
VALUE "ProductName", "VSVersionSample"
VALUE "ProductVersion", "1.0.0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
This information is compiled into the resources or in a resource DLL during compilation in the order in which it was entered. In early versions of windows you had to manually set a pointer to the resource block your wanted and manually parse the version structure to retrieve the information you wanted. Beginning with windows 2000, a version API was introduced to make it easier to access the version information stored in the resources. For this sample, we are only interested in 3 functions GetFileVersionInfoSize, GetFileVersionInfo, and VerQueryValue. The rest of the version API relates to install and upgrade mechanisms to assist the installer during install or an upgrade of the binary files. The steps to retrieve the version info structure is very straight forward.
determine the size of the version info structure
allocate a pointer to hold the version info
retrieve the version info structure and all it’s data
search it for the values you want.
First we get the file name of the component we want to access the version information.
// get the absolute path to the DLL.
if ( ::GetModuleFileName(m_hInstance, szFullPath, sizeof(szFullPath)) > 0 )
{
// Get the size of the VerisonInfo block in the resource file.
dwVerInfoSize = ::GetFileVersionInfoSize(szFullPath, &dwVerHandle);
}
Now we allocate a memory block large enough to hold the version information.
// allocate some memory
hMem = ::GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
if ( hMem )
{
lpstrVffInfo = (LPSTR)::GlobalLock(hMem);
.
.
.
Once we have the memory allocated, we retrieve the version information.
// Get the version info block
bRetCode = ::GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo);
Once we have the version info structure, we need to parse the version information to see if our desired language is defined, if not, we default to english using your default language identifier.
if (bRetCode && dwVerInfoSize && lpstrVffInfo)
{
// now we need to figure out the langaguage, and get the right block based upon
// the translations available.
// Get a codepage from base_file_info_sctructure
sName = "\\VarFileInfo\\Translation";
uVersionLen = 0;
lpVersion = NULL;
bRetCode = ::VerQueryValue((LPVOID)lpstrVffInfo, (LPSTR)(LPCTSTR)sName, (void **)&lpVersion,
(UINT *)&uVersionLen);
if ( bRetCode && uVersionLen && lpVersion) {
sResult.Format("%04x%04x", (WORD)(*((DWORD *)lpVersion)),
(WORD)(*((DWORD *)lpVersion)>>16));
}
else {
sResult.Format("%04X04B0", ::GetUserDefaultLangID());
}
// Add a codepage to base_file_info_sctructure
sName.Format("\\StringFileInfo\\%s\\%s", (LPCTSTR)sResult, InfoItem);
uVersionLen = 0;
lpVersion = NULL;
bRetCode = ::VerQueryValue((LPVOID)lpstrVffInfo, (LPSTR)(LPCTSTR)sName,
(void **)&lpVersion,
(UINT *)&uVersionLen);
if ( bRetCode && uVersionLen && lpVersion) {
sResult = CString(lpVersion);
}
else sResult = "";
}
Here is the complete function. This function has been tested in Multi-byte and Unicode build environments. If you search the internet hard enough (and it’s not that hard) you can find many samples that demonstrate this technique. I borrowed from several different samples to build this function. This sample however, is the only sample that I have found that deals with Unicode and Multi-byte characters with no code changes. The string that you have queried is returned in a CString class object that is defined for ATL and MFC.
/*
Get version info from the resources of a specified DLL.
this version of the app only retrieves the StringFileInfo block parameters.
GetVersionInfo("Comments");
GetVersionInfo("CompanyName");
GetVersionInfo("FileDescription");
GetVersionInfo("FileVersion");
GetVersionInfo("InternalName");
GetVersionInfo("LegalCopyright");
GetVersionInfo("LegalTrademarks");
GetVersionInfo("OriginalFilename");
GetVersionInfo("PrivateBuild");
GetVersionInfo("ProductName");
GetVersionInfo("ProductVersion");
*/
CString GetVersionInfo( LPCTSTR InfoItem)
{
TCHAR szFullPath[MAX_PATH] = {0};
CString sResult;
CString sName;
DWORD dwVerInfoSize = 0; // Size of version information block
DWORD dwVerHandle=0; // An 'ignored' parameter, always '0'
BOOL bRetCode = FALSE;
// get the absolute path to the DLL.
if ( ::GetModuleFileName(m_hInstance, szFullPath, sizeof(szFullPath)/sizeof(TCHAR)) > 0 )
{
// Get the size of the VerisonInfo block in the resource file.
dwVerInfoSize = ::GetFileVersionInfoSize(szFullPath, &dwVerHandle);
}
// got the size, now get the block
if (dwVerInfoSize)
{
LPSTR lpstrVffInfo = NULL;
HANDLE hMem = NULL;
LPSTR lpVersion = NULL; // String pointer to Item text
UINT uVersionLen = 0;
// allocate some memory
hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
if ( hMem )
{
lpstrVffInfo = (LPSTR)GlobalLock(hMem);
// Get the version info block
bRetCode = GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo);
if (bRetCode && dwVerInfoSize && lpstrVffInfo)
{
// now we need to figure out the langaguage, and get the right block based upon
// the translations available.
// Get a codepage from base_file_info_sctructure
sName = "\\VarFileInfo\\Translation";
uVersionLen = 0;
lpVersion = NULL;
bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
(LPSTR)(LPCTSTR)sName,
(void **)&lpVersion,
(UINT *)&uVersionLen);
if ( bRetCode && uVersionLen && lpVersion) {
sResult.Format("%04x%04x", (WORD)(*((DWORD *)lpVersion)),
(WORD)(*((DWORD *)lpVersion)>>16));
}
else {
sResult.Format("%04X04B0", GetUserDefaultLangID());
}
// Add a codepage to base_file_info_sctructure
sName.Format("\\StringFileInfo\\%s\\%s", (LPCTSTR)sResult, InfoItem);
uVersionLen = 0;
lpVersion = NULL;
bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
(LPSTR)(LPCTSTR)sName,
(void **)&lpVersion,
(UINT *)&uVersionLen);
if ( bRetCode && uVersionLen && lpVersion) {
sResult = CString(lpVersion);
}
else sResult = "";
}
GlobalUnlock(hMem);
GLobalFree(hMem);
}
}
return sResult;
}
Feel free to use this code at your discretion, use this code at your own risk.
Like many other I ran into issues after upgrading to mountain lion (OSX 10.8). The install and upgrade of the OS went smoothly, and the upgrade installed the latest version of xcode.
I ran into issues with macports when I ran an upgrade from the terminal. After the upgrade finished I attempted to upgrade my installed ports in the usual fashion, but the self update failed with permissions problems accessing TCL.
To fix this issue I downloaded and installed the latest macports build from the DMG installer: macports.dmg for 10.8
Installing macports from the installer package corrected the selfupgrade problem for me. Now I was having issues with building the ports. None of the command line tools were installed with the OS upgrade. With a little digging around the internet I found that the command line tools are now distributed in a separate installer off of developer.apple.com/downloads. Make sure to download the command line tools for OSX mountain lion.
To fix this issue I downloaded and installed the latest command line tools from developer.apple.com/downloads. You will be required to sign in to download the tools.
Once that was finished, I am able to upgrade all my ports from the command line as you would normally expect.
In my previous article Gitolite administration I covered how to installed and configure the basic elements needed to run Gitolite on your server. At this point I would expect you to be familiar with the basic concepts of Git and gitolite configuration settings. This post demonstrates how to configure hooks Gitolite v2 on your Mac.
Gitolite allows you to configure the settings of the bare git repository it hosts via the gitolite.conf. The most common setting would be to configure the email hooks to send notifications on a push operation. This post shows you how to configure Gitolite to set these parameters through the configuration file.
Normally with git you would place the hook in one of the predefined hooks, we are going to use the post-recieve hook in this sample. Typically this is the most common hook modified for post processing of the push.
Hooks in gitolite
Gitolite mirrors the hooks in each hosted repository. It filters the results through hooks stored in the gitolite settings on the server in the home folder of the git user-created during install. Any hooks you want to have processed get placed inside this folder off the git home. ~/.gitolite/hooks/common. You will have to modify the Gitolite configuration to allow the git repo configuration through the .conf file settings.
.gitolite.rc file changes
On the Git server machine, log on as the Git user, all the modifications will be done on the server for now. In the Git users home folder you have the .gitolite.rc file. This file contains the settings for Gitolite itself, so edit with caution!
Open the .gitolite.rc file from the command shell. I typically use vi at this point. Locate the following block of code and modify the highlighted values
begin
# ------------------------------------------------------------------------------
# VARIABLES WITH A SECURITY IMPACT. READ DOCS BEFORE CHANGING THESE!
# http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite.rc.mkd#_variables_with_a_security_impact
# (or http://sitaramc.github.com/gitolite/doc/gitolite.rc.html#_variables_with_a_security_impact)
# ------------------------------------------------------------------------------
# $GL_ALL_READ_ALL = 0;
$GIT_PATH="";
# original value $GL_GITCONFIG_KEYS = "";
$GL_GITCONFIG_KEYS = '*';
end
This modification allows ALL git config keys that can be set in a bare repo to be configured by gitolite through the .conf file. Doing this can potentially allow unsafe operations that might compromise the repo security model, so be careful. The GL_GITCONFIG_KEYS recognizes a regex expression so, have fun with that.
Run Gitolite setup to compile scripts. Remember to add the administrator public key on the command line.
begin
$ gl-setup adminkey.pub
end
There are other setting that you can make via the .gitolite.rc file. Bu, a warning, proceed with caution. If you don’t understand what is modified, don’t touch it. Also, The new V3 is out, and many settings, in there have been changed. For more information look here: Gitolite V2 advanced RC file settings.
Configure the gitolite hooks
Gitolite chains together the push notification that it receives from the user to the repository that received the push. Gitolite kind of operates like the man in the middle, negotiating the permissions for the user and insuring that any notifications are sent out if Gitolite is configured for that operation. The only hook we are going to examine here is the post-receive hook. This notification is processed when the repository receives a push from another source. This also assumes a non-mirroring push notification, for more information on hooks check out this link: Using hooks.
To add a hook to Gitolite is just as easy as adding it to Git. You need to either provide a Perl script that implements the post processing you want. In this case we want to send an email. You can either write your own, or copy and modify the sample hook installed with Git. I choose the latter. On a Mac you can find the default scripts at/opt/local/share/doc/git-core/contrib/hooks. You can make a symbolic link to the post-receive or copy the file if you want to make modifications. You need to copy the file to ~/.gitolite/hooks/common. make sure the destination file is named post-receive. At this point we are done on the server and we can log out and continue our modifications in the .conf file from your work station.
gitolite.conf changes
Now that we are done on the server, log off and continue the modifications through the gitolite.conf file.