Transfering profiles programatically

Last week I wrote about how to transfer profiles using the Easy Transfer Wizard. That generated some email and some questions in the community about how to do this programmatically.

That's a good question. But unfortunately, the answer isn't that straight forward currently.

In the SAPI documentation you can read about how some of this works, but there's not a single overview of how all this works. So ... I'll try to summarize how it works here.

Here goes ...

In SAPI, recognition profiles are accessed by the engine (and by the application) using the Resource interfaces of SAPI. Specifically, the ISpObjectTokenCategory, IEnumSpObjectTokens, ISpObjectToken, and ISpDataKey interfaces.

To get a list of recognition profiles configured by the user you can use the SpEnumTokens helper function (defined in sphelper.h). You'll need to specify the category you want to enumerate. That's the SPCAT_RECOPROFILES category. This will give you an IEnumSpObjectTokens interface, which is a standard derivative of the IEnum* class of interfaces. Using this interface, you can iterate thru the individual "tokens" by calling IEnumSpObjectTokens::Next -- each of the ISpObjectTokens you get back will represent a single recognition profile.

Given a specific token you can QueryInterface for the ISpObjectToken interface, which derives from ISpDataKey. Then you can ask for the name of the reco profile, by calling ISpDataKey::GetStringValue(NULL, &pszCoTaskMemAllocedProfileName). Be sure and call CoTaskFree on the string returned when you're done with it.

To back up the profile, there are no special helpers, so you'll just have to do a few things the hard way.

First, you should make a backup of the ISpDataKey settings. The easiest way to do this is to use the token's ID to open up the registry location directly. You see, typically the object token is simply a wrapper on top of registry access functions. The token ID is typically actually just nothing more than a path into the registry. For example, for my reco profile that I'm using right now, the token id (retrieved by calling ISpObjectToken::GetID) is: "HKEY_CURRENT_USER\Software\Microsoft\Speech\RecoProfiles\Tokens\{90E13428-A7A5-419B-8034-EA6453E31906}"

So, you'll have to write your own code to iterate thru the registry recursively and back up all the keys and values (with their names) that you find.

There are a few special things that you have to take into consideration when doing this:

1.) If the token id doesn't live directly under "HKEY_CURRENT_USER\Software\Microsoft\Speech\RecoProfiles\Tokens\", you shouldn't assume that the procedure I'm describing here will actually work to back up the recognition profile. If you see a token ID outside of this area, you should abort the backup.

2.) While backing up the profile, you will encounter one or more key names called "Files". Under keys named "Files", the values represent "pointers" to files on disk. You'll have to also back up the files that these "file names" point to.

3.) These files may start with character sequences that look like "%1c%\". If they do, you can replace the values between the percent signs with the text as returned from SHGetFolderPath. For example, one of that data files in my registry under a "Files" key has a "filename" of "%1c%\Microsoft\Speech\Files\TrainingAudio\SP_E18DB2EFB76A432B97889A3DB7210406.wav". The "%1c%" corresponds to a "real" filename of "C:\Documents and Settings\Rob\Local Settings\Application Data\Microsoft\Speech\Files\TrainingAudio\SP_E18DB2EFB76A432B97889A3DB7210406.wav". Again, you can use SHGetFolderPath with the hex value of 0x1c to get the path of "C:\Documents and Settings\Rob\Local Settings\Application Data" and you can put all this together.

4.) When you restore the profile, you should take special care to restore files with different new filenames. If you don't, there could be some collisions. You can get a new filename by calling the ISpObjectToken::GetStorageFileName method.

That's basically it.

Yeah, I know... It's not necessarily the easiest thing you'll ever do, but it is possible, and this is basically how you do it. Hopefully, we'll be able to release an actual example that shows how a real end to end backup and restore works for profiles in the future. But until then, hopefully this post will illuminate some of the magic behind profile management in SAPI.