C API 维护


API 内部命令

C++ 内部对象

处理 API 调用

释放内存


API 内部命令

概述

本节提供 C API 内部结构的概述。它在需要对 DLL 代码进行修改时,为开发人员和维护人员提供帮助。

通信

下面的图表显示发生在 C API 逻辑组件之间的通信。调用的应用程序在附录 A 列出的结构中的 API 层发送和接受数据。在处理请求时,API 层与应用程序服务器进行通信,请求可用的代理。该代理向后与 API NetClient 连接,并请求执行操作。在指定操作及其所需数据后,代理在应用程序服务器上处理信息并 返回结果。


C++ 内部对象

概述

C++ 对象由 C API 的内部函数构成。单击此处可显示内部对象之间的关系。

API 管理员类

APIManager 是 C API 的主要部件,并起下列作用:

内存管理器跟踪 C API 内存分配,以防止在使用 API 的应用程序终止时发生内存漏洞。在请求 SCIM 列表时,SCIM 缓存减少网络通信。

当应用程序调用内存管理器函数时,创建一个 APIManager 实例。在不需要调用 DLL 时,通过调用 Disconnect 来释放 APIManager 使用的内存也是应用程序的责任。

API NetClient 类

APINetClient 类与 ASE NetClient 相似。APINetClient 具有一个新的函数: SyncUserMsg(或 Synchronous User Message),它执行同步调用。像层交互表中显示的一样,APINetClient 必须在处理用户消息前与代理联系。为了启用与该代理的通信,添加了下列函数:

CreateLocalSocket( SOCKET& callbackSocket );
SyncUserMessage( MSGUSERCALL &msgUserCall, ERRORCODE &ec );
ReadUserMessage( SOCKET sock, MSGUSERCALL& message );
CreateConnection( SOCKET remoteSocket );
CopyMessageParms( MSGUSERCALL &destinationMessage, MSGUSERCALL &sourceMessage );
CopyDataType( sL_VALUE &destParm, sL_VALUE &sourceParm );

SyncUserMsg 使用 CreateLocalSocket 在 API 客户机上创建回调套接字。使用基础类 NetClient 的 UserMsg 来将套接字端口号码发送给应用程序服务器。该端口号码传送给可用的代理,代理使用它在回调套接字上与 APINetClient 连接。

在 CreateConnection 中建立了代理和 APINetClient 之间的连接后,可以在网络上发送用户请求。使用 CopyMessageParms 和 CopyDataType 函数,可将来自用户请求的字段复制到代理提供的 MSGUSERCALL 结构中。然后代理处理请求。

AppServerAction 类

在 AppServerAction 类中定义公共接口处理的 APIManager 对应用程序服务器的呼叫。AppServerAction 类是一种虚拟类,需要任何派生类中的后接虚拟方法的实现。

方法 句法 说明
SendMessage
( const void* pMessageParms )
准备并发送消息给应用程序服务器。 pMessageParms 保留一种定义任何要发送的参数的结构。
Prepare Message
( const void* pMessageParms )
根据在 pMessageParms 中传入的参数创建要发送给“网络客户机”的 MSGUSERCALL 结构。
GetAction MessageID 返回与应用程序服务器要执行的操作相对应的消息标识(号码)。
GetResponse MessageID 返回应用程序服务器必须用来响应要完成的操作的消息标识(号码)。

与所有虚拟类一样,不能建立 AppServerAction 类型的对象实例,反之,必须新的类并覆盖虚拟方法。

AppServerAction 类的实例

在需要与应用程序服务器通信时,每种 APIManager 方法创建 AppServerAction 派生的类之一的实例。要向 API 添加新的呼叫,只需创建新的 AppServerAction 派生类并为必需的虚拟方法提供实现。

在 AppServerAction 派生对象上调用 SendMessage 时,完成向服务器的消息传送。该方法使用传送给方法调用的参数构造 APINetClient 使用的 MSGUSERCALL 结构。空闲指针参数 pMessageParms 允许将任何类型的数据传送给方法调用。由于虚拟 AppServerAction 类不具有用于该方法调用的实现,所以派生对象必须能够使用该参数来组装 MSGUSERCALL 结构。通常传送的数据是一种结构。结构传送的一个很好的实例是在 DiagnosticQuery 类中。这个类要求将 REQUEST_CONTEXT 结构移植并传送给 SendMessage 方法。

