Initial commit.
This commit is contained in:
commit
f666825417
19 changed files with 4261 additions and 0 deletions
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Git LFS - binary files
|
||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.DRV filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Force LF line endings on source files
|
||||
*.c text eol=lf
|
||||
*.h text eol=lf
|
||||
*.rc text eol=lf
|
||||
*.def text eol=lf
|
||||
makefile text eol=lf
|
||||
*.TXT text eol=lf
|
||||
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# MSVC 1.52 build artifacts
|
||||
*.obj
|
||||
*.res
|
||||
*.map
|
||||
*.lib
|
||||
*.pdb
|
||||
*.sbr
|
||||
*.bsc
|
||||
*.pch
|
||||
*.ilk
|
||||
|
||||
# Build outputs
|
||||
*.vbx
|
||||
*.drv
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*~
|
||||
BIN
drivers/CYBERCOM.DRV
(Stored with Git LFS)
Normal file
BIN
drivers/CYBERCOM.DRV
(Stored with Git LFS)
Normal file
Binary file not shown.
101
drivers/CYBERCOM.TXT
Normal file
101
drivers/CYBERCOM.TXT
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
CyberCom V1.1.0.0P
|
||||
|
||||
CYBERCOM.DRV 9264 11-26-93 1:06p
|
||||
|
||||
The High Speed Serial Communications Driver for Windows 3.1
|
||||
Designed for 386 (and above) systems using the 16550 serial port
|
||||
chip. (C) CyberSoft Corp 1993
|
||||
|
||||
Requires Windows 3.1, Enhanced Mode, a 16550 UART
|
||||
|
||||
|
||||
INTRODUCTION.
|
||||
-------------
|
||||
CyberCom is a direct replacement for the standard Windows
|
||||
Communications Driver (COMM.DRV).
|
||||
|
||||
* Transfer at up to 115,200 KB with a 16550 serial port chip
|
||||
* Great for V.FAST and Voice modems that require 57,600 Kb
|
||||
* More reliable Transfer with less overhead on your system - Better
|
||||
background operation.
|
||||
* Fewer (if any) over/under runs.
|
||||
|
||||
|
||||
HOW DOES IT WORK?
|
||||
-----------------
|
||||
Don't worry if the following sounds too complicated - just skip over
|
||||
it and move on to the Installation...
|
||||
|
||||
* CyberCom enables the FIFO buffer on the 16550 to be enabled for both
|
||||
receive AND transmit - COMM.DRV only enables the FIFO for receive.
|
||||
|
||||
* The 'interrupt trigger level' has been set to 8 for both transmit and
|
||||
receive. This gives your applications a lot more time to process
|
||||
incoming information. COMM.DRV sets the trigger level to 14 which
|
||||
means that your application only has 2 characters in which read from
|
||||
the FIFO buffer.
|
||||
|
||||
What this means is that your communications applications will get far
|
||||
fewer (if any) 'under runs' or 'over runs' when sending or receiving.
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
1. Copy CYBERCOM.DRV into your Windows\System directory.
|
||||
|
||||
Edit the Windows\SYSTEM.INI file and change the following line:
|
||||
|
||||
From comm.drv=comm.drv
|
||||
To comm.drv=cybercom.drv
|
||||
|
||||
2. If you previously have not taken advantage of the 16550 installed
|
||||
in your computer then ensure that the Windows\SYSTEM.INI file has the
|
||||
following information:
|
||||
|
||||
[386Enh]
|
||||
COMnFIFO=1
|
||||
|
||||
where n is the number of the COM port.
|
||||
|
||||
if your 16550 is installed on, say, COM 1 then
|
||||
|
||||
[386Enh]
|
||||
COM1FIFO=1
|
||||
|
||||
3. Start or Restart windows.
|
||||
|
||||
|
||||
LICENCE
|
||||
-------
|
||||
CyberCom is provided free for non-commercial use.
|
||||
|
||||
You may not distribute CyberCom for profit. If you wish to include
|
||||
CyberCom with your applications then contact CyberSoft at the address
|
||||
below for details about royalty-free licences.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
For further information, upgrades, problems etc please contact us at:
|
||||
|
||||
Attention: Douglas I. Scadlock
|
||||
CyberSoft Corporation Pty Ltd,
|
||||
PO BOX 407 Vaucluse,
|
||||
New South Wales
|
||||
2030 Australia
|
||||
Phone +61 2 805 1077
|
||||
Fax +61 2 805 0897
|
||||
CIS 100033,1723
|
||||
|
||||
|
||||
CAVEAT
|
||||
------
|
||||
THE INFORMATION AND COMMUNICATIONS DRIVER PROVIDED HEREUNDER
|
||||
(COLLECTIVELY REFERRED TO AS "SOFTWARE") IS PROVIDED AS IS WITHOUT
|
||||
WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE.
|
||||
|
||||
(C) CyberSoft Corp 1993
|
||||
(C) Microsoft Corp
|
||||
|
||||
**********************************************************************
|
||||
Sydney 2000 - Home of the Millennium Games
|
||||
1302
drv/commdrv.c
Normal file
1302
drv/commdrv.c
Normal file
File diff suppressed because it is too large
Load diff
27
drv/commdrv.def
Normal file
27
drv/commdrv.def
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
LIBRARY COMM
|
||||
DESCRIPTION 'High-Speed Serial Communications Driver'
|
||||
EXETYPE WINDOWS
|
||||
CODE PRELOAD FIXED
|
||||
DATA PRELOAD FIXED SINGLE
|
||||
HEAPSIZE 1024
|
||||
EXPORTS
|
||||
INICOM @1
|
||||
SETCOM @2
|
||||
SETQUE @3
|
||||
RECCOM @4
|
||||
SNDCOM @5
|
||||
CTX @6
|
||||
TRMCOM @7
|
||||
STACOM @8
|
||||
CEXTFCN @9
|
||||
CFLUSH @10
|
||||
CEVT @11
|
||||
CEVTGET @12
|
||||
CSETBRK @13
|
||||
CCLRBRK @14
|
||||
GETDCB @15
|
||||
SUSPENDOPENCOMMPORTS @17
|
||||
REACTIVATEOPENCOMMPORTS @18
|
||||
COMMWRITESTRING @19
|
||||
READCOMMSTRING @20
|
||||
ENABLENOTIFICATION @100
|
||||
373
drv/commdrv.h
Normal file
373
drv/commdrv.h
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
// commdrv.h - High-speed COMM.DRV replacement
|
||||
//
|
||||
// Types, UART register definitions, port state structure, and prototypes.
|
||||
// Drop-in replacement for Windows 3.1 stock COMM.DRV with proper 16550
|
||||
// FIFO management for reliable operation at 57600 and 115200 baud.
|
||||
|
||||
#ifndef COMMDRV_H
|
||||
#define COMMDRV_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// stdint types for MSVC 1.52 (no stdint.h available)
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef _STDINT_DEFINED
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef long int32_t;
|
||||
typedef unsigned long uint32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
#define _STDINT_DEFINED
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Maximum ports supported
|
||||
// -----------------------------------------------------------------------
|
||||
#define MAX_PORTS 4
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Default buffer sizes
|
||||
// -----------------------------------------------------------------------
|
||||
#define DEFAULT_RX_SIZE 4096
|
||||
#define DEFAULT_TX_SIZE 4096
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// UART register offsets from base address
|
||||
// -----------------------------------------------------------------------
|
||||
#define UART_RBR 0 // Receive Buffer Register (read, DLAB=0)
|
||||
#define UART_THR 0 // Transmit Holding Register (write, DLAB=0)
|
||||
#define UART_DLL 0 // Divisor Latch Low (DLAB=1)
|
||||
#define UART_IER 1 // Interrupt Enable Register (DLAB=0)
|
||||
#define UART_DLM 1 // Divisor Latch High (DLAB=1)
|
||||
#define UART_IIR 2 // Interrupt Identification Register (read)
|
||||
#define UART_FCR 2 // FIFO Control Register (write)
|
||||
#define UART_LCR 3 // Line Control Register
|
||||
#define UART_MCR 4 // Modem Control Register
|
||||
#define UART_LSR 5 // Line Status Register
|
||||
#define UART_MSR 6 // Modem Status Register
|
||||
#define UART_SCR 7 // Scratch Register
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IER bits - Interrupt Enable Register
|
||||
// -----------------------------------------------------------------------
|
||||
#define IER_RDA 0x01 // Received Data Available
|
||||
#define IER_THRE 0x02 // Transmitter Holding Register Empty
|
||||
#define IER_LSI 0x04 // Line Status Interrupt
|
||||
#define IER_MSI 0x08 // Modem Status Interrupt
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IIR bits - Interrupt Identification Register
|
||||
// -----------------------------------------------------------------------
|
||||
#define IIR_PENDING 0x01 // 0=interrupt pending, 1=no interrupt
|
||||
#define IIR_ID_MASK 0x0E // Interrupt ID mask
|
||||
#define IIR_MSR 0x00 // Modem Status change
|
||||
#define IIR_THRE 0x02 // Transmitter Holding Register Empty
|
||||
#define IIR_RDA 0x04 // Received Data Available
|
||||
#define IIR_LSR 0x06 // Line Status (error/break)
|
||||
#define IIR_TIMEOUT 0x0C // Character Timeout (16550 FIFO)
|
||||
#define IIR_FIFO_MASK 0xC0 // FIFO enabled bits
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// FCR bits - FIFO Control Register (write-only)
|
||||
// -----------------------------------------------------------------------
|
||||
#define FCR_ENABLE 0x01 // Enable FIFOs
|
||||
#define FCR_RX_RESET 0x02 // Reset RX FIFO
|
||||
#define FCR_TX_RESET 0x04 // Reset TX FIFO
|
||||
#define FCR_DMA_MODE 0x08 // DMA mode select
|
||||
#define FCR_TRIG_1 0x00 // RX trigger level: 1 byte
|
||||
#define FCR_TRIG_4 0x40 // RX trigger level: 4 bytes
|
||||
#define FCR_TRIG_8 0x80 // RX trigger level: 8 bytes
|
||||
#define FCR_TRIG_14 0xC0 // RX trigger level: 14 bytes
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// LCR bits - Line Control Register
|
||||
// -----------------------------------------------------------------------
|
||||
#define LCR_WLS_MASK 0x03 // Word Length Select mask
|
||||
#define LCR_WLS_5 0x00 // 5 data bits
|
||||
#define LCR_WLS_6 0x01 // 6 data bits
|
||||
#define LCR_WLS_7 0x02 // 7 data bits
|
||||
#define LCR_WLS_8 0x03 // 8 data bits
|
||||
#define LCR_STB 0x04 // Number of Stop Bits (0=1, 1=2)
|
||||
#define LCR_PEN 0x08 // Parity Enable
|
||||
#define LCR_EPS 0x10 // Even Parity Select
|
||||
#define LCR_SPAR 0x20 // Stick Parity
|
||||
#define LCR_SBRK 0x40 // Set Break
|
||||
#define LCR_DLAB 0x80 // Divisor Latch Access Bit
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// MCR bits - Modem Control Register
|
||||
// -----------------------------------------------------------------------
|
||||
#define MCR_DTR 0x01 // Data Terminal Ready
|
||||
#define MCR_RTS 0x02 // Request To Send
|
||||
#define MCR_OUT1 0x04 // Output 1
|
||||
#define MCR_OUT2 0x08 // Output 2 (master interrupt enable)
|
||||
#define MCR_LOOP 0x10 // Loopback mode
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// LSR bits - Line Status Register
|
||||
// -----------------------------------------------------------------------
|
||||
#define LSR_DR 0x01 // Data Ready
|
||||
#define LSR_OE 0x02 // Overrun Error
|
||||
#define LSR_PE 0x04 // Parity Error
|
||||
#define LSR_FE 0x08 // Framing Error
|
||||
#define LSR_BI 0x10 // Break Interrupt
|
||||
#define LSR_THRE 0x20 // Transmitter Holding Register Empty
|
||||
#define LSR_TEMT 0x40 // Transmitter Empty (shift register too)
|
||||
#define LSR_FIFO 0x80 // Error in RX FIFO (16550)
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// MSR bits - Modem Status Register
|
||||
// -----------------------------------------------------------------------
|
||||
#define MSR_DCTS 0x01 // Delta CTS
|
||||
#define MSR_DDSR 0x02 // Delta DSR
|
||||
#define MSR_TERI 0x04 // Trailing Edge Ring Indicator
|
||||
#define MSR_DDCD 0x08 // Delta DCD
|
||||
#define MSR_CTS 0x10 // Clear To Send
|
||||
#define MSR_DSR 0x20 // Data Set Ready
|
||||
#define MSR_RI 0x40 // Ring Indicator
|
||||
#define MSR_DCD 0x80 // Data Carrier Detect
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// PIC constants
|
||||
// -----------------------------------------------------------------------
|
||||
#define PIC_CMD 0x20 // PIC command port
|
||||
#define PIC_DATA 0x21 // PIC data (mask) port
|
||||
#define PIC_EOI 0x20 // End of Interrupt command
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Standard port base addresses and IRQs
|
||||
// -----------------------------------------------------------------------
|
||||
#define COM1_BASE 0x03F8
|
||||
#define COM2_BASE 0x02F8
|
||||
#define COM3_BASE 0x03E8
|
||||
#define COM4_BASE 0x02E8
|
||||
|
||||
#define COM1_IRQ 4
|
||||
#define COM2_IRQ 3
|
||||
#define COM3_IRQ 4
|
||||
#define COM4_IRQ 3
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Baud rate divisor (115200 / baud)
|
||||
// -----------------------------------------------------------------------
|
||||
#define BAUD_DIVISOR_BASE 115200UL
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CBR_* baud rate index constants
|
||||
//
|
||||
// The 16-bit DCB BaudRate field is a UINT (16 bits). Since 115200
|
||||
// exceeds 65535, high baud rates use CBR_* index constants instead
|
||||
// of raw values. Values >= 0xFF00 are indices, not raw rates.
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef CBR_110
|
||||
#define CBR_110 0xFF10
|
||||
#define CBR_300 0xFF11
|
||||
#define CBR_600 0xFF12
|
||||
#define CBR_1200 0xFF13
|
||||
#define CBR_2400 0xFF14
|
||||
#define CBR_4800 0xFF15
|
||||
#define CBR_9600 0xFF16
|
||||
#define CBR_14400 0xFF17
|
||||
#define CBR_19200 0xFF18
|
||||
#define CBR_38400 0xFF1B
|
||||
#define CBR_56000 0xFF1F
|
||||
#define CBR_115200 0xFF24
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 16550 FIFO depth
|
||||
// -----------------------------------------------------------------------
|
||||
#define FIFO_DEPTH 16
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Comm error flags (CE_* -- matches Windows SDK definitions)
|
||||
// -----------------------------------------------------------------------
|
||||
#define CE_RXOVER 0x0001 // Receive Queue overflow
|
||||
#define CE_OVERRUN 0x0002 // Hardware overrun
|
||||
#define CE_RXPARITY 0x0004 // Parity error
|
||||
#define CE_FRAME 0x0008 // Framing error
|
||||
#define CE_BREAK 0x0010 // Break detected
|
||||
#define CE_TXFULL 0x0020 // TX Queue is full
|
||||
#define CE_MODE 0x8000 // Requested mode unsupported
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Comm event flags (EV_* -- matches Windows SDK definitions)
|
||||
// -----------------------------------------------------------------------
|
||||
#define EV_RXCHAR 0x0001 // Any character received
|
||||
#define EV_RXFLAG 0x0002 // Event character received
|
||||
#define EV_TXEMPTY 0x0004 // TX buffer empty
|
||||
#define EV_CTS 0x0008 // CTS changed
|
||||
#define EV_DSR 0x0010 // DSR changed
|
||||
#define EV_RLSD 0x0020 // DCD/RLSD changed
|
||||
#define EV_BREAK 0x0040 // Break received
|
||||
#define EV_ERR 0x0080 // Line status error
|
||||
#define EV_RING 0x0100 // Ring indicator
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CN_* notification codes (lParam for WM_COMMNOTIFY)
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef CN_RECEIVE
|
||||
#define CN_RECEIVE 0x0001
|
||||
#define CN_TRANSMIT 0x0002
|
||||
#define CN_EVENT 0x0004
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// WM_COMMNOTIFY message
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef WM_COMMNOTIFY
|
||||
#define WM_COMMNOTIFY 0x0044
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Handshaking modes
|
||||
// -----------------------------------------------------------------------
|
||||
#define HS_NONE 0
|
||||
#define HS_XONXOFF 1
|
||||
#define HS_RTSCTS 2
|
||||
#define HS_BOTH 3
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// EscapeCommFunction codes (matches Windows SDK)
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef SETXON
|
||||
#define SETXON 1
|
||||
#define SETXOFF 2
|
||||
#define SETRTS 3
|
||||
#define CLRRTS 4
|
||||
#define SETDTR 5
|
||||
#define CLRDTR 6
|
||||
#define RESETDEV 7
|
||||
#define SETBREAK 8
|
||||
#define CLRBREAK 9
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Flush queue selectors
|
||||
// -----------------------------------------------------------------------
|
||||
#define FLUSH_RX 0
|
||||
#define FLUSH_TX 1
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Port state structure
|
||||
// -----------------------------------------------------------------------
|
||||
typedef struct {
|
||||
uint16_t baseAddr; // UART base I/O address
|
||||
uint8_t irq; // IRQ number (3 or 4)
|
||||
int16_t commId; // Port ID (0=COM1, 1=COM2, ...)
|
||||
uint8_t isOpen; // Port open flag
|
||||
uint8_t is16550; // 16550 FIFO detected
|
||||
uint8_t fifoEnabled; // FIFO enabled (COMnFIFO setting)
|
||||
uint8_t fifoTrigger; // RX FIFO trigger FCR bits (FCR_TRIG_*)
|
||||
|
||||
// Ring buffers (GlobalAlloc'd)
|
||||
uint8_t FAR *rxBuf; // Receive ring buffer
|
||||
uint16_t rxSize; // Buffer size
|
||||
uint16_t rxHead; // Write position (ISR writes)
|
||||
uint16_t rxTail; // Read position (app reads)
|
||||
uint16_t rxCount; // Bytes in buffer
|
||||
|
||||
uint8_t FAR *txBuf; // Transmit ring buffer
|
||||
uint16_t txSize; // Buffer size
|
||||
uint16_t txHead; // Write position (app writes)
|
||||
uint16_t txTail; // Read position (ISR reads)
|
||||
uint16_t txCount; // Bytes in buffer
|
||||
|
||||
// DCB shadow
|
||||
uint16_t baudRate; // Current baud rate
|
||||
uint8_t byteSize; // Data bits (5-8)
|
||||
uint8_t parity; // Parity mode
|
||||
uint8_t stopBits; // Stop bits
|
||||
|
||||
// Flow control state
|
||||
uint8_t hsMode; // Handshaking mode
|
||||
uint8_t txStopped; // TX halted by flow control
|
||||
uint8_t rxStopped; // We sent XOFF / dropped RTS
|
||||
uint8_t xonChar; // XON character (default 0x11)
|
||||
uint8_t xoffChar; // XOFF character (default 0x13)
|
||||
uint16_t xoffLim; // Send XOFF when rxCount > rxSize - xoffLim
|
||||
uint16_t xonLim; // Send XON when rxCount < xonLim
|
||||
|
||||
// Modem control shadow
|
||||
uint8_t dtrState; // DTR line state
|
||||
uint8_t rtsState; // RTS line state (when not flow-controlled)
|
||||
|
||||
// Error accumulator
|
||||
uint16_t errorFlags; // CE_* error flags (sticky until read)
|
||||
|
||||
// Event notification
|
||||
uint16_t evtMask; // Event enable mask
|
||||
uint16_t evtWord; // Accumulated events
|
||||
HWND hwndNotify; // Window for WM_COMMNOTIFY
|
||||
int16_t rxNotifyThresh; // CN_RECEIVE threshold (-1=disabled)
|
||||
int16_t txNotifyThresh; // CN_TRANSMIT threshold (-1=disabled)
|
||||
|
||||
// ISR state
|
||||
void (FAR *prevIsr)(void); // Previous ISR in chain
|
||||
uint8_t irqMask; // PIC mask bit for this IRQ
|
||||
uint8_t breakState; // Break signal active
|
||||
|
||||
// Priority transmit
|
||||
int16_t txImmediate; // -1=none, else char to send immediately
|
||||
|
||||
// DCB copy for GetCommState
|
||||
DCB dcb; // Full DCB for GETDCB/SETCOM
|
||||
} PortStateT;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Global port state array
|
||||
// -----------------------------------------------------------------------
|
||||
extern PortStateT ports[MAX_PORTS];
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Exported function prototypes (COMM.DRV API)
|
||||
//
|
||||
// These use the Windows COMM.DRV calling convention: FAR PASCAL
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize);
|
||||
int16_t FAR PASCAL _export setcom(DCB FAR *dcb);
|
||||
int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBuf, int16_t rxSize, char FAR *txBuf, int16_t txSize);
|
||||
int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len);
|
||||
int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len);
|
||||
int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch);
|
||||
int16_t FAR PASCAL _export trmcom(int16_t commId);
|
||||
int16_t FAR PASCAL _export stacom(int16_t commId, COMSTAT FAR *stat);
|
||||
int32_t FAR PASCAL _export cevt(int16_t commId, int16_t evtMask);
|
||||
uint16_t FAR PASCAL _export cevtget(int16_t commId, int16_t evtMask);
|
||||
int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func);
|
||||
int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue);
|
||||
int16_t FAR PASCAL _export csetbrk(int16_t commId);
|
||||
int16_t FAR PASCAL _export cclrbrk(int16_t commId);
|
||||
int16_t FAR PASCAL _export getdcb(int16_t commId, DCB FAR *dcb);
|
||||
void FAR PASCAL _export suspendOpenCommPorts(void);
|
||||
void FAR PASCAL _export reactivateOpenCommPorts(void);
|
||||
int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len);
|
||||
int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len);
|
||||
int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// ISR prototypes (isr.c)
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t hookIsr(PortStateT *port);
|
||||
void unhookIsr(PortStateT *port);
|
||||
void isrDispatch(PortStateT *port);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Internal helpers
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t detect16550(uint16_t baseAddr);
|
||||
void applyBaudRate(PortStateT *port, uint16_t baud);
|
||||
void applyLineParams(PortStateT *port, uint8_t byteSize, uint8_t parity, uint8_t stopBits);
|
||||
void enableFifo(PortStateT *port);
|
||||
void primeTx(PortStateT *port);
|
||||
|
||||
#endif // COMMDRV_H
|
||||
507
drv/isr.c
Normal file
507
drv/isr.c
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
// isr.c - Interrupt service routines for COMM.DRV replacement
|
||||
//
|
||||
// ISR entry points for IRQ3 (COM2/4) and IRQ4 (COM1/3), DPMI interrupt
|
||||
// vector hooking/unhooking, and interrupt dispatch with 16550 FIFO support.
|
||||
|
||||
#include "commdrv.h"
|
||||
#include <dos.h>
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Prototypes
|
||||
// -----------------------------------------------------------------------
|
||||
static void checkFlowRx(PortStateT *port);
|
||||
static void checkNotify(PortStateT *port);
|
||||
static void handleLsr(PortStateT *port, uint8_t lsr);
|
||||
static void handleMsr(PortStateT *port);
|
||||
static void handleRx(PortStateT *port, uint8_t lsr);
|
||||
static void handleTx(PortStateT *port);
|
||||
|
||||
void _far _interrupt isr3(void);
|
||||
void _far _interrupt isr4(void);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Saved previous ISR vectors for IRQ3 and IRQ4
|
||||
// -----------------------------------------------------------------------
|
||||
static void (_far _interrupt *prevIsr3)(void) = NULL;
|
||||
static void (_far _interrupt *prevIsr4)(void) = NULL;
|
||||
|
||||
// Track how many ports are using each IRQ so we know when to unhook
|
||||
static int16_t irq3RefCount = 0;
|
||||
static int16_t irq4RefCount = 0;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// checkFlowRx - Check RX buffer level and assert/deassert flow control
|
||||
// -----------------------------------------------------------------------
|
||||
static void checkFlowRx(PortStateT *port)
|
||||
{
|
||||
uint16_t base;
|
||||
|
||||
base = port->baseAddr;
|
||||
|
||||
if (port->hsMode == HS_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we need to stop remote sender
|
||||
if (!port->rxStopped && port->rxCount >= (port->rxSize - port->xoffLim)) {
|
||||
port->rxStopped = TRUE;
|
||||
|
||||
if (port->hsMode == HS_XONXOFF || port->hsMode == HS_BOTH) {
|
||||
// Send XOFF - queue as immediate character
|
||||
port->txImmediate = port->xoffChar;
|
||||
// Enable THRE to get it sent
|
||||
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) | IER_THRE));
|
||||
}
|
||||
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||
// Drop RTS
|
||||
_outp(base + UART_MCR, (uint8_t)(_inp(base + UART_MCR) & ~MCR_RTS));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we can resume remote sender
|
||||
if (port->rxStopped && port->rxCount <= port->xonLim) {
|
||||
port->rxStopped = FALSE;
|
||||
|
||||
if (port->hsMode == HS_XONXOFF || port->hsMode == HS_BOTH) {
|
||||
// Send XON
|
||||
port->txImmediate = port->xonChar;
|
||||
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) | IER_THRE));
|
||||
}
|
||||
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||
// Raise RTS
|
||||
_outp(base + UART_MCR, (uint8_t)(_inp(base + UART_MCR) | MCR_RTS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// checkNotify - Post WM_COMMNOTIFY if threshold conditions met
|
||||
//
|
||||
// Called at end of ISR dispatch, outside the IIR loop.
|
||||
// Uses PostMessage to avoid reentrancy issues.
|
||||
// -----------------------------------------------------------------------
|
||||
static void checkNotify(PortStateT *port)
|
||||
{
|
||||
uint16_t notifyBits;
|
||||
|
||||
if (!port->hwndNotify) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyBits = 0;
|
||||
|
||||
// CN_RECEIVE: rxCount crossed threshold from below
|
||||
if (port->rxNotifyThresh >= 0 && port->rxCount >= (uint16_t)port->rxNotifyThresh) {
|
||||
notifyBits |= CN_RECEIVE;
|
||||
}
|
||||
|
||||
// CN_TRANSMIT: space available crossed threshold
|
||||
if (port->txNotifyThresh >= 0) {
|
||||
uint16_t txFree = port->txSize - port->txCount;
|
||||
if (txFree >= (uint16_t)port->txNotifyThresh) {
|
||||
notifyBits |= CN_TRANSMIT;
|
||||
}
|
||||
}
|
||||
|
||||
// CN_EVENT: any event bits accumulated
|
||||
if (port->evtWord & port->evtMask) {
|
||||
notifyBits |= CN_EVENT;
|
||||
}
|
||||
|
||||
if (notifyBits) {
|
||||
PostMessage(port->hwndNotify, WM_COMMNOTIFY, (WPARAM)port->commId, (LPARAM)notifyBits);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// handleLsr - Process line status errors
|
||||
// -----------------------------------------------------------------------
|
||||
static void handleLsr(PortStateT *port, uint8_t lsr)
|
||||
{
|
||||
if (lsr & LSR_OE) {
|
||||
port->errorFlags |= CE_OVERRUN;
|
||||
}
|
||||
if (lsr & LSR_PE) {
|
||||
port->errorFlags |= CE_RXPARITY;
|
||||
}
|
||||
if (lsr & LSR_FE) {
|
||||
port->errorFlags |= CE_FRAME;
|
||||
}
|
||||
if (lsr & LSR_BI) {
|
||||
port->errorFlags |= CE_BREAK;
|
||||
port->evtWord |= EV_BREAK;
|
||||
}
|
||||
if (lsr & (LSR_OE | LSR_PE | LSR_FE)) {
|
||||
port->evtWord |= EV_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// handleMsr - Process modem status changes
|
||||
// -----------------------------------------------------------------------
|
||||
static void handleMsr(PortStateT *port)
|
||||
{
|
||||
uint8_t msr;
|
||||
|
||||
msr = (uint8_t)_inp(port->baseAddr + UART_MSR);
|
||||
|
||||
if (msr & MSR_DCTS) {
|
||||
port->evtWord |= EV_CTS;
|
||||
|
||||
// RTS/CTS flow control: stop/start TX based on CTS
|
||||
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||
if (msr & MSR_CTS) {
|
||||
port->txStopped = FALSE;
|
||||
// Resume TX
|
||||
if (port->txCount > 0) {
|
||||
handleTx(port);
|
||||
}
|
||||
} else {
|
||||
port->txStopped = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msr & MSR_DDSR) {
|
||||
port->evtWord |= EV_DSR;
|
||||
}
|
||||
|
||||
if (msr & MSR_TERI) {
|
||||
port->evtWord |= EV_RING;
|
||||
}
|
||||
|
||||
if (msr & MSR_DDCD) {
|
||||
port->evtWord |= EV_RLSD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// handleRx - Read all available bytes from UART into RX ring buffer
|
||||
// -----------------------------------------------------------------------
|
||||
static void handleRx(PortStateT *port, uint8_t lsr)
|
||||
{
|
||||
uint16_t base;
|
||||
uint8_t ch;
|
||||
|
||||
base = port->baseAddr;
|
||||
|
||||
while (lsr & LSR_DR) {
|
||||
// Check for line errors on this byte
|
||||
if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) {
|
||||
handleLsr(port, lsr);
|
||||
}
|
||||
|
||||
ch = (uint8_t)_inp(base + UART_RBR);
|
||||
|
||||
// Check for XON/XOFF if software flow control enabled
|
||||
if (port->hsMode == HS_XONXOFF || port->hsMode == HS_BOTH) {
|
||||
if (ch == port->xonChar) {
|
||||
port->txStopped = FALSE;
|
||||
// Resume TX if we have data
|
||||
if (port->txCount > 0) {
|
||||
handleTx(port);
|
||||
}
|
||||
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||
continue;
|
||||
}
|
||||
if (ch == port->xoffChar) {
|
||||
port->txStopped = TRUE;
|
||||
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Store in ring buffer
|
||||
if (port->rxCount < port->rxSize) {
|
||||
port->rxBuf[port->rxHead] = ch;
|
||||
port->rxHead++;
|
||||
if (port->rxHead >= port->rxSize) {
|
||||
port->rxHead = 0;
|
||||
}
|
||||
port->rxCount++;
|
||||
} else {
|
||||
// Buffer overflow
|
||||
port->errorFlags |= CE_RXOVER;
|
||||
}
|
||||
|
||||
// Set event bit
|
||||
port->evtWord |= EV_RXCHAR;
|
||||
|
||||
// Read LSR again for next byte
|
||||
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||
}
|
||||
|
||||
// Check if we need to assert flow control
|
||||
checkFlowRx(port);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// handleTx - Transmit bytes from TX ring buffer to UART
|
||||
//
|
||||
// For 16550: burst up to 16 bytes per THRE interrupt.
|
||||
// For 8250: send 1 byte at a time.
|
||||
// -----------------------------------------------------------------------
|
||||
static void handleTx(PortStateT *port)
|
||||
{
|
||||
uint16_t base;
|
||||
uint16_t burst;
|
||||
uint16_t i;
|
||||
|
||||
if (port->txStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
base = port->baseAddr;
|
||||
|
||||
// Priority character first
|
||||
if (port->txImmediate >= 0) {
|
||||
_outp(base + UART_THR, (uint8_t)port->txImmediate);
|
||||
port->txImmediate = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (port->txCount == 0) {
|
||||
// Nothing to send -- disable THRE interrupt
|
||||
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) & ~IER_THRE));
|
||||
// TX empty event
|
||||
port->evtWord |= EV_TXEMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Burst size: 16 for FIFO, 1 for non-FIFO or FIFO disabled
|
||||
burst = (port->is16550 && port->fifoEnabled) ? FIFO_DEPTH : 1;
|
||||
if (burst > port->txCount) {
|
||||
burst = port->txCount;
|
||||
}
|
||||
|
||||
for (i = 0; i < burst; i++) {
|
||||
_outp(base + UART_THR, port->txBuf[port->txTail]);
|
||||
port->txTail++;
|
||||
if (port->txTail >= port->txSize) {
|
||||
port->txTail = 0;
|
||||
}
|
||||
port->txCount--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// hookIsr - Hook the interrupt vector for a port's IRQ via DPMI
|
||||
//
|
||||
// Uses INT 31h AX=0204h to get and AX=0205h to set protected-mode
|
||||
// interrupt vectors.
|
||||
//
|
||||
// Returns 0 on success, -1 on error.
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t hookIsr(PortStateT *port)
|
||||
{
|
||||
uint8_t intNum;
|
||||
uint8_t picMask;
|
||||
int16_t *refCount;
|
||||
void (_far _interrupt **prevPtr)(void);
|
||||
void (_far _interrupt *newIsr)(void);
|
||||
void _far *oldVector;
|
||||
uint16_t oldSeg;
|
||||
uint16_t oldOff;
|
||||
|
||||
intNum = port->irq + 8;
|
||||
port->irqMask = (uint8_t)(1 << port->irq);
|
||||
|
||||
if (port->irq == 3) {
|
||||
prevPtr = &prevIsr3;
|
||||
refCount = &irq3RefCount;
|
||||
newIsr = isr3;
|
||||
} else {
|
||||
prevPtr = &prevIsr4;
|
||||
refCount = &irq4RefCount;
|
||||
newIsr = isr4;
|
||||
}
|
||||
|
||||
// Only hook the vector on first use of this IRQ
|
||||
if (*refCount == 0) {
|
||||
// INT 31h AX=0204h: Get Protected Mode Interrupt Vector
|
||||
// BL = interrupt number
|
||||
// Returns: CX:DX = selector:offset of handler
|
||||
_asm {
|
||||
mov ax, 0204h
|
||||
mov bl, intNum
|
||||
int 31h
|
||||
mov oldSeg, cx
|
||||
mov oldOff, dx
|
||||
}
|
||||
|
||||
oldVector = (void _far *)((uint32_t)oldSeg << 16 | oldOff);
|
||||
*prevPtr = (void (_far _interrupt *)(void))oldVector;
|
||||
|
||||
// INT 31h AX=0205h: Set Protected Mode Interrupt Vector
|
||||
// BL = interrupt number
|
||||
// CX:DX = selector:offset of new handler
|
||||
{
|
||||
uint16_t newSeg = _FP_SEG(newIsr);
|
||||
uint16_t newOff = _FP_OFF(newIsr);
|
||||
|
||||
_asm {
|
||||
mov ax, 0205h
|
||||
mov bl, intNum
|
||||
mov cx, newSeg
|
||||
mov dx, newOff
|
||||
int 31h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(*refCount)++;
|
||||
port->prevIsr = (void (FAR *)(void))*prevPtr;
|
||||
|
||||
// Unmask IRQ at PIC
|
||||
picMask = _inp(PIC_DATA);
|
||||
picMask &= ~port->irqMask;
|
||||
_outp(PIC_DATA, picMask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// isrDispatch - Main interrupt dispatch loop
|
||||
//
|
||||
// Called from the ISR entry points with a pointer to the port state.
|
||||
// Reads IIR in a loop until no more interrupts are pending.
|
||||
// Handles in priority order: LSR > RX > TX > MSR.
|
||||
// -----------------------------------------------------------------------
|
||||
void isrDispatch(PortStateT *port)
|
||||
{
|
||||
uint8_t iir;
|
||||
uint8_t lsr;
|
||||
uint16_t base;
|
||||
|
||||
base = port->baseAddr;
|
||||
|
||||
for (;;) {
|
||||
iir = (uint8_t)_inp(base + UART_IIR);
|
||||
|
||||
// Bit 0 set = no interrupt pending
|
||||
if (iir & IIR_PENDING) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (iir & IIR_ID_MASK) {
|
||||
case IIR_LSR:
|
||||
// Line status: read LSR, accumulate errors
|
||||
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||
handleLsr(port, lsr);
|
||||
// If data ready, also handle RX
|
||||
if (lsr & LSR_DR) {
|
||||
handleRx(port, lsr);
|
||||
}
|
||||
break;
|
||||
|
||||
case IIR_RDA:
|
||||
case IIR_TIMEOUT:
|
||||
// Received data available or FIFO timeout
|
||||
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||
handleRx(port, lsr);
|
||||
break;
|
||||
|
||||
case IIR_THRE:
|
||||
// Transmitter holding register empty
|
||||
handleTx(port);
|
||||
break;
|
||||
|
||||
case IIR_MSR:
|
||||
// Modem status change
|
||||
handleMsr(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
checkNotify(port);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// unhookIsr - Restore previous interrupt vector via DPMI
|
||||
// -----------------------------------------------------------------------
|
||||
void unhookIsr(PortStateT *port)
|
||||
{
|
||||
uint8_t intNum;
|
||||
uint8_t picMask;
|
||||
int16_t *refCount;
|
||||
void (_far _interrupt **prevPtr)(void);
|
||||
|
||||
intNum = port->irq + 8;
|
||||
|
||||
if (port->irq == 3) {
|
||||
prevPtr = &prevIsr3;
|
||||
refCount = &irq3RefCount;
|
||||
} else {
|
||||
prevPtr = &prevIsr4;
|
||||
refCount = &irq4RefCount;
|
||||
}
|
||||
|
||||
// Mask IRQ at PIC
|
||||
picMask = _inp(PIC_DATA);
|
||||
picMask |= port->irqMask;
|
||||
_outp(PIC_DATA, picMask);
|
||||
|
||||
(*refCount)--;
|
||||
|
||||
// Only restore vector when last user of this IRQ unhooks
|
||||
if (*refCount <= 0) {
|
||||
uint16_t oldSeg = _FP_SEG(*prevPtr);
|
||||
uint16_t oldOff = _FP_OFF(*prevPtr);
|
||||
|
||||
_asm {
|
||||
mov ax, 0205h
|
||||
mov bl, intNum
|
||||
mov cx, oldSeg
|
||||
mov dx, oldOff
|
||||
int 31h
|
||||
}
|
||||
|
||||
*prevPtr = NULL;
|
||||
*refCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// isr3 - ISR entry point for IRQ3 (COM2 and COM4)
|
||||
// -----------------------------------------------------------------------
|
||||
void _far _interrupt isr3(void)
|
||||
{
|
||||
int16_t i;
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
if (ports[i].isOpen && ports[i].irq == 3) {
|
||||
isrDispatch(&ports[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Send EOI to PIC
|
||||
_outp(PIC_CMD, PIC_EOI);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// isr4 - ISR entry point for IRQ4 (COM1 and COM3)
|
||||
// -----------------------------------------------------------------------
|
||||
void _far _interrupt isr4(void)
|
||||
{
|
||||
int16_t i;
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
if (ports[i].isOpen && ports[i].irq == 4) {
|
||||
isrDispatch(&ports[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Send EOI to PIC
|
||||
_outp(PIC_CMD, PIC_EOI);
|
||||
}
|
||||
72
drv/makefile
Normal file
72
drv/makefile
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# makefile - High-speed COMM.DRV replacement for MSVC 1.52
|
||||
#
|
||||
# Build: nmake
|
||||
# Clean: nmake clean
|
||||
#
|
||||
# Prerequisites:
|
||||
# - MSVC 1.52 (cl, link in PATH)
|
||||
#
|
||||
# Output is COMM.DRV -- drop-in replacement for stock Windows 3.1 driver.
|
||||
#
|
||||
# Install:
|
||||
# 1. Copy COMM.DRV to \WINDOWS\SYSTEM
|
||||
# 2. Edit SYSTEM.INI [boot] section: comm.drv=comm.drv
|
||||
# 3. Add to [386Enh] section: COM1FIFO=1 (for each port in use)
|
||||
# 4. Restart Windows
|
||||
#
|
||||
# Key improvements over stock COMM.DRV:
|
||||
# - Both RX and TX FIFOs enabled (stock: RX only)
|
||||
# - RX trigger level 8 (stock: 14 -- only 2 bytes headroom)
|
||||
# - TX burst writes up to 16 bytes per THRE interrupt (stock: 1 byte)
|
||||
|
||||
CC = cl
|
||||
LINK = link
|
||||
|
||||
# Compiler flags:
|
||||
# -c Compile only
|
||||
# -W3 Warning level 3
|
||||
# -ASw Small model, SS!=DS (Windows DLL)
|
||||
# -Gsw No stack probes, Windows prolog/epilog
|
||||
# -Ow Safe optimizations for Windows
|
||||
# -Zp1 Pack structures on 1-byte boundaries (hardware register layouts)
|
||||
# -Ze Enable Microsoft extensions
|
||||
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp1 -Ze
|
||||
|
||||
# Linker flags:
|
||||
# /NOD No default libraries
|
||||
# /NOE No extended dictionary search
|
||||
# /AL:16 Segment alignment 16
|
||||
LFLAGS = /NOD /NOE /AL:16
|
||||
|
||||
# Libraries
|
||||
# sdllcew Small model DLL C runtime (emulated math, Windows)
|
||||
# libw Windows API import library
|
||||
LIBS = sdllcew libw
|
||||
|
||||
# Output
|
||||
TARGET = comm.drv
|
||||
|
||||
# Objects
|
||||
OBJS = commdrv.obj isr.obj
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Build rules
|
||||
# -----------------------------------------------------------------------
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) commdrv.def
|
||||
$(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), commdrv.def
|
||||
|
||||
commdrv.obj: commdrv.c commdrv.h
|
||||
$(CC) $(CFLAGS) commdrv.c
|
||||
|
||||
isr.obj: isr.c commdrv.h
|
||||
$(CC) $(CFLAGS) isr.c
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Clean
|
||||
# -----------------------------------------------------------------------
|
||||
clean:
|
||||
-del *.obj
|
||||
-del *.drv
|
||||
-del *.map
|
||||
90
vbx/makefile
Normal file
90
vbx/makefile
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# makefile - MSComm VBX control for MSVC 1.52
|
||||
#
|
||||
# Build: nmake
|
||||
# Clean: nmake clean
|
||||
#
|
||||
# Prerequisites:
|
||||
# - MSVC 1.52 (cl, link, rc in PATH)
|
||||
# - VBAPI.LIB (from VBX CDK, or generate with: implib vbapi.lib vbapi.def)
|
||||
#
|
||||
# High-speed serial note:
|
||||
# The stock Windows 3.1 COMM.DRV enables the 16550 FIFO for receive only,
|
||||
# with a trigger level of 14 (leaving only 2 bytes of headroom). This causes
|
||||
# overruns at baud rates above 9600 under load. For reliable operation at
|
||||
# 57600 or 115200, install CyberCom V1.1.0.0P -- a freeware drop-in
|
||||
# replacement included in ..\drivers\CYBERCOM.DRV. It enables both RX and TX
|
||||
# FIFOs with a trigger level of 8, dramatically reducing interrupt overhead.
|
||||
#
|
||||
# Install:
|
||||
# 1. Copy ..\drivers\CYBERCOM.DRV to \WINDOWS\SYSTEM
|
||||
# 2. Edit SYSTEM.INI [boot] section: comm.drv=cybercom.drv
|
||||
# 3. Add to [386Enh] section: COM1FIFO=1 (for each port in use)
|
||||
# 4. Restart Windows
|
||||
#
|
||||
# CyberCom is free for non-commercial use. (C) CyberSoft Corp 1993
|
||||
|
||||
CC = cl
|
||||
LINK = link
|
||||
RC = rc
|
||||
|
||||
# Compiler flags:
|
||||
# -c Compile only
|
||||
# -W3 Warning level 3
|
||||
# -ASw Small model, SS!=DS (Windows DLL)
|
||||
# -Gsw No stack probes, Windows prolog/epilog
|
||||
# -Ow Safe optimizations for Windows
|
||||
# -Zp2 Pack structures on 2-byte boundaries
|
||||
# -Ze Enable Microsoft extensions
|
||||
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp2 -Ze
|
||||
|
||||
# Linker flags:
|
||||
# /NOD No default libraries
|
||||
# /NOE No extended dictionary search
|
||||
# /AL:16 Segment alignment 16
|
||||
LFLAGS = /NOD /NOE /AL:16
|
||||
|
||||
# Libraries
|
||||
# sdllcew Small model DLL C runtime (emulated math, Windows)
|
||||
# libw Windows API import library
|
||||
# commdlg Common dialog import library
|
||||
# vbapi VB API import library
|
||||
LIBS = sdllcew libw commdlg vbapi
|
||||
|
||||
# Output
|
||||
TARGET = mscomm.vbx
|
||||
|
||||
# Objects
|
||||
OBJS = mscomm.obj serial.obj
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Build rules
|
||||
# -----------------------------------------------------------------------
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) mscomm.def mscomm.res
|
||||
$(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), mscomm.def
|
||||
$(RC) mscomm.res $(TARGET)
|
||||
|
||||
mscomm.obj: mscomm.c mscomm.h vbapi.h serial.h
|
||||
$(CC) $(CFLAGS) mscomm.c
|
||||
|
||||
serial.obj: serial.c serial.h
|
||||
$(CC) $(CFLAGS) serial.c
|
||||
|
||||
mscomm.res: mscomm.rc mscomm.h mscomm.bmp
|
||||
$(RC) -r mscomm.rc
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Generate VBAPI.LIB from vbapi.def if not present
|
||||
# -----------------------------------------------------------------------
|
||||
vbapi.lib: vbapi.def
|
||||
implib vbapi.lib vbapi.def
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Clean
|
||||
# -----------------------------------------------------------------------
|
||||
clean:
|
||||
-del *.obj
|
||||
-del *.res
|
||||
-del *.vbx
|
||||
-del *.map
|
||||
BIN
vbx/mscomm.bmp
(Stored with Git LFS)
Normal file
BIN
vbx/mscomm.bmp
(Stored with Git LFS)
Normal file
Binary file not shown.
935
vbx/mscomm.c
Normal file
935
vbx/mscomm.c
Normal file
|
|
@ -0,0 +1,935 @@
|
|||
// mscomm.c - MSComm VBX control implementation
|
||||
//
|
||||
// Provides high-speed serial communications for 16-bit Visual Basic 4.
|
||||
// Implements MODEL, control procedure, property/event handling, and
|
||||
// WM_COMMNOTIFY dispatch via hidden notification windows.
|
||||
//
|
||||
// IMPORTANT: VBDerefControl() returns a pointer into VB's compacting heap.
|
||||
// Any VB API call (VBCreateHsz, VBDestroyHsz, VBFireEvent, etc.) can
|
||||
// trigger heap compaction and invalidate the pointer. All code must
|
||||
// re-deref after such calls before accessing control data again.
|
||||
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include "vbapi.h"
|
||||
#include "serial.h"
|
||||
#include "mscomm.h"
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Global data
|
||||
// -----------------------------------------------------------------------
|
||||
HANDLE ghInstance = NULL;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Convenience macro for re-dereferencing control data after VB API calls
|
||||
// -----------------------------------------------------------------------
|
||||
#define DEREF(hctl) ((MsCommDataT FAR *)VBDerefControl(hctl))
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Forward declarations - Control procedure
|
||||
// -----------------------------------------------------------------------
|
||||
LONG FAR PASCAL _export MsCommCtlProc(HCTL hctl, HWND hwnd, USHORT msg, USHORT wp, LONG lp);
|
||||
LRESULT FAR PASCAL _export NotifyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Forward declarations - Internal functions (alphabetical)
|
||||
// -----------------------------------------------------------------------
|
||||
static void closePort(HCTL hctl, MsCommDataT FAR *pData);
|
||||
static void fireOnComm(HCTL hctl, MsCommDataT FAR *pData, int16_t eventCode);
|
||||
static LONG handleGetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp);
|
||||
static LONG handleSetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp, LONG lp);
|
||||
static void initControlData(HCTL hctl);
|
||||
static int16_t openPort(HCTL hctl, MsCommDataT FAR *pData);
|
||||
static void processEventNotify(HCTL hctl, int16_t commId);
|
||||
static void processReceiveNotify(HCTL hctl, int16_t commId);
|
||||
static void processTransmitNotify(HCTL hctl, int16_t commId);
|
||||
static BOOL registerNotifyClass(HANDLE hInstance);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Forward declarations - DLL entry points
|
||||
// -----------------------------------------------------------------------
|
||||
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine);
|
||||
BOOL FAR PASCAL _export VBINITCC(USHORT usVersion, BOOL fRuntime);
|
||||
void FAR PASCAL _export VBTERMCC(void);
|
||||
void FAR PASCAL _export WEP(int nParam);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Enum value lists (null-separated, double-null terminated by compiler)
|
||||
// -----------------------------------------------------------------------
|
||||
static char szHandshaking[] = "None\0XonXoff\0RtsCts\0Both";
|
||||
static char szInputMode[] = "Text\0Binary";
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Property descriptors
|
||||
// -----------------------------------------------------------------------
|
||||
// offset default enum list enumMax
|
||||
static PROPINFO propCommPort = { "CommPort", PF_fSetMsg | PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, commPort), 0, 1L, NULL, 0 };
|
||||
static PROPINFO propSettings = { "Settings", PF_fSetMsg | PF_fGetMsg | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propPortOpen = { "PortOpen", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, portOpen), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propInput = { "Input", PF_fGetMsg | PF_fNoShow | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propOutput = { "Output", PF_fSetMsg | PF_fNoShow | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propInBufferSize = { "InBufferSize", PF_fSetMsg | PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, inBufferSize), 0, 4096L, NULL, 0 };
|
||||
static PROPINFO propOutBufferSize = { "OutBufferSize", PF_fSetMsg | PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, outBufferSize), 0, 4096L, NULL, 0 };
|
||||
static PROPINFO propInBufferCount = { "InBufferCount", PF_fGetMsg | PF_fNoShow | DT_SHORT, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propOutBufferCount= { "OutBufferCount",PF_fGetMsg | PF_fNoShow | DT_SHORT, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propRThreshold = { "RThreshold", PF_fSetData| PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, rThreshold), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propSThreshold = { "SThreshold", PF_fSetData| PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, sThreshold), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propHandshaking = { "Handshaking", PF_fSetMsg | PF_fGetData | DT_ENUM, OFFSETIN(MsCommDataT, handshaking), 0, 0L, szHandshaking, 3 };
|
||||
static PROPINFO propInputLen = { "InputLen", PF_fSetData| PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, inputLen), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propInputMode = { "InputMode", PF_fSetData| PF_fGetData | DT_ENUM, OFFSETIN(MsCommDataT, inputMode), 0, 0L, szInputMode, 1 };
|
||||
static PROPINFO propDTREnable = { "DTREnable", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, dtrEnable), 0, 1L, NULL, 0 };
|
||||
static PROPINFO propRTSEnable = { "RTSEnable", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, rtsEnable), 0, 1L, NULL, 0 };
|
||||
static PROPINFO propCDHolding = { "CDHolding", PF_fGetMsg | PF_fNoShow | DT_BOOL, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propCTSHolding = { "CTSHolding", PF_fGetMsg | PF_fNoShow | DT_BOOL, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propDSRHolding = { "DSRHolding", PF_fGetMsg | PF_fNoShow | DT_BOOL, 0, 0, 0L, NULL, 0 };
|
||||
static PROPINFO propBreak = { "Break", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, breakState), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propCommEvent = { "CommEvent", PF_fGetData| PF_fNoShow | DT_SHORT, OFFSETIN(MsCommDataT, commEvent), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propNullDiscard = { "NullDiscard", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, nullDiscard), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propEOFEnable = { "EOFEnable", PF_fSetData| PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, eofEnable), 0, 0L, NULL, 0 };
|
||||
static PROPINFO propParityReplace = { "ParityReplace", PF_fSetMsg | PF_fGetMsg | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Property list (NULL-terminated, order must match IPROP_* indices)
|
||||
// -----------------------------------------------------------------------
|
||||
static PPROPINFO propList[] = {
|
||||
PPROPINFO_STD_CTLNAME, // 0 - Name
|
||||
PPROPINFO_STD_INDEX, // 1 - Index
|
||||
PPROPINFO_STD_TAG, // 2 - Tag
|
||||
&propCommPort, // 3
|
||||
&propSettings, // 4
|
||||
&propPortOpen, // 5
|
||||
&propInput, // 6
|
||||
&propOutput, // 7
|
||||
&propInBufferSize, // 8
|
||||
&propOutBufferSize, // 9
|
||||
&propInBufferCount, // 10
|
||||
&propOutBufferCount, // 11
|
||||
&propRThreshold, // 12
|
||||
&propSThreshold, // 13
|
||||
&propHandshaking, // 14
|
||||
&propInputLen, // 15
|
||||
&propInputMode, // 16
|
||||
&propDTREnable, // 17
|
||||
&propRTSEnable, // 18
|
||||
&propCDHolding, // 19
|
||||
&propCTSHolding, // 20
|
||||
&propDSRHolding, // 21
|
||||
&propBreak, // 22
|
||||
&propCommEvent, // 23
|
||||
&propNullDiscard, // 24
|
||||
&propEOFEnable, // 25
|
||||
&propParityReplace, // 26
|
||||
NULL
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Event descriptors
|
||||
// -----------------------------------------------------------------------
|
||||
static EVENTINFO eventOnComm = { "OnComm", 0, 0, NULL, "", 0 };
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Event list (NULL-terminated)
|
||||
// -----------------------------------------------------------------------
|
||||
static PEVENTINFO eventList[] = {
|
||||
&eventOnComm, // 0 - OnComm
|
||||
NULL
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// MODEL definition
|
||||
// -----------------------------------------------------------------------
|
||||
static char szCtlName[] = "MSComm";
|
||||
|
||||
MODEL modelMsComm = {
|
||||
VB300_VERSION, // usVersion
|
||||
MODEL_fInitMsg | MODEL_fInvisAtRun,// fl
|
||||
(PCTLPROC)MsCommCtlProc, // pctlproc
|
||||
0, // fsClassStyle
|
||||
WS_CHILD, // flWndStyle
|
||||
sizeof(MsCommDataT), // cbCtlExtra
|
||||
IDB_MSCOMM, // idBmpPalette
|
||||
szCtlName, // npszDefCtlName
|
||||
NULL, // npszClassName
|
||||
NULL, // npszParentClassName
|
||||
propList, // npproplist
|
||||
eventList, // npeventlist
|
||||
IPROP_COMMPORT, // nDefProp
|
||||
IEVENT_ONCOMM, // nDefEvent
|
||||
0, // nValueProp
|
||||
0x0100 // usCtlVersion (1.00)
|
||||
};
|
||||
|
||||
|
||||
// =======================================================================
|
||||
// Internal helper functions (alphabetical)
|
||||
// =======================================================================
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// closePort - Close the serial port and destroy notification window
|
||||
//
|
||||
// No VB API calls here, so pData remains valid throughout.
|
||||
// -----------------------------------------------------------------------
|
||||
static void closePort(HCTL hctl, MsCommDataT FAR *pData)
|
||||
{
|
||||
if (pData->commId >= 0) {
|
||||
if (pData->breakState) {
|
||||
serialEscape(pData->commId, SERIAL_CLRBREAK);
|
||||
pData->breakState = FALSE;
|
||||
}
|
||||
|
||||
serialClose(pData->commId);
|
||||
pData->commId = -1;
|
||||
}
|
||||
|
||||
if (pData->hwndNotify != NULL) {
|
||||
DestroyWindow(pData->hwndNotify);
|
||||
pData->hwndNotify = NULL;
|
||||
}
|
||||
|
||||
pData->portOpen = FALSE;
|
||||
pData->ctsState = FALSE;
|
||||
pData->dsrState = FALSE;
|
||||
pData->cdState = FALSE;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// fireOnComm - Set commEvent and fire OnComm event to VB
|
||||
//
|
||||
// WARNING: After this function returns, any previously obtained pData
|
||||
// pointer is INVALID. Callers must re-deref via DEREF(hctl).
|
||||
// -----------------------------------------------------------------------
|
||||
static void fireOnComm(HCTL hctl, MsCommDataT FAR *pData, int16_t eventCode)
|
||||
{
|
||||
pData->commEvent = eventCode;
|
||||
VBFireEvent(hctl, IEVENT_ONCOMM, NULL);
|
||||
// pData is now stale - caller must re-deref
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// handleGetProperty - Process VBM_GETPROPERTY for message-based props
|
||||
// -----------------------------------------------------------------------
|
||||
static LONG handleGetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp)
|
||||
{
|
||||
switch (iProp) {
|
||||
case IPROP_SETTINGS:
|
||||
return (LONG)pData->hszSettings;
|
||||
|
||||
case IPROP_INPUT: {
|
||||
COMSTAT stat;
|
||||
int16_t bytesToRead;
|
||||
int16_t bytesRead;
|
||||
int16_t commId;
|
||||
int16_t inputLen;
|
||||
HGLOBAL hMem;
|
||||
char FAR *buf;
|
||||
HSZ hsz;
|
||||
|
||||
if (!pData->portOpen || pData->commId < 0) {
|
||||
return (LONG)VBCreateHsz((_segment)0, "");
|
||||
}
|
||||
|
||||
// Save values to locals before any VB API calls
|
||||
commId = pData->commId;
|
||||
inputLen = pData->inputLen;
|
||||
|
||||
serialGetStatus(commId, &stat);
|
||||
bytesToRead = (int16_t)stat.cbInQue;
|
||||
|
||||
if (inputLen > 0 && bytesToRead > inputLen) {
|
||||
bytesToRead = inputLen;
|
||||
}
|
||||
|
||||
if (bytesToRead <= 0) {
|
||||
return (LONG)VBCreateHsz((_segment)0, "");
|
||||
}
|
||||
|
||||
// Use GlobalAlloc for potentially large buffers (DLL local heap is small)
|
||||
hMem = GlobalAlloc(GMEM_MOVEABLE, (DWORD)bytesToRead + 1);
|
||||
if (hMem == NULL) {
|
||||
return (LONG)VBCreateHsz((_segment)0, "");
|
||||
}
|
||||
|
||||
buf = (char FAR *)GlobalLock(hMem);
|
||||
bytesRead = serialRead(commId, buf, bytesToRead);
|
||||
if (bytesRead <= 0) {
|
||||
GlobalUnlock(hMem);
|
||||
GlobalFree(hMem);
|
||||
return (LONG)VBCreateHsz((_segment)0, "");
|
||||
}
|
||||
|
||||
buf[bytesRead] = '\0';
|
||||
hsz = VBCreateHsz((_segment)0, buf);
|
||||
GlobalUnlock(hMem);
|
||||
GlobalFree(hMem);
|
||||
// pData is stale after VBCreateHsz, but we just return the HSZ
|
||||
return (LONG)hsz;
|
||||
}
|
||||
|
||||
case IPROP_INBUFFERCOUNT: {
|
||||
COMSTAT stat;
|
||||
if (!pData->portOpen || pData->commId < 0) {
|
||||
return 0L;
|
||||
}
|
||||
serialGetStatus(pData->commId, &stat);
|
||||
return (LONG)(int16_t)stat.cbInQue;
|
||||
}
|
||||
|
||||
case IPROP_OUTBUFFERCOUNT: {
|
||||
COMSTAT stat;
|
||||
if (!pData->portOpen || pData->commId < 0) {
|
||||
return 0L;
|
||||
}
|
||||
serialGetStatus(pData->commId, &stat);
|
||||
return (LONG)(int16_t)stat.cbOutQue;
|
||||
}
|
||||
|
||||
// Modem line status: return shadow state maintained by processEventNotify.
|
||||
// The 16-bit comm API only provides transition events (GetCommEventMask),
|
||||
// not current line levels. Shadow state is toggled on each transition.
|
||||
case IPROP_CDHOLDING:
|
||||
return (LONG)(BOOL)pData->cdState;
|
||||
|
||||
case IPROP_CTSHOLDING:
|
||||
return (LONG)(BOOL)pData->ctsState;
|
||||
|
||||
case IPROP_DSRHOLDING:
|
||||
return (LONG)(BOOL)pData->dsrState;
|
||||
|
||||
case IPROP_PARITYREPLACE:
|
||||
return (LONG)pData->hszParityReplace;
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// handleSetProperty - Process VBM_SETPROPERTY for message-based props
|
||||
// -----------------------------------------------------------------------
|
||||
static LONG handleSetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp, LONG lp)
|
||||
{
|
||||
int16_t val;
|
||||
|
||||
switch (iProp) {
|
||||
case IPROP_COMMPORT:
|
||||
val = (int16_t)lp;
|
||||
if (val < 1 || val > 16) {
|
||||
return (LONG)ERR_InvPropVal;
|
||||
}
|
||||
if (pData->portOpen) {
|
||||
return (LONG)ERR_InvPropSet;
|
||||
}
|
||||
pData->commPort = val;
|
||||
return 0L;
|
||||
|
||||
case IPROP_SETTINGS: {
|
||||
HSZ oldHsz;
|
||||
HSZ newHsz;
|
||||
LPSTR lpstr;
|
||||
int16_t commId;
|
||||
int16_t port;
|
||||
|
||||
// Deref the incoming HSZ (VBDerefHsz does not compact)
|
||||
lpstr = VBDerefHsz((HSZ)lp);
|
||||
if (lpstr == NULL) {
|
||||
return (LONG)ERR_InvPropVal;
|
||||
}
|
||||
|
||||
// Save old HSZ, create new one
|
||||
oldHsz = pData->hszSettings;
|
||||
newHsz = VBCreateHsz((_segment)0, lpstr);
|
||||
// pData may be stale after VBCreateHsz - re-deref
|
||||
pData = DEREF(hctl);
|
||||
pData->hszSettings = newHsz;
|
||||
|
||||
if (oldHsz) {
|
||||
VBDestroyHsz(oldHsz);
|
||||
// pData may be stale after VBDestroyHsz - re-deref
|
||||
pData = DEREF(hctl);
|
||||
}
|
||||
|
||||
// If port is open, reconfigure immediately
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
commId = pData->commId;
|
||||
port = pData->commPort;
|
||||
lpstr = VBDerefHsz(pData->hszSettings);
|
||||
if (serialConfigure(commId, port, lpstr) != 0) {
|
||||
return (LONG)ERR_InvPropVal;
|
||||
}
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
case IPROP_PORTOPEN:
|
||||
if ((BOOL)lp) {
|
||||
if (pData->portOpen) {
|
||||
return 0L;
|
||||
}
|
||||
if (openPort(hctl, pData) != 0) {
|
||||
return (LONG)ERR_InvPropVal;
|
||||
}
|
||||
} else {
|
||||
if (!pData->portOpen) {
|
||||
return 0L;
|
||||
}
|
||||
closePort(hctl, pData);
|
||||
}
|
||||
return 0L;
|
||||
|
||||
case IPROP_OUTPUT: {
|
||||
int16_t commId;
|
||||
LPSTR lpstr;
|
||||
|
||||
if (!pData->portOpen || pData->commId < 0) {
|
||||
return (LONG)ERR_InvPropSet;
|
||||
}
|
||||
|
||||
// Save commId before VB API call
|
||||
commId = pData->commId;
|
||||
lpstr = VBDerefHsz((HSZ)lp);
|
||||
if (lpstr != NULL) {
|
||||
int16_t len = (int16_t)lstrlen(lpstr);
|
||||
if (len > 0) {
|
||||
int16_t written = serialWrite(commId, lpstr, len);
|
||||
if (written < 0) {
|
||||
// Re-deref before fireOnComm since pData may be stale
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_TXFULL);
|
||||
// pData stale after fireOnComm, but we just return
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
case IPROP_INBUFFERSIZE:
|
||||
val = (int16_t)lp;
|
||||
if (val < 64) {
|
||||
return (LONG)ERR_InvPropVal;
|
||||
}
|
||||
if (pData->portOpen) {
|
||||
return (LONG)ERR_InvPropSet;
|
||||
}
|
||||
pData->inBufferSize = val;
|
||||
return 0L;
|
||||
|
||||
case IPROP_OUTBUFFERSIZE:
|
||||
val = (int16_t)lp;
|
||||
if (val < 64) {
|
||||
return (LONG)ERR_InvPropVal;
|
||||
}
|
||||
if (pData->portOpen) {
|
||||
return (LONG)ERR_InvPropSet;
|
||||
}
|
||||
pData->outBufferSize = val;
|
||||
return 0L;
|
||||
|
||||
case IPROP_HANDSHAKING:
|
||||
pData->handshaking = (int16_t)lp;
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
serialSetHandshaking(pData->commId, pData->handshaking);
|
||||
}
|
||||
return 0L;
|
||||
|
||||
case IPROP_DTRENABLE:
|
||||
pData->dtrEnable = (BOOL)lp;
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
serialEscape(pData->commId, pData->dtrEnable ? SERIAL_SETDTR : SERIAL_CLRDTR);
|
||||
}
|
||||
return 0L;
|
||||
|
||||
case IPROP_RTSENABLE:
|
||||
pData->rtsEnable = (BOOL)lp;
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
serialEscape(pData->commId, pData->rtsEnable ? SERIAL_SETRTS : SERIAL_CLRRTS);
|
||||
}
|
||||
return 0L;
|
||||
|
||||
case IPROP_BREAK:
|
||||
pData->breakState = (BOOL)lp;
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
serialEscape(pData->commId, pData->breakState ? SERIAL_SETBREAK : SERIAL_CLRBREAK);
|
||||
}
|
||||
return 0L;
|
||||
|
||||
case IPROP_NULLDISCARD: {
|
||||
int16_t commId;
|
||||
BOOL nullDiscard;
|
||||
LPSTR lpstr;
|
||||
|
||||
pData->nullDiscard = (BOOL)lp;
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
// Save locals before VBDerefHsz
|
||||
commId = pData->commId;
|
||||
nullDiscard = pData->nullDiscard;
|
||||
lpstr = VBDerefHsz(pData->hszParityReplace);
|
||||
serialSetOptions(commId, nullDiscard, lpstr);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
case IPROP_PARITYREPLACE: {
|
||||
HSZ oldHsz;
|
||||
HSZ newHsz;
|
||||
int16_t commId;
|
||||
BOOL nullDiscard;
|
||||
LPSTR lpstr;
|
||||
|
||||
lpstr = VBDerefHsz((HSZ)lp);
|
||||
oldHsz = pData->hszParityReplace;
|
||||
newHsz = VBCreateHsz((_segment)0, lpstr ? lpstr : "");
|
||||
// Re-deref after VBCreateHsz
|
||||
pData = DEREF(hctl);
|
||||
pData->hszParityReplace = newHsz;
|
||||
|
||||
if (oldHsz) {
|
||||
VBDestroyHsz(oldHsz);
|
||||
// Re-deref after VBDestroyHsz
|
||||
pData = DEREF(hctl);
|
||||
}
|
||||
|
||||
if (pData->portOpen && pData->commId >= 0) {
|
||||
commId = pData->commId;
|
||||
nullDiscard = pData->nullDiscard;
|
||||
lpstr = VBDerefHsz(pData->hszParityReplace);
|
||||
serialSetOptions(commId, nullDiscard, lpstr);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// initControlData - Initialize per-instance control data
|
||||
//
|
||||
// Re-derefs after each VBCreateHsz to avoid stale pointers.
|
||||
// -----------------------------------------------------------------------
|
||||
static void initControlData(HCTL hctl)
|
||||
{
|
||||
MsCommDataT FAR *pData;
|
||||
HSZ hsz;
|
||||
|
||||
pData = DEREF(hctl);
|
||||
pData->commId = -1;
|
||||
pData->hwndNotify = NULL;
|
||||
pData->commPort = 1;
|
||||
pData->inBufferSize = 4096;
|
||||
pData->outBufferSize = 4096;
|
||||
pData->rThreshold = 0;
|
||||
pData->sThreshold = 0;
|
||||
pData->handshaking = HS_NONE;
|
||||
pData->inputLen = 0;
|
||||
pData->inputMode = INPUT_TEXT;
|
||||
pData->commEvent = 0;
|
||||
pData->portOpen = FALSE;
|
||||
pData->dtrEnable = TRUE;
|
||||
pData->rtsEnable = TRUE;
|
||||
pData->breakState = FALSE;
|
||||
pData->nullDiscard = FALSE;
|
||||
pData->eofEnable = FALSE;
|
||||
pData->ctsState = FALSE;
|
||||
pData->dsrState = FALSE;
|
||||
pData->cdState = FALSE;
|
||||
|
||||
// VBCreateHsz may compact VB's heap - re-deref after each call
|
||||
hsz = VBCreateHsz((_segment)0, "9600,N,8,1");
|
||||
pData = DEREF(hctl);
|
||||
pData->hszSettings = hsz;
|
||||
|
||||
hsz = VBCreateHsz((_segment)0, "?");
|
||||
pData = DEREF(hctl);
|
||||
pData->hszParityReplace = hsz;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// openPort - Open serial port and set up notification window
|
||||
//
|
||||
// Saves all needed control data to locals before VB API calls to avoid
|
||||
// stale pointer issues. Re-derefs when writing results back.
|
||||
// -----------------------------------------------------------------------
|
||||
static int16_t openPort(HCTL hctl, MsCommDataT FAR *pData)
|
||||
{
|
||||
int16_t commId;
|
||||
int16_t port;
|
||||
int16_t inBufSize;
|
||||
int16_t outBufSize;
|
||||
int16_t handshaking;
|
||||
int16_t rThreshold;
|
||||
int16_t sThreshold;
|
||||
BOOL dtrEnable;
|
||||
BOOL rtsEnable;
|
||||
BOOL nullDiscard;
|
||||
HSZ hszSettings;
|
||||
HSZ hszParityReplace;
|
||||
HWND hwndNotify;
|
||||
LPSTR lpstr;
|
||||
|
||||
// Snapshot all values we need (pData may become stale after VB API calls)
|
||||
port = pData->commPort;
|
||||
inBufSize = pData->inBufferSize;
|
||||
outBufSize = pData->outBufferSize;
|
||||
handshaking = pData->handshaking;
|
||||
rThreshold = pData->rThreshold;
|
||||
sThreshold = pData->sThreshold;
|
||||
dtrEnable = pData->dtrEnable;
|
||||
rtsEnable = pData->rtsEnable;
|
||||
nullDiscard = pData->nullDiscard;
|
||||
hszSettings = pData->hszSettings;
|
||||
hszParityReplace = pData->hszParityReplace;
|
||||
|
||||
// Open the comm port
|
||||
commId = serialOpen(port, inBufSize, outBufSize);
|
||||
if (commId < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Configure baud/parity/data/stop
|
||||
lpstr = VBDerefHsz(hszSettings);
|
||||
if (serialConfigure(commId, port, lpstr) != 0) {
|
||||
serialClose(commId);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Apply handshaking
|
||||
serialSetHandshaking(commId, handshaking);
|
||||
|
||||
// Apply null-discard and parity-replace
|
||||
lpstr = VBDerefHsz(hszParityReplace);
|
||||
serialSetOptions(commId, nullDiscard, lpstr);
|
||||
|
||||
// Set DTR and RTS lines
|
||||
serialEscape(commId, dtrEnable ? SERIAL_SETDTR : SERIAL_CLRDTR);
|
||||
serialEscape(commId, rtsEnable ? SERIAL_SETRTS : SERIAL_CLRRTS);
|
||||
|
||||
// Create hidden notification window
|
||||
hwndNotify = CreateWindow(
|
||||
NOTIFY_CLASS,
|
||||
"",
|
||||
WS_POPUP,
|
||||
0, 0, 0, 0,
|
||||
NULL,
|
||||
NULL,
|
||||
ghInstance,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (hwndNotify == NULL) {
|
||||
serialClose(commId);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Store HCTL in notification window for message dispatch
|
||||
SetWindowWord(hwndNotify, 0, (WORD)hctl);
|
||||
|
||||
// Enable comm notifications
|
||||
serialEnableNotify(commId, hwndNotify,
|
||||
rThreshold > 0 ? rThreshold : -1,
|
||||
sThreshold > 0 ? sThreshold : -1);
|
||||
|
||||
// Re-deref to write results back to control data
|
||||
pData = DEREF(hctl);
|
||||
pData->commId = commId;
|
||||
pData->hwndNotify = hwndNotify;
|
||||
pData->portOpen = TRUE;
|
||||
pData->ctsState = FALSE;
|
||||
pData->dsrState = FALSE;
|
||||
pData->cdState = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// processEventNotify - Handle CN_EVENT notification
|
||||
//
|
||||
// Takes commId as a stable local value. Re-derefs pData after every
|
||||
// fireOnComm call since VBFireEvent can trigger heap compaction.
|
||||
// Updates modem line shadow state on CTS/DSR/CD transitions.
|
||||
// -----------------------------------------------------------------------
|
||||
static void processEventNotify(HCTL hctl, int16_t commId)
|
||||
{
|
||||
MsCommDataT FAR *pData;
|
||||
UINT evtMask;
|
||||
|
||||
evtMask = serialGetEventMask(commId, EV_CTS | EV_DSR | EV_RLSD | EV_RING | EV_ERR | EV_BREAK);
|
||||
|
||||
if (evtMask & EV_BREAK) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_BREAK);
|
||||
}
|
||||
|
||||
if (evtMask & EV_CTS) {
|
||||
pData = DEREF(hctl);
|
||||
pData->ctsState = !pData->ctsState;
|
||||
fireOnComm(hctl, pData, COM_EV_CTS);
|
||||
}
|
||||
|
||||
if (evtMask & EV_DSR) {
|
||||
pData = DEREF(hctl);
|
||||
pData->dsrState = !pData->dsrState;
|
||||
fireOnComm(hctl, pData, COM_EV_DSR);
|
||||
}
|
||||
|
||||
if (evtMask & EV_RLSD) {
|
||||
pData = DEREF(hctl);
|
||||
pData->cdState = !pData->cdState;
|
||||
fireOnComm(hctl, pData, COM_EV_CD);
|
||||
}
|
||||
|
||||
if (evtMask & EV_RING) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EV_RING);
|
||||
}
|
||||
|
||||
if (evtMask & EV_ERR) {
|
||||
COMSTAT stat;
|
||||
int16_t errFlags;
|
||||
|
||||
errFlags = serialGetStatus(commId, &stat);
|
||||
|
||||
if (errFlags & CE_FRAME) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_FRAME);
|
||||
}
|
||||
if (errFlags & CE_OVERRUN) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_OVERRUN);
|
||||
}
|
||||
if (errFlags & CE_RXOVER) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_RXOVER);
|
||||
}
|
||||
if (errFlags & CE_RXPARITY) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_RXPARITY);
|
||||
}
|
||||
if (errFlags & CE_TXFULL) {
|
||||
pData = DEREF(hctl);
|
||||
fireOnComm(hctl, pData, COM_EVT_TXFULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// processReceiveNotify - Handle CN_RECEIVE notification
|
||||
// -----------------------------------------------------------------------
|
||||
static void processReceiveNotify(HCTL hctl, int16_t commId)
|
||||
{
|
||||
MsCommDataT FAR *pData;
|
||||
COMSTAT stat;
|
||||
|
||||
pData = DEREF(hctl);
|
||||
|
||||
if (pData->rThreshold <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
serialGetStatus(commId, &stat);
|
||||
|
||||
if ((int16_t)stat.cbInQue >= pData->rThreshold) {
|
||||
fireOnComm(hctl, pData, COM_EV_RECEIVE);
|
||||
// pData stale after fireOnComm, but we just return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// processTransmitNotify - Handle CN_TRANSMIT notification
|
||||
// -----------------------------------------------------------------------
|
||||
static void processTransmitNotify(HCTL hctl, int16_t commId)
|
||||
{
|
||||
MsCommDataT FAR *pData;
|
||||
COMSTAT stat;
|
||||
|
||||
pData = DEREF(hctl);
|
||||
|
||||
if (pData->sThreshold <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
serialGetStatus(commId, &stat);
|
||||
|
||||
if ((int16_t)stat.cbOutQue <= pData->sThreshold) {
|
||||
fireOnComm(hctl, pData, COM_EV_SEND);
|
||||
// pData stale after fireOnComm, but we just return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// registerNotifyClass - Register hidden notification window class
|
||||
// -----------------------------------------------------------------------
|
||||
static BOOL registerNotifyClass(HANDLE hInstance)
|
||||
{
|
||||
WNDCLASS wc;
|
||||
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = NotifyWndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = sizeof(WORD); // Room for HCTL
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = NULL;
|
||||
wc.hCursor = NULL;
|
||||
wc.hbrBackground = NULL;
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = NOTIFY_CLASS;
|
||||
|
||||
return RegisterClass(&wc);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================
|
||||
// Hidden notification window procedure
|
||||
//
|
||||
// Receives WM_COMMNOTIFY from the comm driver. Saves commId to a local
|
||||
// variable and passes it to process* functions, which independently
|
||||
// deref the control data (avoiding stale pointer chains across VBFireEvent).
|
||||
// =======================================================================
|
||||
|
||||
LRESULT FAR PASCAL _export NotifyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
{
|
||||
if (msg == WM_COMMNOTIFY) {
|
||||
HCTL hctl;
|
||||
MsCommDataT FAR *pData;
|
||||
int16_t commId;
|
||||
UINT notifyCode;
|
||||
|
||||
hctl = (HCTL)GetWindowWord(hwnd, 0);
|
||||
if (hctl == 0) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
pData = DEREF(hctl);
|
||||
if (pData == NULL || pData->commId < 0) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
// Save commId to local - stable across VBFireEvent calls
|
||||
commId = pData->commId;
|
||||
notifyCode = LOWORD(lp);
|
||||
|
||||
// Each process* function independently derefs and re-derefs as needed
|
||||
if (notifyCode & CN_RECEIVE) {
|
||||
processReceiveNotify(hctl, commId);
|
||||
}
|
||||
if (notifyCode & CN_TRANSMIT) {
|
||||
processTransmitNotify(hctl, commId);
|
||||
}
|
||||
if (notifyCode & CN_EVENT) {
|
||||
processEventNotify(hctl, commId);
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================
|
||||
// VBX Control procedure
|
||||
// =======================================================================
|
||||
|
||||
LONG FAR PASCAL _export MsCommCtlProc(HCTL hctl, HWND hwnd, USHORT msg, USHORT wp, LONG lp)
|
||||
{
|
||||
MsCommDataT FAR *pData;
|
||||
|
||||
switch (msg) {
|
||||
case VBM_INITIALIZE:
|
||||
initControlData(hctl);
|
||||
return 0L;
|
||||
|
||||
case VBM_SETPROPERTY:
|
||||
pData = DEREF(hctl);
|
||||
return handleSetProperty(hctl, pData, wp, lp);
|
||||
|
||||
case VBM_GETPROPERTY:
|
||||
pData = DEREF(hctl);
|
||||
return handleGetProperty(hctl, pData, wp);
|
||||
|
||||
case WM_DESTROY: {
|
||||
HSZ hszSettings;
|
||||
HSZ hszParityReplace;
|
||||
|
||||
pData = DEREF(hctl);
|
||||
|
||||
// Close port if open
|
||||
if (pData->portOpen) {
|
||||
closePort(hctl, pData);
|
||||
}
|
||||
|
||||
// Save HSZ handles to locals, then destroy them.
|
||||
// VBDestroyHsz may compact the heap, so don't access pData after.
|
||||
hszSettings = pData->hszSettings;
|
||||
hszParityReplace = pData->hszParityReplace;
|
||||
pData->hszSettings = 0;
|
||||
pData->hszParityReplace = 0;
|
||||
|
||||
if (hszSettings) {
|
||||
VBDestroyHsz(hszSettings);
|
||||
}
|
||||
if (hszParityReplace) {
|
||||
VBDestroyHsz(hszParityReplace);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return VBDefControlProc(hctl, hwnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================
|
||||
// DLL entry points
|
||||
// =======================================================================
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// LibMain - DLL initialization
|
||||
// -----------------------------------------------------------------------
|
||||
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
|
||||
{
|
||||
ghInstance = hInstance;
|
||||
|
||||
if (wHeapSize > 0) {
|
||||
UnlockData(0);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// VBINITCC - Called by VB to register the control
|
||||
// -----------------------------------------------------------------------
|
||||
BOOL FAR PASCAL _export VBINITCC(USHORT usVersion, BOOL fRuntime)
|
||||
{
|
||||
if (!registerNotifyClass(ghInstance)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return VBRegisterModel(ghInstance, &modelMsComm);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// VBTERMCC - Called by VB when unloading the control
|
||||
// -----------------------------------------------------------------------
|
||||
void FAR PASCAL _export VBTERMCC(void)
|
||||
{
|
||||
UnregisterClass(NOTIFY_CLASS, ghInstance);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// WEP - DLL termination (required for 16-bit Windows DLLs)
|
||||
// -----------------------------------------------------------------------
|
||||
void FAR PASCAL _export WEP(int nParam)
|
||||
{
|
||||
(void)nParam;
|
||||
}
|
||||
15
vbx/mscomm.def
Normal file
15
vbx/mscomm.def
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
; mscomm.def - Module definition for MSComm VBX control
|
||||
|
||||
LIBRARY MSCOMM
|
||||
DESCRIPTION 'MSComm Serial Communications Control for VB4'
|
||||
EXETYPE WINDOWS
|
||||
|
||||
CODE PRELOAD MOVEABLE DISCARDABLE
|
||||
DATA PRELOAD MOVEABLE SINGLE
|
||||
|
||||
HEAPSIZE 1024
|
||||
|
||||
EXPORTS
|
||||
WEP @1 RESIDENTNAME
|
||||
VBINITCC @2
|
||||
VBTERMCC @3
|
||||
141
vbx/mscomm.h
Normal file
141
vbx/mscomm.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// mscomm.h - MSComm VBX control definitions
|
||||
//
|
||||
// Property/event IDs, comm event constants, and control instance data.
|
||||
|
||||
#ifndef MSCOMM_H
|
||||
#define MSCOMM_H
|
||||
|
||||
#include "vbapi.h"
|
||||
#include "serial.h"
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// stdint types for MSVC 1.52 (shared with serial.h)
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef _STDINT_DEFINED
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef long int32_t;
|
||||
typedef unsigned long uint32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
#define _STDINT_DEFINED
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Resource IDs
|
||||
// -----------------------------------------------------------------------
|
||||
#define IDB_MSCOMM 8000
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Property indices (position in npproplist array)
|
||||
// -----------------------------------------------------------------------
|
||||
#define IPROP_CTLNAME 0
|
||||
#define IPROP_INDEX 1
|
||||
#define IPROP_TAG 2
|
||||
#define IPROP_COMMPORT 3
|
||||
#define IPROP_SETTINGS 4
|
||||
#define IPROP_PORTOPEN 5
|
||||
#define IPROP_INPUT 6
|
||||
#define IPROP_OUTPUT 7
|
||||
#define IPROP_INBUFFERSIZE 8
|
||||
#define IPROP_OUTBUFFERSIZE 9
|
||||
#define IPROP_INBUFFERCOUNT 10
|
||||
#define IPROP_OUTBUFFERCOUNT 11
|
||||
#define IPROP_RTHRESHOLD 12
|
||||
#define IPROP_STHRESHOLD 13
|
||||
#define IPROP_HANDSHAKING 14
|
||||
#define IPROP_INPUTLEN 15
|
||||
#define IPROP_INPUTMODE 16
|
||||
#define IPROP_DTRENABLE 17
|
||||
#define IPROP_RTSENABLE 18
|
||||
#define IPROP_CDHOLDING 19
|
||||
#define IPROP_CTSHOLDING 20
|
||||
#define IPROP_DSRHOLDING 21
|
||||
#define IPROP_BREAK 22
|
||||
#define IPROP_COMMEVENT 23
|
||||
#define IPROP_NULLDISCARD 24
|
||||
#define IPROP_EOFENABLE 25
|
||||
#define IPROP_PARITYREPLACE 26
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Event indices (position in npeventlist array)
|
||||
// -----------------------------------------------------------------------
|
||||
#define IEVENT_ONCOMM 0
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CommEvent constants - Communication events
|
||||
// -----------------------------------------------------------------------
|
||||
#define COM_EV_RECEIVE 1 // Received RThreshold bytes
|
||||
#define COM_EV_SEND 2 // Transmit buffer has SThreshold space
|
||||
#define COM_EV_CTS 3 // CTS line changed
|
||||
#define COM_EV_DSR 4 // DSR line changed
|
||||
#define COM_EV_CD 5 // CD (RLSD) line changed
|
||||
#define COM_EV_RING 6 // Ring indicator detected
|
||||
#define COM_EV_EOF 7 // EOF character received
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CommEvent constants - Error events
|
||||
// -----------------------------------------------------------------------
|
||||
#define COM_EVT_BREAK 1001 // Break signal received
|
||||
#define COM_EVT_FRAME 1004 // Framing error
|
||||
#define COM_EVT_OVERRUN 1006 // Hardware overrun
|
||||
#define COM_EVT_RXOVER 1008 // Receive buffer overflow
|
||||
#define COM_EVT_RXPARITY 1009 // Parity error
|
||||
#define COM_EVT_TXFULL 1010 // Transmit buffer full
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Handshaking enum values
|
||||
// -----------------------------------------------------------------------
|
||||
#define HS_NONE 0
|
||||
#define HS_XONXOFF 1
|
||||
#define HS_RTSCTS 2
|
||||
#define HS_BOTH 3
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// InputMode enum values
|
||||
// -----------------------------------------------------------------------
|
||||
#define INPUT_TEXT 0
|
||||
#define INPUT_BINARY 1
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Control instance data
|
||||
//
|
||||
// Allocated by VB as cbCtlExtra bytes per control instance.
|
||||
// Accessed via VBDerefControl(hctl).
|
||||
// -----------------------------------------------------------------------
|
||||
typedef struct {
|
||||
int16_t commId; // OpenComm handle (-1 = closed)
|
||||
HWND hwndNotify; // Hidden notification window
|
||||
int16_t commPort; // COM port number (1-16)
|
||||
HSZ hszSettings; // Settings string "baud,parity,data,stop"
|
||||
int16_t inBufferSize; // Receive buffer size in bytes
|
||||
int16_t outBufferSize; // Transmit buffer size in bytes
|
||||
int16_t rThreshold; // Receive event threshold (0=disabled)
|
||||
int16_t sThreshold; // Send event threshold (0=disabled)
|
||||
int16_t handshaking; // Handshaking mode (HS_*)
|
||||
int16_t inputLen; // Bytes to read per Input (0=all)
|
||||
int16_t inputMode; // Input mode (INPUT_TEXT/INPUT_BINARY)
|
||||
int16_t commEvent; // Last comm event code
|
||||
BOOL portOpen; // Port open state
|
||||
BOOL dtrEnable; // DTR line enable
|
||||
BOOL rtsEnable; // RTS line enable
|
||||
BOOL breakState; // Break signal active
|
||||
BOOL nullDiscard; // Discard null characters
|
||||
BOOL eofEnable; // Watch for EOF character
|
||||
BOOL ctsState; // Shadow state: CTS line level
|
||||
BOOL dsrState; // Shadow state: DSR line level
|
||||
BOOL cdState; // Shadow state: CD (RLSD) line level
|
||||
HSZ hszParityReplace; // Parity error replacement character
|
||||
} MsCommDataT;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Hidden notification window class name
|
||||
// -----------------------------------------------------------------------
|
||||
#define NOTIFY_CLASS "MsCommNotify"
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Global instance handle (set in LibMain)
|
||||
// -----------------------------------------------------------------------
|
||||
extern HANDLE ghInstance;
|
||||
|
||||
#endif // MSCOMM_H
|
||||
7
vbx/mscomm.rc
Normal file
7
vbx/mscomm.rc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// mscomm.rc - MSComm VBX resource script
|
||||
//
|
||||
// Defines the toolbox bitmap displayed in the VB IDE custom controls palette.
|
||||
|
||||
#include "mscomm.h"
|
||||
|
||||
IDB_MSCOMM BITMAP mscomm.bmp
|
||||
253
vbx/serial.c
Normal file
253
vbx/serial.c
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
// serial.c - Serial port abstraction layer
|
||||
//
|
||||
// Wraps Windows 3.1 comm API for use by the MSComm VBX control.
|
||||
// All functions operate on a comm ID returned by serialOpen().
|
||||
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include "serial.h"
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Prototypes
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialClose(int16_t commId);
|
||||
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
|
||||
int16_t serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
|
||||
int16_t serialEscape(int16_t commId, int16_t func);
|
||||
int16_t serialFlush(int16_t commId, int16_t queue);
|
||||
UINT serialGetEventMask(int16_t commId, UINT mask);
|
||||
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat);
|
||||
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize);
|
||||
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len);
|
||||
int16_t serialSetHandshaking(int16_t commId, int16_t mode);
|
||||
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace);
|
||||
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len);
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialClose - Close an open comm port
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialClose(int16_t commId)
|
||||
{
|
||||
// Drop DTR and RTS before closing
|
||||
EscapeCommFunction(commId, CLRDTR);
|
||||
EscapeCommFunction(commId, CLRRTS);
|
||||
|
||||
return (int16_t)CloseComm(commId);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialConfigure - Set baud, parity, data bits, stop bits
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings)
|
||||
{
|
||||
DCB dcb;
|
||||
char buf[64];
|
||||
int rc;
|
||||
|
||||
// Get current state to preserve existing settings
|
||||
rc = GetCommState(commId, &dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Build DCB from "COMn:baud,parity,data,stop" string
|
||||
wsprintf(buf, "COM%d:%s", (int)port, (LPSTR)settings);
|
||||
rc = BuildCommDCB(buf, &dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ensure binary mode for reliable operation
|
||||
dcb.fBinary = 1;
|
||||
|
||||
// Ensure comm ID matches our open port
|
||||
dcb.Id = (BYTE)commId;
|
||||
|
||||
rc = SetCommState(&dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialEnableNotify - Enable WM_COMMNOTIFY messages
|
||||
// -----------------------------------------------------------------------
|
||||
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold)
|
||||
{
|
||||
// Enable event mask for modem status line changes, errors, and breaks
|
||||
SetCommEventMask(commId, EV_CTS | EV_DSR | EV_RLSD | EV_RING | EV_ERR | EV_BREAK | EV_RXCHAR);
|
||||
|
||||
return EnableCommNotification(commId, hwnd, rxThreshold, txThreshold);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialEscape - Execute escape function (DTR, RTS, break control)
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialEscape(int16_t commId, int16_t func)
|
||||
{
|
||||
LONG rc;
|
||||
|
||||
rc = EscapeCommFunction(commId, func);
|
||||
return (rc == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialFlush - Flush receive or transmit buffer
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialFlush(int16_t commId, int16_t queue)
|
||||
{
|
||||
return (int16_t)FlushComm(commId, queue);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialGetEventMask - Get and clear event mask bits
|
||||
// -----------------------------------------------------------------------
|
||||
UINT serialGetEventMask(int16_t commId, UINT mask)
|
||||
{
|
||||
return GetCommEventMask(commId, mask);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialGetStatus - Get error status and buffer counts
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat)
|
||||
{
|
||||
return (int16_t)GetCommError(commId, stat);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialOpen - Open a COM port with specified buffer sizes
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize)
|
||||
{
|
||||
char name[8];
|
||||
int commId;
|
||||
|
||||
wsprintf(name, "COM%d", (int)port);
|
||||
commId = OpenComm(name, (UINT)inBufSize, (UINT)outBufSize);
|
||||
|
||||
return (int16_t)commId;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialRead - Read data from receive buffer
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len)
|
||||
{
|
||||
return (int16_t)ReadComm(commId, (LPSTR)buf, len);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialSetHandshaking - Apply handshaking mode
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialSetHandshaking(int16_t commId, int16_t mode)
|
||||
{
|
||||
DCB dcb;
|
||||
int rc;
|
||||
|
||||
rc = GetCommState(commId, &dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Clear all flow control settings
|
||||
dcb.fOutxCtsFlow = 0;
|
||||
dcb.fOutxDsrFlow = 0;
|
||||
dcb.fOutX = 0;
|
||||
dcb.fInX = 0;
|
||||
dcb.fRtsDisable = 0;
|
||||
dcb.fRtsflow = 0;
|
||||
|
||||
switch (mode) {
|
||||
case HANDSHAKE_XONXOFF:
|
||||
dcb.fOutX = 1;
|
||||
dcb.fInX = 1;
|
||||
dcb.XonChar = 0x11; // Ctrl-Q
|
||||
dcb.XoffChar = 0x13; // Ctrl-S
|
||||
dcb.XonLim = 256;
|
||||
dcb.XoffLim = 256;
|
||||
break;
|
||||
|
||||
case HANDSHAKE_RTSCTS:
|
||||
dcb.fOutxCtsFlow = 1;
|
||||
dcb.fRtsflow = 1;
|
||||
break;
|
||||
|
||||
case HANDSHAKE_BOTH:
|
||||
dcb.fOutxCtsFlow = 1;
|
||||
dcb.fRtsflow = 1;
|
||||
dcb.fOutX = 1;
|
||||
dcb.fInX = 1;
|
||||
dcb.XonChar = 0x11;
|
||||
dcb.XoffChar = 0x13;
|
||||
dcb.XonLim = 256;
|
||||
dcb.XoffLim = 256;
|
||||
break;
|
||||
|
||||
case HANDSHAKE_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dcb.Id = (BYTE)commId;
|
||||
rc = SetCommState(&dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialSetOptions - Apply null-discard and parity-replace settings
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace)
|
||||
{
|
||||
DCB dcb;
|
||||
int rc;
|
||||
|
||||
rc = GetCommState(commId, &dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dcb.fNull = nullDiscard ? 1 : 0;
|
||||
|
||||
if (parityReplace != NULL && parityReplace[0] != '\0') {
|
||||
dcb.fParity = 1;
|
||||
dcb.PeChar = parityReplace[0];
|
||||
} else {
|
||||
dcb.fParity = 0;
|
||||
dcb.PeChar = '\0';
|
||||
}
|
||||
|
||||
dcb.Id = (BYTE)commId;
|
||||
rc = SetCommState(&dcb);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// serialWrite - Write data to transmit buffer
|
||||
// -----------------------------------------------------------------------
|
||||
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len)
|
||||
{
|
||||
return (int16_t)WriteComm(commId, (LPSTR)buf, len);
|
||||
}
|
||||
106
vbx/serial.h
Normal file
106
vbx/serial.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// serial.h - Serial port abstraction header
|
||||
//
|
||||
// Wraps Windows 3.1 comm API (OpenComm, ReadComm, WriteComm, etc.)
|
||||
// for use by the MSComm VBX control.
|
||||
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// stdint types for MSVC 1.52 (no stdint.h available)
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef _STDINT_DEFINED
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef long int32_t;
|
||||
typedef unsigned long uint32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
#define _STDINT_DEFINED
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Handshaking modes
|
||||
// -----------------------------------------------------------------------
|
||||
#define HANDSHAKE_NONE 0
|
||||
#define HANDSHAKE_XONXOFF 1
|
||||
#define HANDSHAKE_RTSCTS 2
|
||||
#define HANDSHAKE_BOTH 3
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Flush queue selectors
|
||||
// -----------------------------------------------------------------------
|
||||
#define FLUSH_RX 0
|
||||
#define FLUSH_TX 1
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Escape function constants (mirrors EscapeCommFunction values)
|
||||
// -----------------------------------------------------------------------
|
||||
#define SERIAL_SETDTR SETDTR
|
||||
#define SERIAL_CLRDTR CLRDTR
|
||||
#define SERIAL_SETRTS SETRTS
|
||||
#define SERIAL_CLRRTS CLRRTS
|
||||
#define SERIAL_SETBREAK SETBREAK
|
||||
#define SERIAL_CLRBREAK CLRBREAK
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Function prototypes
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// Open a COM port with specified buffer sizes.
|
||||
// Returns comm ID (>= 0) on success, negative on error.
|
||||
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize);
|
||||
|
||||
// Close an open comm port. Drops DTR and RTS before closing.
|
||||
// Returns 0 on success, negative on error.
|
||||
int16_t serialClose(int16_t commId);
|
||||
|
||||
// Configure baud, parity, data bits, stop bits from a settings string.
|
||||
// settings format: "baud,parity,data,stop" (e.g., "9600,N,8,1")
|
||||
// Returns 0 on success, negative on error.
|
||||
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
|
||||
|
||||
// Apply handshaking mode to an open port.
|
||||
// mode: HANDSHAKE_NONE, HANDSHAKE_XONXOFF, HANDSHAKE_RTSCTS, HANDSHAKE_BOTH
|
||||
// Returns 0 on success, negative on error.
|
||||
int16_t serialSetHandshaking(int16_t commId, int16_t mode);
|
||||
|
||||
// Apply null-discard and parity-replace settings to an open port.
|
||||
// Returns 0 on success, negative on error.
|
||||
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace);
|
||||
|
||||
// Read data from receive buffer.
|
||||
// Returns number of bytes read, or negative on error.
|
||||
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len);
|
||||
|
||||
// Write data to transmit buffer.
|
||||
// Returns number of bytes written, or negative on error.
|
||||
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len);
|
||||
|
||||
// Get comm error status and buffer counts. Clears the error state.
|
||||
// Returns error flags, fills stat with buffer counts.
|
||||
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat);
|
||||
|
||||
// Execute an escape function (DTR, RTS, break control).
|
||||
// func: SERIAL_SETDTR, SERIAL_CLRDTR, SERIAL_SETRTS, etc.
|
||||
// Returns 0 on success, negative on error.
|
||||
int16_t serialEscape(int16_t commId, int16_t func);
|
||||
|
||||
// Get modem status lines via GetCommEventMask.
|
||||
// Returns event mask bits (EV_CTS, EV_DSR, EV_RLSD, EV_RING).
|
||||
UINT serialGetEventMask(int16_t commId, UINT mask);
|
||||
|
||||
// Enable WM_COMMNOTIFY messages to the specified window.
|
||||
// rxThreshold: fire CN_RECEIVE when this many bytes available (-1=disable)
|
||||
// txThreshold: fire CN_TRANSMIT when this much space free (-1=disable)
|
||||
// Returns TRUE on success.
|
||||
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
|
||||
|
||||
// Flush receive and/or transmit buffers.
|
||||
// queue: FLUSH_RX or FLUSH_TX
|
||||
// Returns 0 on success, negative on error.
|
||||
int16_t serialFlush(int16_t commId, int16_t queue);
|
||||
|
||||
#endif // SERIAL_H
|
||||
29
vbx/vbapi.def
Normal file
29
vbx/vbapi.def
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
; vbapi.def - VB API import library definition
|
||||
;
|
||||
; Defines the VB API functions exported by the VBAPI module.
|
||||
; Use with IMPLIB to generate the import library:
|
||||
;
|
||||
; implib vbapi.lib vbapi.def
|
||||
;
|
||||
; The VBAPI module is the VB runtime that exports these functions.
|
||||
; When VB loads a VBX, it resolves imports against this module.
|
||||
|
||||
LIBRARY VBAPI
|
||||
|
||||
EXPORTS
|
||||
VBRegisterModel @100
|
||||
VBDefControlProc @101
|
||||
VBFireEvent @102
|
||||
VBCreateHsz @103
|
||||
VBDestroyHsz @104
|
||||
VBDerefHsz @105
|
||||
VBCreateHlstr @106
|
||||
VBDestroyHlstr @107
|
||||
VBGetHlstr @108
|
||||
VBSetHlstr @109
|
||||
VBDerefControl @110
|
||||
VBGetMode @111
|
||||
VBSetControlProperty @112
|
||||
VBGetControlProperty @113
|
||||
VBGetControlHwnd @114
|
||||
VBGetHInstance @115
|
||||
266
vbx/vbapi.h
Normal file
266
vbx/vbapi.h
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// vbapi.h - Reconstructed VBX CDK header
|
||||
//
|
||||
// Reconstructed from the published Visual Basic Custom Control (VBX)
|
||||
// specification for use with MSVC 1.52 targeting 16-bit Visual Basic 4.
|
||||
// Types, structures, constants, and API prototypes match the VBX CDK.
|
||||
|
||||
#ifndef VBAPI_H
|
||||
#define VBAPI_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Packing: VBX CDK structures use byte packing
|
||||
// -----------------------------------------------------------------------
|
||||
#pragma pack(1)
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Core types
|
||||
// -----------------------------------------------------------------------
|
||||
typedef WORD HCTL; // Handle to a VBX control instance
|
||||
typedef WORD HSZ; // Handle to a VB-managed string
|
||||
typedef LONG HLSTR; // Handle to a VB long string
|
||||
typedef USHORT ERR; // Error code
|
||||
|
||||
// Segment type for MSVC 1.52
|
||||
#ifndef _SEGMENT_DEFINED
|
||||
typedef unsigned short _segment;
|
||||
#define _SEGMENT_DEFINED
|
||||
#endif
|
||||
|
||||
// Flag type
|
||||
typedef DWORD FL;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Forward declarations and pointer types
|
||||
// -----------------------------------------------------------------------
|
||||
typedef struct tagMODEL MODEL;
|
||||
typedef struct tagPROPINFO PROPINFO;
|
||||
typedef struct tagEVENTINFO EVENTINFO;
|
||||
typedef struct tagPARMINFO PARMINFO;
|
||||
|
||||
typedef MODEL FAR *LPMODEL;
|
||||
typedef PROPINFO *PPROPINFO; // Near pointer (DLL data segment)
|
||||
typedef EVENTINFO *PEVENTINFO; // Near pointer (DLL data segment)
|
||||
typedef PARMINFO *PPARMINFO; // Near pointer (DLL data segment)
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Control procedure type
|
||||
// -----------------------------------------------------------------------
|
||||
typedef LONG (FAR PASCAL *PCTLPROC)(HCTL, HWND, USHORT, USHORT, LONG);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// VB version constants
|
||||
// -----------------------------------------------------------------------
|
||||
#define VB_VERSION 0x0300
|
||||
#define VB300_VERSION 0x0300
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Data types (bits 0-7 of PROPINFO.fl)
|
||||
// -----------------------------------------------------------------------
|
||||
#define DT_HSZ 0x01
|
||||
#define DT_SHORT 0x02
|
||||
#define DT_LONG 0x03
|
||||
#define DT_BOOL 0x04
|
||||
#define DT_COLOR 0x05
|
||||
#define DT_ENUM 0x06
|
||||
#define DT_REAL 0x07
|
||||
#define DT_XPOS 0x08
|
||||
#define DT_XSIZE 0x09
|
||||
#define DT_YPOS 0x0A
|
||||
#define DT_YSIZE 0x0B
|
||||
#define DT_PICTURE 0x0C
|
||||
#define DT_HLSTR 0x0D
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Property flags (bits 8+ of PROPINFO.fl)
|
||||
// -----------------------------------------------------------------------
|
||||
#define PF_datatype 0x000000FFL // Mask for data type field
|
||||
#define PF_fPropArray 0x00000100L
|
||||
#define PF_fSetData 0x00000200L
|
||||
#define PF_fSetMsg 0x00000400L
|
||||
#define PF_fNoShow 0x00000800L
|
||||
#define PF_fNoRuntimeW 0x00001000L
|
||||
#define PF_fGetData 0x00002000L
|
||||
#define PF_fGetMsg 0x00004000L
|
||||
#define PF_fGetHszMsg 0x00008000L
|
||||
#define PF_fUpdateOnEdit 0x00010000L
|
||||
#define PF_fEditable 0x00020000L
|
||||
#define PF_fPreHwnd 0x00040000L
|
||||
#define PF_fDefVal 0x00080000L
|
||||
#define PF_fNoInitDef 0x00100000L
|
||||
#define PF_fNoRuntimeR 0x00200000L
|
||||
#define PF_fSaveData 0x00400000L
|
||||
#define PF_fSaveMsg 0x00800000L
|
||||
#define PF_fLoadDataOnly 0x01000000L
|
||||
#define PF_fLoadMsgOnly 0x02000000L
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// MODEL flags
|
||||
// -----------------------------------------------------------------------
|
||||
#define MODEL_fArrows 0x00000001L
|
||||
#define MODEL_fFocusOk 0x00000002L
|
||||
#define MODEL_fMnemonic 0x00000004L
|
||||
#define MODEL_fDesInteract 0x00000008L
|
||||
#define MODEL_fInitMsg 0x00000010L
|
||||
#define MODEL_fLoadMsg 0x00000020L
|
||||
#define MODEL_fInvisAtRun 0x00000040L
|
||||
#define MODEL_fGraphical 0x00000080L
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// VBX control messages
|
||||
// -----------------------------------------------------------------------
|
||||
#define VBM__BASE (WM_USER + 0x0600)
|
||||
#define VBM_INITIALIZE (VBM__BASE + 0)
|
||||
#define VBM_SETPROPERTY (VBM__BASE + 1)
|
||||
#define VBM_GETPROPERTY (VBM__BASE + 2)
|
||||
#define VBM_CHECKPROPERTY (VBM__BASE + 3)
|
||||
#define VBM_MNEMONIC (VBM__BASE + 4)
|
||||
#define VBM_CREATED (VBM__BASE + 5)
|
||||
#define VBM_LOADED (VBM__BASE + 6)
|
||||
#define VBM_SAVEPROPERTY (VBM__BASE + 7)
|
||||
#define VBM_LOADPROPERTY (VBM__BASE + 8)
|
||||
#define VBM_METHOD (VBM__BASE + 9)
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Error codes
|
||||
// -----------------------------------------------------------------------
|
||||
#define ERR_None 0
|
||||
#define ERR_InvPropVal 380 // Invalid property value
|
||||
#define ERR_InvPropSet 383 // Can't set property at this time
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Standard property pointers
|
||||
//
|
||||
// Magic values recognized by VB as built-in properties.
|
||||
// -----------------------------------------------------------------------
|
||||
#define PPROPINFO_STD_CTLNAME ((PPROPINFO)0)
|
||||
#define PPROPINFO_STD_INDEX ((PPROPINFO)1)
|
||||
#define PPROPINFO_STD_HWND ((PPROPINFO)2)
|
||||
#define PPROPINFO_STD_TAG ((PPROPINFO)5)
|
||||
#define PPROPINFO_STD_LEFT ((PPROPINFO)6)
|
||||
#define PPROPINFO_STD_TOP ((PPROPINFO)7)
|
||||
#define PPROPINFO_STD_WIDTH ((PPROPINFO)8)
|
||||
#define PPROPINFO_STD_HEIGHT ((PPROPINFO)9)
|
||||
#define PPROPINFO_STD_ENABLED ((PPROPINFO)17)
|
||||
#define PPROPINFO_STD_VISIBLE ((PPROPINFO)18)
|
||||
#define PPROPINFO_STD_PARENT ((PPROPINFO)21)
|
||||
#define PPROPINFO_STD_DRAGMODE ((PPROPINFO)22)
|
||||
#define PPROPINFO_STD_DRAGICON ((PPROPINFO)23)
|
||||
#define PPROPINFO_STD_NONE ((PPROPINFO)NULL)
|
||||
|
||||
// Aliases matching the plan's naming convention
|
||||
#define PPROPINFO_STD_NAME PPROPINFO_STD_CTLNAME
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// PROPINFO - Property descriptor
|
||||
// -----------------------------------------------------------------------
|
||||
struct tagPROPINFO {
|
||||
PSTR npszName; // Property name (near pointer, DLL DS)
|
||||
FL fl; // Data type (bits 0-7) | Property flags (bits 8+)
|
||||
BYTE offsetData; // Byte offset in control's cbCtlExtra data
|
||||
BYTE infoData; // Type-specific info (e.g., max string length)
|
||||
LONG dataDefault; // Default value
|
||||
PSTR npszEnumList; // DT_ENUM: null-separated, double-null terminated
|
||||
BYTE enumMax; // DT_ENUM: maximum valid value
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// PARMINFO - Event parameter descriptor
|
||||
// -----------------------------------------------------------------------
|
||||
struct tagPARMINFO {
|
||||
PSTR npszName; // Parameter name
|
||||
FL fl; // Data type
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// EVENTINFO - Event descriptor
|
||||
// -----------------------------------------------------------------------
|
||||
struct tagEVENTINFO {
|
||||
PSTR npszName; // Event name
|
||||
USHORT cParms; // Number of parameters
|
||||
USHORT cwParms; // Size of parameters in words
|
||||
PPARMINFO npParmInfo; // Parameter info array (NULL if no params)
|
||||
PSTR npszParmProf; // Parameter profile string (e.g., "value As Integer")
|
||||
FL fl; // Event flags
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// MODEL - Control model definition
|
||||
//
|
||||
// VB3+ version (usVersion = VB300_VERSION) for VB4 compatibility.
|
||||
// Registered with VBRegisterModel() in VBINITCC.
|
||||
// -----------------------------------------------------------------------
|
||||
struct tagMODEL {
|
||||
USHORT usVersion; // VB_VERSION (0x0300)
|
||||
FL fl; // MODEL_* flags
|
||||
PCTLPROC pctlproc; // Control procedure
|
||||
USHORT fsClassStyle; // Window class style bits
|
||||
USHORT flWndStyle; // Window style bits
|
||||
USHORT cbCtlExtra; // Bytes of per-instance data
|
||||
USHORT idBmpPalette; // Toolbox bitmap resource ID
|
||||
PSTR npszDefCtlName; // Default control name (near)
|
||||
PSTR npszClassName; // Window class name (NULL = default)
|
||||
PSTR npszParentClassName; // Parent class name (NULL = default)
|
||||
PPROPINFO *npproplist; // Property list (NULL-terminated array)
|
||||
PEVENTINFO *npeventlist; // Event list (NULL-terminated array)
|
||||
BYTE nDefProp; // Default property index
|
||||
BYTE nDefEvent; // Default event index
|
||||
BYTE nValueProp; // Value property index
|
||||
USHORT usCtlVersion; // Control version (BCD, user-defined)
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Utility macro: byte offset of a field within a structure
|
||||
// -----------------------------------------------------------------------
|
||||
#define OFFSETIN(type, field) ((BYTE)&(((type *)0)->field))
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// VB API function prototypes
|
||||
//
|
||||
// These functions are exported by the VB runtime (VBAPI module) and
|
||||
// resolved at load time when VB loads the VBX.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// Control registration
|
||||
BOOL FAR PASCAL VBRegisterModel(HANDLE hmodule, MODEL FAR *lpmodel);
|
||||
|
||||
// Default control procedure
|
||||
LONG FAR PASCAL VBDefControlProc(HCTL hctl, HWND hwnd, USHORT msg, USHORT wp, LONG lp);
|
||||
|
||||
// Event firing
|
||||
ERR FAR PASCAL VBFireEvent(HCTL hctl, USHORT iEvent, LPVOID lpparams);
|
||||
|
||||
// String handle management
|
||||
HSZ FAR PASCAL VBCreateHsz(_segment seg, LPSTR lpsz);
|
||||
void FAR PASCAL VBDestroyHsz(HSZ hsz);
|
||||
LPSTR FAR PASCAL VBDerefHsz(HSZ hsz);
|
||||
|
||||
// Long string handle management
|
||||
HLSTR FAR PASCAL VBCreateHlstr(LPVOID lpdata, USHORT cbLen);
|
||||
void FAR PASCAL VBDestroyHlstr(HLSTR hlstr);
|
||||
LPSTR FAR PASCAL VBGetHlstr(HLSTR hlstr, USHORT FAR *lpcbLen);
|
||||
ERR FAR PASCAL VBSetHlstr(HLSTR FAR *phlstr, LPVOID lpdata, USHORT cbLen);
|
||||
|
||||
// Control data access
|
||||
LPVOID FAR PASCAL VBDerefControl(HCTL hctl);
|
||||
|
||||
// Runtime mode query (TRUE = runtime, FALSE = design)
|
||||
BOOL FAR PASCAL VBGetMode(void);
|
||||
|
||||
// Control property access
|
||||
ERR FAR PASCAL VBSetControlProperty(HCTL hctl, USHORT iProp, LONG data);
|
||||
LONG FAR PASCAL VBGetControlProperty(HCTL hctl, USHORT iProp);
|
||||
|
||||
// Control window handle
|
||||
HWND FAR PASCAL VBGetControlHwnd(HCTL hctl);
|
||||
|
||||
// Instance handle
|
||||
HANDLE FAR PASCAL VBGetHInstance(void);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Restore default packing
|
||||
// -----------------------------------------------------------------------
|
||||
#pragma pack()
|
||||
|
||||
#endif // VBAPI_H
|
||||
Loading…
Add table
Reference in a new issue