Watch, Follow, &
Connect with Us

Chris’ Debugger Blog

RAD Studio 2010: Delay Load import exceptions

One of the things I do after a product release (or an update release, for that matter) is keep an eye on the Incident Reports that are submitted to QualityCentral via the Send Report wizard that is built into the IDE.  A few years ago, I wrote a tool that makes it really easy to see which reports are coming in at a high frequency.  The data gleaned from this tool is really helpful when looking for "big bang for the buck" type bug fixes to go after for an update (or future product release).  One issue that I’ve seen reported at a moderate level (about a dozen reports, so far) for the 2010 release is related to the new "delay-loaded import" feature.  You can learn more about this feature by reading the following posts by Allen Bauer and Christian Wimmer:

http://blogs.embarcadero.com/abauer/2009/08/25/38894

http://blogs.embarcadero.com/abauer/2009/08/29/38896

http://blog.delphi-jedi.net/2009/08/29/version-checking-for-delphi/

In Christian’s post and in Allen’s second post, there is discussion of the exception that is raised when a delay-loaded method can not be found at runtime.  It is this exception that is prompting D2010 users to submit QC reports.  In fact, every single one of these reports I’ve seen so far are coming from developers who are using the JVCL at design time in the IDE — in particular it appears that these developers are using a TJvCustomSpeedButton descendant.  Based on the exception, it would appear that each report was generated on a pre-Vista OS.

The error message associated with this exception is either "External Exception $C0FB007E" or "External Exception $C0FB007F."  Here is the top of the stack trace for one of these reports (this is what you’ll see if you click the "Details" button on the error message in the IDE):


[7C812A6B]{kernel32.dll} RaiseException + $52
[50011BDB]{rtl140.bpl  } System.@_delayLoadHelper (Line 302, "C:\Builds\TP\rtl\sys\delayhlp.c" + 158) + $D
[500B0681]{rtl140.bpl  } UxTheme.uxtheme.dll (Line 5112, "UxTheme.pas" + 0) + $5
[500B0712]{rtl140.bpl  } UxTheme.DrawThemeTextEx (Line 5138, "UxTheme.pas" + 1) + $20
[490EE508]{JvCoreD14R.bpl} JvThemes.DrawGlassableText + $C4
[490EE54C]{JvCoreD14R.bpl} JvThemes.DrawGlassableText + $108
[49ACF1B4]{JvStdCtrlsD14R.bpl} JvSpeedButton.TJvxButtonGlyph.DrawButtonText + $C4
[49ACF2A6]{JvStdCtrlsD14R.bpl} JvSpeedButton.TJvxButtonGlyph.DrawEx + $E2
[49ACDA81]{JvStdCtrlsD14R.bpl} JvSpeedButton.TJvImageSpeedButton.PaintImage + $C5
[49AE416C]{JvStdCtrlsD14R.bpl} JvToolEdit.TJvEditButton.PaintImage + $78
[49ACCC96]{JvStdCtrlsD14R.bpl} JvSpeedButton.TJvCustomSpeedButton.Paint + $336
[49AE4078]{JvStdCtrlsD14R.bpl} JvToolEdit.TJvEditButton.Paint + $98
[501CE436]{vcl140.bpl  } Graphics.TCanvas.SetPenPos (Line 4050, "Graphics.pas" + 1) + $9
[5029F685]{vcl140.bpl  } Controls.TGraphicControl.WMPaint (Line 13141, "Controls.pas" + 7) + $5
[49ACD58E]{JvStdCtrlsD14R.bpl} JvSpeedButton.TJvCustomSpeedButton.WMPaint + $B6
[50295DFC]{vcl140.bpl  } Controls.TControl.WndProc (Line 7062, "Controls.pas" + 91) + $6

 

The exception is raised because in Delphi 2010, the DrawThemeTextEx declaration in UxTheme.pas was changed from using the LoadLibrary/GetProcAddress mechanism mentioned by Allen to use the new delay-loaded mechanism. 

Below if the pre-D2010 version of the implementation of DrawThemeTextEx:
 


function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer;
  iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD;
  pRect: PRect; var pOptions: TDTTOpts): HResult;
begin
  if Assigned(_DrawThemeTextEx) then
    Result := _DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText,
      dwTextFlags, pRect, pOptions)
  else
  begin
    Result := E_NOTIMPL;
    if ThemeLibrary > 0 then
    begin
      _DrawThemeTextEx := GetProcAddress(ThemeLibrary, 'DrawThemeTextEx'); // Do not localize
      if Assigned(_DrawThemeTextEx) then
        Result := _DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText,
          cchText, dwTextFlags, pRect, pOptions);
    end;
  end;
end;

And here is the D2010 version:


function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer;
iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD;
pRect: PRect; var pOptions: TDTTOpts): HResult; stdcall; external themelib name 'DrawThemeTextEx' delayed;

Because of the change, existing code that calls this API on a pre-Vista system would now generate the aforementioned delay-load exception.  With the old mechanism, calling this method would have resulted in a "E_NOTIMPL" return value.   The JVCL code that calls this API, had error-handling code in place to properly handle the E_NOTIMPL return value, but this did not help with the new exception-raising mechanism.   I just checked, and it looks like the JVCL team has addressed this issue.  The sources contained in the current JVCL tip revision properly handle the situation by doing a Windows version check before calling the API (see revision 12491 of JVThemes.pas, committed on September 4th, 2009).  That said, if you are using the JVCL and have not yet updated to the most recent sources, you may still run into this exception in D2010 when running on Windows XP (or any pre-Vista version of Windows).  To avoid this, I would suggest updating your JVCL sources.  If that’s not practical, I would at least apply the changes made in the aforementioned revision.
 

