See you soon at the next webinar ! :D
C.
"...programar es fácil, hacer programas es difĂcil..."
UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
HIX -> https://github.com/carles9000/hix

Mr. Antonio,
I have sent you an email regarding the registration procedure.
Please have a look at it.
Thank you.
Mulyadi
"actionButtons": {
"commands": [
{
"name": "▶️32",
"color": "cyan",
"command": "buildh.bat ${fileBasenameNoExtension}",
"cwd": "${fileDirname}"
},
{
"name": "▶️64",
"color": "cyan",
"command": "buildh64.bat ${fileBasenameNoExtension}",
"cwd": "${fileDirname}"
},
{
"name": "🛠️32",
"color": "cyan",
"command": "go32.bat",
"cwd": "${fileDirname}"
},
{
"name": "🛠️64",
"color": "cyan",
"command": "go64.bat",
"cwd": "${fileDirname}"
}
],
"defaultColor": "white",
"reloadButton": "↻",
"loadNpmCommands": false
},set path=c:\bcc77\bin
c:\harbour\bin\win\bcc\hbmk2 test.hbp -comp=bcc > info.log
rem code info.log
if errorlevel 0 test.exeset bcc=bcc7764
set path=c:\%bcc%\bin
set HB_USER_CFLAGS=-Ic:\%bcc%\INCLUDE\windows\crtl -Ic:\%bcc%\INCLUDE\windows\sdk -Lc:\%bcc%\LIB
set HB_USER_LDFLAGS=-Lc:\%bcc%\LIB;c:\%bcc%\LIB\psdk
c:\harbour\bin\win\bcc64\hbmk2.exe test.hbp -comp=bcc64
rem -xhb
rem code info.log
if errorlevel 0 test.exe-gui
-Ic:\fwh\include
test.prg
-workdir=
-optim-
-ldflag+=-aa
-Lc:\fwh\lib
-l{x86}fiveh
-l{x86}fivehc
-l{x86_64}five64
-l{x86_64}fivec64
-lgdiplus
-lole32
-lOleDlg
-lversion
xhb.hbc
hbct.hbc
hbwin.hbc
hbmzip.hbc
hbziparc.hbc
hbfoxpro.hbc@setlocal
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64
c:\harbour\bin\win\msvc64\hbmk2 hbmcp.hbp -comp=msvc64
@endlocalhbmcp.prg
-lgdiplus
-lole32
-lOleDlg
-lversion
-lucrt
-luxtheme
xhb.hbc
hbct.hbc
hbwin.hbc
hbmzip.hbc
hbziparc.hbc
hbfoxpro.hbc
-ldflag=/NODEFAULTLIB:msvcrt
-ldflag+=/NODEFAULTLIB:libucrt#include "fileio.ch"
// Configuraci├│n global del servidor
#define LOG_FILE_PATH "c:\temp\hbmcp.log"
#define SERVER_NAME "Harbour-xbase"
#define SERVER_VERSION "0.0.3"
#define PROTOCOL_VERSION "2024-11-05"
#define MAX_INPUT_BUFFER 4096
PROCEDURE Main()
LOCAL cInput, cResponse
ErrorBlock( { | oError| LogFile( "error: ", oError:Description ) } )
WHILE .T.
cInput := StdIn()
IF Empty( cInput )
EXIT // Salir si no hay entrada (EOF)
ENDIF
LogFile( "in: ", cInput )
cResponse := ProcessMessage( cInput )
IF ! Empty( cResponse )
LogFile( "out: ", cResponse )
StdOut( cResponse )
ENDIF
END
LogFile( "exit: ", "termina" )
RETURN
// Funci├│n para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
LOCAL cResponse := ""
LOCAL hJson, cId, cMethod, hParams, cToolName
LOCAL cCode, xResult, cResult
// Decodificar el JSON para obtener el m├®todo y el ID
hb_jsonDecode( cInput, @hJson )
cMethod = hJson[ "method" ]
if hb_HHasKey( hJson, "id" )
cId = AllTrim( Str( hJson[ "id" ] ) )
endif
do case
case cMethod == "initialize"
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{' + ;
'"protocolVersion":"2024-11-05",' + ;
'"capabilities":{' + ;
'"tools":{},' + ;
'"resources":{},' + ;
'"prompts":{}' + ;
'},' + ;
'"serverInfo":{"name":"Harbour-xbase","version":"0.0.2"}' + ;
'}' + ;
'}' + hb_eol()
case cMethod == "notifications/initialized"
cResponse := hb_eol()
case cMethod == "resources/list"
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"error":{"code":-32601,"message":"Method not found"}' + ;
'}' + hb_eol()
case cMethod == "prompts/list"
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"error":{"code":-32601,"message":"Method not found"}' + ;
'}' + hb_eol()
case cMethod == "tools/list"
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{' + ;
'"tools": [' + ;
'{' + ;
'"name":"get_time",' + ;
'"description":"Returns the current system time in HH:MM:SS format. Useful for logging or timestamping operations within the Harbour runtime environment.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{},' + ;
'"required":[],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'},' + ;
'{' + ;
'"name":"hb_version",' + ;
'"description":"Returns the version of the Harbour runtime environment. Use this to verify compatibility or for debugging purposes.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{},' + ;
'"required":[],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'},' + ;
'{' + ;
'"name":"hb_compiler",' + ;
'"description":"Returns the name of the compiler used to build the Harbour runtime environment. Helpful for diagnosing build-related issues.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{},' + ;
'"required":[],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'},' + ;
'{' + ; // Nueva herramienta hb_macro
'"name":"hb_macro",' + ;
'"description":"Executes a Harbour macro expression provided as a string and returns the result as a string. Useful for dynamic code execution within the Harbour runtime environment.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{"code":{"type":"string"}},' + ;
'"required":["code"],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'},' + ;
'{' + ; // Nueva herramienta: informaci├│n del sistema
'"name":"system_info",' + ;
'"description":"Returns comprehensive system information including OS, memory, disk space, and environment variables.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{},' + ;
'"required":[],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'},' + ;
'{' + ; // Nueva herramienta: ejecutar comando del sistema
'"name":"exec_command",' + ;
'"description":"Executes a system command and returns its output. Use with caution.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{"command":{"type":"string"},"timeout":{"type":"number"}},' + ;
'"required":["command"],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'},' + ;
'{' + ; // Nueva herramienta: manejo de archivos
'"name":"file_operations",' + ;
'"description":"Performs file operations like read, write, list directory contents.",' + ;
'"inputSchema":{' + ;
'"type":"object",' + ;
'"properties":{"operation":{"type":"string"},"path":{"type":"string"},"content":{"type":"string"}},' + ;
'"required":["operation","path"],' + ;
'"additionalProperties":false,' + ;
'"$schema":"http://json-schema.org/draft-07/schema#"' + ;
'}' + ;
'}' + ;
']' + ;
'}' + ;
'}' + hb_eol()
case cMethod == "tools/call"
cToolName = hJson[ "params" ][ "name" ]
IF cToolName == "get_time"
// Obtener la hora actual en formato "HH:MM:SS"
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{"content":[{"type":"text","text":"' + Time() + '"}]}' + ;
'}' + hb_eol()
ELSEIF cToolName == "hb_version"
// Obtener la versi├│n de Harbour
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{"content":[{"type":"text","text":"' + Version() + '"}]}' + ;
'}' + hb_eol()
ELSEIF cToolName == "hb_compiler"
// Obtener el nombre del compilador
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{"content":[{"type":"text","text":"' + hb_Compiler() + '"}]}' + ;
'}' + hb_eol()
ELSEIF cToolName == "hb_macro"
// Nueva herramienta: ejecutar macro y devolver resultado como cadena
// Verificar que el par├Ămetro "code" existe y es una cadena
IF hb_HHasKey( hJson[ "params" ], "arguments" ) .AND. ;
hb_HHasKey( hJson[ "params" ][ "arguments" ], "code" ) .AND. ;
ValType( hJson[ "params" ][ "arguments" ][ "code" ] ) == "C"
cCode := hJson[ "params" ][ "arguments" ][ "code" ]
// Ejecutar la macro y convertir el resultado a cadena
BEGIN SEQUENCE
xResult := &( cCode )
cResult := hb_ValToExp( xResult )
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{"content":[{"type":"text","text":"' + cResult + '"}]}' + ;
'}' + hb_eol()
cResponse := StrTran( cResponse, '""', '"' )
RECOVER
// En caso de error en la macro, devolver un error JSON-RPC
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"error":{"code":-32602,"message":"Invalid macro expression"}' + ;
'}' + hb_eol()
END SEQUENCE
ELSE
// Error: par├Ămetro "code" no proporcionado o inv├Ălido
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"error":{"code":-32602,"message":"Missing or invalid code parameter"}' + ;
'}' + hb_eol()
ENDIF
ELSEIF cToolName == "system_info"
// Nueva herramienta: informaci├│n del sistema
cResponse := CreateSuccessResponse( cId, GetSystemInfo() )
ELSEIF cToolName == "exec_command"
// Nueva herramienta: ejecutar comando del sistema
IF hb_HHasKey( hJson[ "params" ], "arguments" ) .AND. ;
hb_HHasKey( hJson[ "params" ][ "arguments" ], "command" )
LOCAL cCommand := hJson[ "params" ][ "arguments" ][ "command" ]
LOCAL nTimeout := iif( hb_HHasKey( hJson[ "params" ][ "arguments" ], "timeout" ), ;
hJson[ "params" ][ "arguments" ][ "timeout" ], 30 )
cResponse := CreateSuccessResponse( cId, ExecuteCommand( cCommand, nTimeout ) )
ELSE
cResponse := CreateErrorResponse( cId, -32602, "Missing command parameter" )
ENDIF
ELSEIF cToolName == "file_operations"
// Nueva herramienta: operaciones con archivos
IF hb_HHasKey( hJson[ "params" ], "arguments" ) .AND. ;
hb_HHasKey( hJson[ "params" ][ "arguments" ], "operation" ) .AND. ;
hb_HHasKey( hJson[ "params" ][ "arguments" ], "path" )
LOCAL cOperation := hJson[ "params" ][ "arguments" ][ "operation" ]
LOCAL cPath := hJson[ "params" ][ "arguments" ][ "path" ]
LOCAL cContent := iif( hb_HHasKey( hJson[ "params" ][ "arguments" ], "content" ), ;
hJson[ "params" ][ "arguments" ][ "content" ], "" )
cResponse := CreateSuccessResponse( cId, FileOperations( cOperation, cPath, cContent ) )
ELSE
cResponse := CreateErrorResponse( cId, -32602, "Missing operation or path parameter" )
ENDIF
ELSE
// Error: herramienta no encontrada o argumentos inv├Ălidos
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"error":{' + ;
'"code":-32602,' + ;
'"message":"Invalid params"' + ;
'}' + ;
'}' + hb_eol()
ENDIF
// Manejar m├®todos no soportados
otherwise
cResponse := ;
'{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"error":{' + ;
'"code":-32604,' + ;
'"message":"Method not found"' + cMethod +;
' }' + ;
'}' + hb_eol()
endcase
RETURN cResponse
// Funci├│n para obtener informaci├│n del sistema
FUNCTION GetSystemInfo()
LOCAL cInfo := ""
LOCAL cOS := OS()
LOCAL nMemory := Memory(0) // Memoria disponible
cInfo += "Operating System: " + cOS + hb_eol()
cInfo += "Available Memory: " + AllTrim(Str(nMemory)) + " bytes" + hb_eol()
cInfo += "Current Directory: " + CurDir() + hb_eol()
cInfo += "Harbour Version: " + Version() + hb_eol()
cInfo += "Compiler: " + hb_Compiler() + hb_eol()
cInfo += "Date/Time: " + DToC(Date()) + " " + Time() + hb_eol()
// Informaci├│n adicional del entorno
LOCAL aEnvVars := { "PATH", "TEMP", "USERNAME", "COMPUTERNAME" }
LOCAL i
cInfo += hb_eol() + "Environment Variables:" + hb_eol()
FOR i := 1 TO Len(aEnvVars)
cInfo += aEnvVars[i] + ": " + GetEnv(aEnvVars[i]) + hb_eol()
NEXT
RETURN cInfo
// Funci├│n para ejecutar comandos del sistema
FUNCTION ExecuteCommand( cCommand, nTimeout )
LOCAL cResult := ""
LOCAL cTempFile := "c:\temp\hbmcp_cmd_" + AllTrim(Str(Seconds())) + ".tmp"
DEFAULT nTimeout TO 30
BEGIN SEQUENCE
// Ejecutar comando y redirigir salida a archivo temporal
LOCAL cFullCommand := cCommand + " > " + cTempFile + " 2>&1"
LOCAL nExitCode := hb_run( cFullCommand )
// Leer resultado del archivo temporal
IF File( cTempFile )
cResult := MemoRead( cTempFile )
FErase( cTempFile ) // Limpiar archivo temporal
ENDIF
cResult += hb_eol() + "Exit code: " + AllTrim(Str(nExitCode))
RECOVER
cResult := "Error executing command: " + cCommand
END SEQUENCE
RETURN cResult
// Funci├│n para operaciones con archivos
FUNCTION FileOperations( cOperation, cPath, cContent )
LOCAL cResult := ""
DO CASE
CASE Upper(cOperation) == "READ"
IF File( cPath )
cResult := MemoRead( cPath )
ELSE
cResult := "Error: File not found - " + cPath
ENDIF
CASE Upper(cOperation) == "WRITE"
IF MemoWrit( cPath, cContent )
cResult := "File written successfully: " + cPath
ELSE
cResult := "Error writing file: " + cPath
ENDIF
CASE Upper(cOperation) == "LIST"
LOCAL aFiles := Directory( cPath + "\*.*" )
LOCAL i
cResult := "Directory listing for: " + cPath + hb_eol()
FOR i := 1 TO Len(aFiles)
cResult += aFiles[i][F_NAME] + " (" + AllTrim(Str(aFiles[i][F_SIZE])) + " bytes) " + ;
DToC(aFiles[i][F_DATE]) + " " + aFiles[i][F_TIME] + hb_eol()
NEXT
CASE Upper(cOperation) == "EXISTS"
cResult := iif( File(cPath), "File exists", "File does not exist" )
CASE Upper(cOperation) == "DELETE"
IF FErase( cPath ) == 0
cResult := "File deleted successfully: " + cPath
ELSE
cResult := "Error deleting file: " + cPath
ENDIF
OTHERWISE
cResult := "Unknown operation: " + cOperation + ". Available: READ, WRITE, LIST, EXISTS, DELETE"
ENDCASE
RETURN cResult
// Funci├│n mejorada de logging con timestamps y niveles
FUNCTION LogFile( cKey, cValue, cLevel )
LOCAL nHandle, lSuccess, cLogEntry
LOCAL cLogPath := LOG_FILE_PATH
DEFAULT cLevel TO "INFO"
// Crear entrada de log con timestamp
cLogEntry := DToC(Date()) + " " + Time() + " [" + cLevel + "] " + cKey + cValue + hb_eol()
// Rotaci├│n de logs si el archivo es muy grande (>1MB)
IF File( cLogPath ) .AND. FileSize( cLogPath ) > 1048576
RotateLogFile( cLogPath )
ENDIF
if ! File( cLogPath )
// Crear el archivo si no existe
nHandle := fopen( cLogPath, FO_WRITE + FO_CREAT )
else
// Abrir el archivo para a├▒adir contenido
nHandle := fopen( cLogPath, FO_WRITE )
endif
IF nHandle > 0
FSeek( nHandle, 0, FS_END ) // Mover el puntero al final del archivo
lSuccess = fWrite( nHandle, cLogEntry )
fclose( nHandle )
RETURN lSuccess
ENDIF
RETURN .F.
// Funci├│n para rotar archivos de log
FUNCTION RotateLogFile( cLogPath )
LOCAL cBackupPath := StrTran( cLogPath, ".log", "_" + DToS(Date()) + ".log" )
// Renombrar archivo actual como backup
hb_run( "move " + cLogPath + " " + cBackupPath )
RETURN .T.
// Funci├│n para obtener tama├▒o de archivo
FUNCTION FileSize( cFileName )
LOCAL nHandle, nSize := 0
nHandle := fopen( cFileName, FO_READ )
IF nHandle > 0
FSeek( nHandle, 0, FS_END )
nSize := FTell( nHandle )
fclose( nHandle )
ENDIF
RETURN nSize
// Funci├│n para validar estructura JSON-RPC
FUNCTION ValidateJsonRpc( hJson )
LOCAL lValid := .T.
// Verificar versi├│n JSON-RPC
IF !hb_HHasKey( hJson, "jsonrpc" ) .OR. hJson["jsonrpc"] != "2.0"
RETURN .F.
ENDIF
// Verificar que tenga m├®todo
IF !hb_HHasKey( hJson, "method" ) .OR. ValType( hJson["method"] ) != "C"
RETURN .F.
ENDIF
RETURN lValid
// Funci├│n para crear respuesta de error est├Ăndar
FUNCTION CreateErrorResponse( cId, nCode, cMessage, cData )
LOCAL cResponse
DEFAULT cData TO ""
cResponse := '{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + iif( Empty(cId), "null", cId ) + ',' + ;
'"error":{' + ;
'"code":' + AllTrim(Str(nCode)) + ',' + ;
'"message":"' + cMessage + '"' + ;
iif( Empty(cData), "", ',"data":"' + cData + '"' ) + ;
'}' + ;
'}' + hb_eol()
RETURN cResponse
// Funci├│n para crear respuesta de ├®xito est├Ăndar
FUNCTION CreateSuccessResponse( cId, cContent )
LOCAL cResponse
cResponse := '{' + ;
'"jsonrpc":"2.0",' + ;
'"id":' + cId + ',' + ;
'"result":{"content":[{"type":"text","text":"' + cContent + '"}]}' + ;
'}' + hb_eol()
RETURN cResponse
#pragma BEGINDUMP
#include <hbapi.h>
HB_FUNC( STDIN )
{
char buffer[ 1024 ];
if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
{
// Eliminar salto de línea final, si existe
size_t len = strlen( buffer );
if( len > 0 && buffer[ len - 1 ] == '\n' )
buffer[ len - 1 ] = '\0';
hb_retc( buffer );
}
else
{
hb_retc( "" ); // Retornar cadena vacía en caso de EOF
}
}
HB_FUNC( STDOUT )
{
if( HB_ISCHAR( 1 ) )
{
fputs( hb_parc( 1 ), stdout );
fflush( stdout ); // Forzar la escritura inmediata
}
}
#pragma ENDDUMP"actionButtons": {
"commands": [
{
"name": "▶️32",
"color": "cyan",
"command": "buildh.bat ${fileBasenameNoExtension}",
"cwd": "${fileDirname}"
},
{
"name": "▶️64",
"color": "cyan",
"command": "buildh64.bat ${fileBasenameNoExtension}",
"cwd": "${fileDirname}"
},
{
"name": "🛠️32",
"color": "cyan",
"command": "go32.bat",
"cwd": "${fileDirname}"
},
{
"name": "🛠️64",
"color": "cyan",
"command": "go64.bat",
"cwd": "${fileDirname}"
}
],
"defaultColor": "white",
"reloadButton": "↻",
"loadNpmCommands": false
},Antonio,
Could you share a setup guide for VS Code ? I will admit to a disadvantage at the seminar because of language barriers. I downloaded the program and even found the Harbour extensions, but nothing for FiveWin ( which I assume would be helpful ).
I would like to work with this. I also subscribed to GitHub ... but I'm not sure if I've done any of that right.
Are all files ( libraries, etc ) the ones we have on our local computer ? I was planning to use this on a Mac but of course I do not have any Windows files on it.
I suppose I will have a learning curve ...
or is there a way to activate CoPilot with Visual Studio Community which is already installed on my Windows computers ?
Tim
Dear Tim,
> I downloaded the program and even found the Harbour extensions, but nothing for FiveWin ( which I assume would be helpful ).
This is correct. Just the Harbour extension is required.
Do you see Copilot "activated" from vscode ?
You can just use Copilot from vscode or from GitHub
I found I can also activate copilot in Visual Studio Community with Github. I will try working with that. Since I already can do builds in VS, there is very little to setup.
I prefer UE Studio for editing and it now has AI integration. That is another strategy. We can no longer use external editors like UE in Visual Studio.