PdxLib.Connector
1.0.0
dotnet add package PdxLib.Connector --version 1.0.0
NuGet\Install-Package PdxLib.Connector -Version 1.0.0
<PackageReference Include="PdxLib.Connector" Version="1.0.0" />
<PackageVersion Include="PdxLib.Connector" Version="1.0.0" />
<PackageReference Include="PdxLib.Connector" />
paket add PdxLib.Connector --version 1.0.0
#r "nuget: PdxLib.Connector, 1.0.0"
#:package PdxLib.Connector@1.0.0
#addin nuget:?package=PdxLib.Connector&version=1.0.0
#tool nuget:?package=PdxLib.Connector&version=1.0.0
PdxLib Connector
Read Borland Paradox databases (.DB tables, .MB blob files, .PX indexes) from .NET.
Two parts:
pxlib.dll— pxlib 0.6.8 built as a self-contained Windows x64 DLL with MSVC.PdxLib.Connector— anet9.0class library binding it, exposing a read-only managed API plus aDbDataReader.
using PdxLib.Connector;
using var table = ParadoxTable.Open(@"C:\data\CUSTOMER.DB");
Console.WriteLine($"{table.TableName}: {table.RecordCount} rows, cp{table.CodePage}");
foreach (ParadoxField field in table.Fields)
Console.WriteLine($" {field.Name} {field.Type}({field.Length}) -> {field.ClrType.Name}");
foreach (ParadoxRecord row in table.Records)
Console.WriteLine($"{row["CUSTNO"]}\t{row.GetString("NAME")}");
Feeding System.Data consumers:
using var table = ParadoxTable.Open(path);
using var reader = table.CreateDataReader();
var dt = new DataTable();
dt.Load(reader); // or SqlBulkCopy, Dapper, ...
Layout
pxlib-0.6.8/ vendored upstream source (7 documented edits, see native/PATCHES.md)
native/
generated/config.h hand-written config.h for MSVC
generated/paradox.h paradox.h.in configured with iconv/recode off
pxlib_ext.{h,c} PX_malloc, PX_free, PX_free_record, px_fopen_utf8
smoketest/pxsmoke.c native round-trip test
build-native.cmd builds pxlib.dll
build-smoketest.cmd builds and runs pxsmoke.exe
PATCHES.md every change made to upstream pxlib
artifacts/native/win-x64/ pxlib.dll, pxlib.lib, pxlib.pdb
src/PdxLib.Connector/ the class library
tests/PdxLib.Connector.Tests/ xunit suite
Building
Requires Visual Studio (or Build Tools) with the Desktop development with C++ workload, and the .NET 9 SDK.
native\build-native.cmd # -> artifacts\native\win-x64\pxlib.dll
native\build-smoketest.cmd # native round trip; must print SMOKE TEST PASSED
dotnet build
dotnet test
Build the native DLL first — the managed project copies it into its output directory, and
without it every test fails with DllNotFoundException.
.github/workflows/ci.yml runs exactly that sequence on
windows-latest for every push and pull request against main.
Releasing
.github/workflows/release.yml runs the sequence above on
a Windows runner, packs, and publishes to nuget.org. Push a tag to release:
git tag v0.1.0
git push origin v0.1.0
The tag minus its leading v becomes the package version, so <Version> in the csproj is
only a fallback for local packing. Running the workflow by hand from the Actions tab builds
and packs without publishing unless publish is ticked — use that to check a candidate
before tagging.
Publishing uses Trusted Publishing rather than a stored API key: the job exchanges a GitHub OIDC token for a nuget.org key valid for one hour, so no long-lived credential exists to leak. Setup, once:
- nuget.org → your username → Trusted Publishing → new policy, with repository owner
Neciota, repositorydotnet-paradox-connector, workflow filerelease.yml(the file name alone, no path), environment blank. - Add the repository secret
NUGET_USER: your nuget.org profile name, not the email address.
Renaming release.yml invalidates the policy — the workflow file name is part of what
nuget.org matches on.
Design notes
Read-only. The native DLL exports pxlib's full API including the writers, but the managed layer binds and exposes only the read path. Writing to a live Paradox table risks corrupting it, and extraction is the use case here.
Text encoding is handled in managed code. The DLL is built with PX_USE_ICONV 0, so
PX_set_targetencoding is inoperative and pxlib returns raw bytes. ParadoxTable reads
the header's DOS codepage and decodes with CodePagesEncodingProvider — no libiconv to
ship, and .NET's legacy-codepage support is more dependable. Override per table with
ParadoxOpenOptions.Encoding when a table's header codepage is wrong.
No VC++ redistributable. Built with /MT, so pxlib.dll imports only KERNEL32.dll.
The consequence is that its heap is private, which is why PX_free/PX_free_record had to
be added — see native/PATCHES.md.
Read-only files and Unicode paths work. Upstream calls fopen(path, "rb+"), which
fails on read-only media and cannot express a non-ANSI path. The patched build falls back
to "rb" and opens via _wfopen.
Type mapping
| Paradox | CLR |
|---|---|
Alpha |
string (trailing padding trimmed; disable with TrimAlphaPadding) |
MemoBlob, FormattedMemoBlob |
string |
Short |
short |
Long, AutoInc |
int |
Number |
double |
Currency, Bcd |
decimal |
Logical |
bool |
Date, Timestamp |
DateTime |
Time |
TimeSpan |
Bytes, Blob, Ole, Graphic |
byte[] |
NULL fields come back as null (DBNull.Value through ParadoxDataReader).
Paradox quirks worth knowing — all of these are properties of the on-disk format, and each has a test pinning it:
- An empty
Alphavalue is indistinguishable from NULL. Paradox marks a value present by setting the high bit of its first byte, and an empty string leaves the field zeroed. It reads back asnull, not"". - A
Bytesvalue starting with0x00reads as NULL. There is no separate null flag for that type, so a leading zero byte means "absent". short.MinValueandint.MinValuecannot be stored. Integers are held asvalue XOR sign-bit, and all-zero bytes mean NULL — so the minimum of each type maps exactly onto the null sentinel. Every other value, including0and both maxima, round-trips.- Dates and timestamps use an unusual origin.
Dateis a day count where 1 is0001-01-01;Timestampis milliseconds where86_400_000is0001-01-01. The conversions are pinned against pxlib's own formatters inDateTimeConversionTests.
One upstream bug was fixed rather than worked around: pxlib returned a NULL Number,
Currency or Timestamp as 0.0 with its null flag clear, making it indistinguishable
from a real zero. The information was destroyed inside pxlib, so the fix had to be in C —
see patch 5 in native/PATCHES.md.
Threading
ParadoxTable is not thread-safe — pxlib keeps one read-cache block and a file cursor per
document. Open one table per thread.
Testing
dotnet test needs no sample data: the suite writes real Paradox files using pxlib's own
writer and reads them back through the public API.
To also run against genuine tables:
set PXLIB_TEST_DB=C:\data\CUSTOMER.DB # a single table
set PXLIB_TEST_DIR=C:\data # or every .DB beneath a folder
dotnet test -v normal
RealDataTests then enumerates every record of every configured table. With neither
variable set those two tests do nothing and say so in their output (xunit 2 has no dynamic
skip, hence -v normal to see the message). Running these against real production tables
is the most valuable check available — the generated fixture cannot cover real-world header
variants, encryption, or unusual codepages.
Licence
LGPL-2.1-or-later — LICENSE.txt.
PdxLib.Connector links pxlib, which is itself LGPL-2.1-or-later. Note that the
pxlib 0.6.8 tarball ships a COPYING containing the GPL v2 text even though its
sources grant the Lesser GPL; the vendored copy is kept verbatim, and the licence
actually relied on is in LICENSE.txt. For the modifications made to pxlib, the
components it carries from other authors, and the LGPL §6 relinking position, see
THIRD-PARTY-NOTICES.md and
native/PATCHES.md.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- System.Text.Encoding.CodePages (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 131 | 7/27/2026 |