Up to date ISAPI include and example (64bit ready)

Share your advanced PureBasic knowledge/code with the community.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Up to date ISAPI include and example (64bit ready)

Post by freak »

The following is an example for an ISAPI extension. This is a dll that can run inside the Internet Information Service and provides an alternative to CGI to run executable code on the server.

This is an adaption from an earlier example from me here. It has been updated to work with 64bit and to support the latest IIS version. I tried it under Windows7 (with IIS 7.5). Note that if you use an earlier version of IIS, you may have to modify the version info in the include.

Below is a walk-through on how to get the example working.

The include:

Code: Select all

; /********
; *
; *  Copyright (c) 1995  Process Software Corporation
; *
; *  Copyright (c) 1995-1999  Microsoft Corporation
; *
; *
; *  Module Name  : HttpExt.h
; *
; *  Abstract :
; *
; *     This Module contains  the Structure definitions And prototypes For the
; *      HTTP Server Extension Interface used To build ISAPI Applications
; *
; ******************/


; /************************************************************
;  *   Manifest Constants
;  ************************************************************/

#HSE_VERSION_MAJOR         =  7      ;// major version of this spec
#HSE_VERSION_MINOR         =  5      ;// minor version of this spec
#HSE_LOG_BUFFER_LEN        = 80
#HSE_MAX_EXT_DLL_NAME_LEN  =256

#HSE_VERSION    = #HSE_VERSION_MINOR | ( #HSE_VERSION_MAJOR << 16 )

; //
; // the following are the status codes returned by the Extension DLL
; //

#HSE_STATUS_SUCCESS                     =  1
#HSE_STATUS_SUCCESS_AND_KEEP_CONN       =  2
#HSE_STATUS_PENDING                     =  3
#HSE_STATUS_ERROR                       =  4

; //
; // The following are the values To request services With the
; //   ServerSupportFunction().
; //  Values from 0 To 1000 are reserved For future versions of the Interface

#HSE_REQ_BASE                           =  0
#HSE_REQ_SEND_URL_REDIRECT_RESP         =  ( #HSE_REQ_BASE + 1 )
#HSE_REQ_SEND_URL                       =  ( #HSE_REQ_BASE + 2 )
#HSE_REQ_SEND_RESPONSE_HEADER           =  ( #HSE_REQ_BASE + 3 )
#HSE_REQ_DONE_WITH_SESSION              = ( #HSE_REQ_BASE + 4 )
#HSE_REQ_END_RESERVED                   =  1000

; //
; //  These are Microsoft specific extensions
; //

