ISAPI Module (DLL) runtime error

The project I am working on is deployed as a DLL ISAPI Module and as a standalone .exe. I use FlexCel to generate XLS Reports and convert them to PDF. The standalone EXE works fine, while the DLL crashes every time I try to generate PDFs.


    [77BE8E19]{ntdll.dll   }  Unknown function at RtlIntegerToUnicodeString(:0)
    [77BE8E19]{ntdll.dll   } Unknown function at RtlIntegerToUnicodeString
    [028A78E5]{TTCGui_IIS.dll} _GDIPlusUIClasses.__fastcall _Gdiplusuiclasses::TGdipUIFont::TGdipUIFont
    [028ABAAB]{TTCGui_IIS.dll} _GDIPlusGraphicsFactory.__fastcall _Gdiplusgraphicsfactory::TGDIPlusGraphicsFactory::CreateFont
    [028BD704]{TTCGui_IIS.dll} _UIClasses.TUIFont.__fastcall _Uiclasses::Tuifont::TUIFont::CreateNew
    [02978A0A]{TTCGui_IIS.dll} _UExcelFont.ExcelFont.__fastcall _Uexcelfont::Excelfont::TExcelFont::CreateFont
    [02978976]{TTCGui_IIS.dll} _UExcelFont.TFontCache.__fastcall _Uexcelfont::Tfontcache::TFontCache::GetFont
    [029C6694]{TTCGui_IIS.dll} _UFlexCelRender.FlexCelRender._Uflexcelrender::Flexcelrender::FlexCelRender::DrawText
    [029C5FD9]{TTCGui_IIS.dll} _UFlexCelRender.FlexCelRender.__fastcall _Uflexcelrender::Flexcelrender::FlexCelRender::DrawCell
    [029C5D77]{TTCGui_IIS.dll} _UFlexCelRender.FlexCelRender.__fastcall _Uflexcelrender::Flexcelrender::FlexCelRender::DrawCell
    [029C4266]{TTCGui_IIS.dll} _UFlexCelRender.FlexCelRender.__fastcall _Uflexcelrender::Flexcelrender::FlexCelRender::GetMaxVisibleCol
    [029CF274]{TTCGui_IIS.dll} _UFlexCelRender.FlexCelRender.__fastcall _Uflexcelrender::Flexcelrender::FlexCelRender::Get_MaxVisibleCol
    [029D07F8]{TTCGui_IIS.dll} _UFlexCelPdfExport.FlexCelPdfExport.__fastcall _Uflexcelpdfexport::Flexcelpdfexport::TFlexCelPdfExport::ExportSheet
    [029D05F0]{TTCGui_IIS.dll} _UFlexCelPdfExport.FlexCelPdfExport.__fastcall _Uflexcelpdfexport::Flexcelpdfexport::TFlexCelPdfExport::ExportSheet
    [01CE24DC]{TTCGui_IIS.dll} FORMUTILSREPORT.OBJ.FormUtils::ExportToPdf



The FlexCel modules (.lib) are linked statically to the project, no dynamic libraries are used.

I found that RtlIntegerToUnicodeString is supposed to be found in Ntoskrnl.lib

I suspect that the issue is the same as this one http://steema.com/wp/blog/2014/03/12/teechart-and-gdi-inside-a-dll/

I couldn't link GdiPlus.lib because in flxGDIPAPI_XE.pas you use gdiplus.dll explicitly (line 60)

(**************************************************************************\
*
*   GDI+ Private Memory Management APIs
*
\**************************************************************************)

const WINGDIPDLL = 'gdiplus.dll';



line 6028

implementation

  function GdipAlloc; external WINGDIPDLL name 'GdipAlloc' delayed;
  procedure GdipFree; external WINGDIPDLL name 'GdipFree' delayed;
  function GdiplusStartup; external WINGDIPDLL name 'GdiplusStartup' delayed;
  procedure GdiplusShutdown; external WINGDIPDLL name 'GdiplusShutdown' delayed;

  function GdipCreatePath; external WINGDIPDLL name 'GdipCreatePath' delayed;
  function GdipCreatePath2; external WINGDIPDLL name 'GdipCreatePath2' delayed;

Hi,


For using FlexCel in a dll, you need to initialize GDI+ first (FlexCel can't do it from the dll, it has to be done from the main app)

There are 2 methods in FlexCel: FlexCelDllInit and
FlexCelDllShutdown which you should call before calling FlexCel. This is explained in "using flexcel inside a dll" in the API guide.

Please try calling FlexCelDllInit in the loading of the isapi (GetExtensionVersion) and FlexCelDllShutDown in TerminateExtension()

Let me know if it works


Indeed, the problem is with the initialiation of GDI+. The problem is not that GDI+ is a dll (that's just the way it is, you can't link it, but it is a part of the OS). The problem is that you can't init gdi+ from DLLMain, and the initialization sections of delphi are called in DLLMain, So our code is like this:


initialization
{$IF_NOT_RUNNING_IN_DLL}
GDIPlusStartup(..)
{$ENDIF}
end.

From this link: (mentioned in FlexCel API guide)
<quote>

Do not call GdiplusStartup or <strong ns="http://www.w3.org/1999/">GdiplusShutdown</strong> in DllMain or in any function that is called by DllMain. If you want to create a DLL that uses GDI+, you should use one of the following techniques to initialize GDI+:

</quote>

Perfect. It worked. Thanks Adrian. I was positive that FlexCel was initializing correctly because the reports were fine. I was wrong.


Hi there,

I am getting this problem and I am using an ISAPI DLL through IIS. (i'm using XE7 btw)

I just wanted to clarify what to do here because as it is a ISAPI DLL there isn't a main exe that can initialize things before it calls the dll?

But it seems a few people have worked through that one, but I can't reconcile how they would have gotten it to work with IIS?

Thanks,

Toni Brown

Actually just to clarify sorry

The error I am getting is this one :

"Error trying to create an empty image of dimensions 1 x 1 and PixelFormat "2498570". Probably out of memory."

Which I think is related to the GDI+ initialization question as well


Hi,

Indeed this problem is normally that you didn't call FlexCelDllInit/Shutdown.

In an ISAPI DLL you have the events GetExtensionVersion and TerminateExtension where you can call init/shutdown, as mentioned in the post above:

In the case you are using a normal datasnap isapi, 
if you look at the generated code for an empty app, you will see it exports both methods:



exports
  GetExtensionVersion,
  HttpExtensionProc,
  TerminateExtension;




And in Web.Win.IsapiAPP unit you have:

So what you would need to do is

Assign the OnTerminate event and call FlexCelDllShutdown there:
http://docwiki.embarcadero.com/Libraries/XE8/en/Web.Win.ISAPIApp.TISAPIApplication.OnTerminate

And create your own GetExtensionVersion method which calls the GetExtensionVersion method in ISApiApp, but also calls FlexCelDllInit. Then export that method instead of the one it is exported.


But also note: for cases where you don't have any init method, or if you don't want to mess with GetExtensionVersion/OnTerminate, you can just call FlexCelDllInit/ShutDown in your app before and after using FlexCel every time. Those are fast methods anyway, so it won't really matter:

procedure CreateReport;
begin
   FlexCelDllInit;
   try
      //Code that uses FlexCel
   finally
      FlexCelDllShutDown;
   end;
end;

Perfect. Thank you so much.

Toni