Programming with TAPI sucks

Some days I really want to strangle Microsoft engineers.


 


For the past couple days I’ve been working on a project where I want a CE device to dial in to an XP desktop. In a perfect world, I’d just use RAS, create an entry, and go.  Well this isn’t a perfect world.


 


Spent several hours yesterday trying different chipsets and PCs to finally determine that my device modem seems to be not-so-good and it won’t negotiate at the default 56kbps that the chipset uses (than you Alex Feinman for the help).


 


The workaround is to manually slow it down with an AT command.  Using terminal on the device I just type it in and boom – it’s set.  Next step was to try to repro that simple operation in code.  This is where the fun begins. Well this extra dialing data is set via the RasSetEntryProperties API in the lpb parameter.  The parameter docs say “Pointer to a buffer that contains device-specific configuration information. This is opaque TAPI device configuration information.” 


 


It gives a reference to the TAPI lineGetDevConfig API for getting that data.  So we look over there and fine that it’s a VARSTRING parameter.  Simple enough eh?  I fill in a VARSTRING with the command and send it in.  RasSetEntryProperties returns a success.


 


For fun I then open the connection via the UI’s Network Connections.  Click on the “Configure” button and the shell completely locks up.  That can’t be good.


 


So I figure, I’ll see what the UI generates and use that to figure out what I screwed up.  I create a connection entry manually and add the info into the dialog.  Now clicking “Configure” works fine, so it’s back to the code.


 


I call RasGetEntryProperties on the manually created entry to see what it looks like.  I see my data’s in there, but it certainly isn’t aligned as a VARSTRING and I have no idea what the “header” data is.  The length reported is also *way* bigger than my data.


 


  0x001291C0  30 00 00 00 78 00 00 00 10 01 00 00 00 4b 00 00  0…x……..K..


  0x001291D0  00 00 08 00 00 00 61 00 74 00 2b 00 6d 00 73 00  ……a.t.+.m.s.


  0x001291E0  3d 00 76 00 33 00 32 00 2c 00 2c 00 31 00 34 00  =.v.3.2.,.,.1.4.


  0x001291F0  34 00 30 00 30 00 2c 00 32 00 38 00 38 00 30 00  4.0.0.,.2.8.8.0.


  0x00129200  30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0……………


  etc.


 


Alright, so what’s going on here?  Off to the Platform Builder source (and this is why every developer should get the PB eval and install it – the source is gold) to see how the default UI is doing it.  A little GREP on the dialog text and I find that the UI handling this is, not surprisingly NETUI.


 


So after a long while tracing through convoluted code (Source Insight is an invaluable tool here) it appears that you’re supposed to call lineConfigDialogEdit and have TAPI give a dialog to fill this structure.  Well what if you don’t want user interaction (like in my case where the user would have no idea what to enter)?  Or even more fun, what if it’s a headless device?


 


Ok, so time to regroup.  Let’s search all the PB source for anything calling lineSetDevConfig to see if there’s something to base my work on.  Turns out that there are a few samples, but most of them just do a lineGetDevConfig, save that data somewhere, then send it back in – they don’t actually modify it.


 


One exception is the sample RASENTRY app.  It calls lineGetDevConfig, and then appears to alter that config not through lineSetDevConfig but instead by calling lineDevSpecific.  It uses this to change the baud rate.  It’s not exactly what I need (I need to prevent the modem from negotiating a v.90 or v.92 connection) but a start, right?


 


So a little work and I ended up with this (well more than this, but it’s the heart of it):


 


            result = lineGetDevConfig(tapiId, config, DEV_CLASS_COMM_DATAMODEM);


            ucd.dwCommand = UNIMDM_CMD_CHG_DEVCFG;


            ucd.lpszDeviceClass = DEV_CLASS_COMM_DATAMODEM;


            ucd.lpDevConfig = config;


            ucd.dwOption = UNIMDM_OPT_CFGBLOB;


            ucd.dwValue = (DWORD)MY_CUSTOM_DIALUP_STRING;


            result = lineDevSpecific(hLine, 0, NULL, &ucd, sizeof(ucd));


 


I run it and all APIs return success.  Hooray!  Right?  Open the connection with the UI on the device and the “Extra Settings” box is still blank.  At this point I have to chalk it up to a huge WTF in the Microsoft code.


 


At this point I have no option but to go the route that the UNIMODEM dialer went (again in PB source) and set it in the registry at HKEY_LOCAL_MACHINEDriversUnimodemInit.  Some may argue that this is easier and I should have gone that route in the first place, but that just doesn’t sit well with me and not just because it affects all Unimodem connections on the device. 


 


I strive to understand what is going on when I’m writing code because it not only makes it easier to debug aberrant behavior but it also helps make me a better developer all around. I hate black-box programming – that is programming where I don’t fully understand how something works from end to end.  Somehow TAPI gets that data into the blob returned by RasGetEntryProperties, but I’ll be damned if I can figure it out. 


 


The actual blob is created or copied to  HKCUCommRasBook<connection name>DevCfg and it appears to be an extension of HKLMDriversUnimodemDevConfig, but they are slightly different and what those differences mean is totally undocumented.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s