Hi Scott,
My company markets a helpdesk application that is web based and for
performing database administration tasks we have a custom application (.exe)
that is client/server based. This app is written using pure SDK (no
MFC/ATL/OWL). This code is pretty old (nearly a decade) and it supports only
English characters. It comprises of 3 main dlls which are pure C dlls (no
COM) with exported C++ classes. For the next release of our main app, we
intend to support multiple languages and so the management decided to
convert this legacy app also to web based. We have written a .NET wrapper
(written in C# and managed C++) which eventually calls into the low-level
dll's and the UI is in ASP.NET. The UI calls into the wrapper which in turn
calls into the legacy dlls. And for the most parts this works ok. But in the
old app, whenever you performed any operation a small window would open and
display status messages. For example when you performed an operation to
check the database for correctness, it would show messages like 'Checking
views...', 'Checking tables'... and so on. The calls to display these
messages are placed in the low-level dlls.
Now my job is to display these progress messages through the ASP.NET UI.
This is quite a challenge and we have figured out how to get the progress
message calls to end-up at the ASP level using delegates at the wrapper
level and function pointers at the dll level and this also works just fine.
So when the dll makes a call to show a progress message, it calls a method
at the low-level which calls the function pointer which points to the
delegate in the UI. So when my UI method is called, I just have to send it
the client (which is a browser). And this is anything but trivial.
I looked at a very good article on MSDN by Dino Esposito (Build your ASP.NET
pages on a richer Bedrock) but his solution just tells you when the
operation completes. Its not interactive. I have to mimic the exact behavior
of the old app, which is to open a small window and display the messages
within that window and then to close the window once the operation
completes.
For testing purposes I created a test page (Test2.aspx) that does NOT have a
code-behind class. And took the following steps:
1. Created a marker interface IRequireProgressMessage.
2. Put a <%@ Implements interface="IRequireProgressMessage" %> directive at
the top of my page Test2.aspx.
3. I wrote a server-side script inline within the page inside the <body> tag
and just before the <form> tag.
4. Within the block I put a check for postback and if it succeeds then I
emitted the following client-side script:
<script >
var oWin = window.showModelessDialog("blank.html",'','');
var oDoc = oWin.document;
</script>
5. After emitting this I flush the buffer just to be sure. At this point we
haven't yet called into the wrapper and the client has opened a modeless
window for displaying the progress messages. blank.html contains a single
<div> element with the id 'msgDiv'.
6. After this I setup the infrastructure to receive calls to SetMessage from
the dlls. This involves implementing an interface with one method
SetMessage(string strMessage). The implementation of SetMessage emits the
following client side script:
"<script>oDoc.all.msgDiv.innerHTML= '" +strMessage + "' </script>"
after checking whether the current handler
(System.Web.HttpContext.Current.Handler) is of the type
IRequireProgressMessage.
7. Call a method of the wrapper object. For this I created a test() method
on the wrapper which wraps a test() method on one of the lower level
objects. This method sends 2 messages with a delay of 2 seconds.
This works fine and the two messages are displayed in the modeless dialog.
However, this works only when you have the script within the .aspx page. But
the UI guys have already finished most of their work and they have used
code-behind throughout. The calls into the wrapper are placed from within
the Page_Load method. Now the problem with Page_Load is that if I emit my
script there and even if I flush the buffer, it won't get sent to the
browser. I don't know what goes on behind, but I think it just gets buffered
into another intermediate buffer and is emitted when Render gets called. So
I thought of overriding the Render method, move all of the processing from
Page_Load into the overridden method and then call into the wrapper after
rendering the <body> tag. For this I derived a new class from
System.Web.UI.Page, overrode the Render method. In this method I loop the
Controls collection and keep a check whether we rendered a LiteralControl
with the text "<body" in it. If it does it calls a virtual method
PostBodyRender. This method will be overridden by the code-behind class and
from within this do the processing that Page_Load used to do. It will first
emit the script that gets the modeless dialog to display if the page is
posted back and then do the rest of the processing, which involves setting
up the messaging infrastructure and calling into the wrapper method.
And this too works fine, but (and a big bad BUT) it gets munged if there is
some in-line script within the page!!! All aspx pages set the <title> from
server-side code. When this happens there's only one item in the Controls
collection which is the HtmlForm object representing the single <form>
element in the page. I just can't figure out when is the rest of the html
rendered and what happens to the <body> element. I figured, I must be doing
something wrong in Render and felt that there must be some standard way of
overriding the Render method which led me to post this here.
Whew! This turned out to be quite a big post! I tried to be verbose just to
be sure you have all the information. Let me know if you need anything else.
Thanks in advance.
Shardul.