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
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
- Install IIS using Control Panel -> Programs and Features -> Turn Windows features on or off
- 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
- After the installation, type "localhost" in your browser. A page with the IIS logo should come up if IIS is installed
- 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.
- Open "Internet Information Services (IIS) Manager" from the Startmenu
- Select the root of the tree on the left and then open "Handler Mappings" in the main view
- Select the entry named "isapi-dll", right-click and select "Edit..."
- In the "Executable" field, enter "C:\inetpub\wwwroot\IsapiExample.dll"
- Click "Request Restrictions" and on the "Access" tab make sure "Execute" is selected. Hit ok on the second dialog.
- 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
- In your browser, open "http://localhost/IsapiExample.dll". If all worked right, you should see the example page generated by the dll.
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.