HOME - - - - - - - TUTORIALS INDEX - - - - - - - - - - - - Other material for programmers

Delphi: Accessing Port Hardware and how to use InpOut32.dll



This tutorial is intended to help you understand some software which simplifies attaching electronics, even homemade electronics, to your PC via the parallel port. (Or, with more work, the serial port.) You may also want to read my answers to questions about the hardware issues.

In particular, it helps you with using InpOut32.dll, an external resource which some kind people have made freely available, to save you reinventing certain wheels. You don't need prior experience with DLLs.

If you just want a program to turn individual pins of your parallel port on or off, and don't want to fool around with writing your own, there's my freeware "SheepdogCurtainCloser". No sourcecode available, sorry. Works with XP.

There's also the .exe within the .zip archive containing the source code for DD79, an application which is set up more or less as the application described in this tutorial. That too lets you turn bits on and off, and the sourcecode is provided.


Does InpOut32 still work? WEll it did in December 2009, anyway, on an XP machine. Also, in March 09 I had a helpful email from another programmer saying it worked fine for him with Delphi 7 on a Windows XP (Professional, SP3) machine. But shhh... don't tell Mr.G about some goalposts he could move. Between Codegear's (to whom Borland sold Delphi) lack of interest in "the little guy", and other things, I'm beginning to like Linux/ Netbeans more and more.

And does it work well? The same person told me he'd used it with an Excel VBA app every day since Sept 08 without hassle.



Thank you Logix4U, Programmer's Heaven and Everyday with Practical Electronics magazine! Back in October 2003 they made life easier for anyone wanting to access their computer's parallel or serial ports. At last! A single method that works for Win 9x, NT, 2000 and XP. Without much messy overhead.

Three brief asides:

Aside 1: Most of the material on the web speaks of InpOut32 as being a way to access the parallel port... and it is. Working with the parallel port is relatively simple. InpOut32 should also allow you to access your serial port. The reason I haven't tried to do it is that the serial port is a bigger pain to work with. InpOut32 lets you send and read bytes to/ from the addresses assigned to the parallel port. Working with serial ports is done much the same way, so, if you care to master the chips you will be talking to, I see no reason why you shouldn't access them with InpOut32, too.

Aside 2a: This isn't really the place for it, but I want to get SOME reference onto my site. While researching just now, I came across Erlich Industrial Development's great site which may be of interest to the sort of person I suspect will have come the page you are reading. It sells educational electronics kits which connect your computer to the outside world though a USB port. There seems to be a tie in with Jan Axelson, who will be known to some readers as a long-standing friend of the "do-it-yourself" electronics hobbyist. Do note the little bit of bad news (?) at the bottom of EID's page about USB product and vendor IDs.... the "big players" (could Microsoft have had a hand?) are squeezing the little hobbyists out, again, in the name of "ease of use"... though I have to admit, I see some value in USB product/vendor ID systems... I just wonder why it is expensive to obtain one. Speaking of "big players", EID is no garage-based, one man band. Explore their site, and be grateful that they have someone who has set up the bare PCBs, kits, components division, which must be a pretty minor distraction in their overall scheme. Disclaimer: I must confess: I have not yet tried anything from them. Too busy writing tutorials for you!!

Aside 2b: If interfacing via USB sounds good to you, there's also the Dallas MicroLan (aka 1-Wire solution, about which I have posted many tutorials).


Before getting to The Good Stuff, a little background on two issues....

Issue 1: Back in the late 1980s, if you wanted to wink some LEDs on and off, you wired them to a printer cable, and then wrote a program which changed the value stored in a specific memory location. In Basic, the command was POKE, in Pascal, we used PORT. This worked even in early versions of Windows, and Delphi 1 still had a PORT command.

Then Windows got "better". Direct access to specific memory locations became a bad idea, and from Delphi 2 the PORT command was missing.

Issue 2: One of the strengths, I think, of Delphi is that, in general, you don't use DLLs. As much as I like that, in the case of solving the read/write parallel port problem I was prepared to look at using one... and I was delighted to find that things aren't very complicated. From that you should infer a warning: I'm not experienced in matters DLL... but the following works for me. Please educate me if anything in it is wrong or unwise!

As I understand DLLs, at least as far as is necessary to use the port reading/writing DLL from Logix4U, a DLL is a discrete file in your system which has a header which allows other programs to interact with it. The bulk of this tutorial is about how to make a program written by you interact with someone else's DLL.

