Server Access Programmer's Guide:
Using eRoom SAAPI with Microsoft .NET
As of eRoom version 7.01, SAAPI can be used from Microsoft.NET languages,
with some important restrictions.
The following requirements and restrictions continue to apply with the
following caveat:
Due to the technological restrictions by Microsoft, eRoom allows Visual
Basic .NET custom extensions. However, custom extensions should implement
Marshal.ReleaseComObject() to release the associated eRoom COM objects.
Requirements and Restrictions
eRoom version 7.01 or later. Do not use eRoom V6 or
earlier with Microsoft.NET
You cannot use .NET to implement eRoom Extensions directly
(including custom frames, dialogs, custom commands, viewers, or UIEvents,
nor can .NET components be used in synchronous event handlers. eRoom extensions
are still native ASP/COM.
SAAPI must be called from a Single Threaded Apartment
(STA) thread. Do not use SAAPI in .NET components or web services or other
non-STA applications.
IERUApplication::Close() must be called before returning
from your code while still on the same thread. It is imperative that you
use the ERUNetApplication object (”r;eRoom.NETApplication”)
object and call IERUApplication::Close() prior to returning from your
code. This allows eRoom to release underlying storage objects while on
the same execution thread. This is required since .NET objects are non-deterministically
destroyed on another thread by the .NET garbage collector. Failure
to call ::Close() before returning control from your .NET code can cause
serious problems on the eRoom server.
Always use AspCompat=true in ASPX pages that use SAAPI
The ASPNET user must have ”r;Act as part of Operating
System” rights. (Controlled via ”r;Local Security Policy”
in Control Panel.)
When using ERUUserContext objects (via IERUApplication::LoginUser()
or new ERUUserContextClass() & ImpersonateUser()) to temporarily establish
a user context, you must release the underlying storage objects by calling
Marshal.ReleaseComObject or the new 7.2.1 IERUClosable::CloseObject()
method. Failure to do so will leave the usercontext active until
IERUApplication::Close() is called to close all objects created in the
current thread.
eRoom.eRoomAPI Primary Interop Assembly
We recommend that you use the eRoom Primary Interop assembly when accessing
eRoom SAAPI from Microsoft.NET. This allows eRoom types to be passed between
.NET components. This is a strong named assembly. By default, the eRoom.eRoomAPI.dll
file is installed in the c:\Program Files\eRoom\eRoom Server directory
with eRoom release 7.2.1.
You must register the eRoom Primary Interop assembly (eRoom.eRoomAPI.dll)
with Global Assemble Cache (GAC) before it can be referenced from your
.NET applications. If this is already registered, you should unregister
eRoom.eRoomAPI.dll and re-register it.
To unregister eRoom’s Primary Interop Assembly, execute the following
command:
gacutil /u c:\program files\eRoom\eRoom Server\eRoom.eRoomAPI.dll
To register eRoom’s Primary Interop Assembly, execute the following
command:
gacutil /i c:\program files\eRoom\eRoom Server\eRoom.eRoomAPI.dll
Note: You must re-register
the eRoom Primary Interop assembly when you upgrade the eRoom server.
Strong named assemblies are required if the assembly is referenced by
another assembly with a strong name. You must use strong names when the
assembly is to be exposed to COM as a COM component, and when the assembly
is to be installed to the GAC. Using strong named imports of eRoomAPI
is recommended for these reasons.
The default Visual Studio mechanism for referencing a COM component
produces a non-strong named import in both VB and C#. In C#, it is possible
to give the import a strong name using properties on the reference. In
VB there is no way to do this within the IDE.
Using eRoom.eRoom API Primary Interop Assembly
If you have not already registered the Primary Interop Assembly with
GAC, see the instructions for doing so in the previous section.
To add a reference to the eRoom Primary Interop Assembly from MS Visual
Studio .NET, select ”r;.NET” tab in the ”r;Project-References&ldots;”
dialog. After clicking ”r;Browse&ldots;”, select the ”r;eRoom.eRoomAPI.dll”
assembly from you eRoom server directory.
For Example:
”r;\Program Files\eRoom\eRoom Server\eRoom.eRoomAPI.dll”
After adding a reference to your application, you access eRoom.eRoomAPI
types by adding a "using" statement to classes and modules.
For example, ”r;using eRoom.eRoomAPI”
Using System.Array and IERUCollection for VARIANT parameters
SAAPI uses the VARIANT datatype containing SAFEARRAY for a number of
properties and method input and outputs. Note that SAAPI arrays are always
returned as 1-based SAFEARRAYS. SAAPI will accept either an object containing
a SAFEARRAY or an ERUCollectionClass() object for parameters that accept
a SAFEARRAY.
// Using System.Array for SAFEARRAY
System.Array acells = (System.Array)query.GetRowCells(resultNumber);
for(int j = 1;j <= acells.Length;j++ )
{
Response.Write ( "cell "+ j + "="+ acells.GetValue(j)
+ "<br>");
}
See the TestNetWebApp example--referred to in the Using
SAAPI in .NET ASPX Pages section below--for more System.Array and
ERUCollectionClass() examples.)
Releasing the ERUUserContext After Impersonation
If your code uses IERUApplication::LoginUser() or new ERUUserContextClass(),
and its ImpersonateUser() method to establish a eRoom user context under
.NET, you must explicitly destroy the underlying storage objects to return
to the previous user context. This can be done by calling the Microsoft.NET
Marshal.ReleaseComObject() method or calling IERUClosable::CloseObject()
method (added in 7.2.1). This will release the eRoom user context associated
with the object and return you to the previous user context (if any).
Note that you do not need to explicitly release the user context with
::ReleaseComObject or ::CloseObject() if you don’t plan to return
to the previous user context before calling ERUNetApplication::Close().
When writing libraries/helper functions that establish a user context
temporarily, you must release the user context immediately to ensure that
the caller returns to their preexisting user context.
Building .NET Applications
Example .Net Application
SAAPI must be called from an Single Thread Apartment (STA) thread. For
a simple, one thread application, such as a console or form, you can use
the STAThread attribute. In C#, you can achieve this as follows:
using System;
using System.Threading;
using System.Diagnostics;
using eRoomAPI;
namespace DotNetSAAPISample
{
class SAAPISample
{
[STAThread]
static void Main(string[] args)
{
new SAAPISample().DoWork();
}
void DoWork()
{
IERUApplication eRoomApplication
= null;
try
{
ApartmentState apartmentState
= Thread.CurrentThread.ApartmentState;
if (apartmentState != ApartmentState.STA)
throw new ApplicationException("The SAAPISample.DoWork
+ method must be invoked from an STA thread.);
eRoomApplication = new ERUNetApplicationClass();
Console.WriteLine(
"The eRoom Server version
is "
+ eRoomApplication.VersionNumber + ".");
}
finally
{
if(eRoomApplication != null)
eRoomApplication.Close();
}
}
}
}
VB.Net projects are STAThread by default.
Note that the test of Thread.CurrentThread.ApartmentState is redundant
with the use of STAThread attribute. Where the Thread.CurrentThread.ApartmentState
test is especially important is when the code using eRoom objects is within
a .dll or other method where the developer of the eRoom code cannot guarantee
that the calling code is an STA thread.
One common example of this is .Net WebMethod, such as a .asmx page.
The AspCompat attribute that works to give an .aspx page a STA threading
at invocation of the Page_Load event does not work with .asmx pages. Consequently,
an application using SAAPI from a WebMethod needs to spawn a second STA
thread, or use a thread pooler that can return an STA thread. In this
case, it is a good idea to instantiate all eRoom objects in a try block
and call IERUApplication.Close() from a finally block. This is true for
C#. If there is any doubt of your threading model, be certain to programmatically
verify that you are running in an STA.
There are various ways to spawn an STA thread or construct a thread
pooler. Consult Microsoft.NET documentation or MSDN for details on .NET
programming.
Using SAAPI in .NET ASPX Pages
You cannot write eRoom extensions directly as ASPX pages, but you can
use eRoom SAAPI in .NET ASPX pages within some guidelines. See Requirements
and Restrictions.
Always use AspCompat=true
You must use AspCompat=true in your ASPX pages. For example:
<%@ Page Language="vb" AspCompat=true AutoEventWireup="false"
Codebehind="WebForm1.aspx.vb" Inherits="TestNetWebAppp.WebForm1"%>
Note: Using AspCompat=true
does not cause all page processing to use an STA. Only processing in or
after the Page_Load event, and before or during Page_Unload, is STA. Never
instantiate eRoom objects from code that executes globally to the module.
See ”r;correct” and ”r;incorrect”
examples below.
Always call ERUNetApplication::Close() before returning from your code
You must call ERUNetApplication::Close() while on the same thread that
was used to acquire a SAAPI object. This allows eRoom to dispose of underlying
storage objects while on the same thread that created the objects. There
should be no execution path (including errors) that doesn’t call
::Close() just prior to returning. This is best accomplished by using
Page_Unload to call ERUNetApplication::Close(). Alternately, use a finally{}
block or error handlers to ensure that all paths call ::Close() prior
to returning control.
Example .NET ASPX Web Application
Please refer to the ”r;TestNetWebApp” eRoom sample for a
complete C# ASP.NET example. The sample is located in the ...\Toolkit\Samples\MSDotNet\TestNetWebApp
directory.
Installing the TestNetWebAppSample
Register the eRoom Primary Interop Assembly with the
Global Assembly Cache Utility. (See the section eRoom.eRoomAPI Primary
Interop Assembly above for more information.) Note: You must re-register
the eRoom Primary Interop Assembly when you upgrade to the eRoom server.
On the eRoom Server, copy the contents of TestNetWebApp.zip
to your IIS server (ie. \INetPub\wwwroot).
On the Windows Start menu, go to Settings > Control
Panel > Administrative Tools and double click on Internet Services
Manager.
Select the Default Web Site, then right click on the
TestNetWebApp folder and choose Properties.
On the Directory tab, click the Create button in the
Application Settings section, then select Low (IIS Process) from the Application
Protection menu.
On the Directory Security tab, click the Edit button
in the Anonymous access and authentication control section. On the Authentication
Methods dialog, make sure that Anonymous access is selected, and all options
under Authenticated acess are deselected. Click OK to close the Authentication
Methods dialog.
Click OK to close the TestNetWebApp Properties dialog.
On the Windows Start menu, go to Settings > Control
Panel > Administrative Tools and double click on Local Security Policy.
Under Local Policies, select User Rights Assignment. On the Policy list,
double-click on Act as par of the operating system.
On the Local Security Policy Setting dialog, verify
that the Effective Policy Setting is enabled and click OK.
On the Select Users or Groups dialog, choose ASPNET
from the Name list, click Add, then click OK.
Now you should be able to run the TestNetWebApp sample.
Open your browser and go to the following URL: http://servername/TestNetWebApp/WebForm1.aspx
Enter an eRoom Name and Password and log into eRoom.
Select a room from the User’s Member eRooms list and click OK. You
should see a list of the items in the Home Page for the eRoom.
Correct and Incorrect Examples
Below you will find the important excerpts from the ”r;TestNetWebApp”
example. This C# Web application uses a codebehind class to manage the
eRoom application object and call IERUApplication::Close() in Page_Unload().
Correct Examples:
File 1 of 2: (TestNetWebApp\WebForm1.aspx)
<%@ Page language="c#" AspCompat=true Codebehind="WebForm1.aspx.cs"
AutoEventWireup="false" Inherits="TestNetWebApp.WebForm1"
%>
!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR"
Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE"
Content="C#">
<meta name="vs_defaultClientScript"
content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1"
method="post" runat="server">
<asp:Button id="OKButton"
style="Z-INDEX: 103; LEFT: 230px; POSITION: absolute; TOP: 100px"
runat="server" Width="100px" Height="28px"
Text="OK"></asp:Button>
</form>
</body>
</HTML>
File 2 of 2: (TestNetWebApp\WebForm1.aspx.cs/ application)
// Another example using system using statements omitted
for clarity.
using System.Threading;
using eRoomAPI;
using System.Runtime.InteropServices; // needed for
COMException and ReleaseComObject
namespace TestNetWebApp
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
public class WebForm1 : System.Web.UI.Page
{
// Declare an interface for our
application object
IERUApplication eRoomApplication;
protected System.Web.UI.WebControls.Button
OKButton;
private void Page_Load(object
sender, System.EventArgs e)
{
// Create the eRoom NET application
object
eRoomApplication = new ERUNetApplicationClass();
Response.Write("Page_Load:
The eRoom Server version is "
+ eRoomApplication.VersionNumber
+ ".<br>");
}
private void Page_Unload(object
sender, System.EventArgs e)
{
If (eRoomApplication != null)
{
// release eRoom objects while
still on same execution thread
eRoomApplication.Close();
eRoomApplication = null;
}
}
private void InitializeComponent()
{
this.OKButton.Click += new System.EventHandler(this.OKButton_Click);
this.Unload += new System.EventHandler(this.Page_Unload);
this.Load += new System.EventHandler(this.Page_Load);
}
private void OKButton_Click(object
sender, System.EventArgs e)
{
try {
// access the eRoom site via SAAPI
IERUUserContext uc = new ERUUserContextClass();
// Establish user context as builtin
site admin user.
// Alternately, you could determine
the loggedin eRoom user from // session cookies.
uc.ImpersonateUser (eRoomAPI.ERUBuiltinUserID.erUserSiteAdminsitrator,
eRoomAPI.ERUParameterType.erParamTypeID);
// Access the eRoom site object
IERUSite site = uc.Site;
Response.Write ("Site="
+ site.Name);
}
catch (Exception e) {
// handler error here
}
finally
{
// revert to previous user
If (uc !=null)
{
// Release the user context NOW.
// WARNING: Failure
to release user context will result
// in
this user context remaining active for the caller.
// Note: eRoom 7.2.1 adds IERUClosable;
// You can use Marshal.ReleaseObject
for same result.
// IERUClosable closable = (IERUClosable)uc;
// closable.CloseObject();
Marshal.ReleaseComObject(uc);
uc=null;
}
}
}
}
}
In the correct example, the ERUNetApplicationClass() is instantiated
in Page_Load and the Close() method is called in Page_Unload.
Incorrect example:
<%@ Page Language="C#" AspCompat=true %>
<%@ Import namespace="eRoomAPI" %>
<script runat="server">
IERUApplication eRoomApplication = new ERUNetApplicationClass();
public void Page_Load()
{
}
</script>
Note that in the incorrect example, the ERUNetApplicationClass is instantiated
at the module level (i.e., in the page constructor) before Page_Load.
SAAPI Must be called from a Single Threaded Apartment (STA)
Calls to eRoomAPI (SAAPI) must be from an STA thread.
You can verify that you are running in an single threaded apartment
(STA) by using the following Microsoft.Net code:
ApartmentState apartmentState = Thread.CurrentThread.ApartmentState;
if (apartmentState != ApartmentState.STA)
<throw new System.Exception("Must
be run in STA!");
Note: To access System.Threading,
you must add the appropriate reference to the top of your module:
Visual Basic:
Imports System.Threading
C#:
using System.Threading;
Troubleshooting Errors and Problems
There a few specific errors that are returned by SAAPI when working
in the .NET environment.
EROOM_E_SAAPI_OBJ_CLOSED (H8004039B) -- You are trying to access
a SAAPI object after calling ERUNetApplication::Close() method. You shouldn’t
access SAAPI objects after calling ::Close().
EROOM_E_SAAPI_OBJ_WRONG_THREAD_OWNER (H8004039C) &endash;- You are trying
to access an object that was created on a different thread. You can only
access SAAPI objects on the thread in which they were created.
eTrace.exe is eRoom’s debug tracing facility. Running eTrace with
levels Server=2, Storage=3 will show when facility and site connections
are aquired and released.
You may trace your own debugging information to eTrace’s ”r;Custom”
subsystem by using the ERUTraceMessageClass().
For example:
IERUTraceMessage etrace= new ERUTraceMessageClass();
etrace.Trace (3, ”r;SomeContext::Something happened!”);
etrace.TraceError (”r;SomeContext::Something Bad Happend!”);
Output traced with ::Trace() method will appear in eTrace.exe if the
”r;Custom” subsystem’s trace level is greater than or
equal to the traced level. ::TraceError() will always trace regardless
of trace level. |