Как я могу установить соединение с базой данных ADO в «TISAPIApplication» перед обработкой входящих запросов?

TADOConnection не удается подключиться в разделе инициализации приложения приложения Delphi ISAPI (TISAPIApplication):

Приложение создано с помощью Delphi XE SPI под управлением Win 7 64/IIS 7.5 и WinServer 2008 RS2 — оно не может подключаться к ADO в глобальном контексте приложения ISAPI. (Пример кода использует MS-SQLServer OLEDB, но мы также не можем использовать поставщика Sybase ASE.)

Следующий код дает сбой при вызове conn.Open - TADOConnection.open никогда не возвращается - приложение ISAPI зависает в стиле la-la, никаких исключений не возникает:

library ISAPIBareBones;

uses
  ActiveX,
  ADODB,

    (...)

var
  conn: TADOConnection;

begin

  CoInitFlags := COINIT_MULTITHREADED;
  Application.Initialize;

  coinitialize(nil);
  conn := TADOConnection.Create(Application);
  conn.ConnectionString := 'Provider=SQLOLEDB.1;xxx';

//Fails here:

  try
    conn.Open;
  except on e:exception do
    logException(e)
  end;


  Application.WebModuleClass := WebModuleClass;
  Application.Run;

end.

Тот же код в конкретном обработчике запросов (Delphi webAction) работает нормально.

Мы подозреваем проблему с правами выполнения в IIS на уровне приложения ISAPI. Но, насколько мы можем судить, весь стек приложений IIS от самого веб-сервера до конкретного виртуального каталога и самой ISAPI dll работает под одними и теми же учетными данными с одинаковыми привилегиями выполнения.

Между тем, мой обходной путь заключался в том, чтобы инициализировать инфраструктуру базы данных из вызова ответа http (поток ISAPI), а затем просто проверять, инициализирована ли она при каждом последующем вызове. Это работает, но обременяет меня некоторыми ограничениями, с которыми я бы предпочел не иметь дело.

Как установить соединение с базой данных ADO в экземпляре TISAPIApplication перед обработкой входящих запросов.


person Vector    schedule 31.08.2011    source источник
comment
Что произойдет, если вы используете 'Provider=SQLOLEDB;xxx'; вместо?   -  person Michael Riley - AKA Gunny    schedule 01.09.2011
comment
Поскольку код отлично работает в контексте потока (а также во многих других местах с одной и той же строкой подключения), проблема, похоже, не связана с проблемами провайдера/строки подключения. И это также успешно и неудачно точно так же с соединением Sybase - снова указывает на то, что это не проблема поставщика/строки подключения.   -  person Vector    schedule 01.09.2011
comment
Что вы подразумеваете под разделом инициализации приложения?   -  person Ondrej Kelle    schedule 16.01.2015
comment
Я спрашиваю, потому что ваш comment код, который я опубликовал, фактически выполняется как экспорт dll, вызываемый IIS, а не в loadLibrary-DllMain, что противоречит коду, фактически опубликованному в вопросе.   -  person Ondrej Kelle    schedule 16.01.2015
comment
@TOndrej - возможно, я ошибаюсь, но я не верю, что это правильно: приложение Delphi ISAPI инициализируется только после выполнения loadLibary. В коде, размещенном после кода applicaiton.initlialize, доступен объект веб-приложения TISAPI. Обратите внимание на мой комментарий к mrabat: вызовы Delphi можно выполнять после запуска applicaiton.initlialize. Раздел инициализации приложения означает просто раздел кода, который инициализирует объект приложения TISAPI.   -  person Vector    schedule 16.01.2015
comment
Конечно, код в DLL можно запустить только после загрузки DLL. Код, который вы разместили в своем вопросе (основной блок проекта DLL begin..end), выполняется один раз после загрузки DLL хост-приложением (в данном случае IIS). С другой стороны, запросы ISAPI выполняются службами IIS, вызывающими экспортированные функции ISAPI. Из-за этого противоречия неясно, в чем на самом деле проблема. Термин раздел инициализации приложения также неоднозначен, поскольку в Delphi есть разделы инициализации модулей (но не приложений).   -  person Ondrej Kelle    schedule 16.01.2015
comment
Я изменил заголовок и отредактировал вопрос. Я верю, что теперь стало понятнее. Tnx.   -  person Vector    schedule 16.01.2015


Ответы (2)


