OK, here's an example that I was able to find right away. Will need to poke around a bit for other examples, as it's been a while. This example operates as a standalone fcgi service, waits for requests from an fcgi web server. Not fully tested, but worked a while ago when I was messing with lighttpd. The nice thing about doing it as an external service is that you can start and stop the service independently of the web server.
I can send you a binary .exe if you like, to test with your web server.
#COMPILE EXE "FCGIServer.exe"
#REGISTER ALL
#INCLUDE "win32api.inc"
#INCLUDE "fcgiapp.inc"
'-- Requires Don Dickinson's PB Service installer/handler code
'-- Available at http://www.greatwebdive.com
'-- ********************* NOTE ***********************************
'-- To install service: fcgiserver -install
'-- To start fcgi server: net start "fastcgi server"
'-- To stop fcgi server: net stop "fastcgi server"
'-- To remove service: fcgiserver -uninstall
'-- Tcp port is hardcoded here (1027), but could be command line parm or from config file, etc.
'-- To test, you will need a FastCGI-capable web server, that can talk to remote fcgi's
'-- via tcp protocol. I suggest using lighttpd, but Zeus and Apache are options, also.
'-- Set your web server to route "*.fcgi" requests to the ip address and port where this
'-- app is running, e.g. "127.0.0.1" and 1027
#INCLUDE "pb_srvc.bas"
$SERVICE_NAME = "FastCGI Server"
$SERVICE_DISPLAY_NAME = "PowerBasic FastCGI Server"
'-- Wrapper for FCGX_GetParam
FUNCTION GetParam(szParam AS ASCIIZ, envp AS FCGX_ParamArray) AS STRING
LOCAL s AS DWORD
LOCAL p AS ASCIIZ PTR
s = FCGX_GetParam(szParam, envp)
p = s
IF p = %NULL THEN
FUNCTION = ""
ELSE
FUNCTION = TRIM$(@p)
END IF
END FUNCTION
'-- Print the environment strings
SUB PrintEnv(request AS FCGX_Request, szLabel AS ASCIIZ)
LOCAL count AS LONG
LOCAL p AS ASCIIZ PTR
LOCAL envLen AS LONG
CALL FCGX_FPrintF(request.pOut, "%s:<br><pre>", szlabel)
DO
p = request.@envp[count]
IF p = %NULL THEN EXIT DO
CALL FCGX_FPrintF(request.pOut, "%s<br>", @p)
INCR count
LOOP
CALL FCGX_FPrintF(request.pOut, "</pre><p>")
END SUB
'-- Monitor service status, shut down FCGI library when service is stopped
FUNCTION MonitorThread ( BYVAL hServerThread AS LONG ) AS LONG
DO
SLEEP 1
'-- This function tells us if the user stopped the service.
IF pbsIsShutdown() THEN
'-- Tell the FCGI library that we're shutting down
CALL FCGX_ShutdownPending
SLEEP 10
'-- Exit this thread
EXIT DO
END IF
LOOP
END FUNCTION
'-- Simple 404 handler
SUB Error_404(request AS FCGX_Request)
'-- Write the error response
CALL FCGX_FPrintF(request.pOut, _
"Content-type: text/html" & $CRLF & _
$CRLF & _
"<html><title>Error</title><body><br/>" & _
"<h1>404 Not found</h1><p>" & _
"The requested resource does not exist on this server." & _
"</body></html>" )
END SUB
'-- Simple echo handler
SUB Echo(request AS FCGX_Request)
LOCAL ContentLength AS STRING
LOCAL i AS LONG
LOCAL ch AS LONG
LOCAL length AS LONG
STATIC count AS LONG
LOCAL html AS STRING
'-- Increment request counter
INCR count
'-- Write the response to the output stream
CALL FCGX_FPrintF(request.pOut, _
"Content-type: text/html" & $CRLF & _
$CRLF & _
"<html><title>FastCGI echo (fcgiapp version)</title><body><br/>" & _
"<h1>PB FastCGI echo (fcgiapp version)</h1><br/>" & _
"<pre>" & _
"Request number: %ld, Process ID: %ld<br/>" & _
"</pre>" & $NUL, _
BYVAL count, _
BYVAL GetCurrentProcessId() )
'-- See if there's any content
ContentLength = GetParam("CONTENT_LENGTH", request.envp)
length = VAL(ContentLength)
IF length <= 0 THEN
CALL FCGX_PutS("No data from standard input.<br/><p>", request.pOut)
ELSE
CALL FCGX_PutS("Standard input:<br/><pre>", request.pOut)
FOR i = 0 TO length-1
ch = FCGX_GetChar(request.pIn)
IF ch < 0 THEN
CALL FCGX_PutS("Error: Not enough bytes received on standard input<br/>", request.pOut)
EXIT FOR
END IF
CALL FCGX_PutChar(ch, request.pOut)
NEXT i
CALL FCGX_PutS("</pre><br/><p>", request.pOut)
END IF
'-- Echo the environment array
PrintEnv request, "Request Environment"
'-- Close html tags
CALL FCGX_PutS("</body></html>", request.pOut)
END SUB
'-- Main thread. Switch all requests to handler functions
FUNCTION ServerThread ( BYVAL foo AS LONG ) AS LONG
LOCAL request AS FCGX_Request
LOCAL fd AS LONG
LOCAL ret AS LONG
LOCAL scriptName AS STRING
TRY
'-- Initialize
ret = FCGX_Init()
'-- Get a socket descriptor for our tcp port
fd = FCGX_OpenSocket ":1027", 5
'-- Pass the socket descriptor to fcgi
ret = FCGX_InitRequest( request, fd, 0& )
'-- If we need to connect to a database, this is a good place to do it,
'-- outside the main FCGI loop, so that it will only be called when we
'-- start the service, not with each CGI request
'-- Main FCGI loop - wait for requests, and switch them to handler functions
DO WHILE FCGX_Accept_r( request ) >= 0&
'-- Tune this for your web server - they don't pass all keys
scriptName = LCASE$(GetParam("PATH_INFO", request.envp))
IF scriptName = "" THEN
scriptName = LCASE$(GetParam("SCRIPT_NAME", request.envp))
END IF
'-- Hand requests to handler functions
SELECT CASE scriptName
CASE "", "/", "/echo.fcgi"
Echo request
CASE "/myfunction.fcgi"
'-- Anything you want can be handled in this switch statement
'-- Load and call functions in a DLL
'-- Call additional handler functions that you have added
'-- Etc.
CASE ELSE
' Echo request
Error_404 request
END SELECT
CALL FCGX_Finish_r(request)
LOOP
CATCH
'-- Any error handler code goes here
FINALLY
'-- Any cleanup code goes here - close database connections, etc.
END TRY
END FUNCTION
'-- Main function
FUNCTION PBMAIN() AS LONG
LOCAL hServerThread AS LONG
LOCAL hMonitorThread AS LONG
LOCAL foo AS LONG
'-- Create main and monitor threads
THREAD CREATE ServerThread(0) TO hServerThread
THREAD CREATE MonitorThread(0) TO hMonitorThread
'-- Start the service
pbsInit 0, $SERVICE_NAME, $SERVICE_DISPLAY_NAME
'-- Cleanup the thread handles
THREAD CLOSE hMonitorThread TO foo
THREAD CLOSE hServerThread TO foo
END FUNCTION