Initial commit.

This commit is contained in:
Scott Duensing 2026-02-23 20:53:02 -06:00
commit f666825417
19 changed files with 4261 additions and 0 deletions

11
.gitattributes vendored Normal file
View 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
View 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

Binary file not shown.

101
drivers/CYBERCOM.TXT Normal file
View 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

File diff suppressed because it is too large Load diff

27
drv/commdrv.def Normal file
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

935
vbx/mscomm.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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