Я думаю, проблема в том, что код, который вы запускаете, выполняется в основной процедуре dll. Эта часть инициализации очень ограничительна, например. вы не можете загружать какие-либо dll, и вам не разрешено вызывать CoInitialize (см. http://msdn.microsoft.com/en-us/library/ms678543%28v=vs.85%29.aspx). Все ваши ActiveX вызовут некоторые проблемы, которые, скорее всего, и являются причиной вашего исключения.

person mrabat    schedule 01.09.2011

Часть begin ... end или initialization в dll является эквивалентом Delphi для dllmain в C++. Таким образом, применяются те же ограничения, включая:

  • Не вызывайте CoInitialize
  • Не вызывать COM-функции

Это означает, что вы не можете создать соединение ADO.

Знаете ли вы, что происходит, когда вы звоните TADOConnection.Create(Application);?

Так что то, что вы пытаетесь сделать, не сработает. А даже если и было, то не стоит этого делать. Вот несколько лучших объяснений:

http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/dn633971(v=vs.85).aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/27/63401.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/28/63880.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2014/08/21/10551659.aspx

MSDN предлагает создать подключение к базе данных в ПолучитьВерсиюРасширения. Вот как инициализируется ваша isapi dll. Это не только для сообщения версии расширения. Так что это путь. Создайте свою собственную функцию GetExtensionVersion, которая инициализирует вашу базу данных, а затем вызовите предыдущую функцию Delphi.

library Project1;

uses
  Winapi.ActiveX,
  System.Win.ComObj,
  Web.WebBroker,
  Web.Win.ISAPIApp,
  Web.Win.ISAPIThreadPool,
  Winapi.Isapi2,
  Winapi.Windows,
  WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule};

{$R *.res}

function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
begin
  Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver);
  // create your ado connection here
end;


exports
  GetExtensionVersion,
  HttpExtensionProc,
  TerminateExtension;

begin
  CoInitFlags := COINIT_MULTITHREADED;
  Application.Initialize;
  Application.WebModuleClass := WebModuleClass;
  Application.Run;
end.
person Sebastian Z    schedule 16.01.2015
comment
Это имело бы смысл, если бы не тот факт, что код инициализации для TISAPIApplication делает некоторые очень интересные вещи в этом сегменте, включая работу с COM, насколько я могу судить из беглого изучения кода VCL сейчас. Однако, возможно, TADOConnection.Create(Application) просто слишком интересен .... - person Vector; 16.01.2015
comment
Я не вижу ничего страшного в отношении dllmain в TISAPIApplication прямо сейчас. Даже если бы это было так, это не означало бы, что все в порядке. Есть шанс, что вам что-то сойдет с рук, но это может сработать сейчас и потерпеть неудачу завтра. Можно создавать обычные классы, если они не вызывают COM-функции во время создания. Я добавил еще две ссылки на msdn, которые объясняют проблему. - person Sebastian Z; 17.01.2015
comment
То, что я увидел, было не в самом приложении TISAPIA, а в одном из родителей где-то там. Неважно - я вижу в блогах MSDN, что можно сделать что-то злое и не попасться на удочку - это, по сути, попадание или промах - но, по-видимому, TADOConnection.Create всегда промах. Вы проделали необходимую работу, и я думаю, что принял ваш ответ и дал вам награду. Я подожду немного и посмотрю, появится ли что-то более конкретное или, возможно, хорошее решение. - person Vector; 17.01.2015
comment
Я подозреваю, что после инициализации TISAPIApplication и вызова Application.run это должно быть безопасно. Но я не могу найти хороший хук, который можно было бы использовать для кода ADO - где-нибудь, чтобы вызвать его после того, как Application.run был вызван, и приложение готово обрабатывать запросы, но до того, как запрос поступит. - person Vector; 17.01.2015
comment
Application.run по-прежнему вызывается из dllmain. Так что это все равно небезопасно. Но я обнаружил, что msdn предлагает использовать GetExtensionVersion для инициализации базы данных. Так что это путь. Я добавил это в свой ответ. - person Sebastian Z; 17.01.2015
comment
Application.run по-прежнему вызывается из dllmain. Так что это все еще небезопасно Да, я думаю, что в какой-то момент я попытался поместить код ADO после Application.run, и он все равно завис. Я попробую ваше новое предложение - должен быть какой-то крючок, который можно использовать для этого - GetExtensionVersion - на который я никогда не обращал внимания, похоже, это может быть то, что я ищу. - person Vector; 17.01.2015
comment
GetExtensionVersion не работает. Надо будет попробовать еще раз, более тщательно. - person Vector; 19.01.2015
comment
Я думаю, вам следует попробовать еще раз более тщательно. MSDN говорит: Инициализация обрабатывается функцией точки входа GetExtensionVersion. Роль этой функции заключается в выполнении всей инициализации, включая создание рабочих потоков, объектов синхронизации, подключений к базе данных, а также в установке версии ISAPI, которая использовалась для построения DLL. - person Sebastian Z; 19.01.2015
comment
Наконец-то я заработал - мне пришлось сначала очистить какой-то другой код, который незаконно выполнялся в DLLMain. Теперь я подключил все это к GetExtensionVersion , и, похоже, все работает хорошо. - person Vector; 23.01.2015