linbpq/WinmorControl.c

842 lines
17 KiB
C
Raw Permalink Normal View History

2022-08-28 09:35:46 +01:00
// Program to start or stop a Software TNC on a remote host
// Should work with ARDOP, WINMOR or VARA
// Version 1. 0. 0. 1 June 2013
// Version 1. 0. 2. 1 April 2018
// Updated to support running programs with command line parameters
// Add option to KILL by program name
// Version 1. 0. 3. 1 April 2019
// Add remote rigcontrol feature
// Version 1. 0. 3. 2 Feb 2020
// Fix Rigcontol
// Version 1. 0. 3. 3 Dec 2020
// Set working directory when starting TNC
// Version 1. 0. 3. 4 Jan 2021
// Add trace window
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <Psapi.h>
#define BPQICON 400
WSADATA WsaData; // receives data from WSAStartup
#define WSA_READ WM_USER + 1
HINSTANCE hInst;
char AppName[] = "WinmorControl";
char Title[80] = "WinmorControl";
// Foward declarations of functions included in this code module:
ATOM MyRegisterClass(CONST WNDCLASS*);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap);
VOID WINAPI txCompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap);
VOID CATThread();
int TimerHandle = 0;
LOGFONT LFTTYFONT ;
HFONT hFont;
struct sockaddr_in sinx;
struct sockaddr rx;
SOCKET sock;
int udpport = 8500;
int addrlen = sizeof(struct sockaddr_in);
BOOL MinimizetoTray=FALSE;
VOID __cdecl Debugprintf(const char * format, ...)
{
char Mess[10000];
va_list(arglist);
va_start(arglist, format);
vsprintf(Mess, format, arglist);
strcat(Mess, "\r\n");
OutputDebugString(Mess);
return;
}
char * strlop(char * buf, char delim)
{
// Terminate buf at delim, and return rest of string
char * ptr;
if (buf == NULL) return NULL; // Protect
ptr = strchr(buf, delim);
if (ptr == NULL) return NULL;
*(ptr)++ = 0;
return ptr;
}
BOOL KillOldTNC(char * Path)
{
HANDLE hProc;
char ExeName[256] = "";
DWORD Pid = 0;
DWORD Processes[1024], Needed, Count;
unsigned int i;
if (!EnumProcesses(Processes, sizeof(Processes), &Needed))
return FALSE;
// Calculate how many process identifiers were returned.
Count = Needed / sizeof(DWORD);
for (i = 0; i < Count; i++)
{
if (Processes[i] != 0)
{
hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]);
if (hProc)
{
GetModuleFileNameEx(hProc, 0, ExeName, 255);
if (_memicmp(ExeName, Path, strlen(ExeName)) == 0)
{
Debugprintf("Killing Pid %d %s", Processes[i], ExeName);
TerminateProcess(hProc, 0);
CloseHandle(hProc);
return TRUE;
}
CloseHandle(hProc);
}
}
}
return FALSE;
}
KillTNC(int PID)
{
HANDLE hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID);
Debugprintf("KillTNC Called Pid %d", PID);
if (hProc)
{
TerminateProcess(hProc, 0);
CloseHandle(hProc);
}
return 0;
}
RestartTNC(char * Path)
{
STARTUPINFO SInfo; // pointer to STARTUPINFO
PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION
int i, n = 0;
char workingDirectory[256];
SInfo.cb=sizeof(SInfo);
SInfo.lpReserved=NULL;
SInfo.lpDesktop=NULL;
SInfo.lpTitle=NULL;
SInfo.dwFlags=0;
SInfo.cbReserved2=0;
SInfo.lpReserved2=NULL;
Debugprintf("RestartTNC Called for %s", Path);
strcpy(workingDirectory, Path);
i = strlen(Path);
while (i--)
{
if (workingDirectory[i] == '\\' || workingDirectory[i] == '/')
{
workingDirectory[i] = 0;
break;
}
}
while (KillOldTNC(Path) && n++ < 100)
{
Sleep(100);
}
if (CreateProcess(NULL, Path, NULL, NULL, FALSE,0 ,NULL ,workingDirectory, &SInfo, &PInfo))
Debugprintf("Restart TNC OK");
else
Debugprintf("Restart TNC Failed %d ", GetLastError());
}
char * RigPort = NULL;
char * RigSpeed = NULL;
HANDLE RigHandle = 0;
int RigType = 0; // Flag for possible RTS/DTR
#define RTS 1
#define DTR 2
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
if (lpCmdLine[0])
{
// Port Name and Speed for Remote CAT
RigPort = _strdup(lpCmdLine);
RigSpeed = strlop(RigPort, ':');
}
if (!InitApplication(hInstance))
return (FALSE);
if (!InitInstance(hInstance, nCmdShow))
return (FALSE);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer(NULL, TimerHandle);
return (msg.wParam);
}
//
//
// FUNCTION: InitApplication(HANDLE)
//
// PURPOSE: Initializes window data and registers window class
//
// COMMENTS:
//
// In this function, we initialize a window class by filling out a data
// structure of type WNDCLASS and calling either RegisterClass or
// the internal MyRegisterClass.
//
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
// Fill in window class structure with parameters that describe
// the main window.
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
// wc.lpszMenuName = MAKEINTRESOURCE(BPQMENU) ;
wc.lpszClassName = AppName;
// Register the window class and return success/failure code.
return RegisterClass(&wc);
}
//
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
HFONT FAR PASCAL MyCreateFont( void )
{
CHOOSEFONT cf;
LOGFONT lf;
HFONT hfont;
// Initialize members of the CHOOSEFONT structure.
cf.lStructSize = sizeof(CHOOSEFONT);
cf.hwndOwner = (HWND)NULL;
cf.hDC = (HDC)NULL;
cf.lpLogFont = &lf;
cf.iPointSize = 0;
cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY;
cf.rgbColors = RGB(0,0,0);
cf.lCustData = 0L;
cf.lpfnHook = (LPCFHOOKPROC)NULL;
cf.lpTemplateName = (LPSTR)NULL;
cf.hInstance = (HINSTANCE) NULL;
cf.lpszStyle = (LPSTR)NULL;
cf.nFontType = SCREEN_FONTTYPE;
cf.nSizeMin = 0;
cf.nSizeMax = 0;
// Display the CHOOSEFONT common-dialog box.
ChooseFont(&cf);
// Create a logical font based on the user's
// selection and return a handle identifying
// that font.
hfont = CreateFontIndirect(cf.lpLogFont);
return (hfont);
}
VOID COMSetDTR(HANDLE fd)
{
EscapeCommFunction(fd, SETDTR);
}
VOID COMClearDTR(HANDLE fd)
{
EscapeCommFunction(fd, CLRDTR);
}
VOID COMSetRTS(HANDLE fd)
{
EscapeCommFunction(fd, SETRTS);
}
VOID COMClearRTS(HANDLE fd)
{
EscapeCommFunction(fd, CLRRTS);
}
OVERLAPPED txOverlapped;
HANDLE txEvent;
char RXBuffer[1024];
int RXLen;
int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength)
{
BOOL fReadStat ;
COMSTAT ComStat ;
DWORD dwErrorFlags;
DWORD dwLength;
BOOL ret;
// only try to read number of bytes in queue
ret = ClearCommError(fd, &dwErrorFlags, &ComStat);
if (ret == 0)
{
int Err = GetLastError();
return 0;
}
dwLength = min((DWORD) MaxLength, ComStat.cbInQue);
if (dwLength > 0)
{
fReadStat = ReadFile(fd, Block, dwLength, &dwLength, NULL) ;
if (!fReadStat)
{
dwLength = 0 ;
ClearCommError(fd, &dwErrorFlags, &ComStat ) ;
}
}
return dwLength;
}
BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite)
{
BOOL fWriteStat;
DWORD BytesWritten;
DWORD ErrorFlags;
COMSTAT ComStat;
int Err, txLength;
Err = WaitForSingleObject(txEvent, 1000);
if (Err == WAIT_TIMEOUT)
{
Debugprintf("TX Event Wait Timout");
}
else
{
ResetEvent(txEvent);
Err = GetOverlappedResult(RigHandle, &txOverlapped, &txLength, FALSE);
}
memset(&txOverlapped, 0, sizeof(OVERLAPPED));
txOverlapped.hEvent = txEvent;
fWriteStat = WriteFile(fd, Block, BytesToWrite,
&BytesWritten, &txOverlapped);
if (!fWriteStat)
{
Err = GetLastError();
if (Err != ERROR_IO_PENDING)
{
ClearCommError(fd, &ErrorFlags, &ComStat);
return FALSE;
}
}
return TRUE;
}
HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits)
{
char szPort[80];
BOOL fRetVal ;
COMMTIMEOUTS CommTimeOuts ;
char buf[100];
HANDLE fd;
DCB dcb;
sprintf( szPort, "\\\\.\\%s", pPort);
// open COMM device
fd = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL );
if (fd == (HANDLE) -1)
{
char Msg[80];
sprintf(Msg, "%s could not be opened - Error %d", pPort, GetLastError());
MessageBox(NULL, Msg, "WinmorControl", MB_OK);
return FALSE;
}
// setup device buffers
SetupComm(fd, 4096, 4096);
// purge any information in the buffer
PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT |
PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
// set up for overlapped I/O
CommTimeOuts.ReadIntervalTimeout = 20;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
CommTimeOuts.ReadTotalTimeoutConstant = 0 ;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
// CommTimeOuts.WriteTotalTimeoutConstant = 0 ;
CommTimeOuts.WriteTotalTimeoutConstant = 500 ;
SetCommTimeouts(fd, &CommTimeOuts);
dcb.DCBlength = sizeof(DCB);
GetCommState(fd, &dcb);
dcb.BaudRate = speed;
dcb.ByteSize = 8;
dcb.Parity = 0;
dcb.StopBits = TWOSTOPBITS;
dcb.StopBits = Stopbits;
// setup hardware flow control
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE ;
dcb.fOutxCtsFlow = 0;
dcb.fRtsControl = RTS_CONTROL_DISABLE ;
// setup software flow control
dcb.fInX = dcb.fOutX = 0;
dcb.XonChar = 0;
dcb.XoffChar = 0;
dcb.XonLim = 100 ;
dcb.XoffLim = 100 ;
// other various settings
dcb.fBinary = TRUE ;
dcb.fParity = FALSE;
fRetVal = SetCommState(fd, &dcb);
if (fRetVal)
{
if (SetDTR)
EscapeCommFunction(fd, SETDTR);
else
EscapeCommFunction(fd, CLRDTR);
if (SetRTS)
EscapeCommFunction(fd, SETRTS);
else
EscapeCommFunction(fd, CLRRTS);
}
else
{
sprintf(buf,"%s Setup Failed %d ", pPort, GetLastError());
OutputDebugString(buf);
CloseHandle(fd);
return 0;
}
txEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// Start Read Thread
_beginthread(CATThread, 0, 0);
return fd;
}
VOID CloseCOMPort(HANDLE fd)
{
SetCommMask(fd, 0);
// drop DTR
COMClearDTR(fd);
// purge any outstanding reads/writes and close device handle
PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
CloseHandle(fd);
}
HWND hMonitor;
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
u_long param=1;
BOOL bcopt=TRUE;
char Msg[255];
int ret, err;
hInst = hInstance; // Store instance handle in our global variable
WSAStartup(MAKEWORD(2, 0), &WsaData);
hWnd = CreateWindow(AppName, Title, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 420,275,
NULL, NULL, hInstance, NULL);
if (!hWnd) {
return (FALSE);
}
// setup default font information
LFTTYFONT.lfHeight = 12;
LFTTYFONT.lfWidth = 8 ;
LFTTYFONT.lfEscapement = 0 ;
LFTTYFONT.lfOrientation = 0 ;
LFTTYFONT.lfWeight = 0 ;
LFTTYFONT.lfItalic = 0 ;
LFTTYFONT.lfUnderline = 0 ;
LFTTYFONT.lfStrikeOut = 0 ;
LFTTYFONT.lfCharSet = OEM_CHARSET ;
LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ;
LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ;
LFTTYFONT.lfQuality = DEFAULT_QUALITY ;
LFTTYFONT.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ;
lstrcpy( LFTTYFONT.lfFaceName, "Fixedsys" ) ;
hFont = CreateFontIndirect(&LFTTYFONT) ;
// hFont = MyCreateFont();
SetWindowText(hWnd,Title);
hMonitor = CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
2,2,400,230, hWnd, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
sock = socket(AF_INET,SOCK_DGRAM,0);
if (sock == INVALID_SOCKET)
{
err = WSAGetLastError();
sprintf(Msg, "Failed to create UDP socket - error code = %d", err);
MessageBox(NULL, Msg, "WinmorControl", MB_OK);
return FALSE;
}
if (WSAAsyncSelect(sock, hWnd, WSA_READ, FD_READ) > 0)
{
wsprintf(Msg, TEXT("WSAAsyncSelect failed Error %d"), WSAGetLastError());
MessageBox(hWnd, Msg, TEXT("WinmorControl"), MB_OK);
closesocket(sock);
return FALSE;
}
// ioctlsocket (sock, FIONBIO, &param);
setsockopt (sock,SOL_SOCKET,SO_BROADCAST,(const char FAR *)&bcopt,4);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = htons(udpport);
ret = bind(sock, (struct sockaddr *) &sinx, sizeof(sinx));
if (ret != 0)
{
// Bind Failed
err = WSAGetLastError();
sprintf(Msg, "Bind Failed for UDP socket - error code = %d", err);
MessageBox(NULL, Msg, "WinmorControl", MB_OK);
return FALSE;
}
if (RigPort)
{
int Speed = 9600;
if (RigSpeed)
Speed = atoi(RigSpeed);
RigHandle = OpenCOMPort(RigPort, Speed, FALSE, FALSE, FALSE, TWOSTOPBITS);
if (RigHandle)
{
if (RigType == RTS)
COMClearRTS(RigHandle);
else
COMSetRTS(RigHandle);
if (RigType == DTR)
COMClearDTR(RigHandle);
else
COMSetDTR(RigHandle);
}
else
return FALSE; // Open Failed
}
TimerHandle = SetTimer(hWnd, 1, 100, NULL);
return TRUE;
}
void Trace(char * Msg)
{
int index = SendMessage(hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Msg);
if (index > 1200)
do
index=index=SendMessage(hMonitor, LB_DELETESTRING, 0, 0);
while (index > 1000);
if (index > -1)
index=SendMessage(hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0));
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
char Msg[256];
int len;
switch (message)
{
case WSA_READ: // Notification on data socket
len = recvfrom(sock, Msg, 256, 0, &rx, &addrlen);
if (len <= 0)
return 0;
Msg[len] = 0;
if (_memicmp(Msg, "REMOTE:", 7) == 0)
{
Trace(Msg);
RestartTNC(&Msg[7]);
}
else
if (_memicmp(Msg, "KILL ", 5) == 0)
{
Trace(Msg);
KillTNC(atoi(&Msg[5]));
}
else
if (_memicmp(Msg, "KILLBYNAME ", 11) == 0)
{
Trace(Msg);
KillOldTNC(&Msg[11]);
}
else
{
// Anything else is Rig Control
len = WriteCOMBlock(RigHandle, Msg, len);
}
break;
case WM_SYSCOMMAND:
wmId = LOWORD(wParam); // Remember, these are...
wmEvent = HIWORD(wParam); // ...different for Win32!
switch (wmId) {
case SC_MINIMIZE:
if (MinimizetoTray)
return ShowWindow(hWnd, SW_HIDE);
else
return (DefWindowProc(hWnd, message, wParam, lParam));
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0);
}
BOOL EndCATThread = FALSE;
VOID CATThread()
{
DWORD dwLength = 0;
int Length, ret;
HANDLE Event;
OVERLAPPED Overlapped;
EndCATThread = FALSE;
Event = CreateEvent(NULL, TRUE, FALSE, NULL);
memset(&Overlapped, 0, sizeof(OVERLAPPED));
Overlapped.hEvent = Event;
ReadFile(RigHandle, RXBuffer, 1024, &RXLen, &Overlapped);
while (EndCATThread == FALSE)
{
ret = WaitForSingleObject(Event, 10000);
if (ret == WAIT_TIMEOUT)
{
if (EndCATThread)
{
CancelIo(RigHandle);
CloseHandle(RigHandle);
RigHandle = INVALID_HANDLE_VALUE;
CloseHandle(Event);
EndCATThread = FALSE;
return;
}
continue;
}
ResetEvent(Event);
ret = GetOverlappedResult(RigHandle, &Overlapped, &Length, FALSE);
if (ret && Length)
{
// got something so send to BPQ
sendto(sock, RXBuffer, Length, 0, &rx, sizeof(struct sockaddr));
}
memset(&Overlapped, 0, sizeof(OVERLAPPED));
Overlapped.hEvent = Event;
ReadFile(RigHandle, RXBuffer, 1024, &RXLen, &Overlapped);
}
}