Serverentwicklung mithilfe von Kontexthandles

Aus sicht der Serverprogrammentwicklung ist ein Kontexthandle ein nicht typisierter Zeiger. Serverprogramme initialisieren Kontexthandles, indem sie auf Daten im Arbeitsspeicher oder auf eine andere Speicherform (z. B. Dateien auf Datenträgern) verweisen.

Für instance angenommen, dass ein Client ein Kontexthandle verwendet, um eine Reihe von Updates für einen Datensatz in einer Datenbank anzufordern. Der Client ruft eine Remoteprozedur auf dem Server auf und übergibt ihr einen Suchschlüssel. Das Serverprogramm durchsucht die Datenbank nach dem Suchschlüssel und ruft die ganzzahlige Datensatznummer des übereinstimmenden Datensatzes ab. Der Server kann dann einen Zeiger auf void an einem Speicherspeicherort zeigen, der die Datensatznummer enthält. Wenn sie zurückgegeben wird, muss die Remoteprozedur den Zeiger als Kontexthandle über den Rückgabewert oder die Parameterliste zurückgeben. Der Client muss den Zeiger jedes Mal an den Server übergeben, wenn er Remoteprozeduren aufgerufen hat, um den Datensatz zu aktualisieren. Bei jedem dieser Aktualisierungsvorgänge hat der Server den Void-Zeiger in einen Zeiger auf eine ganze Zahl umgewandelt.

Nachdem das Serverprogramm das Kontexthandle auf Kontextdaten verweist, gilt das Handle als geöffnet. Handles, die einen NULL-Wert enthalten, werden geschlossen. Der Server verwaltet ein geöffnetes Kontexthandle, bis der Client eine Remoteprozedur aufruft, die ihn schließt. Wenn die Clientsitzung beendet wird, während das Handle geöffnet ist, ruft die RPC-Laufzeit die Herunterlaufroutine des Servers auf, um das Handle frei zu geben.

Das folgende Codefragment veranschaulicht, wie ein Server ein Kontexthandle implementieren kann. In diesem Beispiel verwaltet der Server eine Datendatei, in die der Client mithilfe von Remoteprozeduren schreibt. Die Kontextinformationen sind ein Dateihandle, das den aktuellen Speicherort in der Datei nachverfolgt, an dem der Server Daten schreibt. Das Dateihandle wird als Kontexthandle in der Parameterliste für Remoteprozeduraufrufe gepackt. Eine Struktur enthält den Dateinamen und das Dateihandle. Die Schnittstellendefinition für dieses Beispiel wird unter Schnittstellenentwicklung mithilfe von Kontexthandles gezeigt.

/* cxhndlp.c (fragment of file containing remote procedures) */
typedef struct 
{
     FILE* hFile;
     char   achFile[256];
} FILE_CONTEXT_TYPE;

Die Funktion RemoteOpen öffnet eine Datei auf dem Server:

short RemoteOpen(
    PPCONTEXT_HANDLE_TYPE pphContext,
    unsigned char *pszFileName)
{
    FILE               *hFile;
    FILE_CONTEXT_TYPE  *pFileContext;
 
    if ((hFile = fopen(pszFileName, "r")) == NULL) 
    {
        *pphContext = (PCONTEXT_HANDLE_TYPE) NULL;
        return(-1);
    }
    else 
    {
        pFileContext = (FILE_CONTEXT_TYPE *) 
                       MIDL_user_allocate(sizeof(FILE_CONTEXT_TYPE));
        pFileContext->hFile = hFile;
        // check if pszFileName is longer than 256 and if yes, return
        // an error
        strcpy_s(pFileContext->achFile, srlen(pszFileName), pszFileName);
        *pphContext = (PCONTEXT_HANDLE_TYPE) pFileContext;
        return(0);
    }
}

Die Funktion RemoteRead liest eine Datei auf dem Server.

short RemoteRead(
    PCONTEXT_HANDLE_TYPE phContext, 
    unsigned char *pbBuf, 
    short *pcbBuf) 
{ 
    FILE_CONTEXT_TYPE *pFileContext; 
    printf("in RemoteRead\n"); 
    pFileContext = (FILE_CONTEXT_TYPE *) phContext; 
    *pcbBuf = (short) fread(pbBuf, sizeof(char), 
                            BUFSIZE, 
                            pFileContext->hFile); 
    return(*pcbBuf); 
}

Die Funktion RemoteClose schließt eine Datei auf dem Server. Beachten Sie, dass die Serveranwendung dem Kontexthandle im Rahmen der Close-Funktion NULL zuweisen muss. Dies kommuniziert an den Server-Stub und die RPC-Laufzeitbibliothek, die das Kontexthandle gelöscht wurde. Andernfalls wird die Verbindung geöffnet, und schließlich kommt es zu einem Kontext-Rundown.

void RemoteClose(PPCONTEXT_HANDLE_TYPE pphContext)
{
    FILE_CONTEXT_TYPE *pFileContext;
 
    if (*pphContext == NULL)
    {
        //Log error, client tried to close a NULL handle.
        return;
    }
    pFileContext = (FILE_CONTEXT_TYPE *)*pphContext;
    printf("File %s closed.\n", pFileContext->achFile);
    fclose(pFileConext->hFile);
    MIDL_user_free(pFileContext);
 
    // This tells the run-time, when it is marshalling the out 
    // parameters, that the context handle has been closed normally.
    *pphContext = NULL;
}

Hinweis

Es wird zwar erwartet, dass der Client ein gültiges Kontexthandle an einen Aufruf mit [in, out] Richtungsattributen übergibt, aber RPC lehnt NULL-Kontexthandles für diese Kombination von Richtungsattributen nicht ab. Das NULL-Kontexthandle wird als NULL-Zeiger an den Server übergeben. Der Servercode für Aufrufe, die ein [in, out]-Kontexthandle enthalten, sollte geschrieben werden, um eine Zugriffsverletzung zu vermeiden, wenn ein NULL-Zeiger empfangen wird.