If you want a more gentle introduction to DLLs, you might want to read my introduction to DLLs, at least the first part, before proceeding. You don't need to know anything about writing DLLs to use what is in this tutorial about accessing things via your parallel and serial


Moving on.... we're nearly to the part of the tutorial in which we'll write a program to access the parallel port...

Lastly before getting to The Good Stuff there is one detail and two worries to address....

I like my system to stay nice and simple. Windows crashes often enough without any help from me. I've always disliked DLLs because I don't know what they're up to, because of the clutter they add, and because of the potential for confusion. Consider the following: Suppose two packages EACH come with a DLL called FredsStuff..... but the stuff in the two DLLs is not the same. The answer, I'm told, is that if the FredsStuff.DLL that came with package A is installed in the folder holding package A's .exe file, and B's is in B's folder, then all is well.

Now suppose you have three packages, living nicely each in their own folders, which all use a DLL with a name that no one else is using. The three DLLs are identical in content as well as in name. Do you need to have three copies of the DLL? No. You can put one copy in the Windows system folder (usually C:\Windows\System\), and all should be well.

A warning: Please be very careful when working with this software. Other than seeing, in general, how to use DLLs, there's no point unless you are also doing some hardware things.... and mistakes there CAN DAMAGE YOUR COMPUTER. See my page about using the parallel port for more information on the risks, and more advice on the things that "should" be okay.... but I ACCEPT NO LIABILITY FOR ANYTHING THAT ARISES from your use of this material. Now we can put the lawyer back in his box, I hope.

Another chore before we start to program: You need to find the address of your parallel port. If this paragraph daunts you, just try $378 for the port address. In Win 9x, right click on "My Computer", click on "Properties", and click on the "Device Manage" tab. There should be a "ports" entry in the list. Expand that if it is collapsed, double click the "Printer Port" line. Select the resources tab. Look at the first "Input/Output range" line. Make a note of the first number there. It will probably be 0378. That's your printer port's first address. It is expressed in hex. Just put a dollar sign in front of it when you type it into your program, and all will be well.


At last... The Good Stuff...

I've posted a zip archive of the source code and the .exe of something like what is described below. It was written on an XP machine, using Delphi 4. Working fine 9 December 2009.

Start a new project (application). (I was using Delphi 2 under Win98SE when I developed this tutorial)

Put a label on the form, big enough for a 3 digit number.

Add a scroll bar. (ScrollBar object is on the "standard" tab.)

Most of the default properties are fine, but change the following:
   Max:255
   Min:0
Double click on the scroll bar. This will create an empty "Change" event handler. Put the following in between the "begin" and the "end":

Label1.caption:=inttostr(ScrollBar1.position);

Run the program. Moving the scroll bar thumb tab should cause the number showing in the label to change.

(If you know about hex numbers, and how easily they convert to binary in your head, you might want to use...

Label1.caption:=inttohex(ScrollBar1.position,2);

... so that when we get the program writing to the parallel port you can more easily see if what you are getting is RIGHT! But if you don't see what I'm talking about, you can just ignore this.)

