Hello
Did anybody coded any ISAPI dll with PB?
Any code, help, etc?
Thanks in advance
developing ISAPI dlls in PB?
developing ISAPI dlls in PB?
ARGENTINA WORLD CHAMPION
Here is a Power Basic sample code
Code: Select all
removed copyrighted code
ARGENTINA WORLD CHAMPION
some month ago i did a thread in german forum about freaks source and my problems with post.
http://www.purebasic.fr/german/viewtopi ... ight=isapi
It was just a try. I will also be waiting.
http://www.purebasic.fr/german/viewtopi ... ight=isapi
It was just a try. I will also be waiting.
I am sorry this took so long. I had forgotten about it for a while
PB4 makes this really much easier than before with stuff like
Prototypes, Fixed length strings and such.
Here is my PB4 Isapi include file:
Here is a simple example extension:
Some notes:
Threads:
ISAPI is per definition threaded. When multiple connections are incomming, each one
will be handled inside a separate thread. So you should compile your extension
with the threadsafe switch turned on.
But: During the testing of this with PB4, i discovered some bigger issues with threaded dll's.
We will try to fix this with the next beta (beta10). Until then, you will just have to
compile this without the threadsafe switch and make sure you do not bomb
out your server with simultanous requests.
Running this with the threadsafe option right now is guaranteed to produce a crash sooner or later.
Unicode:
To ease the use, i defined all string pointers in the include file as PB strings,
and all in-structure strings as fixed length strings. This however means
that it cannot be easily compiled in unicode mode.
To compile a unicode extension, you will need to create a different includefile.