On a related note, it would probably be a good idea for the IDE to give a better error message in this particular case.  I think that if the IDE used a solution similar to one presented by Allen in his second post, that would definitely make things much more user-friendly should an error like this occur again in the future.   Telling the user that a particular entry point is missing from a particular module is much nicer than giving an external exception with an obscure error code.   I’ll be investigating such a solution for a future release.

 UPDATE: I just noticed that Andreas Hausladen blogged on this exact issue a few months ago:  http://andy.jgknet.de/blog/?p=660

Posted by Chris Hesik on November 2nd, 2009 under C++Builder, Delphi, IDE, RAD Studio |



7 Responses to “RAD Studio 2010: Delay Load import exceptions”

  1. Uwe Schuster Says:

    Some additional information how to get updated JVCL sources:

    Andy blogged about the change

    "Once upon a delayed JVCL time…"
    http://andy.jgknet.de/blog/?p=660

    He has also updated his binary JVCL installer after committing revision 12491.

    "ID: 27212, JEDI VCL 3.38.0.1 Binary Installer"
    http://cc.embarcadero.com/Item/27212

    Other options to get an updated JVCL + JCL are our daily zips

    http://jcl.sf.net/daily/
    http://jvcl.sf.net/daily/

    or our SVN versions

    http://wiki.delphi-jedi.org/index.php?title=Repository

  2. Xepol Says:

    I still feel that this particular solution "delayed" is only half implemented, and as such is worse than if it was actually left out entirely. (my original thoughts are recorded as comment’s in the first Allen Bauer post referenced here)

    And as for your "here is the D2010 version" -> You are wrong, and the fact that the JVCL team had to do additional work to deal with it underscores that.

    For what little this keyword actually brings to the table, combined with all the work that has to still be done in spite of it, I would lobby the Delphi team to remove this keyword in the next version of the compiler - continuing down this road will only lead to similar problems cropping up again and again.

  3. Chris Hesik Says:

    Xepol: Yes, it seems that some RTL changes that were made to take advantage of this new directive were made at the expense of some backward-compatibility. The fact that the JVCL had to change is proof of this. I haven’t yet had a chance to talk to the RTL folks about this, but I’m guessing the thinking was that the added benefit was perceived to outweigh the drawbacks. At any rate, the main goal of this blog post was to help any D2010 & JVCL users who may be running into this issue, since I noticed a fair number of people are, in fact, running into it and reporting it to us. By the way, I didn’t do a google search on this issue until a few minutes ago, and when I did, I discovered that Andreas Hausladen actually blogged about this same issue back in early September. I’ve added a link to his blog post at the end of this post.

  4. Xepol Says:

    My objection is less that it breaks reverse compatility - more that it requires 95% of the same code you would have previously used and adds in new potential problems because what little compiler magic exists actually hides important details.

    The question has to be asked: considering the additional complexity required to support this keyword in the compiler, and any code that actually requires it and considering what little actual gain results from the delayed keyword and how unlikely a universally excepted fix is likely to be - perhaps it would be best to remove the feature now.

    After all, writing delayed load code isn’t all that hard for those of us who actually need to accomplish it, and in order to use the delayed keyword, we still have to write 95% of the code we would normally have to write without it.

    Please, seriously consider just removing it. A wizard that produces the boiler plate code for delayed loading would be easier to support and provide more value in the short and long run.

  5. Roger Says:

    I completely disagree with the comments of Chris and Xepol. The reason that the JVCL code failed was sloppy programming in that they tried to call a DLL-function when the DLL didn’t exist. If they had used the D2010 version without the delayed keyword then the whole application would fail to execute when the DLL did not exist at run time. I do agree this is easier to debug.

    The D2010 should read:

    function _DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer; iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD; pRect: PRect; var pOptions: TDTTOpts): HResult; stdcall; external themelib name ‘DrawThemeTextEx’ delayed;

    function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer; iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD; pRect: PRect; var pOptions: TDTTOpts): HResult;
    begin
    if ThemeLibrary > 0 then Result := _DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, wTextFlags, ect, pOptions) else Result := E_NOTIMPL;
    end;

    This is still simpler than the D2009 version. Furthermore the D2009 also requires the declaration of the variable, e.g.
    _DrawThemeTextEx:procedure(hTheme: HTHEME; hdc: HDC; iPartId: Integer; iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD; pRect: PRect; var pOptions: TDTTOpts): HResult.

    This must be copied, pasted and rearranged from the DLL header supplied by the vendor of the DLL (though the extra burden is lighter when the header is only supplied in c as is often the case). Appending delayed onto the end of the extern declaration is much easier. Overall, I make the LOC for D2009 to be 15 against 5 for D2010. Remember, this is per function and a DLL will typically have tens of functions. I make that 30% not 95%.

    The delayed keyword is extremely useful for wrapping a large DLL containing many functions into a class because, in this case, only one check for the existence of the DLL need be made once at run time before the object can be used. I have to write delayed load code and it is painful compared to static code.

    I agree about reverse compatibility but the same can be said for generics, anonymous methods and all the other new language features.

  6. Olivier Sannier Says:

    Hello all,

    We have just released JVCL 3.39 along with JCL 2.1 to address these issues and a few others.
    Files can be downloaded here:

    http://sourceforge.net/projects/jvcl/files/
    http://sourceforge.net/projects/jcl/files/

    We are sorry for the inconvenience that this error has caused. Please rest assured that we are trying our best that no such issue would arise in the future.

    Regards
    Olivier

  7. Chris Hesik Says:

    Uwe and Olivier:

    Thanks for the extra info. And thank you for your continued support of Delphi via the JCL and JVCL. Apparently your comments were sitting in limbo, waiting to get approved by me. I hadn’t noticed them until this morning.

Leave a Comment



Server Response from: BLOGS1