Put a copy of inpout32.dll from www.logix4u.net into the same folder as your project. You can download a zip file with that and a whole bunch of related stuff from Logix4U by clicking here. (With WiunZip, you can pull just the DLL out of the archive after you've downloaded it.

And, finally, make sure that your printer is NOT plugged into your printer port (!), and then...

Revise all of ScrollBar1Change so that it becomes what you see below. If your parallel port wasn't at $378 change the reference to $378!

procedure TForm1.ScrollBar1Change(Sender: TObject);
var bWriteMe, bErr:byte;
function Out32(wAddr:word;bOut:byte):byte; stdcall; external 'inpout32.dll';
begin
bWriteMe:=ScrollBar1.position;
Label1.caption:=inttostr(bWriteMe);
bErr:=(Out32($378,bWriteMe));
end;
... and when you run it, and move the scollbar thumb tab. The state of the parallel port's data pins should change. You can see them change with a voltmeter, or a simple circuit with a few LEDs and resistors

If the code won't compile as shown (it will in Delphi 2), try moving the
function Out32(wAddr:word;bOut:byte):byte; stdcall; external 'inpout32.dll';
line to just after the uses line near the start of the program. It is a pity to have it become a global resource when it is only relevant to ScrollBar1Change, but it may be necessary to have it global in post-Version-2 versions of Delphi. (Feedback on this from readers would be appreciated. Send me your report, including which version of Delphi you are using, please.)

A little mystery: On one system I was using, I did get changing states on the data pins... but they were exactly the opposite of what I expected.

In the code above, the TForm1. may be different in what will be right for you. It simply derived from the name of the form, as usual. The Out32 has to be Out32 to achieve the connection to the DLL. bWriteME and bErr could be changed to names of your preference.

If you get "Debugger kernel error. Error code:1", then look to see if the .dll is in the right place, or to see if you are trying to call a function not present in the dll. Also, for either "problem", a simple typo may be the reason for the error message. The computer won't find Out32 if you've told it to look for Out23!!.

WARNING: While this works, I may have used the wrong data types for one or more of bWriteMe, bErr, and wAddr. I will try to get this uncertainty cleared up.

Putting the $378 in as it appears above is fine in a tiny demo program. In a bigger program, where there might be several references to the printer port address, it would be best to put....

const PrinterPortAddr=$378;

... near the start of your program, and just use PrinterPortAddr when you want to refer to the printer port address. Half of you, gentle readers, will not need to be told this. The other half of you won't see why it is a good idea. Trust me? Do it this way, and one day you'll thank me!

Yes... bErr should be tested to see that all was well. I'm not sure what value you should look for, though. The documentation at www.logix4u.net is good. You can find it there if you're going to test bErr.

So much for sending things to the parallel port.


Now for reading things from it. (Reading is not in DD79, the application supplied in the .zip archive you can download.)

Fancy modern ports can support bi-directional use of the main data pins. I'm not going to get into that here. I am going to show you how to get input across the 4 bits that have always been for input.

Inputs: If you read address PrinterPortAddr+1 (i.e. $379 on most machines), you can discover the state of 5 pins. They determine the state of bits 3-7 of the byte at PrinterPortAddr+1. The bits are mapped and named as follows:

Bit Pin Name
3   15   Error
4   13   Select In
5   12   Paper Empty
6   10   Acknowledge
7   11   Busy
(A trap for the unwary... 'Busy' is inverted 'just inside' the computer. Thus if you apply a '1' to all of the pins, you'll see 01111xxx when you read 889! Isn't computing fun?)



Add second label and a button to your form, and make the button do the following when clicked:

procedure TForm1.Button1Click(Sender: TObject);
function Inp32(wAddr:word):byte; stdcall; external 'inpout32.dll';
begin
label2.caption:='When last read, the port was showing '+
   inttostr((Inp32($379)and $F8));
end;
(The "and $F8" stuff just hides the three bits that are not inputs and could each be 0 or 1.)

Again: The $379 will work on most machines, but if your printer port isn't at $378, then you just use a number which is one more than where your printer port is. Also again: You can't change the name "Inp32", and I may have assigned the wrong types to things.

Again, the...
function Inp32(wAddr:word):byte; stdcall; external 'inpout32.dll';
...may have to go just after the uses clause.

Now: That wasn't too bad, was it? The good news: It will work, you could just use it. There is another way of calling functions from DLLs, which is probably better in some ways, though it is less transparent, and requires some memory management.

I haven't been able to write this alternative up yet.... but everything you need to know is at the great guide to calling things in DLL with Delphi at... an article at Programmer's Heaven.


At the beginning of this, I thanked Logix4U, Programmer's Heaven and Everyday with Practical Electronics magazine. It was EPE that told me about the DLL's availability (pg. 698, Oct 2003). Logix4 are big heroes, of course, for releasing the DLL and its source code as freeware. I learned how to use the functions in a DLL from the article at Programmer's Heaven.
Here's another thing about parallel port access as a footnote... and thanks to Martijn Haak in the Netherlands for contributing this....

The BIOS data segment (40h) in your PC has a table with the address(es) of the parallel port(s) in your system.

0040:0008 has the address of LPT1, usually 378h
0040:000A has the address of LPT2
0040:000A has the address of LPT3, and
0040:000A has the address of LPT4

            powered by FreeFind
  Site search Web search
Site Map    What's New    Search This search merely looks for the words you enter. It won't answer "Where can I download InpOut32?"


Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: Yes.. I do enjoy compiling these things for you. I hope they are helpful. However... this doesn't pay my bills!!! Sheepdog Software (tm) is supposed to help do that, so if you found this stuff useful, (and you run a Windows or MS-DOS PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.

Link to Tutorials main page
How to email or write this page's editor, Tom Boyd


Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....