Database application development system with improved methods for distributing and executing objects across multiple tiers6006230Abstract A database client/server development system providing support for remote sessions with user-created application objects is described. When a user desires to create a "remoteable" object from a user object, the user assigns a proxy name or alias, thereby providing a mechanism to differentiate the real (local) version of the object from a remote version of that object. When the user creates the proxy, the system generates all of the interface definitions for the object (i.e., to access its internal functionality) which are capable of being proxied (i.e., "proxiable"). A given object can reside locally, remotely, or both; the proxy mechanism allows the application to resolve at runtime which version of the object to invoke. When the user's final application is deployed, the proxy object is deployed at the client machine for use at the client for accessing the real version at the server. If desired, an application can be deployed with a real copy of the object, in addition to the proxy. In such a case, the client can serve as a server (of that object), in addition to being a client of objects from other servers. Claims What is claimed is: Description MICROFICHE APPENDIX
______________________________________
/* Service Manager Interface */
/* Carries Connection-wide data */
typedef struct SMI.sub.-- Command
CHAR szPBAppname[MAXNAME - 1];
LONG lPBAppMajorVer;
LONG lPBAppMinorVer;
CHAR szPBComputer[MAX.sub.-- COMPUTER.sub.-- NAME.sub.-- SIZE];
BYTE bAcceptConnections: 1,
bDevtMode: 1,
bPCodeExe: 1,
bFlags: 5;
CLIENTID Client;
UINT iMaxBuf;
INT iMaxAgents;
LONG lReturnCode;
LONG lLastError;
LONG TimeOutThreshold;
HANDLE hServerConnection;
HANDLE hClientThread;
HANDLE hServerContext;
HANDLE hClientContext;
HANDLE hIntLock;
HINSTANCE hmDSE;
HINSTANCE hmDriver;
PB.sub.-- CRITICAL.sub.-- SECTION
csIntLock;
CHAR szConnectName[MAXPATH - 1];
ppbstg.sub.-- anchor
PBstg;
LPVOID pTransport;
LONG PBstgSubPool;
PSHHASH pshClient;
LPSTR lpszUserId;
LPSTR lpszPass;
LPSTR lpszConnectString;
PCS.sub.-- MSG pCmd;
LPSTR lpszPBLibraryList;
PSERVER.sub.-- STATS
pSrvStat;
pDPBTrace pTRACE;
LPVOID pUserData;
LPSTR lpszDriverParm;
LPVOID lpLocalConnect;
PRT.sub.-- ERROR.sub.-- STRUCT
pRuntimeError;
DWORD dwStartupTickCount;
HANDLE hTimerThread;
LPSTR lpszOptions;
PSHLIST pOptionList;
SMIFARPROC fpCommDriverCall;
} SMI.sub.-- COMMAND, FAR * PSMI.sub.-- COMMAND;
______________________________________
The variable name szPBAppname stores the name of the AppServer or application server. The variable name 1PBAppMajorVer stores the AppServer's major version. The variable name 1PBAppMinorVer stores the AppServer's minor version. The variable name szPBComputer stores the computer's "sharename." This indicates the location of the computer on the network. The variable name bAcceptConnections will return "TRUE" if transport is accepting connections; bDevtMode will return "TRUE" if in development mode; and bPCodeExe will return "TRUE" if running a P-Code executable; and bFlags stores unused or FLAG bits. The variable Client stores Client-side only information which serves as a Key of the Client connection. The variable iMaxBuf stores the maximum buffer size to allocate. The maximum buffer size is employed for communication buffers, on the server side. The variable iMaxAgents stores the maximum number of active (server) threads for service. The variable 1ReturnCode stores a return code for connect errors. The variable 1LastError stores a connect error Return code from GetLastError. The variable TimeOutThreshold indicates a timer threshold for connection. This data member is employed for detecting inactive clients (i.e., clients which have been inactive beyond the TimeOutThreshold). The variable hServerConnection stores a Handle to a dedicated server thread. The variable hClientThread stores thread ID (TID) of a Data Window worker thread. The variable hServerContext stores an Event Handle to server RPC lock. The variable hClientContext stores an Event Handle to client RPC lock. The variable hIntLock stores a Mutex Handle to lock interface across processes. The variable hmDSE stores a Module Handle to Remote Object Manager. The variable hmDriver stores a Module Handle to connection driver. The variable csIntLock stores a Critical Section object pointer--employed to synch threads. The variable szConnectName stores the Connection name. The variable PBstg stores an anchor to a PB stg, an internal PowerBuilder structure. The variable pTransport stores a pointer to a transport object. The variable PBstgSubPool stores a PB stg sub-pool (memory block). The variable pshClient stores a pointer to a client context table. The variable lpszUserId stores a Login user ID. The variable lpszPass stores a Login user password. The variable lpszConnectString stores Login user connectstring. The variable pCmd stores Client/Server Message structure. The variable lpszPBLibraryList stores .PBL Library list pointer. The variable pSrvStat stores Server statistics (e.g., how many reads and writes). The variable pTRACE stores a handle to trace structure. The variable pUserData stores a pointer or handle to user private data. The variable lpszDriverParm stores Driver Specific Parameters. The variable lpLocalConnect stores a pointer to connect object for a local loopback driver. The variable pRuntimeError stores a client-side pointer to an error (err) struct returned on call. The variable dwStartupTickCount stores a TickCount to protect against counter roll-over. The variable hTimerThread stores a Handle of a timeout daemon. The variable lpszOptions stores a pointer to driver/transport options. The variable pOptionList stores a pointer to options parsed into a list. The variablefpCommDriverCall stores a function pointer to "DPBSendRequest" entry point of communication driver. B. Methodology At the outset of a distributed PowerBuilder session, a connection object (ConnectObject) is created (as previously described). In an exemplary embodiment, the object is created from a PowerBuilder connection class, which may be constructed as follows.
______________________________________
system type Connection from ConnectObject
// . . . contstructor/destructor code
public:
function long ConnectToServer () &
system library "pbdse050.d11" &
alias for "PBOBJIConnectToRemote"
function long DisconnectServer () &
system library "pbdse050.d11" &
alias for "PBOBJIDisconnectRemote"
indirect string UserID {UserIDSet(*value),UserIDGet()}
indirect string ConnectString
{ConnectStringSet(*value),ConnectStringGet()}
indirect string Password {PasswordSet(*value),PasswordGet()}
function long GetServerInfo (ref ConnectionInfo info[]) &
system library "pbdse050.d11" &
alias for "PBOBJIGetRemoteInfo"
function long RemoteStopListening() &
system library "pbdse050.d11" &
alias for "PBOBJIRemoteServerStopListen"
function long RemoteStopConnection(string ClientID) &
system library "pbdse050.d11" &
alias for "PBOBJIRemoteServerStopUserConnection"
event Error(uint ErrorNumber, &
readonly string ErrorText, &
readonly string ErrorObject, &
readonly string ErrorScript, &
uint ErrorLine, &
ref ExceptionAction Action, &
ref any ReturnValue)
end type
______________________________________
The connection object returns an array of "ConnectionInfo," from its GetServerInfo method. Each ConnectionInfo object, in turn, is an instance of the following PowerBuilder system class, ConnectionInfo.
______________________________________
system type ConnectionInfo from Structure
string Location
string UserID
string ClientID
datetime
ConnectTime
datetime
LastCallTime
long CallCount
boolean.
Busy
end type
______________________________________
As shown, a ConnectionInfo object stores information characterizing a particular server connection. The connection object itself includes functionality for supporting a server connection, such as ConnectToServer and DisconnectServer methods. Additionally, the object stores strings supporting each connection, including a UserID, a ConnectString (context), and a Password. The ConnectionObject also includes methods or functions supporting remote communication, including RemoteStopListening and RemoteStopConnection methods. A proxy object on the client is created as an object instance from a PowerBuilder system class, RemoteObject, which may be constructed as follows.
______________________________________
// remoteobject represents the proxy object on client
system type RemoteObject from NonVisualObject
protected:
private ObjHandle handle
// ctor
subroutine create.sub.-- object (readonly String s) &
system library "pbdse050.d11" &
alias for "PBOBJICreateRemoteObject"
//dtor
subroutine destroy.sub.-- object () &
system library "pbdse050.d11" &
alias for "PBOBJIDestroyRemoteObject"
public:
function any invoke.sub.-- method (readonly string s, uint ns, &
ref any args[]) &
system library "pbdse050.d11" &
alias for "PBOBJIInvokeRemoteMethod"
subroutine SetConnect(Connection c) &
system library "pbdse050.d11" &
alias for "PBOBJISetConnectObject"
end type
______________________________________
The proxy (remote) object which inherits from NonVisualObject (NVO), forwards any requests to the previously-mentioned InvokeRemoteMethod API routine. The proxy object also includes a SetConnect method call, (corresponding to the SetConnect handler 534). The CreateConnectObject method is invoked (by the object manager) when a ConnectObject is created. In an exemplary embodiment, the method or function may be constructed as follows.
______________________________________
//
// this function is called by the Object manager when a CONNECT
// object is created.
//
extern "C" PBWINAPI (INT, PBOBJICreateConnectObject)
PRT.sub.-- THIS rtThis,
UINT uiArgCount
)
{
LPSTR lpszClassName=0;
BOOL bNull = FALSE;
OB.sub.-- INST.sub.-- ID obInst;
PVOID pDispatcher = NULL;
PBOBJIConnect *pConnect=0;
POB.sub.-- DATA pDispObject=0, pobStringArg=0;
// if we are building an executeable,
// then don't actual do anything.
RETURN.sub.-- IF.sub.-- BUILDING.sub.-- EXECUTEABLE(rtThis)
// Get the object instance for which the call was made
ot.sub.-- get.sub.-- curr.sub.-- obinst.sub.-- expr(rtThis, &obInst,
&bNull);
if (bNull)
return FAILURE; // this error should never occur
pobStringArg = ot.sub.-- get.sub.-- next.sub.-- evaled.sub.-- arg
(rtThis);
lpszClassName = ot.sub.-- get.sub.-- data.sub.-- str (rtThis,
pobStringArg);
if (bNull)
return FAILURE; // this error should never occur
// allocate our own internal connection object which we will hang
// off of the powerbuilder connection object.
pConnect = new(rtThis->stgthis,0) PBOBJIConnect(rtThis);
pDispatcher = (PVOID)pConnect;
pDispObject = ob.sub.-- get.sub.-- field.sub.-- data(rtThis, obInst,
FLD.sub.-- REMOTEOB.sub.-- HANDLE);//XXXXXHandle);
if (!pDispObject)
{
delete pConnect;
return FAILURE; // this error should never occur
}
ob.sub.-- set.sub.-- data.sub.-- ptr(pDispObject, pDispatcher,
HANDLE.sub.-- TYPE, 0);
return SUCCESS;
}
______________________________________
As shown, the method is invoked with a "this" (i.e., runtime "this" or self pointer) together with a count of parameters or arguments. The actual arguments passed are not passed on the stack but, instead, passed off a runtime list. The method operates by retrieving the object instance for which the call was made. If the object instance is never instantiated, the method will return "failure." Instance data is extracted, including an argument string and class name. Now, storage can be allocated for the ConnectObject which is hung off the object instance (PowerBuilder ConnectObject). In the event that no errors occur, the method returns "success." A connection is created from the class PBOBJIConnect, which may be constructed as follows.
______________________________________
// define class representing Connection object
class PBOBJIConnect : public PBOBJICommon
PSHLIST pObList; // list of objects created off of this
connection
private:
UINT bConnected : 1, // true if we are connected
bCallInProgress : 1, // true if a call is in progress
bDisconnectAfterCall : 1, // true if we should disconnect after
call
bDestroyConnectAfterCall : 1; // true if we should disconnect/destroy
the connection after call
CLIENTID idClientConnect;
RT.sub.-- ERROR.sub.-- STRUCT rtErrorInfo; // runtime error information
OB.sub.-- INST.sub.-- ID obid.sub.-- connectobject; // The powerbuilder
object id for
this instance of
// the connect object.
VOID ClearError() // clear the current error information
{
SetErrorCode(0);
SetErrorString("");
}
public:
PBOBJIConnect(PRT.sub.-- THIS rt) : PBOBJICommon(rt)
{
bConnected = 0;
bCallInProgress = 0;
bDisconnectAfterCall = 0;
bDestroyConnectAfterCall = 0;
pbstg.sub.-- memset(&rtErrorInfo,0,sizeof(rtErrorInfo));
sm.sub.-- cmd.pRuntimeError = &rtErrorInfo;
BOOL bNullFlag;
ot.sub.-- get.sub.-- curr.sub.-- obinst.sub.-- expr(rtThis,&obid.sub.--
connectobject,&bNullFlag);
pObList = 0;
}
// PRE: IsInCall() == TRUE
void DestroyLater ()
{
bDestroyConnectAfterCall = 1; // set flag to destroy after
method invoke
PB.sub.-- ASSERT(IsInCall());
}
// PRE: IsInCall() == TRUE
void DisconnectLater()
{
bDisconnectAfterCall = 1; // set flag to Disconnect after
method invoke
PB.sub.-- ASSERT(IsInCall());
}
BOOL QDisconnectNow()
}
return bDisconnectAfterCall;
}
BOOL QDestroyNow()
{
return bDestroyConnectAfterCall;
}
BOOL IsInCall()
{
return bCallInProgress;
}
void SetInCallStatus(BOOL bInCall)
{
bCallInProgress = bInCall;
}
// connect to the server machine
BOOL LinkUpToServer(ppbstg.sub.-- anchor stgthis);
// disconnect from the server
BOOL DisconnectServer(LPSTR lpszClientId=0);
BOOL IsConnected()
{
return bConnected;
}
// virtual function from PBOBJICommon that returns true if this object
has been activated
// (ie; is connected or is listening for communications)
BOOL IsActive()
{
return IsConnected();
}
PRT.sub.-- THIS GetRtThis(void) const{return rtThis;}
PRT.sub.-- ERROR.sub.-- STRUCT GetrtErrorInfo() {return &rtErrorInfo;}
//runtime error information
// handle error processing, returns TRUE if an error occured
// bRetryOperation will be set to TRUE if the operation should be
retried
BOOL ErrorProcessing
(BOOL &bRetryOperation, BOOL &bValueWasReturned,BOOL
bTriggerApplicationEvent,BOOL& bSendANullBack);
// get remote client information
BOOL GetRemoteInfo
(POB.sub.-- DATA pClientInfoArray,LONG FAR * pItemCount);
// tell the remote server to stop listening
BOOL RemoteServerStopListen();
// returns the Powerbuilder object instance for connection object
POB.sub.-- INST.sub.-- ID GetPBObjectID()
{
return &obid.sub.-- connectobject;
}
// register a remote object as belonging to this server connection
void RegisterProxy(PBOBJIRemoteOB* pobProxy);
// unregister a remote object from this server connection
void UnRegisterProxy(PBOBJIRemoteOB* pobProxy);
// disconnect all remote objects from this server connection
void DisconnectObjects ();
};
______________________________________
As shown, the class includes a data member, pObList, representing a list of objects created off of this (object) connection. This represents a pointer list of all the objects which had been remotely instantiated. Also shown, the class includes a constructor which serves to reset flags and fill in the ConnectionObject instance handle. The class also defines several functions which return context information. For instance, QDisconnectNow queries the object for determining whether the connection can be disconnected. QDestroyNow queries the object for determining whether it can be destroyed. IsInCall returns information for indicating whether the object is currently processing a call. RegisterProxy serves to register a remote object as belonging to a particular server connection. This is how an object ends up in the private collection list of objects (that are instantiated). In a corresponding manner, UnRegisterProxy reverses the process--unregistering a remote object from the particular server connection. DisconnectObjects disconnects all remote objects from the server connection. The remote or proxy object, which exists at the client, is created from a RemoteObject class, RemoteOB, which may be constructed as follows.
______________________________________
// Define class representing the RemoteObject Powerbuilder class.
// This class will exist in the client environment and is used as
// the handle to the remote object on the server.
class PBOBJIRemoteOB
PBOBJIConnect* pConnect; // connection to the server
PBOBJI.sub.-- OBJECTID32 obidRemote; // handle to the Remote object
instance
LPSTR lpszRemoteClass; // remote object class name
PRT.sub.-- THIS rtThis; // points to local rtThis that this object is
an instance of
public:
PBOBJIRemoteOB(
PBOBJIConnect* pUseConnect,
LPSTR lpszRemoteClassName,
PRT.sub.-- THIS rtThis);
.about.PBOBJIRemoteOB();
LONG InvokeRemoteMethod(
LPSTR lpszName,
UINT iParameters,
POB.sub.-- DATA pArgArray);
void SetConnect (PBOBJIConnect* pUseConnect);
void ClearConnect()
{
pConnect = 0;
obidRemote = 0;
}
// return true if the object has been created on the server
BOOL IsActive()
{
return (0 != pConnect);
}
};
______________________________________
Of particular interest is the data member obidRemote. This represents a handle to the remote object instance. Additionally, the class includes a data member for storing a back pointer to a Connection Object. The system type RemoteObject, which inherits from Non VisualObject, may be constructed as follows.
______________________________________
// remoteobject represents the proxy object on client
system type RemoteObject from NonVisualObject
protected:
private ObjHandle handle
// ctor
subroutine create.sub.-- object (readonly String s) &
system library "pbdse050.d11" &
alias for "PBOBJICreateRemoteObject"
//dtor
subroutine destroy.sub.-- object () &
system library "pbdse050.d11" &
alias for "PBOBJIDestroyRemoteObject"
public:
function any invoke.sub.-- method (readonly string s, uint ns, &
ref any args[]) &
system library "pbdse050.d11" &
alias for "PBOBJIInvokeRemoteMethod"
subroutine SetConnect(Connection c) &
system library "pbdse050.d11" &
alias for "PBOBJISetConnectObject"
end type
______________________________________
The proxy objects created on the client side inherit from RemoteObject. As shown, the RemoteObject class includes a constructor and destructor. The constructor creates an instance of a RemoteObject. Invocation of the constructor leads to, in turn, invocation of a CreateRemoteObject method, which may be constructed as follows.
______________________________________
//
// this function is called by the Object manager when a RemoteObject
// object is created.
//
extern "C" PBWINAPI (INT, PBOBJICreateRemoteObject)
( - PRT.sub.-- THIS rtThis,
UINT uiArgCount
{
.
.
.
PBOBJIRemoteOB *pOb=0;
// allocate our own internal remote object which we will hang
// off of the powerbuilder object instance.
pOb = new (rtThis->stgthis,0) PBOBJIRemoteOB(0,lpszClassName,rtThis);
.
.
.
}
______________________________________
This leads to allocation of a remote object, PBOBJIRemoteOB. This is employed as a handle to the instance of the RemoteObject on the server. When SetConnect is called at the client, the corresponding SetConnect is invoked at the server. At that point, the RemoteObject is created. At that point, the client can use the handle to RemoteObject for invoking remote methods. The actual method for invocation of remote methods, InvokeRemoteMethod, may be constructed as follows.
______________________________________
LONG PBOBJIRemoteOB::InvokeRemoteMethod
LPSTR 1pszName,
// remote method name
UINT iParameters,
// number of parameters in 'pArgArray'
POB.sub.-- DATA pArgArray
// array of parameters
)
{
BOOL bRetryoperation;
// the invoke method is retried
if true
BOOL bValueWasReturned;
// TRUE if the user's
error script caused a value to be returned already
BOOL bSendANullBack = FALSE;
LONG rc = SUCCESS;
BOOL bAnErrorOccured = FALSE;
PSHLIST pSmiParameters;
PSMIDATA psmidataReturnType;
OB.sub.-- DATA
obReturnObjectInstance; // if the return value is a
structure
// then the instance will be in here
PBOBJIDeferMemoryRelease MemCollection(rtThis->stgthis);
do // repeat invoke method whie bRetryOperation is TRUE
{
pSmiParameters = 0;
psmidataReturnType = 0;
bRetryOperation = FALSE;
bValueWasReturned = FALSE;
bAnErrorOccured = FALSE;
bSendANullBack = FALSE;
if(pConnect->IsInCall())
// only one called allowed at a time
{
pconnect->SMIBlockPtrGet()->1Returncode = DSE.sub.-- CALL.sub.-- IN.sub.--
PROGRESS;
bAnErrorOccured = TRUE;
}
if(!bAnErrorOccured)
{
BOOL bHaveArrayVarInfoFlags = FALSE;
// true if we have array info flags accumulated
// for carrying out a method call.
USHORT FAR* pusVarInfoFlags = 0;
// points to
the array of var info flags.
INT iNumVarInfoFlags = 0;
// how many var info flags
are in the array.
pConnect->SetInCallStatus(TRUE);
// set the in method call
flag
pConnect->SMIBlockPtrGet()->1ReturnCode = 0
// default to passing the first parameter in 'pArgArray' to the
method,
// but for built in functions we can extract starting parameters
// out for our own usuage.
UINT iStartingPBArguementNumber = 0;
// should we trace
USHORT bTraceDSECalls =
DPBTraceQueryOption (pConnect->TraceInfoGet(), bDSECalls);
// allocate array of SMI parameter buffers
psmidataReturnType =
(PSMIDATA)pbstg.sub.-- alloc(rtThis->stgthis,sizeof(SMIDATA) *
(iParameters + 1),0);
pbstg.sub.-- memset(psmidataReturnType, 0, sizeof
(*psmidataReturnType) * (iParameters +1));
MemCollection.Add(psmidataReturnType);
psmidataReturnType[0].iDataType = SMI.sub.-- VOID;
OBJIParmSignatureDecode pMethodDescription(rtThis,1pszName);
// setup the first parameter as representing the return type
pSmiParameters =
PBOBJISetupReturnDefinition(
rtThis,
&pMethodDescription,
&psmidataReturnType[0],
pConnect->SMIBlockPtrGet(),
&obReturnObjectInstance,
MemCollection);
OBJIPOBDATAReferenceInfo
RefInfo(PBOBJISignatureGetRefParmCount(&pMethodDescription),
rtThis);
PBOBJIVarInfo
VarFlagInfo(rtThis);
PBOBJIBuildParameterList(
rtThis,
pConnect->SMIBlockPtrGet(),
iParameters,
pArgArray,
pSmiParameters,
psmidataReturnType,
iStartingPBArguementNumber,
&pMethodDescription,
&RefInfo,
VarFlagInfo,
MemCollection
);
// release structure name (if we got one)
pMethodDescription.ReleaseStructureName();
LPSTR 1pszVarInfo;
// if we are passing standalone arrays, get the var info flags (hex
string of flags)
if(1pszVarInfo = VarFlagInfo.GetStringRepresentation())
{
pMethodDescription.MergeVarInfo(1pszVarInfo);
}
if(pConnect->SMIBlockPtrGet()->1ReturnCode) // check for
failure code before the call
{
rc = FALSE;
}
else
rc = SMIClientInvokeObjectMethod(
// do the actual remote call
pConnect->SMIBlockPtrGet(),
pMethodDescription.GetSignatureString(),
obidRemote,
pSmiParameters);
// MethodInvokeTrace(pConnect, psmiParameters, 1pszName,
obidRemote,1pszRemoteClass
if (bTraceDSECalls)
{
CHAR szDummy[128];
CHAR szData[128];
LPSTR 1pszDummy;
LPSTR 1pszAttrName = 0;
LPSTR 1pszMethodName = 0;
LPSTR 1pszData;
LPSTR 1pszType = "Not implemented yet";
INT iLooper;
PSHLIST pParmList = pSmiParameters;
PSMIDATA pSmiData = 0;
PSMIDATA pSmiData2 = 0;
pbstg.sub.-- strncpy(szDummy, 1pszName,sizeof(szDummy) - 1);
1pszDummy = szDummy;
1pszData = szData;
if (pbstg.sub.-- strstr(1pszName, PBOBJIBuiltInPREFIX) == 1pszName) //
attribute access
{
BOOL bIsSet;
// get past '@' sign
1pszDummy ++;
// attribute name is stored as the dataitem of 2nd parm in parmlist
pSmiData = SMIGetFirstInterfaceItem(pSmiParameters);
pSmiData2 = SMIGetNextInterfaceItem(pSmiparameters);
1pszType = PBOBJIDecodeTypeToString(psmiData);
1pszAttrName = (LPSTR) (pSmiData2->1pDataItem);
bIsSet = !pbstg.sub.-- memcmp(1pszDummy,
PBOBJIBuiltInSETPARAMETER, pbstg.sub.-- strlen
(PBOBJIBuiltInSETPARAMETER));
// "Get/Set (obj id) ClassName.AttributeName, Type: typename,
SUCCEEDED/FAILED"
DPBTracePrintResourceMessageStart(pConnect->TraceInfoGet(),
DPB.sub.-- TRACE.sub.-- IDS.sub.-- 6)
(ULONG)obidRemote, (bIsSet ? : "Get"),
1pszRemoteClass, 1pszAttrName, 1pszType,
(rc ? "SUCCEEDED" : "FAILED")
DPBTracePrintResourceMessageEnd();
}
else // method invocation
{
// Extract methodname from signature
1pszMethodName = pbstg.sub.-- strstr(1pszDummy,
PBOBJIBuiltInPREFIX);
*1pszMethodName = 0;
1pszMethodName = 1pszDummy = szDummy;
// show parms
for (pSmiData = SMIGetFirstInterfaceItem(pSmiParameters),
iLooper = 0;
pSmiData != NULL;
pSmiData = SMIGetNextInterfaceItem(pSmiParameters), iLooper ++)
{
1pszType = PBOBJIDecodeTypeToString(pSmiData);
// Parm# out, Type: typestring, by Reference/Value
if (iLooper)
{
DPBTracePrintResourceMessageStart(pConnect->TraceInfoGet(),
DPB.sub.-- TRACE.sub.-- IDS.sub.-- 7)
,iLooper, 1pszType, (pSmiData->bDataByReference ? "Reference" :
"Value")
DPBTracePrintResourceMessageEnd();
}
else // first parm, ie return value
{
// "Called: (obj id) ClassName.MethodName"
DPBTracePrintResourceMessageStart(pConnect->TraceInfoGet(),
DPB.sub.-- TRACE.sub.-- IDS.sub.-- 4)
,(ULONG)obidRemote, 1pszRemoteClass, 1pszMethodName
DPBTracePrintResourceMessageEnd();
// "Return Type: typename, SUCCEEDED/FAILED
DPBTracePrintResourceMessageStart(pConnect->TraceInfoGet(),
DPB.sub.-- TRACE.sub.-- IDS.sub.-- 8)
,1pszType, (rc ? "SUCCEEDED" : "FAILED")
DPBTracePrintResourceMessageEnd();
}
} // for
} // method invocation
} // bTraceDSECalls
if(rc == SUCCESS)
{
// update reference arguments
RefInfo.BackPatch(pConnect->SMIBlockPtrGet(),pSmiParameters);
}
} // end of if we are not in the middle of a call already
bAnErrorOccured =
pConnect- >ErrorProcessing(bRetryOperation,bValueWasReturned,
TRUE,bSendANullBack);
pconnect->SetIncallStatus(FALSE);
// clear the in
method call flag
if (bAnErrorOccured)
rc = FAILURE;
if(bSendANullBack .vertline..vertline. (rc == SUCCESS
&& !bValueWasReturned))
{
// pass the return value back
PBOBJIDeliverReturnValue(rtThis,psmidataReturnType,
&obReturnObjectInstance,bSendANullBack);
}
SMIDestroyInterface(pConnect->SMIBlockPtrGet(),
pSmiParameters);
MemCollection.Release();
}
while(bRetryOperation);
if(pConnect->QDestroyNow()) // if we defered destroying
the Connection until after the method
// call then do it now.
{
// disconnect from the server
pConnect->DisconnectServer();
delete pConnect;
pConnect = 0;
}
else if(pConnect->QDisconnectNow()) // if we defered disconnecting
until after the method call.
{
// disconnect from the server
pConnect->DisconnectServer();
pConnect = 0;
}
return rc;
}
______________________________________
At the outset, the method establishes a "do" loop for resetting local variables and testing whether a call is in progress. If the method succeeds, it will only iterate through the loop once. After some additional housekeeping, the method will now proceed to allocate an array of SMI parameter buffers. There exists one entry in the list for each parameter. Thereafter, the method performs the actual remote call, as indicated by invocation of SMIClientInvokeObjectMethod call. If the call is successful, the method back patches the referenced parameters (i.e., updates the reference parameters or arguments). If an error occurs, on the other hand, an error script is invoked. The method performs cleanup before returning. Steps include resetting the call status flag and passing back the return value. If the retry flag is set to true, the "do/while" loop will repeat for another iteration. Otherwise, the method performs final cleanup by destroying the connection (or selecting to defer disconnecting). A method for setting up the result or return value for a remote function, SetupReturnDefinition, may be constructed as follows.
______________________________________
// routine to setup the result or return value for the remote function
// setup the passed smidata buffer as a variable to be returned from a
method call
PSHLIST PBOBJISetupReturnDefinition
PRT.sub.-- THIS rtThis,
OBJIParmSignatureDecode FAR *pMethodDescription,
PSMIDATA psmidataReturnType,
PSMI.sub.-- COMMAND psmi,
POB.sub.-- DATA pobdata, // if the return value is a structure, then
this
will be set to
// the structure instance
PBOBJIDeferMemoryRelease& MemCollection
)
{
PSHLIST pSmiParameters =
SMICreateInterface(psmi,
NULL);
// see if the return type is a structure
if(pMethodDescription->bIsReturnTypeAStructure())
{
LPSTR lpszStructureName = pMethodDescription->GetStructureName();
OB.sub.-- INST.sub.-- ID oid;
INT iResultCode = rt.sub.-- create.sub.-- obinst(rtThis,lpszStructureName,
&oid);
if(SUCCESS != iResultCode)
{
PB.sub.-- ASSERT(0 && "couldn't create structure");
}
OB.sub.-- CLASS.sub.-- HNDL class.sub.-- hndl = ob.sub.-- get.sub.--
obinst.sub.-- class.sub.-- hndl
(rtThis,oid);
ob.sub.-- set.sub.-- data.sub.-- obinst (pobdata, oid, class.sub.--
hndl.class.sub.-- id, FALSE);
ob.sub.-- set.sub.-- data.sub.-- structure (rtThis, pobdata);
OBJIPOBDATAArg oFuncArg(rtThis,pobdata);
PBOBJIAddValueToParameterList(
rtThis,
psmi,
pSmiParameters,
&oFuncArg,
psmidataReturnType,
FALSE,
TRUE,
NULL,
TRUE, // set the force NULLS to on
MemCollection);
}
// determine the return data type
else
{
switch(pMethodDescription->GetReturnType())
{
default:
case NO.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- VOID;
break;
case ANY.sub.-- TYPE: // we don't know what the type is yet
psmidataReturnType->iDataType = SMI.sub.-- ANY;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = 0;
break;
case UINT.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- UINT;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(USHORT);
break;
case BOOL.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- BOOLEAN;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(SHORT);
break;
case CHAR.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- CHAR;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(CHAR);
break;
case INT.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- INT;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(SHORT);
break;
case LONG.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- LONG;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(LONG);
break;
case FLOAT.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- REAL;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(FLOAT);
break;
case DOUBLE.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- DOUBLE;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(DOUBLE);
break;
case ULONG.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- ULONG;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(ULONG);
break;
case STRING.sub.-- TYPE:
psmidataReturnType->iDataLength = SMI.sub.-- STRING;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bAllowLengthChange = 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = 0; // we don't know how long
the string will be
break;
case DATE.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- DATE;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(SH.sub.-- TIME);
break;
case TIME.sub.-- TYPE;
psmidataReturnType->iDataType = SMI.sub.-- TIME;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(SH.sub.-- TIME);
break;
case DATETIME.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- DATETIME;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = sizeof(SH.sub.-- TIME);
break;
case BINARY.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- BLOB;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bAllowLengthChange = 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = 0; // we don't know how long
the blob will be
break;
case DEC.sub.-- TYPE:
psmidataReturnType->iDataType = SMI.sub.-- DECIMAL;
psmidataReturnType->bPBHeapAllocate = 1;
psmidataReturnType->bFreeItemStg= 1;
psmidataReturnType->bDataByReference = 1; // indicate that we want it
back
psmidataReturnType->iDataLength = iPBOBIMaxDecimalBufferSize;
break;
}
SMIAddInterfaceItem(psmi,pSmiParameters,psmidataReturnType);
}
return pSmiParameters;
}
______________________________________
The method is invoked with the runtime context and the signature of the remote procedure call (which identifies its argument list). The POB.sub.-- DATA argument, a PowerBuilder storage argument, is employed for returning a result which is a structure or an object. The method begins by creating an interface, by invoking SMICreateInterface. This creates the linked list which will manage the procedure call arguments. Next, the method examines the return type to determine if it is a structure. Otherwise, it determines a data type (e.g., integer, boolean, and the like) for the return value. Thereafter, the method invokes an AddValueToParameterList subroutine, for storing context information about the return type. Further, information is stored indicating whether memory is already allocated for the return type (or does the service manager need to allocate memory). Together with this, the method also indicates whether the value is returned by value or by reference. A BuildParameterList function is provided to build individual RemoteObject parameters into an intermediate form. In an exemplary embodiment, this may be constructed as follows.
______________________________________
// routine to build individual remote object parameters into their
intermediate
// non-PowerBuilder form
static void PBOBJIBuildParameterList
PRT.sub.-- THIS rtThis,
PSMI.sub.-- COMMAND psmi,
UINT iParameters,
POB.sub.-- DATA pArgArray,
PSHLIST pSmiParameters,
PSMIDATA pSMIDataArray,
UINT iStartingPBArguementNumber,
OBJIParmSignatureDecode FAR* pMethodDescription,
OBJIPOBDATAReferenceInfo FAR* pRefInfo,
PBOBJIVarInfo& VarFlagInfo,
PBOBJIDeferMemoryRelease& MemCollection
)
{
PBO.sub.-- DATA pArgment; // parameter passed in
PVOID pArray; // array of parameters passed in
PSMIDATA psmidata = 0;
OBJIPOBDATAArg oFuncArg(rtThis);
INT iIndexIntoBackPatchArray;
INT iIndexIntoOBDATARefPtrArray;
// setup for params passed by ref
pRefInfo->SetIndexForFirstSmiBackPatchSlot
(iIndexIntoBackPatchArray);
pRefInfo->SetIndexForFirstOBDATARefPtr
(iIndexIntoOBDATARefPtrArray);
// ptr to array which packages params -- array of "any types"
pArray = ot.sub.-- access.sub.-- refarg.sub.-- array(rtThis,pArgArray);
// Get signature -- reflects params
pMethodDescription->GetFirstParameterType();
// loop all params
for (UINT i = iStartingPBArguementNumber;
i < iParameters;
i++,pMethodDescription->GetNextParameterType())
{
// get next
pArgument = ob.sub.-- array.sub.-- item(rtThis, pArray, i + 1);
// get ptr to it
oFuncArg.SetArg(pArgument);
// is this a ref param
BOOL bStickySMIReferenceFlag =
pMethodDescription->bIsCurrentParameterReference();
psmidata = &pSMIDataArray[i + 1];
if(pMethodDescription->bIsCurrentParameterStructure())
// is this parameter a structure or user object?
{ // see if we need to promote the object name to a
descandent name
// get name
LPSTR lpszStructureName = pMethodDescription->GetStructureName();
LPSTR lpszActualName = 0;
OB.sub.-- CLASS.sub.-- ID obClassIdSystem;
RT.sub.-- CLASS.sub.-- DESCRIP Descrip;
OB.sub.-- INST.sub.-- ID obinst;
OB.sub.-- CLASS.sub.-- HNDL obClassHndl;
// if descendant, change curr. name to descendant name
if(pMethodDescription->bIsCurrentParameterArray())
{
POB.sub.-- ARRAY.sub.-- INST arrayinst = ob.sub.-- get.sub.-- data.sub.--
arrayinst (pArgument);
obClassHndl = ob.sub.-- array.sub.-- class.sub.-- hndl(rtThis, arrayinst
);
}
else
{
obinst = ob.sub.-- get.sub.-- data.sub.-- obinst(pArgument);
obClassHndl = ob.sub.-- get.sub.-- obinst.sub.-- class.sub.-- hndl(rtThis,
obinst);
}
if(SUCCESS ==
rtGetClassDescrip(rtThis,obClassHndl,&Descrip,&obClassIdSystem))
{
lpszActualName = Descrip.pchClassName;
}
if(pbstg.sub.-- strcmpi(lpszActualName,lpszStructureName))
{
pMethodDescription->RenameStructure(lpszActualName); // use the new
class name
}
}
// Add to list
PBOBJIAddValueToParameterList(
rtThis,
psmi,
pSmiParameters,
&oFuncArg,
psmidata,
TRUE,
bStickySMIReferenceFlag,
&VarFlagInfo,
FALSE,
MemCollection
);
// Housekeeping for ref parms
// check if its passed by reference (references are passed to us by
value,
// but the any array is passed by reference so that we can update
the value
if(pMethodDescription->bIsCurrentParameterReference())
{
psmidata->bDataByReference = TRUE; // set the by reference flag
pRefInfo->SetNextSmiBackPatchSlot
(iIndexIntoBackPatchArray, psmidata);
pRefInfo->SetNextOBDATARefPtr
(iIndexIntoOBDATARefPtrArray,pArgument);
}
}
}
______________________________________
At the outset, the method performs housekeeping/setup for parameters passed by reference. An array has been allocated up front by the caller (of this function) to track all the "back" pointers. The housekeeping steps, in essence, reset the index into that array to the first element of the array. Back pointers in the array are set to point to the storage allocated with objects being passed by reference or address. Next, the method stores (locally) a pointer to the array which packages parameters. Internally, all parameters had been packaged into a flat array. Thereafter, the method gets the "signature" for the parameters; this reflects the parameter types. With this information, the method is now ready to loop through all parameters. A "for" loop is established for looping through all parameters. Each iteration of the loop gets the "next" parameter. During execution of the loop, the method determines the name of the parameter or, if appropriate, a descendant name. Now, the method is ready to add the parameter information to a parameter value list. At the conclusion of the loop, the method has created an internal representation of the data associated with the parameters. After performing housekeeping for reference parameters, the method concludes. The system is now ready to "marshall" the method parameters. Marshalling is the process whereby the parameters are flattened into a data buffer (i.e., as a byte stream). At a high level, this functionality is performed by MarshallMethodParms which may be constructed as follows.
______________________________________
/*
************************************************************
* Name: MarshallMethodParms
* Synopsis: Flatten method call parameter data into buffer from
* interface list. Be sensitive to whether doing client or
* server marshalling as it relates to parameters by ref
* or value. pBuf points to start of Parmdata area
* Parmdata area layout is:
* INT ItemCount - 0 if no parms or return value
* INT iDispToNextItem;
* **Item instance**
* INT iDataType
* INT iDispToNextItem
* DATA DataBytes
* Returns: TRUE if parmlist successfully marshalled, FALSE if error
*
************************************************************
*/
PBWINAPI(BOOL, MarshallMethodParms)
(
PSMI.sub.-- COMMAND pSMI,
PSMI.sub.-- ITEM pBuf,
PSHLIST pshParmList,
PCLIENT pcStg)
{
// . . . setup
pcStg->iParmCount = (INT) 0;
SET.sub.-- SMITEM.sub.-- TYPE(pBuf,(SHORT)0); // Note: this is NO LONGER
ITEMCOUNT
SET.sub.-- SMITEM.sub.-- LENGTH(pBuf,(LONG) 0); // This IS: initialize
item
count
SET.sub.-- SMITEM.sub.-- LENGTH(pBuf,(LONG) 0); // This IS: initialize
item
count
id ((pcStg->pCBuffer =
(PSMIDATA)shlist.sub.-- get.sub.-- first(pshParmList)) == NULL)
{
PB.sub.-- ASSERT(pcStg->pCBuffer);
SET.sub.-- SMI.sub.-- ERROR(pSMI,BAD.sub.-- PARMLIST,0)
return (FALSE);
}
// Result field is 1st in list - if not VOID, set DataByReference flag
if (pcStg->pCBuffer->iDataType != SMI.sub.-- VOID)
pcStg->pCBuffer->bDataByReference = ON;
// point past item hdr
pcStg->pBufWrk =
(PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBuWrk) + DATAITEMHDR);
pcSTg->pMSG->iMsgLen += DATAITEMHDR;
// Subroutine call to Marshall
if (!Marshall(pSMI,pBuf,pshParmList,pcStg))
return (FALSE);
else
return (TRUE);
}
______________________________________
This method largely serves as a setup wrapper for another method, Marshall, which may be constructed as follows.
______________________________________
/*
************************************************************
* Name: Marshall
* Synopsis: marshall interface items into buffer
* Returns: TRUE if parmlist successfully un-marshalled, FALSE if
error
************************************************************
*/
PBWINAPI (BOOL, Marshall)
PSMI.sub.-- COMMAND PSMI,
PSMI.sub.-- ITEM pBuf,
PSHLIST pshParmList,
PCLIENT pcStg)
{
// while elements in the linked list . . .
while (pcStg->pCBuffer)
{
// special case nested
// if item begins structure get nested list (unless the structure
is null)
while (pcStg->pCBuffer->bItemStartStruct &&
pcStg->pCBuffer->iDataType != SMI.sub.-- STRUCT.sub.-- WITH.sub.-- NULL)
// insert placeholder into buffer . . . treat as void/with.sub.-- null
SET.sub.-- SMITEM.sub.-- TYPE
(pcStg->pBufWrk, pcStg->pCBuffer->iDataType);
SET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk, (DATAITEMHDR +
PcStg->pCBuffer->iDataLength));
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBufWrk) +
DATAITEMHDR);
pcStg->pMSG->iMsgLen += DATAITEMHDR;
pcStg->iParmCount++; // account for structure beginning item
pcStg->pshSNest (pcStg->iNestLvl) = pshParmList;
pcStg->bStruct = TRUE;
pshParmList = (PSHLIST) pcStg->pCBuffer->lpDataItem;
if (!pshParmList .parallel.
!(pcStg->pCBuffer = (PSMIDATA)shlist.sub.-- get.sub.-- first(pshParmList))
)
{
PB.sub.-- ASSERT(pshParmList);
SET.sub.-- SMI.sub.-- ERROR
(pSMI,INTERFACE.sub.-- MISMATCH,ALLOC.sub.-- ERR.sub.-- MSG)
return (FALSE);
}
pcStg->iNestLvl++;
if (pcStg->iNestLvl == MAX.sub.-- NEST);
{
PB.sub.-- ASSERT(pcStg->iNestLvl < MAX.sub.-- NEST);
SET.sub.-- SMI.sub.-- ERROR
(pSMI,INTERFACE.sub.-- MISMATCH,ALLOC.sub.-- ERR.sub.-- MSG)
return (FALSE);
}
}
// Will it fit in buffer?
// if item will exceed MAXMSG length, get another buffer to hold
1st fragment
if((pcStg->pMSG->iMsgLen + (pcStg->pCBuffer->iDataLength +
DATAITEMHDR)) >= MAXMSG)
{
// if not marshalling: i.e. if parm is call-by-value unless on client
// don't get another buffer
if (pcStg->bMarshallingType == CLIENT.sub.-- MARSHALLING
.parallel. (pcStg->pCBuffer->bDataByReference))
{
// Mark end of current buffer
SET.sub.-- SMITEM.sub.-- TYPE
(pcStg->pBufWrk, (SHORT) SMI.sub.-- END.sub.-- OF.sub.-- BUFFER);
pcStg->pMSG->iMsgLen += sizeof(SHORT) + (2 * DATAITEMHDR);
// Get another buffer
pcStg->pMSG = SMIGetMSGBuffer(pSMI,pcStg->pMSG);
if (!pcStg->pMSG)
{
SET.sub.-- SMI.sub.-- ERROR
(pSMI,INTERFACE.sub.-- MISMATCH,ALLOC.sub.-- ERR.sub.-- MSG)
return (FALSE);
}
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (((PCHAR) pcStg->pMSG) +
SECONDARY.sub.-- BUFFER.sub.-- HDR);
if ((pcStg->pCBuffer->iDataLength + DATAITEMHDR) < MAXMSG )
pcStg->pMSG->iMsgLen += (2 * SECONDARY.sub.-- BUFFER.sub.-- HDR);
}
}
// By type . . .
switch(pcStg->pCBuffer->iDataType)
{
case SMI.sub.-- VOID:
case SMI.sub.-- ANY.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- STRING.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- INT.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- LONG.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- UINT.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- ULONG.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- DECIMAL.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- REAL.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- DOUBLE.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- BOOLEAN.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- CHAR.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- BLOB.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- DATE.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- TIME.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- DATETIME.sub.-- WITH.sub.-- NULL:
case SMI.sub.-- STRUCT.sub.-- WITH.sub.-- NULL:
// Set type and length
SET.sub.-- SMITEM.sub.-- TYPE
(pcStg->pBufWrk,pcStg->pCBuffer->iDataType);
SET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk,DATAITEMHDR);
// ptr to byte stream itself
pcStg->pBufWrk = (PSMI.sub.-- ITEM) ((PCHAR)pcStg->pBufWrk) +
DATAITEMHDR);
pcStg->pMSG->iMsgLen += DATAITEMHDR;
// Track num of params passed
pcStg->iParmCount++;
break;
case SMI.sub.-- ANY:
case SMI.sub.-- CHAR:
case SMI.sub.-- DECIMAL:
case SMI.sub.-- STRING:
case SMI.sub.-- DATE:
case SMI.sub.-- TIME:
case SMI.sub.-- DATETIME:
case SMI.sub.-- BLOB:
// Handling for ptr array (used for these types)
SET.sub.-- SMITEM.sub.-- TYPE(pcStg->pBufwrk,pcStg->pCBuffer->iDataType);
// if parm is call-by-value && not on client entry length =
DATAITEMHDR
if (pcStg->bMarshallingType == CLIENT.sub.-- MARSHALLING
.parallel. (pcStg->pCBuffer->bDataByReference))
SET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk, (DATAITEMHDR +
pcStg->pCBuffer->iDataLength));
else
{
SET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk, DATAITEMHDR);
pcStg->pCBuffer->iDataLength = 0;
}
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBufWrk) +
DATAITEMHDR);
// Don't marshall if parm is call-by-value unless on client
if (PcStg->bMarshallingType == CLIENT.sub.-- MARSHALLING
.parallel. (pcStg->pCBuffer->bDataByReference))
{
if (pcStg->pCBuffer->iDataLength > 0)
if (PcStg->pCBuffer->iDataLength >= MAXMSG)
{
pcStg->pItemPtr = pcStg->pCBuffer->lpDataItem;
pcStg->lTotalLength = pcStg->pCBuffer->iDataLength;
if (!SMIMarshallLargeItem(pSMI,pcStg->pMSG,pcStg))
SET.sub.-- SMI.sub.-- ERROR
(pSMI,INTERFACE.sub.-- MISMATCH,ALLOC.sub.-- ERR.sub.-- MSG)
return (FALSE);
}
// Re-establish current buffer
while (pcStg->pMSG->NextBuf)
{
pcStg->pMSG = (PCS.sub.-- MSG) pcStg->pMSG->NextBuf;
}
// pcStg->pMSG->iMsgLen set by SMIMarshallLargeItem
pcStg->pBufwrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pMSG) +
pcStg->pMSG->iMsgLen);
pcStg->iParmCount++;
break;
}
else
pbstg.sub.-- huge.sub.-- memcpy(pcStg->pBufWrk,
pcStg->pCBuffer->lpDataItem,
pcStg->pCBuffer->iDataLength);
}
else
if (PcStg->pCBuffer->iDataLength > 0)
if (pcStg->pCBuffer->iDataLength >= MAXMSG)
{
pcStg->pBufwrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBufWrk) -
DATAITEMHDR);
SET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufwrk, DATAITEMHDR);
if (!pcStg->pMSG->iMsgLen)
pcStg->pMSG->iMsgLen +=
GET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk) + (2 * DATAITEMHDR);
else
pcStg->pMSG->iMsgLen +=
GET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk);
pcStg->pBufWrk = (PSMI.sub.-- ITEM) ((PCHAR)pcStg->pBufWrk) +
DATAITEMHDR);
pcStg->iParmCount++;
break;
}
// add parameter len to length of message
pcStg->pMSG->iMsgLen += (pcStg->pCBuffer->iDataLength +
DATAITEMHDR);
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBufWrk) +
pcStg->pCBuffer->iDataLength);
pcStg->iParmCount++;
break;
case SMI.sub.-- INT: // PB type INT = C SHORT
case SMI.sub.-- LONG: // PB type LONG = C LONG
case SMI.sub.-- UNIT: // PB type UNIT = C USHORT
case SMI.sub.-- ULONG: // PB type ULONG = C ULONG
case SMI.sub.-- REAL: // PB type REAL = C FLOAT
case SMI.sub.-- DOUBLE: // PB type DOUBLE = C DOUBLE
case SMI.sub.-- BOOLEAN: // PB type BOOLEAN = C SHORT
SET.sub.-- SMITEM.sub.-- TYPE
(psStg->pBufWrk, pcStg->pCBuffer->iDataType);
SET.sub.-- SMITEM.sub.-- LENGTH(pcStg->pBufWrk,(DATAITEMHDR +
pcStg->pCBuffer->iDataLength));
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBufWrk) +
DATAITEMHDR);
// Don't marshall if parm is call-by-value unless on client
if (pcStg->bMarshallingType == CLIENT.sub.-- MARSHALLING
.parallel. (pcStg->pCBuffer->bDataByReference))
{
id (pcSAtg->pCBuffer->lpDataItem != NULL)
{
id (pcStg->pCBuffer->iDataLength == sizeof(SHORT))
{ // i.e. 2 byte binary
if (pcStg->pCBuffer->iDataType == SMI.sub.-- INT
.parallel. pcStg->pCBuffer->iDataType == SMI.sub.-- BOOLEAN)
SET.sub.-- SMITEM.sub.-- DATA.sub.-- SHORT(pcStg->pBufWrk,
((SHORT) *((SHORT FAR*)
pcStg->PCBuffer->lpDataItem)));
if (pcStg->pCBuffer->iDataType == SMI.sub.-- UINT)
SET.sub.-- SMITEM.sub.-- DATA.sub.-- USHORT(pcStg->pBufWrk,
((USHORT) *((USHORT FAR*)
PcStg->pCBuffer->lpDataItem)));
}
if (pcStg->pCBuffer->iDataLength == sizeof(LONG))
{ // i.e. 4 byte binary
if (pcStg->pCBuffer->iDataType == SMI.sub.-- LONG)
SET.sub.-- SMITEM DATA.sub.-- LONG(pcStg->pBufWrk,
((LONG) *((LONG FAR*)
pcStg->pCBuffer->lpDataItem)));
if (pcStg->pCBuffer->iDataType == SMI.sub.-- ULONG)
SET.sub.-- SMITEM.sub.-- DATA.sub.-- ULONG(pcStg->pBufWrk,
((ULONG) *((ULONG FAR*)
pcStg->pCBuffer->lpDataItem)));
if (PcStg->PCBuffer->iDataType == SMI.sub.-- REAL)
SET SMITEM.sub.-- DATA.sub.-- FLOAT(pcStg->pBufWrk,
((FLOAT) *((FLOAT FAR*)
pcStg->pCBuffer->lpDataItem)));
}
// i.e. 8 byte binary
if (pcStg->pCBuffer->iDataLength == sizeof(DOUBLE))
if (pcStg->pCBuffer->iDataType == SMI.sub.-- DOUBLE)
SET.sub.-- SMITEM.sub.-- DATA.sub.-- DOUBLE(pcStg->pBufWrk,
((DOUBLE) *((DOUBLE FAR*)
pcStg->PCBuffer->lpDataItem)));
}
}
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (((PCHAR)pcStg->pBufWrk) +
pcStg->pCBuffer->iDataLength);
// add parameter len to length of message
pcStg->pMSG->iMsgLen += (pcStg->pCBuffer->iDataLength +
DATAITEMHDR);
pcStg->iParmCount++;
break;
case SMI.sub.-- ARRAY:
// Call special array handler -- support for NULLs
if (!SMIMarshallArray(pSMI,pcStg->pMSG,pcStg->pBufWrk,pcStg))
{
SET.sub.-- SMI ERROR
(pSMI,INTERFACE.sub.-- MISMATCH,ALLOC.sub.-- ERR.sub.-- MSG)
return (FALSE);
}
// pcStg->pCurrBufPtr set by SMIUnMarshallArray
pcStg->pBufWrk = (PSMI.sub.-- ITEM) (LPVOID) pcStg->pCurrBufPtr);
break;
default:
break;
}
pcStg->pCBuffer = (PSMIDATA)shlist.sub.-- get.sub.-- next(pshParmList);
if (pcStg->pCBuffer == NULL)
if (pcStg->bStruct) we just completed a structure
{
pcStg->iNestLvi--;
if (pcStg->iNestLvl >= 0)
{
pshParmList = pcStg->pshSNest[pcStg->iNestLvl];
// if null list, we are marshalling array of struct . . . return
if (!pshParmList)
save current buffer pointer
pcStg->pCurrBufPtr = (LPVOID) pcStg->pBufWrk;
return(TRUE);
}
pcStg->pCBuffer = (PSMIDATA)shlist.sub.-- get.sub.-- next(pshParmList);
}
else
pcStg->bStruct = FALSE;
}
if (pcstg->pCBuffer == NULL)
break;
}
}
// store item count for all marshalled buffers
Within the Marshall method, a "while" loop is established before traversing the parameter list. Within the loop, additional processing is performed on any nested data types (e.g., structures or records), for getting a corresponding nested list (for those nested values). The parameter data is now ready for placing within buffers. How the values are actually placed within various buffers depends on the underlying data type for the parameter. This processing is provided by the switch statement, which switches on data type. Based on the type, each value is taken from the list and packaged into the byte stream. During this process, the method keeps track of the number of parameters added as well as the size of data which needs to be written. Complementing marshalling is the process of "unmarshalling." Unmarshalling is the specific process of unpacking the information out of the buffer and storing it into the corresponding interface definition. The task of sending a request will be illustrated in the context of a particular driver. To send a request to a named pipes server, for instance, the system employs a SendRequest method; it may be constructed as follows.
______________________________________
/*
************************************************************
Name: SendRequest
Synopsis: Send a request to NP AppServer thread
Returns: Nothing.
************************************************************
*/
PBCALLBACK (VOID, DPBSendRequest)
PSMI.sub.-- COMMAND pSMI)
{
LONG lpServerThreadID = NULL;
HANDLE hThread = NULL;
DWORD dwRC = NULL;
WTRC wstg;
PWTRC pWstg = &wstg;
#ifdef NP.sub.-- SERVER.sub.-- INCLUDED
LONG lThreadCount = 0;
SYSTEM.sub.-- INFO SysInfo;
DWORD dwMinProcesses = 1;
#endif
// Init
if (SMILOCK == NULL)
GETSMI;
PB.sub.-- ASSERT(pSMI->pSrvStat);
// Prep for locking of the RPC
CLIENT.sub.-- RPC(dwRC,pWstg->errbuf);
// Trace . . .
if (DPBTraceQueryOption(pSMI->pTRACE,bALLTrace))
{
if (LoadString(hInst,(DNPBASE + DNPC000),
pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf
(pWstg->errbuf,pWstg->formatbuf,SMI.sub.-- USER,GetCurrentThreadId( ));
DpBTracePrintText(pSMI->pTRACE,.(CHAR *) &pWstg->errbuf);
}
// For client straight func call; for server spawn new thread
PB.sub.-- THREAD.sub.-- CREATE ((LPSECURITY.sub.-- ATTRIBUTES)NULL,
(DWORD) 0,
// Actual send request
(LPTHREAD.sub.-- START.sub.-- ROUTINE) PipeRequestProc,
pSMI,
(DWORD) 0,
(LPDWORD) &lpServerThreadID);
// Wait on lock (for thread to say it initialized properly)
CLIENT.sub.-- RPC.sub.-- WAIT(dwRC,pWstg->errbuf);
// Server version handle killing spawned threads at shutdown
#ifdef NP.sub.-- SERVER.sub.-- INCLUDED
if (pSMI->pCmd)
{
if (pSMI->pCmd->iMsgType == SHUTDOWN.sub.-- DRIVER)
{
// trace . . .
if (DPBTraceQueryOption(pSMI->pTRACE,bALLTrace))
{
if (LoadString(hInst, (DNPBASE +
DNPS080),pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf
(pWstg->errbuf,pWstg->formatbuf,SMI.sub.-- USER,GetcurrentThreadId( ));
DPBTracePrintTest(pSMI->pTRACE,(CHAR *) &pWstg->errbuf);
}
GetSystemInfo (&SysInfo);
dwMinProcesses = 2 * SysInfo.dwNumberOfProcessors;
// force a yield to let accepting threads to terminate
Sleep(0);
// now try a few times to see if threads will go to 0,
// give about a second for each thread that we have outstanding
for (1ThreadCount = dwMinProcesses; 1ThreadCount > 0;
1ThreadCount--)
{
LOCKSMISTAT;
if (pSMI->pSrvStat->1ServerThreads == 0)
{
UNLOCKSMISTAT;
return;
}
UNLOCKSMISTAT;
// give it 2 seconds before checking again
Sleep(2000);
}
}
}
#endif
}
______________________________________
After initial thread management/housekeeping steps, the method performs the actual send request (here, PipeRequestProc for a named pipe driver). A complementary routine is present on the server, for servicing the call. At the server, a new thread of execution is spawned for servicing the call. The PipeRequestProc method, on the other hand, may be implemented as follows.
______________________________________
/**********************************************************
* PROCEDURE: PipeRequestProc (SMI.sub.-- COMMAND pSMI)
*
* PURPOSE:
* This thread function processes individual pipe requests
*
.backslash.**********************************************************/
VOID PipeRequestProc
{
PSMI.sub.-- COMMAND pSMI)
{
PNP.sub.-- WSTG pWstg; // Working storage instance pointer
UINT rc;
DWORD RC = NULL;
DWORD dwRC = NULL;
CHAR errbuf[MAX.sub.-- LINE] = {0};
// As long as Client is single-threading request using
CLIENT.sub.-- RPC macro
// it shouldn't be necessary to lock SMI object for Read/Writes, undef
LOCK/UNLOCK
// this isn't true when code is included and called by NP Server DLL
#ifndef NP.sub.-- SERVER.sub.-- INCLUDED
#ifdef LOCKSMI
#undef LOCKSMI
#undef UNLOCKSMI
#define LOCKSMI
#define UNLOCKSMI
#endif // #def LOCKSMI
#endif // #ifndef NP.sub.-- SERVER.sub.-- INCLUDED
// Get memory for comm driver -- working heap for thread
pWstg = GlobalAlloc(GPTR,sizeof(NP.sub.-- WSTG));
if (pWstg == NULL) // fail alloc
{
LOCKSMI;
pSMI->lLastError = GetLastError( );
pSMI->lReturnCode = ALLOC.sub.-- ERR.sub.-- WSTG;
UNLOCKSMI;
// release lock -- let client continue
CLIENT.sub.-- RPCRTN(dwRC,pWstg->errbuf);
PB.sub.-- EXIT.sub.-- THREAD(ALLOC.sub.-- ERR.sub.-- WSTG,errbuf);
}
// trace . . .
if (DPBTraceQueryOption(pSMI->pTRACE,bALLTrace))
{
if (LoadString(hInst,(DNPBASE + DNPC010),
pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf
(pWstg->errbuf,pWstg->formatbuf,SMI.sub.-- USER,GetCurrentThreadId( ));
DPBTracePrintText(pSMI->pTRACE,(CHAR *) &pWstg->errbuf);
}
// Keep list of memory objects
// anchor memory instance info for subsequent allocs
LOCKSMI;
pWstg->hMemObj[0] = pWstg;
pWstg->iMemIx = 0;
if (pSMI->szPBComputer == "") // if no name provided force local
pSMI->szpBComputer[0] = `.`;
// construct name of pipe
wsprintf (pWstg->PipeName,
"%s%s%s%s",
".backslash..backslash..backslash..backslash.",pSMI->szPBComputer,
".backslash..backslash.PIPE.backslash..backslash.",pSMI->szPBAppname);
// output buffer = request
pWstg->pOMsg = pSMI->pCmd;
pWstg->iSize = MAX NET.sub.-- SIZE;
pWstg->iMsgSize = pWstg->pOMsg->iMsgLen;
UNLOCKSMI;
//
//* Allocate input buffer . . . output buffer(s) address passed in on
request
//
SMI.sub.-- GET.sub.-- MSG(pWstg->pIMsg); // get I/O buffer
dwRC = GetVersion( );
// Overlap I/O (Win 32) or not
#if defined (PBWIN32)
if (dwRC < 0.times.80000000)
// Windows NT
pWstg->bIsWin40 = FALSE;
else
if (LOBYTE(LOWORD(dwRC)) < 4)
// Win32s
pWstg->bIsWin40 = FALSE;
else
// Windows 95
pWstg->bIsWin40 = TRUE;
#else
// Win16
pWstg->bIsWin40 = FALSE;
#endif
// Retry loop
RetrySend:
pWstg->bRequestError = FALSE; // Clear error flag
// Housekeeping for named pipe
BUMPSTATCOUNTUP (lServerCalls)
if (!pSMI->hServerConnection)
{
if (!pWstg->bIsWin40)
pWstg->hPipe = CreateFile (pWstg->PipeName, // Pipe name.
GENERIC.sub.-- WRITE // Generic access, read/write.
.vertline. GENERIC.sub.-- READ,
FILE.sub.-- SHARE.sub.-- READ // Share both read and write.
FILE.sub.-- SHARE.sub.-- WRITE ,
NULL, // No security.
OPEN.sub.-- EXISTING, // Fail if not existing.
FILE.sub.-- FLAG.sub.-- OVERLAPPED, // Use overlap.
NULL); // No template.
else
pWstg->hPipe = CreateFile (pWstg->PipeName, // Pipe name.
GENERIC.sub.-- WRITE // Generic access, read/write.
.vertline. GENERIC.sub.-- READ,
FILE.sub.-- SHARE.sub.-- READ // Share both read and write.
.vertline. FILE.sub.-- SHARE.sub.-- WRITE ,
NULL, // No security.
OPEN.sub.-- EXISTING, // Fail if not existing.
NULL, // Don't use overlap.
NULL); // No template.
if (pWstg->hPipe == INVALID.sub.-- HANDLE.sub.-- VALUE
.parallel. pWstg->hPipe == NULL)
{
pWstg->retCode = GetLastError( );
// pipe wasn't found . . . server named pipe not active
if ((pWstg->retCode == ERROR.sub.-- SEEK.sub.-- ON.sub.-- DEVICE)
.parallel.
(pWstg->retCode == ERROR.sub.-- FILE.sub.-- NOT.sub.-- FOUND))
{
BUMPSTATCOUNTUP (lTotalServerWriteErrors)
LOCKSMI;
if (pSMI->pCmd->iMsgType != SHUTDOWN.sub.-- DRIVER)
{
pSMI->lLastError = pWstg->retCode;
pSMI->lReturnCode = SERVER.sub.-- NOT.sub.-- ACTIVE;
}
else
{
pSMI->lReturnCode = 0L;
pSMI->lLastError = 0L;
}
UNLOCKSMI;
if (DPBTraceQueryOption (pSMI->pTRACE,bALLTrace)
&& DPBTraceQueryOption (pSMI->pTRACE,bDNPLife)
&& pSMI->pCmd->iMsgType != SHUTDOWN.sub.-- DRIVER)
{
if (LoadString(hInst, (DNPBASE +
DNPC020),pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf (pWstg->errbuf, pWstg->formatbuf, SMI.sub.-- USER,
GetCurrentThreadId( ),pSMI->lLastError);
DPBTracePrintText(pSMI->pTRACE,pWstg->errbuf);
}
CLIENT.sub.-- RPCRTN(dwRC,pWstg->errbuf);
FreeAllThreadMem pWstg);
PB.sub.-- EXIT.sub.-- THREAD(CREATE.sub.-- FILE.sub.-- ERROR,errbuf);
}
else
if (pWstg->retCode == ERROR PIPE.sub.-- BUSY)
if (PSMI->pCmd->iMsgType == SHUTDOWN.sub.-- DRIVER)
{
pSMI->lReturnCode = 0L;
pSMI->lLastError = 0L;
CLIENT.sub.-- RPCRTN(dwRC,pWstg->errbuf);
FreeAllThreadMem(pWstg);
PB.sub.-- EXIT.sub.-- THREAD(0,errbuf);
}
if (DPBTraceQueryOption(pSMI->pTRACE,bALLTrace)
&& DPBTraceQueryOption(pSMI->pTRACE,bDNPLife))
{
if (LoadString(hInst, (DNPBASE +
DNPC030),pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf (pWstg->errbufWstg->formatbuf,SMI.sub.-- USER,
GetCurrentThreadId( ));
DPBTracePrintText(pSMI->pTRACE,pWstg->errbuf);
}
// Return code
rc = WaitNamedPipe(pWstg->PipeName,
NMPWAIT.sub.-- USE.sub.-- DEFAULT.sub.-- WAIT);
if (!rc)
{
// Check error
rc = GetLastError( );
if ((rc == ERROR.sub.-- PIPE.sub.-- BUSY)
&& pSMI->pCmd->iMsgType != SHUTDOWN.sub.-- DRIVER)
{
pSMI->lReturnCode = SERVER.sub.-- BUSY;
pSMI->lLastError = pWstg->retCode;
}
else
{
pWstg->retCode = GetLastError( );
LOCKSMI;
if (pSMI->pCmd->iMsgType != SHUTDOWN.sub.-- DRIVER)
{
pSMI->lReturnCode = CREATE.sub.-- FILE.sub.-- ERROR;
pSMI->lLastError = pWstg->retCode;
}
else
{
pSMI->lReturnCode = 0L;
pSMI->lLastError = 0L;
}
UNLOCKSMI;
BUMPSTATCOUNTUP (lTotalClientWriteErrors)
if (DPBTraceQueryOption(pSMI->pTRACE,bALLTrace)
&& DPBTraceQueryOption(pSMI->pTRACE,bDNPLife)
&& pSMI->pCmd->iMsgType != SHUTDOWN.sub.-- DRIVER)
{
if (LoadString(hInst, (DNPBASE +
DNPC040),pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf (pWstg->errbuf, pWstg->formatbuf,SMI.sub.-- USER,
GetCurrentThreadId( ),pWstg->retCode);
DPBTracePrintText(pSMI->pTRACE,pWstg->errbuf);
}
}
// Return and clean up
CLIENT.sub.-- RPCRTN(dwRC,pWstg->errbuf);
FreeAllThreadMem(pWstg);
PB.sub.-- EXIT.sub.-- THREAD(CREATE.sub.-- FILE.sub.-- ERROR,errbuf);
}
else
{
// Retryable error
BUMPSTATCOUNTUP (lTotalClientWriteRetry)
pSMI->hServerConnection = NULL;
// Loop again
goto RetrySend;
}
}
LOCKSMI;
pSMI->lReturnCode = CREATE.sub.-- FILE.sub.-- ERROR;
pSMI->lLastError = pWstg->retCode;
UNLOCKSMI;
if (DPBTraceQueryOption(pSMI->pTRACE,bALLTrace)
&& DPBTraceQueryOption(pSMI->pTRACE,bDNPLife))
{
if (LoadString(hInst,(DNPBASE +
DNPC050),pWstg->formatbuf,MAX.sub.-- LINE))
wsprintf (pWstg->errbuf, pWstg->formatbuf, SMI.sub.-- USER,
GetCurrentThreadId( ) ,pSMI->lLastError);
DPBTracePrintText (pSMI->pTRACE, pWstg->errbuf);
}
CLIENT.sub.-- RPCRTN(dwRC,pWstg->errbuf);
FreeAllThreadMem(pWstg);
PB.sub.-- EXIT.sub.-- THREAD(CREATE.sub.-- FILE.sub.-- ERROR,errbuf);
}
}
// Server connection is OK
else
pWstg->hPipe = pSMI->hServerConnection;
//
//* Create and init overlapped structure for writes.
//
pbstg.sub.-- memset (&pWstg->OverLapWrt, 0, sizeof(OVERLAPPED));
if (!pWstg->bIsWin40)
{
pWstg->hEventWrt = CreateEvent (NULL, TRUE, FALSE, NULL);
pWstg->OverLapWrt.hEvent = pWstg->hEventWrt;
}
//
//* Send (Write) Request
//
pWstg->pFirstMsg = pWstg->pOMsg;
pWstg->bMultiBufIO = FALSE;
WriteNext:
BUMPSTATCOUNTUP (lTotalClientWrites)
if (!pWstg->bIsWin40)
// Actual write (overlap)
pWstg->retCode = WriteFile (pWstg->hPipe,
pWstg->pOMsg,
pWstg->iMsgSize,
(LPDWORD) &pWstg->bytesWritten,
&pWstg->OverLapWrt);
else
// Actual write (non-overlap)
pWstg->retCode = WriteFile (pWstg->hPipe,
pWstg->pOMsg,
pWstg->iMsgSize,
(LPDWORD)&pWstg->bytesWritten,
NULL);
if (!pWstg->retCode) // Wait on overlapped if need be.
{
pWstg->lastError = GetLastError( );
if (pWstg->lastError == ERROR.sub.-- IO.sub.-- PENDING)
{
// If pending, wait on event
RC = WaitForSingleObject(pWstg->hEventWrt,INFINITE);
RC = GetOverlappedResult (pWstg->hPipe,
&pWstg->OverLapWrt,
&pWstg->bytesTransRd,
FALSE);
}
else
{
// Other type of error
BUMPSTATCOUNTUP (lTotalClientWriteErrors)
BUMPSTATCOUNTUP (ITotalClientWriteRetry)
As shown, the driver includes a call to place the request on the wire (i.e., network). Appended herewith as Microfiche Appendix A are additional source listings providing further description of the present invention. While the invention is described in some detail with specific reference to a single preferred embodiment and certain alternatives, there is no intent to limit the invention to that particular embodiment or those specific alternatives. Thus, the true scope of the present invention is not limited to any one of the foregoing exemplary embodiments but is instead defined by the appended claims.
|
| ||||||||||
