winapiiisiis-7.5iis-8isapi

Is their any alternative of HSE_REQ_TRANSMIT_FILE for large file offset?


I have a problem with the HSE_TF_INFO Structure member Offset that is needed by HSE_REQ_TRANSMIT_FILE .

Let me explain my problem :

I have an ISAPI extension (32bits) that send back images to web client (JPEG less than 100 KBytes). As there is a large number of these images (more than 5 millions), I have created a file format that store (and indexe) all the images in a single file. Of course this file is larger than the 4GBytes limits of 32bits offsets.

So regarding the request, my ISAPI Extension seek at the good offset in the large file, read the small image and send it back to the client. II try to improve response time of my ISAPI extension by sending this small images asynchronously as it seems to be more efficient.

So I try the HSE_REQ_TRANSMIT_FILE that is exactly what I need to send the small image wrapped in the large file with :

  1. a file Handle (on the large file);
  2. an Offset (of the small image);
  3. and the size (of my small image).

But the HSE_TF_INFO::Offset is a DWORD, so limited to 4 Go !!!!! So what is strange is that the ReadFile function (to read from a handle) as an OVERLAPED (structure that seems to be designed for async I/O) that as two Offset members (Offset and OffsetHigh) to emulate 64bits offsets and so support large file seeking.

Is there a kind of HSE_TF_INFO_EX that can support large file offset ? or maybe is there a special way to use HSE_TF_INFO to seek in more than 4Gbytes files ?

Thanks for your help...


Solution

  • you can use HSE_REQ_VECTOR_SEND - this is more general than HSE_REQ_TRANSMIT_FILE. how you can view HSE_REQ_VECTOR_SEND let send any numbers of HSE_VECTOR_ELEMENT structures. how we can view HSE_VECTOR_ELEMENT let send file or memory buffer. in case file - cbOffset (offset into the file) and cbSize ( number of bytes of file data from cbOffset) - both is ULONGLONG - so no restriction by size

    demo (purely for clarity) how we can convert HSE_REQ_VECTOR_SEND to more general HSE_REQ_VECTOR_SEND

    BOOL Repack(LPEXTENSION_CONTROL_BLOCK lpECB, LPHSE_TF_INFO ti)
    {
        // code have same effect as call 
        // lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_TRANSMIT_FILE, ti, 0, 0);
    
        if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_IO_COMPLETION, ti->pfnHseIO, 0, (DWORD*)ti->pContext))
        {
            HSE_VECTOR_ELEMENT Element[3] = {
                { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER, ti->pHead, 0, ti->HeadLength },
                { HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE, ti->hFile, ti->Offset, ti->BytesToWrite },
                { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER, ti->pTail, 0, ti->TailLength }
            };
    
            HSE_RESPONSE_VECTOR rv = {
                HSE_IO_ASYNC|HSE_IO_DISCONNECT_AFTER_SEND|HSE_IO_FINAL_SEND,
                0,
                0,
                RTL_NUMBER_OF(Element),
                Element
            };
    
            if (!Element[2].cbSize) rv.nElementCount--;
    
            if (!Element[0].cbSize) rv.lpElementArray++, rv.nElementCount--;
    
            if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_VECTOR_SEND, &rv, 0, 0))
            {
                return TRUE;
            }
        }
    
        return FALSE;
    }
    

    so you in general need next code if you need full 64bit size and offset:

    struct TRANSMIT_FILE_CONTEXT 
    {
        ULONGLONG _BytesToWrite;
        ULONGLONG _cbOffset;
        HANDLE _hFile;
        char _szHeaders[];
    
        TRANSMIT_FILE_CONTEXT(HANDLE hFile, ULONGLONG BytesToWrite, ULONGLONG _cbOffset);
    
        ~TRANSMIT_FILE_CONTEXT();
    
        void* operator new(size_t cb, ULONG cbHeaders)
        {
            return ::operator new(cb + cbHeaders);
        }
    
        void operator delete(void* p)
        {
            ::operator delete(p);
        }
    
        static VOID WINAPI OnIoComplete (
            LPEXTENSION_CONTROL_BLOCK lpECB,
            PVOID pContext,
            DWORD /*cbIO*/,
            DWORD dwError
            )
        {
            DbgPrint("OnIoComplete(%p, %u)\n", lpECB->ConnID, dwError);
    
            dwError = (dwError == NOERROR ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR);
    
            lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_DONE_WITH_SESSION, &dwError, 0, 0);
    
            delete reinterpret_cast<TRANSMIT_FILE_CONTEXT*>(pContext);
        }
    
        DWORD Send(LPEXTENSION_CONTROL_BLOCK lpECB)
        {
            if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_IO_COMPLETION, OnIoComplete, 0, (DWORD*)this))
            {
                HSE_VECTOR_ELEMENT Element[2] = {
                    { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER, _szHeaders, 0, strlen(_szHeaders) },
                    { HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE, _hFile, _cbOffset, _BytesToWrite }
                };
    
                HSE_RESPONSE_VECTOR rv = {
                    HSE_IO_ASYNC|HSE_IO_DISCONNECT_AFTER_SEND|HSE_IO_FINAL_SEND,
                    0,
                    0,
                    RTL_NUMBER_OF(Element),
                    Element
                };
    
                if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_VECTOR_SEND, &rv, 0, 0))
                {
                    return HSE_STATUS_PENDING;
                }
            }
    
            delete this;
    
            return HSE_STATUS_ERROR;
        }
    };
    
    DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
    {
      //...
                    if (TRANSMIT_FILE_CONTEXT* p = new(cb + 1) TRANSMIT_FILE_CONTEXT(*))
                    {
                        //...
    
                        return p->Send(lpECB);
                    }
        //...
    
        return HSE_STATUS_ERROR;
    }
    

    also note that both HSE_REQ_VECTOR_SEND and HSE_REQ_TRANSMIT_FILE internal call HttpSendResponseEntityBody