#HSE_REQ_MAP_URL_TO_PATH                =  (#HSE_REQ_END_RESERVED+1)
#HSE_REQ_GET_SSPI_INFO                  =  (#HSE_REQ_END_RESERVED+2)
#HSE_APPEND_LOG_PARAMETER               =  (#HSE_REQ_END_RESERVED+3)
#HSE_REQ_IO_COMPLETION                  =  (#HSE_REQ_END_RESERVED+5)
#HSE_REQ_TRANSMIT_FILE                  =  (#HSE_REQ_END_RESERVED+6)
#HSE_REQ_REFRESH_ISAPI_ACL              =  (#HSE_REQ_END_RESERVED+7)
#HSE_REQ_IS_KEEP_CONN                   =  (#HSE_REQ_END_RESERVED+8)
#HSE_REQ_ASYNC_READ_CLIENT              =  (#HSE_REQ_END_RESERVED+10)
#HSE_REQ_GET_IMPERSONATION_TOKEN        =  (#HSE_REQ_END_RESERVED+11)
#HSE_REQ_MAP_URL_TO_PATH_EX             =  (#HSE_REQ_END_RESERVED+12)
#HSE_REQ_ABORTIVE_CLOSE                 =  (#HSE_REQ_END_RESERVED+14)
#HSE_REQ_GET_CERT_INFO_EX               =  (#HSE_REQ_END_RESERVED+15)
#HSE_REQ_SEND_RESPONSE_HEADER_EX        =  (#HSE_REQ_END_RESERVED+16)
#HSE_REQ_CLOSE_CONNECTION               =  (#HSE_REQ_END_RESERVED+17)
#HSE_REQ_IS_CONNECTED                   =  (#HSE_REQ_END_RESERVED+18)
#HSE_REQ_MAP_UNICODE_URL_TO_PATH        =  (#HSE_REQ_END_RESERVED+23)
#HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX     =  (#HSE_REQ_END_RESERVED+24)
#HSE_REQ_EXEC_UNICODE_URL               =  (#HSE_REQ_END_RESERVED+25)
#HSE_REQ_EXEC_URL                       =  (#HSE_REQ_END_RESERVED+26)
#HSE_REQ_GET_EXEC_URL_STATUS            =  (#HSE_REQ_END_RESERVED+27)
#HSE_REQ_SEND_CUSTOM_ERROR              =  (#HSE_REQ_END_RESERVED+28)
#HSE_REQ_IS_IN_PROCESS                  =  (#HSE_REQ_END_RESERVED+30)
#HSE_REQ_REPORT_UNHEALTHY               =  (#HSE_REQ_END_RESERVED+32)
#HSE_REQ_NORMALIZE_URL                  =  (#HSE_REQ_END_RESERVED+33)
#HSE_REQ_VECTOR_SEND                    =  (#HSE_REQ_END_RESERVED+37)
#HSE_REQ_GET_ANONYMOUS_TOKEN            =  (#HSE_REQ_END_RESERVED+38)
#HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK=  (#HSE_REQ_END_RESERVED+40)
#HSE_REQ_GET_UNICODE_ANONYMOUS_TOKEN    =  (#HSE_REQ_END_RESERVED+41)
#HSE_REQ_GET_TRACE_INFO                 =  (#HSE_REQ_END_RESERVED+42)
#HSE_REQ_SET_FLUSH_FLAG                 =  (#HSE_REQ_END_RESERVED+43)
#HSE_REQ_GET_TRACE_INFO_EX              =  (#HSE_REQ_END_RESERVED+44)
#HSE_REQ_RAISE_TRACE_EVENT              =  (#HSE_REQ_END_RESERVED+45)
#HSE_REQ_GET_CONFIG_OBJECT              =  (#HSE_REQ_END_RESERVED+46)
#HSE_REQ_GET_WORKER_PROCESS_SETTINGS    =  (#HSE_REQ_END_RESERVED+47)
#HSE_REQ_GET_PROTOCOL_MANAGER_CUSTOM_INTERFACE_CALLBACK = (#HSE_REQ_END_RESERVED+48)
#HSE_REQ_CANCEL_IO                      = (#HSE_REQ_END_RESERVED+49)
#HSE_REQ_GET_CHANNEL_BINDING_TOKEN      = (#HSE_REQ_END_RESERVED+50)

; //
; //  Bit Flags For TerminateExtension
; //
; //    HSE_TERM_ADVISORY_UNLOAD - Server wants To unload the extension,
; //          extension can Return TRUE If OK, FALSE If the server should Not
; //          unload the extension
; //
; //    HSE_TERM_MUST_UNLOAD - Server indicating the extension is about To be
; //          unloaded, the extension cannot refuse.
; //

#HSE_TERM_ADVISORY_UNLOAD                 = $00000001
#HSE_TERM_MUST_UNLOAD                     = $00000002

; //
; // Flags For IO Functions, supported For IO Funcs.
; //  TF means ServerSupportFunction( HSE_REQ_TRANSMIT_FILE)
; //

#HSE_IO_SYNC                    =  $00000001  ; // For WriteClient
#HSE_IO_ASYNC                   =  $00000002  ; // For WriteClient/TF/EU
#HSE_IO_DISCONNECT_AFTER_SEND   =  $00000004  ; // For TF
#HSE_IO_SEND_HEADERS            =  $00000008  ; // For TF
#HSE_IO_NODELAY                 =  $00001000  ; // turn off nagling 
                                              
; //                                          
; // These three are only used by VectorSend
; //

#HSE_IO_FINAL_SEND               = $00000010
#HSE_IO_CACHE_RESPONSE           = $00000020
#HSE_IO_TRY_SKIP_CUSTOM_ERRORS   = $00000040


; /************************************************************
;  *   Type Definitions
;  ************************************************************/

; //
; // Structure passed To GetExtensionVersion()
; //