PB4 makes this really much easier than before with stuff like
Prototypes, Fixed length strings and such.
Here is my PB4 Isapi include file:
Code: Select all
;- ISAPI Constants
#HSE_VERSION_MAJOR = 6 ;/* major version of this spec */
#HSE_VERSION_MINOR = 0 ;/* 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
#HSE_STATUS_SUCCESS = 1
#HSE_STATUS_SUCCESS_AND_KEEP_CONN = 2
#HSE_STATUS_PENDING = 3
#HSE_STATUS_ERROR = 4
#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
#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_SEND_URL_EX = #HSE_REQ_END_RESERVED+4
#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_TERM_ADVISORY_UNLOAD = $00000001
#HSE_TERM_MUST_UNLOAD = $00000002
#HSE_IO_SYNC = $00000001 ;/* For WriteClient */
#HSE_IO_ASYNC = $00000002 ;/* For WriteClient/TF*/
#HSE_IO_DISCONNECT_AFTER_SEND = $00000004 ;/* For TF */
#HSE_IO_SEND_HEADERS = $00000008 ;/* For TF
#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
#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_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER = 0
#HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE = 1
#HSE_APP_FLAG_IN_PROCESS = 0
#HSE_APP_FLAG_ISOLATED_OOP = 1
#HSE_APP_FLAG_POOLED_OOP = 2
;- ISAPI Structures
; Prototypes for the EXTENSION_CONTROL_BLOCK structure
;
Prototype GetServerVariable(ConnID, lpszVariableName.s, *lpvBuffer, *lpdwSizeofBuffer.LONG)
Prototype WriteClient(ConnID, *Buffer, *lpdwSizeofBuffer.LONG, dwSync)
Prototype ReadClient(ConnID, *lpvBuffer, *lpdwSize.LONG)
Prototype ServerSupportFunction(ConnID, dwServerSupportFunction, *lpvBuffer, *lpdwSizeofBuffer.LONG, *lpdwDataType.LONG)
Structure EXTENSION_CONTROL_BLOCK
cbSize.l ; Size of this structure.
dwVersion.l ; Version info of this specification.
ConnID.l ; Context number not to be modified!
dwHttpStatusCode.l ; HTTP Status code.
lpszLogData.s{#HSE_LOG_BUFFER_LEN}; Null-terminated log info. (why call it lpsz when it is not a pointer !?)
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.l ; Pointer to cbAvailable bytes.
lpszContentType.s ; Content type of client data.
GetServerVariable.GetServerVariable ; Server callback functions
WriteClient.WriteClient
ReadClient.ReadClient
ServerSupportFunction.ServerSupportFunction
EndStructure
Structure HSE_VERSION_INFO
dwExtensionVersion.l
lpszExtensionDescription.s{#HSE_MAX_EXT_DLL_NAME_LEN}
EndStructure
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.w[#MAX_PATH]; // Physical path root mapped to (unicode version)
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
Prototype PFN_HSE_IO_COMPLETION(*lpECP.EXTENSION_CONTROL_BLOCK, *pContect, cbIO, dwError)
Structure HSE_TF_INFO
pfnHseIO.PFN_HSE_IO_COMPLETION
pContext.l
hFile.l
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.l; // Head buffer to be sent before file data
HeadLength.l; // header length
pTail.l; // Tail buffer to be sent after file data
TailLength.l; // tail length
dwFlags.l; // includes HSE_IO_DISCONNECT_AFTER_SEND, ...
EndStructure
Structure HSE_SEND_HEADER_EX_INFO
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?
EndStructure
Structure HSE_EXEC_URL_USER_INFO
hImpersonationToken.l
pszCustomUserName.s
pszCustomAuthType.s
EndStructure
Structure HSE_EXEC_URL_ENTITY_INFO
cbAvailable.l
lpbData.l
EndStructure
Structure HSE_EXEC_URL_STATUS
uHttpStatusCode.w
uHttpSubStatus.w
dwWin32Error.l
EndStructure
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
EndStructure
Structure HSE_EXEC_UNICODE_URL_USER_INFO
hImpersonationToken.l
pszCustomUserName.s
pszCustomAuthType.s
EndStructure
Structure HSE_EXEC_UNICODE_URL_INFO
pszUrl.s; // URL to execute
pszMethod.s; // Method
pszChildHeaders.s; // 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
EndStructure
Structure HSE_CUSTOM_ERROR_INFO
pszStatus.s
uHttpSubError.w
fAsync.l
EndStructure
Structure HSE_VECTOR_ELEMENT
ElementType.l; // Type of element (buffer/file/fragment etc)
pvContext.l; // The context representing the element to be sent
cbOffset.q; // Offset from the start of hFile
cbSize.q; // Number of bytes to send
EndStructure
Structure HSE_RESPONSE_VECTOR
dwFlags.l; // combination of HSE_IO_* flags
pszStatus.s; // Status line to send like "200 OK"
pszHeaders.s; // Headers to send
nElementCount.l; // Number of HSE_VECTOR_ELEMENT's
*lpElementArray.HSE_VECTOR_ELEMENT; // Pointer to those elements
EndStructure
Structure HSE_TRACE_INFO
fTraceRequest.l
TraceContextId.b[16]
dwReserved1.l
dwReserved2.l
EndStructure
Code: Select all
XIncludeFile "Isapi.pb"
; GetExtensionVersion is called when the dll is loaded
;
ProcedureDLL GetExtensionVersion(*pver.HSE_VERSION_INFO)
*pver\dwExtensionVersion = #HSE_VERSION ; set the version info (6.0)
*pver\lpszExtensionDescription = "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
Threads:
ISAPI is per definition threaded. When multiple connections are incomming, each one
will be handled inside a separate thread. So you should compile your extension
with the threadsafe switch turned on.
But: During the testing of this with PB4, i discovered some bigger issues with threaded dll's.
We will try to fix this with the next beta (beta10). Until then, you will just have to
compile this without the threadsafe switch and make sure you do not bomb
out your server with simultanous requests.
Running this with the threadsafe option right now is guaranteed to produce a crash sooner or later.
Unicode:
To ease the use, i defined all string pointers in the include file as PB strings,
and all in-structure strings as fixed length strings. This however means
that it cannot be easily compiled in unicode mode.
To compile a unicode extension, you will need to create a different includefile.
quidquid Latine dictum sit altum videtur
Very nice work Freak.
I am trying to expand on this using it with apache.
But from this dll, I cannot open a console. Cannot open a window. Cannot open msgbox. Can't runprogram to pipe data.
Does anyone know how I can realtime output data to a console and get around apache seemingly hooking these calls?
I really dont want to serve stats to a browser if at all possible.
Thanks for the help.
I am trying to expand on this using it with apache.
But from this dll, I cannot open a console. Cannot open a window. Cannot open msgbox. Can't runprogram to pipe data.
Does anyone know how I can realtime output data to a console and get around apache seemingly hooking these calls?
I really dont want to serve stats to a browser if at all possible.
Thanks for the help.