在 C API 只需要一个或两个参数(而不是一个完整的结构)就可以将 MSGUSERCALL 移植到应用程序服务器时,将向 AppServerAction 派生类添加附加数据成员。然后,添加到派生类的附加方法可能设置这些内部成员。例如,HistoryQuery 类只需要一个长整型数据(请求标识)即可完成其 MSGUSERCALL。在 APIManager 调用 SetRequestID(添加到 HistoryQuery 对象的附加方法)时,设置该必需的参数。通过允许 pMessageParms 参数为 NULL,可以简化对 SendMessage 的调用。

注:如果添加了附加的方法来在派生的类中设置必需的数据成员,则在调用 SendMessage 之前调用它们是很重要的。

以下是某些 Application ServerAction 派生类的列表及其对 pMessageParm 成员的使用:

派生类 对 pMessageParm 的使用
DiagnosticQuery 传送 REQUEST_CONTEXT 结构。
HistoryQuery 传送类专用的变体方法设置的 NULL.MSGUSERCALL 成员。
SCIMQuery 传送 SCIM_KEY 结构。
ProblemQuery 传送 PROBLEM_FILTER 结构。
ProblemSubmission 传送 PROBLEM_CLOSURE 结构。

处理 API 调用

概述

本节介绍用于处理函数调用的内部 API 代码。还提供了一个图表,介绍各个对象在代码中生成的一些列呼叫。实例代码在前一个 Application.cpp 的实例上构建。

查询代码

概述

下列代码是从调用 InquireRequests API 函数的文件中取出的。

注:单击此处可查找到显示进程序列的图表。

InquireRequests 代码实例

该实例在以前提供的代码上建立,显示 API 在处理 InquireRequests 函数调用时的内部代码。

{
long nAPIHandle;
long nErrorCode; 
Connect( &nAPIHandle, "indy_pp_es1", 8000, "EXAV", NULL, &nErrorCode); 
PROBLEM_RECORD* pProblemList = NULL;
PROBLEM_FILTER filter; 
InitProblemFilter( nAPIHandle, &filter ); 
filter.system = new char[16];
strcpy( filter.system, "PC APPLICATIONS" );
filter.component = new char[13];
strcpy( filter.component, "WEB BROWSERS" );
filter.item = new char[9];
strcpy( filter.item, "NETSCAPE" ); 
long max_requested = -1; // no restriction on number to retrieve
int nResult = InquireRequests( nAPIHandle, &filter, &pProblemList,
&max_requested, & nErrorCode ); 
FreeRequestList( nAPIHandle, &pProblemList ); 
Disconnect( nAPIHandle );
}

tsdcapi.cpp 代码实例

tsdcapi.cpp 中的代码将调用传送给 APIManager 对象。从 nAPIHandle 变量获得作为参数的指向 APIManager 对象的指针。

int _Export InquireRequests( long nAPIHandle, const PROBLEM_FILTER*
pProblemFilter, PROBLEM_RECORD** ppProblem-List, long* pnMaxRequested, long* pnError,
const EXTENDED_DATA* pUserDefinedData /*=NULL*/ )
{
APIManager *pAPIManager;
pAPIManager = (APIManager*)nAPIHandle; 
return ( pAPIManager->InquireRequests( *pProblemFilter, *ppProblem-List,
FALSE, *pnMaxRequested, *pnError, pUserDefinedData )
);
}

代码解释

APIManager 中的代码显示 AppServerAction 派生对象之一、ProblemQuery 和对 SendMessage 的调用的创建。请注意调用 SendMessage 之前必须调用的方法:

许多 AppServerAction 派生对象都需要这些函数。它们标识处理请求和设置要检索的请求的数目的应用程序服务器。

注:其他 AppServerAction 派生对象可能需要除这些函数之外的附加设置函数。

SetDetailedRecords 是 ProblemQuery 类的函数。它设置一个开关来告知应用程序服务器,它是要显示各个返回的问题的详细信息,还是摘要信息。还要注意的是传送给 ProblemQuery 新运算符的参数。由于所有应用程序服务器消息实际上都由 APINetClient 发送,所以有必要指定 AppServerAction 对象使用的 APINetClient。

在函数部分的开始,对 PROBLEM_FILTER 结构的校验进行了比较。这可以确保在调用 API 函数之前,API 用户对结构进行初始化。否则,结构可能包含会产生错误的未初始化的指针。

AddWatchBlock 代码实例

APIManager 中的最后一个函数是 AddWatchedBlock。该函数请求 APIManager 类中的内存管理器跟踪分配的内存,以便在消毁 APIManager 对象时可以删除它。

