How To: Modify injected (attributed) code in an ATL control that is crashing

(ATL Control Visual Studio 2003 Crashes long value) I found a problem in the injected code from Visual Studio 2003 for an ATL control that had a property that was a long (VT_I4) value.  You could reliably crash the control if you put it into an html page and had jscript in a loop, setting the value over and over (100 X) in this case would crash it.  To make the crash reliable, I turned off BSTR caching and set full gflags.  You could see the issue was the freeing of a BSTR twice. 

Since Visual Studio 2003 is in extended support, getting a fix for this injected code was not possible so I had to fix it myself.  The easiest fix is to simply build the control using Visual Studio 2008!

I STRONGLY recommend that rather than doing this for the control, instead simply build with the latest Visual Studio to avoid the problem.

You can generate the injected code in the project as follows:

  • In the Solution Explorer right click on AtlCom1 and choose ‘Properties’
  • Enable: ‘Expand Attributed Source’ Yes (/Fx)  in the C/C++ Output Files section.
  • Clean and Build the project

You will notice some files that have a .mrg string in the filename.  This is the injected code from the compiler.  You can edit these, remove the .mrg and rebuild to get your code changes.

Generating the merged code, I found that the ::Invoke call was calling ::VariantChangeType( ) on a variable that was passed in.  This is evil because this code does not own the Variant passed in!  What was happening is that the type was being changed to a long and calling ::SysFreeString on the BSTR in the passed in Variant.  To fix this I just changed the code to make a copy of the Variant and pass that in instead.  Actually I kind of cheated here and simply generated the injected code from building the project under Visual Studio 2005 and compared this to the 2003 code since I knew that code ran without causing the access violation.

Here are the steps I took:

  • Save a copy of the control code that has the Invoke code <<somefile>>.h. 
  • Delete <<somefile>>.h. 
  • Rename the generated file <<somefile>>.mrg.h to <<somefile>>.h to replace the old file.
  • Edit <<somefile>>.h as follows:
    a. Find the  ::Invoke function definition and add a local variable ATL::CComVariant rgVars[1]; to the function.
    b. Find this in the ::Invoke function: if (wFlags & 4) and replace the assignment of v = rgpVars[0]; in this block of code with this code:

 rgVars[0] = *rgpVars[0];
 v = &rgVars[0];

Now your code block in that section should look something like this:

if (wFlags & 4) {
                    if (pDispParams->cArgs != 1) {
                        return DISP_E_BADPARAMCOUNT;
                    }
                  
      rgVars[0] = *rgpVars[0];
      v = &rgVars[0];

                    if (v->vt != VT_I4 && FAILED(__VariantChangeType(v, &v0, VT_I4))) {

  • You can compile the project and the problem will be circumvented.

Again...

I STRONGLY recommend that rather than doing this for the control, instead simply build with the latest Visual Studio to avoid the problem.