Structure HSE_VERSION_INFO
  dwExtensionVersion.l
  lpszExtensionDesc.s{#HSE_MAX_EXT_DLL_NAME_LEN}
EndStructure


; //
; // Structure passed To extension Procedure on a new request
; //

Prototype GetServerVariable(hConn, lpszVariableName, lpvBuffer, lpdwSize)
Prototype WriteClient(ConnID, Buffer, lpdwBytes, dwReserved)
Prototype ReadClient(ConnID, lpvBuffer, lpdwSize)
Prototype ServerSupportFunction(hConn, dwHSERequest.l, lpvBuffer, lpdwSize, lpdwDataType)

Structure EXTENSION_CONTROL_BLOCK
  cbSize.l;                 // size of this struct.
  dwVersion.l;              // version info of this spec
  ConnID.i;                 // Context number not to be modified!
  dwHttpStatusCode.l;       // HTTP Status code
  lpszLogData.s{#HSE_LOG_BUFFER_LEN};// null terminated log info specific to this Extension DLL
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf    
  
  lpszMethod.s;             // REQUEST_METHOD
  lpszQueryString.s;        // QUERY_STRING
  lpszPathInfo.s;           // PATH_INFO
  lpszPathTranslated.s;     // PATH_TRANSLATED

  cbTotalBytes.l;           // Total bytes indicated from client
  cbAvailable.l;            // Available number of bytes
  lpbData.i;                // pointer to cbAvailable bytes

  lpszContentType.s;        // Content type of client data
  
  GetServerVariable.GetServerVariable
  WriteClient.WriteClient
  ReadClient.ReadClient
  ServerSupportFunction.ServerSupportFunction
EndStructure


; //
; //  Bit field of flags that can be on a virtual directory
; //

#HSE_URL_FLAGS_READ          =  $00000001    ;// Allow For Read
#HSE_URL_FLAGS_WRITE         =  $00000002    ;// Allow For Write
#HSE_URL_FLAGS_EXECUTE       =  $00000004    ;// Allow For Execute
#HSE_URL_FLAGS_SSL           =  $00000008    ;// Require SSL
#HSE_URL_FLAGS_DONT_CACHE    =  $00000010    ;// Don't cache (vroot only)
#HSE_URL_FLAGS_NEGO_CERT     =  $00000020    ;// Allow client SSL certs
#HSE_URL_FLAGS_REQUIRE_CERT  =  $00000040    ;// Require client SSL certs
#HSE_URL_FLAGS_MAP_CERT      =  $00000080    ;// Map SSL cert To NT account
#HSE_URL_FLAGS_SSL128        =  $00000100    ;// Require 128 bit SSL
#HSE_URL_FLAGS_SCRIPT        =  $00000200    ;// Allow For Script execution

#HSE_URL_FLAGS_MASK          =  $000003ff

; //
; //  Structure For extended information on a URL mapping
; //

Structure HSE_URL_MAPEX_INFO
  lpszPath.s{#MAX_PATH}; // Physical path root mapped to
  dwFlags.l;            // Flags associated with this URL path
  cchMatchingPath.l;    // Number of matching characters in physical path
  cchMatchingURL.l;     // Number of matching characters in URL

  dwReserved1.l
  dwReserved2.l
EndStructure 


Structure HSE_UNICODE_URL_MAPEX_INFO
  lpszPath.u[#MAX_PATH]; // Physical path root mapped to
  dwFlags.l;            // Flags associated with this URL path
  cchMatchingPath.l;    // Number of matching characters in physical path
  cchMatchingURL.l;     // Number of matching characters in URL
EndStructure


; //
; // PFN_HSE_IO_COMPLETION - callback function For the Async I/O Completion.
; //

Prototype PFN_HSE_IO_COMPLETION(*pECB.EXTENSION_CONTROL_BLOCK, pContect.i, cbIO.l, dwError.l)


; //
; // HSE_TF_INFO defines the type For HTTP SERVER EXTENSION support For
; //  ISAPI applications To send files using TransmitFile.
; // A pointer To this object should be used With ServerSupportFunction()
; //  For HSE_REQ_TRANSMIT_FILE.
; //

Structure HSE_TF_INFO 
  
  ; //
  ; // callback And context information
  ; // the callback function will be called when IO is completed.
  ; // the context specified will be used during such callback.
  ; //
  ; // These values (If non-NULL) will override the one set by calling
  ; //  ServerSupportFunction() With HSE_REQ_IO_COMPLETION
  ; //
  pfnHseIO.PFN_HSE_IO_COMPLETION
  pContext.i
  
  ; // file should have been opened With FILE_FLAG_SEQUENTIAL_SCAN
  hFile.i
  
  ; //
  ; // HTTP header And status code
  ; // These fields are used only If HSE_IO_SEND_HEADERS is present in dwFlags
  ; //
  
  pszStatusCode.s; // HTTP Status Code  eg: "200 OK"
  
  BytesToWrite.l;  // special value of "0" means write entire file.
  Offset.l;        // offset value within the file to start from
  
  pHead.i;         // Head buffer to be sent before file data
  HeadLength.l;    // header length
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf  
  pTail.i;         // Tail buffer to be sent after file data
  TailLength.l;    // tail length
  
  dwFlags.l;       // includes HSE_IO_DISCONNECT_AFTER_SEND, ...

EndStructure


; //
; // HSE_SEND_HEADER_EX_INFO allows an ISAPI application To send headers
; // And specify keep-alive behavior in the same call.
; //

Structure HSE_SEND_HEADER_EX_INFO

  ; //
  ; // HTTP status code And header
  ; //  
  pszStatus.s;  // HTTP status code  eg: "200 OK"
  pszHeader.s;  // HTTP header
  
  cchStatus.l;  // number of characters in status code
  cchHeader.l;  // number of characters in header
  
  fKeepConn.l;  // keep client connection alive?
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf  
EndStructure
; 
; //
; // Flags For use With HSE_REQ_EXEC_URL
; //

#HSE_EXEC_URL_NO_HEADERS                     =  $02
#HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR     =  $04
#HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE    =  $10
#HSE_EXEC_URL_DISABLE_CUSTOM_ERROR           =  $20
#HSE_EXEC_URL_SSI_CMD                        =  $40
#HSE_EXEC_URL_HTTP_CACHE_ELIGIBLE            =  $80
          
; //
; // HSE_EXEC_URL_USER_INFO provides a new user content For use With
; // HSE_REQ_EXEC_URL
; //

Structure HSE_EXEC_URL_USER_INFO 
  hImpersonationToken.i;
  pszCustomUserName.s;
  pszCustomAuthType.s;
EndStructure

; //
; // HSE_EXEC_URL_ENTITY_INFO describes the entity body To be provided
; // To the executed request using HSE_REQ_EXEC_URL
; //

Structure HSE_EXEC_URL_ENTITY_INFO  
  cbAvailable.l
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf 
  lpbData.i
EndStructure

; //
; // HSE_EXEC_URL_STATUS provides the status of the last HSE_REQ_EXEC_URL 
; // call
; //

Structure HSE_EXEC_URL_STATUS 
  uHttpStatusCode.u
  uHttpSubStatus.u
  dwWin32Error.l
EndStructure

; //
; // HSE_EXEC_URL_INFO provides a description of the request To execute
; // on behalf of the ISAPI.  
; //

Structure HSE_EXEC_URL_INFO
  pszUrl.s;                       // URL to execute
  pszMethod.s;                    // Method
  pszChildHeaders.s;              // Request headers for child
 *pUserInfo.HSE_EXEC_URL_USER_INFO; // User for new request
 *pEntity.HSE_EXEC_URL_ENTITY_INFO; // Entity body for new request
  dwExecUrlFlags.l;               // Flags
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf 
EndStructure

; //
; // HSE_EXEC_UNICODE_URL_USER_INFO provides a new user content For use With
; // HSE_REQ_EXEC_UNICODE_URL
; //

Structure HSE_EXEC_UNICODE_URL_USER_INFO
  hImpersonationToken.i
  pszCustomUserName.s
  pszCustomAuthType.s
EndStructure

; //
; // HSE_EXEC_UNICODE_URL_INFO provides a description of the request To execute
; // on behalf of the ISAPI.  
; //

Structure HSE_EXEC_UNICODE_URL_INFO
  pszUrl.i;                              // URL to execute
  pszMethod.i;                           // Method
  pszChildHeaders.i;                     // Request headers for child
 *pUserInfo.HSE_EXEC_UNICODE_URL_USER_INFO; // User for new request
 *pEntity.HSE_EXEC_URL_ENTITY_INFO;         // Entity body for new request
  dwExecUrlFlags.l;                      // Flags
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf  
EndStructure

; //
; // HSE_CUSTOM_ERROR_INFO structured used in HSE_REQ_SEND_CUSTOM_ERROR
; // 

Structure HSE_CUSTOM_ERROR_INFO
  pszStatus.s
  uHttpSubError.u
  __padding.u
  fAsync.l
EndStructure


; //
; // structures For the HSE_REQ_VECTOR_SEND ServerSupportFunction
; //


; //
; // Types of vector-elements currently supported
; //
#HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER     =  0
#HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE       =  1

; //
; // element of the vector
; //

Structure HSE_VECTOR_ELEMENT
  ElementType.l;  // Type of element (buffer/file/fragment etc)
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding.l
  CompilerEndIf 
  pvContext.i;    // The context representing the element to be sent
  cbOffset.q; // Offset from the start of hFile
  cbSize.q;   // Number of bytes to send
EndStructure
; 
; //
; // The whole vector To be passed To the ServerSupportFunction
; //

Structure HSE_RESPONSE_VECTOR
  dwFlags.l;                          // combination of HSE_IO_* flags
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding1.l
  CompilerEndIf 
  pszStatus.s;                        // Status line to send like "200 OK"
  pszHeaders.s;                       // Headers to send

  nElementCount.l;                    // Number of HSE_VECTOR_ELEMENT's
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    __padding2.l
  CompilerEndIf 
 *lpElementArray.HSE_VECTOR_ELEMENT ;    // Pointer to those elements
EndStructure


Prototype PFN_HSE_CACHE_INVALIDATION_CALLBACK(pszUrl)

; //
; // HSE_TRACE_INFO Structure used To get Debug trace info
; // from core web server
; //

Structure HSE_TRACE_INFO
  ; //
  ; // Recommendation from IIS To trace the request
  ; //
  
  fTraceRequest.l
  
  ; //
  ; // The unique trace context ID For the current request
  ; //
  
  TraceContextId.b[16];
  
  ; //
  ; // Reserved For future use
  ; //
  
  dwReserved1.l
  dwReserved2.l   
EndStructure

; 
; //
; // HSE_REQ_GET_TRACE_INFO_EX SSF uses 
; // the HTTP_TRACE_CONFIGURATION Structure defined in httptrace.h
; //
; 
; //
; // HSE_REQ_RAISE_TRACE_EVENT SSF uses 
; // the HTTP_TRACE_EVENT Structure defined in httptrace.h
; //
; 
; //
; // SSF_REQ_GET_WORKER_PROCESS_SETTINGS returns IWpfSettings pointer.
; // IWpfSettings is defined in the wpframework.h
; //
; 
; //
; // SSF_REQ_GET_CONFIG_OBJECT returns INativeConfigurationSystem pointer.
; // INativeConfigurationSystem is defined in the nativerd.h
; //
; 
; //
; // HSE_GET_PROTOCOL_MANAGER_CUSTOM_INTERFACE_CALLBACK returns pointer To
; // PFN_HSE_GET_PROTOCOL_MANAGER_CUSTOM_INTERFACE_CALLBACK function type
; //

Prototype PFN_HSE_GET_PROTOCOL_MANAGER_CUSTOM_INTERFACE_CALLBACK(pszProtocolManagerDll, pszProtocolManagerDllInitFunction, dwCustomInterfaceId.l, ppCustomInterface)

; //
; // Flags For determining application type
; //

#HSE_APP_FLAG_IN_PROCESS   = 0
#HSE_APP_FLAG_ISOLATED_OOP = 1
#HSE_APP_FLAG_POOLED_OOP   = 2


CompilerIf #PB_Compiler_Thread = 0
  CompilerError "ISAPI Extensions must be compiled with threadsafe enabled!"
CompilerEndIf

CompilerIf #PB_Compiler_Unicode
  CompilerError "This include is designed to be used in ASCII mode only"
CompilerEndIf
Example code:

Code: Select all

;
;  Example ISAPI Dll
;

XIncludeFile "IsapiInclude.pb"


; GetExtensionVersion is called when the dll is loaded
;
ProcedureDLL GetExtensionVersion(*pver.HSE_VERSION_INFO)

  *pver\dwExtensionVersion = #HSE_VERSION                        ; set the version info 
  *pver\lpszExtensionDesc  = "ISAPI extension test in PureBasic" ; Its a fixed length string so we can just assign the text
   
  ProcedureReturn #True  ; return success. returning False will cause the dll to be unloaded again
EndProcedure

; This is just a small helper function to wrap the GetServerVariable call
; for easier access
;
Procedure.s ServerVariable(*pECB.EXTENSION_CONTROL_BLOCK, Name$)
  *Buffer = AllocateMemory(1000)
  Size    = 1000
  Value$  = ""
 
  If *pECB\GetServerVariable(*pECB\ConnID, @Name$, *Buffer, @Size) = #True
    Value$ = PeekS(*Buffer, Size)
  EndIf
   
  FreeMemory(*Buffer)
  ProcedureReturn Value$
EndProcedure

; This is the main function that is called for each client request
; *pECB contains all needed data
;
ProcedureDLL HttpExtensionProc(*pECB.EXTENSION_CONTROL_BLOCK)

  ; Lets build some HTML code to send back
  ;
  Html$ = "<html><head><title>ISAPI extension test in PureBasic</title></head><body>"
  Html$ + "<br><hr><center>ISAPI extension test in PureBasic</center><hr>"
  Html$ + "<br><br>"
 
  ; Some information is directly provided through the structure, other stuff
  ; can be read with the GetServerVariable callback:
  ;
  Html$ + "Some examples for input data:<hr>"
  Html$ + "<b>REQUEST METHOD:</b> "  + *pECB\lpszMethod         + "<br>"
  Html$ + "<b>QUERY_STRING:</b> "    + *pECB\lpszQueryString    + "<br>"
  Html$ + "<b>PATH_INFO:</b> "       + *pECB\lpszPathInfo       + "<br>"
  Html$ + "<b>PATH_TRANSLATED:</b> " + *pECB\lpszPathTranslated + "<br>"
  Html$ + "<b>CONTENT TYPE:</b> "    + *pECB\lpszContentType    + "<br>"   
  Html$ + "<b>REMOTE_ADDR:</b> "     + ServerVariable(*pECB, "REMOTE_ADDR") + "<br>" 
  Html$ + "<br><br>"
 
  Html$ + "DATA sent from the client:(" + Str(*pECB\cbTotalBytes) + " Bytes)<hr>"   
  Html$ + "<pre>"
 
  ; An important note on POST data from the client:
  ;
  ; 'cbTotalBytes' indicates the total number of bytes available from the client.
  ; 'cbAvailable' indicates the number of bytes directly available in the 'lpbData'
  ; Buffer. These two may differ (for larger number of datas). So the correct way is
  ; to read anything inside 'lpbData', and then call the ReadClient() callback as shown
  ; below until all data is read from the client, if there is more than 'cbAvailable' bytes available
  ;
  If *pECB\cbAvailable > 0   
    Html$ + PeekS(*pECB\lpbData, *pECB\cbAvailable)   
  EndIf
 
  If *pECB\cbTotalBytes > *pECB\cbAvailable
    BytesRead = *pECB\cbAvailable
    *Buffer = AllocateMemory(1000)
   
    If *Buffer
      While BytesRead < *pECB\cbTotalBytes
        Size = 1000
        *pECB\ReadClient(*pECB\ConnID, *Buffer, @Size)
        Html$ + PeekS(*Buffer, Size)
        BytesRead + Size
      Wend
     
      FreeMemory(*Buffer)
    EndIf
  EndIf
   
  Html$ + "</pre>"
  Html$ + "<br><br>"
  Html$ + "Test: Enter something and it will be sent to the dll via POST method.<hr>"
  Html$ + "<form action="+Chr(34)+ServerVariable(*pECB, "URL")+Chr(34)+" method=POST>"
  Html$ + "<textarea name="+Chr(34)+"text"+Chr(34)+" rows=10 cols=60>Enter text</textarea><br>"
  Html$ + "<input type=submit>"
  Html$ + "</form>"
  Html$ + "</body></html>"
 
  ; Send the response header with the ServerSupportFunction() callback
  ;       
  Header.HSE_SEND_HEADER_EX_INFO
  Header\pszStatus = "200 OK"
  Header\pszHeader = "Content-type: text/html" + Chr(13)+Chr(10)+Chr(13)+Chr(10)
  Header\cchStatus = Len(Header\pszStatus)
  Header\cchHeader = Len(Header\pszHeader)
  Header\fKeepConn = 0 
  *pECB\ServerSupportFunction(*pECB\ConnID, #HSE_REQ_SEND_RESPONSE_HEADER_EX, @Header, 0, 0)
 
  ; Send the html data with the WriteClient() callback:
  ;
  Length = Len(Html$)
  *pECB\WriteClient(*pECB\ConnID, @Html$, @Length, 0)

  ; return success
  ;
  ProcedureReturn #HSE_STATUS_SUCCESS
EndProcedure

; TerminateExtension is called before unloading the dll.
;
ProcedureDLL TerminateExtension(dwFlags)
  ProcedureReturn #True
EndProcedure


Here is an example on how to get it to work (tested on Win7x64 pro)
  1. Install IIS using Control Panel -> Programs and Features -> Turn Windows features on or off
  2. Make sure at least the following components are selected:
    • Internet Information Services
    • Internet Information Services -> Web Management Tools -> IIS Management Console
    • Internet Information Services -> World Wide Web Services Application Development Features -> ISAPI Extensions
  3. After the installation, type "localhost" in your browser. A page with the IIS logo should come up if IIS is installed
  4. Compile the example to a dll and place it in "C:\inetpub\wwwroot\IsapiExample.dll".
    • Note that you probably cannot compile directly to the folder due to permissions, so compile somewhere else and then move the file there with explorer.
    • If your system is 64bit, you should compile the dll using PBx64. You can run 32bit ISAPI extensions, but it requires extra configuration and I did not actually try that.
  5. Open "Internet Information Services (IIS) Manager" from the Startmenu
  6. Select the root of the tree on the left and then open "Handler Mappings" in the main view
  7. Select the entry named "isapi-dll", right-click and select "Edit..."
  8. In the "Executable" field, enter "C:\inetpub\wwwroot\IsapiExample.dll"
  9. Click "Request Restrictions" and on the "Access" tab make sure "Execute" is selected. Hit ok on the second dialog.
  10. Hit ok on the first dialog. A requester is shown asking if you want to allow the extension. Click "Yes".
    • What this does is create an entry under "ISAPI and CGI restrictions" on the main page. You can also add an entry there manually or later allow/deny an extension there
  11. In your browser, open "http://localhost/IsapiExample.dll". If all worked right, you should see the example page generated by the dll.
Those steps worked for me. If you have trouble, feel free to ask.

Some more tips:

Unicode: The strings in the ISAPI structures must remain in ascii even if you compile in unicode mode (except for the few structures that have explicit unicode variants). Therefore i suggest to compile the dll in ascii mode for simplicity. I have designed the include to directly use PB strings where possible. If you do want to compile in unicode mode, you have to change the include and replace all strings with pointers, then Poke/Peek them manually to convert between ascii and unicode.

Debugging: You cannot use the PB debugger to debug the dll. I suggest to use the OutputDebugString_() API function and use DebugView (http://technet.microsoft.com/en-us/sysi ... 96647.aspx) to monitor it. Note that you need to run DebugView as an Administrator and select "Capture -> Capture Global Win32" in the menu to see the output from your dll.

Restarting: During development you probably need to recompile/replace the dll a lot. You cannot do that though while the server still has the dll loaded. To unload the dll you can restart the server. Run the "iisreset" command from the commandline (needs Administrator privileges) to restart the server. Then the dll is no longer loaded until the next time you access it with the browser.
quidquid Latine dictum sit altum videtur
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Up to date ISAPI include and example (64bit ready)

Post by dige »

Thank you very much Freak! I have performed all the steps on a Windows x64 / IIS 8 sytem.
But the request "http://127.0.0.1/IsapiExample64.dll" does not run the dll. The browser ask
me to open/download the dll...

EDIT: Did it get to run with IIS 6!! :-D
"Daddy, I'll run faster, then it is not so far..."
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Up to date ISAPI include and example (64bit ready)

Post by freak »

I had the same problem until i got the handler mapping thing right. It is important that the dll has 'execute' permissions there.
quidquid Latine dictum sit altum videtur
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Up to date ISAPI include and example (64bit ready)

Post by RichAlgeni »

Thanks for your hard work on this Freak!
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Up to date ISAPI include and example (64bit ready)

Post by dige »

@Freak: I would like to get information about the client, like USER_AGENT. I guess I need to call the GetHeader() function.
Unfortunately, I have no idea how to do it.. Could you help me?
"Daddy, I'll run faster, then it is not so far..."
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Up to date ISAPI include and example (64bit ready)

Post by freak »

You can use the ServerVariable() procedure from the above example. The server variable "HTTP_USER_AGENT" probably contains what you need.

A full list of variables can be found here: http://msdn.microsoft.com/en-us/library ... 90%29.aspx
quidquid Latine dictum sit altum videtur
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Up to date ISAPI include and example (64bit ready)

Post by dige »

Thank you Freak! :D
"Daddy, I'll run faster, then it is not so far..."
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Up to date ISAPI include and example (64bit ready)

Post by RichAlgeni »

Freak, can you add multiple managed handlers? Or is it better to have one handler that can call multiple child dll's?

My thinking is that I would like to have multiple small routines that are specific to the functionality needed.

Thanks for your help!

Rich
swhite
Enthusiast
Enthusiast
Posts: 789
Joined: Thu May 21, 2009 6:56 pm

Re: Up to date ISAPI include and example (64bit ready)

Post by swhite »

Currently I just get a blank webpage when I try your example. I use my web server with another ISAPI dll so I am may just have a conflict that I need to resolve.

I also wondered if something similar could be created to work with gwan on linux. I realize the ISAPI is a Windows only function so what I was thinking was a way use PureBasic with gwan (http://www.gwan.com/).

Thanks,
Simon
Simon White
dCipher Computing
swhite
Enthusiast
Enthusiast
Posts: 789
Joined: Thu May 21, 2009 6:56 pm

Re: Up to date ISAPI include and example (64bit ready)

Post by swhite »

Now I get the download dll situation even though the allow execute is checked. In the past this was usually related to not having the MIME settings correct.

Simon
Simon White
dCipher Computing
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Up to date ISAPI include and example (64bit ready)

Post by RichAlgeni »

So far I love it, works great!

Been on vacation, I will update when I get back to Florida.

BTW: NH was 5 below zero this morning, got a foot of snow yesterday. :cry:
swhite
Enthusiast
Enthusiast
Posts: 789
Joined: Thu May 21, 2009 6:56 pm

Re: Up to date ISAPI include and example (64bit ready)

Post by swhite »

Hi

I finally have it working correctly. I had to fix the following issues.

1) The Advanced Settings under DefaultAppPool was set to Enable 32 Bit Applications. I had to set this to false because I created a 64bit dll.
2) I had to remove the MIME Type for "dll" because this caused IIS to download the dll instead of executing it.

You can also just create a new application pool if you want to leave the DefaultAppPool with the ability to run 32bit dlls.

I did the following:

1) I created a Purebasic Application Pool that did not handle 32bit dlls.
2) I created a new folder C:\inetpub\wwwroot\Purebasic and moved the ISAPIExample.dll into this folder.
3) I then created an application called PureBasic in my default website and set the physical path to the folder created in step 2 and assigned it to the Purebasic Application Pool.
4) Then I use the following URL http://localhost/purebasic/isapiExample.dll

You can use any name you like I just used Purebasic so I could differentiate it from other web applications.

Hopefully this information will help others.

Simon
Simon White
dCipher Computing
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Up to date ISAPI include and example (64bit ready)

Post by RichAlgeni »

More information after much trial and error.

You don't necessarily need to load the dll into a specific web directory, but you will need to be specific when you create the map handler. Here is what I did: In IIS Manager, under Sites, you will see 'Default Web Site.' When expanded, you will see the subfolders from the web root. I created a separate webroot folder, then created sub folders from there. One of the subfolders I have just happens to be called 'mapping.' Don't confuse this with 'Handler Mapping,' one of apps I have to serve is maps. I clicked on the 'mapping' folder which brought up the features view. Double click on 'Handler Mapping,' then click on 'Add Script Map' on the right hand side of the page. Under 'Request Path', I just entered the dll name. Under 'Executable', browse to the directory of your dll and click on it. Under 'Name', I just used the dll name again. As Freak mentioned, change the 'Access' under 'Request Restrictions' to Execute. I left the 'Mapping' and 'Verb' tabs as they were. When you click through on Ok, You will get a request to authorize or enable the handler. Just click on 'Yes' there. You should be all set.

As I work more with this, I will add my finding here. So far, I love this!

Thanks again Freak!

Rich
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Up to date ISAPI include and example (64bit ready)

Post by dige »

Someone here who has some experience with IIS8 and ISAP Modules?

My problem is, I always just get to run only one module.
It is always the one that I have recently imported.
Or with other words, its the one in the order at the top.

With IIS 6 there was'nt that problem. How can I use more than one ISAP Module with IIS8?
"Daddy, I'll run faster, then it is not so far..."
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Up to date ISAPI include and example (64bit ready)

Post by RichAlgeni »

When you created the script map, did you name it '*.dll?' That's what I did and had the same problem where I could only run module. When I change the name to the dll name instead of a wildcard, I was able to run multiple modules.

When you are starting up and using Anonymous mode, be careful for permission problems if you are writing files such as a log into different directories.
Post Reply