int APIManager::InquireRequests( const PROBLEM_FILTER& problem_filter,
PROBLEM_RECORD* &pProblemList, BOOL bDetailedRecords,
long&max_requested, long& pnError, const EXTENDED_DATA* pUserDefinedData
/*=NULL*/ )
{
NETHNDL hnet;
int requestStatus; 
if ( strcmp( problem_filter.checksum, GetChecksum() ) != 0 )
{
pnError = SAI_CHECKSUM_FAILURE;
return SAI_ERROR;
}
requestStatus = OpenNetConnection( hnet, pnError );
if ( requestStatus != SAI_OK )
{
return requestStatus; // pnError should be set by OpenNetConnection
} 
ProblemQuery* pProblemQuery = new ProblemQuery( &m_NetClient );
pProblemQuery->SetApplication ServerHandle( hnet );
pProblemQuery->SetDetailedRecords( bDetailedRecords );
pProblemQuery->SetRetrievalMaximum( max_requested ); 
requestStatus = pProblemQuery->SendMessage( &problem_filter, pUser-DefinedData); 
if ( requestStatus == SAI_OK )
{
pProblemQuery->GetProblemList( pProblemList );
max_requested = pProblemQuery->GetRetrievalCount(); 
if ( pProblemList != NULL )
{
// Add the memory to the memory manager for tracking
m_MemoryManager.AddWatchedBlock( (long)pProblemList,
WatchedMemory::
LIST_OF_PROBLEMS );
}
}
   else
{
pnError = pProblemQuery->GetLastError();
} 
delete pProblemQuery; 
CloseNetConnection( hnet ); 
return requestStatus;
}

ProblemQuery.cpp 代码实例

由于是从 AppSvrAction 类派生来的,ProblemQuery 类包含下面两个 MSGUSERCALL 类成员:

PrepareMessage 设置 m_AppServerMessage,以便可以将其发送给 NetClient。

注:APINetClient 始终用 MSGUSERCALL 结构来发送响应,因此,存在可以俘获它的 m_AppServerResponse。

该消息的字段可以用于确定 APINetClient 返回的消息。将该响应与从 GetResponseMessageID 获得的期望的响应进行了对比,以确保 APINetClient 执行正确的操作。然后,SendMessage 确定通过对 CalculateRetrievalCount 的调用返回的完成记录的数目。将 m_AppServerMessage 发送给 APINetClient 后,就不再需要它。调用 FreeMessage 来确保清除消息中列出的字符串是很重要的。

int ProblemQuery::SendMessage( const void* pMessageParms )
{
if ( m_nHNet == -1 )
{
m_nLastError = SAI_INVALID_PARAMETER;
return SAI_ERROR; // Unable to process message without a diagnostic type
}
PrepareMessage( pMessageParms ); // Prepare the message to be sent 
SAERRORCODE ec;
m_AppServerResponse = m_pNetClient->SyncUserMsg(m_AppServerMessage, ec ); 
FreeMessage(); // Free the used message 
if ( ec == SASuccess )
{
if ( m_AppServerResponse.pPseudo == NULL )
{
m_nRetrievalCount = 0;
return SAI_OK; // nothing to process
}
else if (m_AppServerResponse.pPseudo->message == GetResponseMessageID()
)
{
m_nLastError = CalculateRetrievalCount();
if ( m_nLastError != SAI_OK )
{
return SAI_ERROR;
}
   else
{
return SAI_OK;
}
}
else // We did not get the appropriate response from the KML app
server
{
m_nLastError = SAI_WRONG_RESPONSE;
return SAI_ERROR;
}
}
else // Return the last error code
{
m_nLastError = ec;
return SAI_CLIENT_ERROR;
}
}

释放内存

概述

其他应用程序不能删除一个应用程序占有的内存。对同样是应用程序的 DLL 也一样。由于 C DLL 分配返回调用的应用程序的 PROBLEM_RECORD 列表使用的内存,所以 DLL 必须提供一种让应用程序释放被使用的列表的方法。这通过调用 FreeRequestList 来实现:

FreeRequestList( nAPIHandle, &pProblemList );

FreeRequestList 需要 APIHandle 和作为参数的列表地址。

释放内存的其他函数

除 FreeRequestList 之外,还有几个清除内存的 C API 函数。其中两个是 FreeSolutionList 和 FreeHistoryList。这些函数分别释放 SOLUTION_RECORD 和 HISTORY_RECORD 结构的列表。

注:作为一种预防,在调用 Disconnect 时,API 释放分配的内存,因此没有必要调用这些函数。

提供释放函数,是为了在执行使用 API 的应用程序而又需要保留内存的情况下提供方便。