Server и отчет из BLOB-а
Все еще изучаю возможность использовать FR 3 в качестве сервера отчетов у нас на предприятии. Очередная забавность.
Все отчеты лежат в базе в BLOB-е. Судя по документации, сервер может загрузить такой отчет используя событие OnGetReport. Чем и пользуюсь. И при попытке посмотреть такой отчет в IE получаю на сервере системную ошибку "отказано в доступе".
Расследование показывает, что ошибка возникает на RecreateWnd при присвоении DialogPage.BorderStyle. Что в общем-то и естественно, учитывая что создает диалоговое окно (т.е. загружает отчет из базы) один поток, ( Synchronize(DoOnGetReport) ), а строит отчет другой...
При этом примеры отчетов из поставки работают отлично, т.к. там загрузка отчета из файла идет в том же потоке, где он будет потом строиться.
Все отчеты лежат в базе в BLOB-е. Судя по документации, сервер может загрузить такой отчет используя событие OnGetReport. Чем и пользуюсь. И при попытке посмотреть такой отчет в IE получаю на сервере системную ошибку "отказано в доступе".
Расследование показывает, что ошибка возникает на RecreateWnd при присвоении DialogPage.BorderStyle. Что в общем-то и естественно, учитывая что создает диалоговое окно (т.е. загружает отчет из базы) один поток, ( Synchronize(DoOnGetReport) ), а строит отчет другой...
При этом примеры отчетов из поставки работают отлично, т.к. там загрузка отчета из файла идет в том же потоке, где он будет потом строиться.
Комментарии
Оказывается, чтобы отчет, загруженый на frxReportServer.OnGetReport, надо после загрузки отчета из базы обязательно прописать Report.FileName (по крайней мере для отчетов с формами запроса, я делаю Report.FileName := ReportName, хотя, похоже, неважно что там написать - лишь бы не пустую строку). Иначе после заполнения формы получим от сервера 403 - нет доступа.
Фишка в том, что если при разборе заголовка запроса в frxServerSession.ParseHTTPHeader не находится фраза report="ReportName", то возвращает как раз 403. А этот самый ReportName в заголовке прописывается в TfrxReportSession.DoSaveForm как WebForm.ReportName := ExtractReportName(FReport.FileName).
Вот.
P.S. Ничего что я здесь об ошибках? А то, к сожалению, на ru-boarde соответствующая тема больше авторами не читается, в том числе и мои пожелания/замечания/исправления там нечитаны уже недели три лежат
Так вот, есть еще маленькое предложение по облегчению жизни пользователям
Если в TfrxReportSession.ShowReportDialog добавить две строчки
FReport.DoNotifyEvent(Page, Page.OnActivate);
и
FReport.DoNotifyEvent(Page, Page.OnDeactivate);
после DialogActive := True и DialogActive := False соответственно.
Тогда не приходится в отчетах создавать пустую страницу для датасетов и присвоения их параметрам значений из диалоговой формы, а можно датасет положить прямо в диалоговую форму, которая запрашивает параметры отчета и на OnDeactivate написать что-нибудь вроде
procedure Page3OnDeactivate(Sender: TfrxComponent);
begin
Set('InvoiceNumber', edInvoiceNum.Text);
end
а параметру в датасете сказать, что значение его берется из InvoiceNember.
Этим достигается возможность тройственного использования отчета
1. Выбрать в приложении в гриде нужную запись и сказать напечатать отчет по ней, при этом передать в отчет переменную InvoiceNumber и скрыть диалоговую форму запроса.
2. Показать отчет из приложения с запросом нужной записи через диалоговую форму отчета.
3. То же самое, но не из приложения, а в web-браузере
а какой выход ты нашел?
Так убрал Synchronize , благо при загрузке отчета ничего потокозависимого не делается
связи не вижу..блин
Хотя бы юнит в uses. Не знаю почему - не разбирался еще, но где-то он пытается к этому ADO обращаться.
все равно сыпет
в чем дело?
ведь при вызове synchronize поток,строящий отчет,замораживается до загрузки отчета
Тогда ищи что изменял сам, тут больше ничего сказать не могу. У меня на CoInitialize ругался, когда я пытался выкинуть ненужный мне ADO из сервера. Разбираться в проблеме не стал, просто вернул все на место.
Потому что отчет загружается в одном потоке (основном потоке приложения), при этом загружается диалоговая страница и, видимо, создается форма (т.е. окно) для нее. А строится отчет в другом потоке, при этом он пытается установить этой диалоговой форме BorderStyle bsDialog вместо bsSized. Для этого делается RecreateWnd, который на самом деле разрушение окна с последующим его созданием с новыми свойствами. Получается, что создано окно одним потоком, а разрушить его пытается другой. Что, согласно msdn невозможно. Вот система и говорит, что у потока, который строит отчет нет доступа к окну, созданному в основном потоке приложения.