This is a source code sample for replacing the DosBeep and Dos16Beep functions to make it sound on the multimedia audio device instead of the PC speaker.
Program is distributed as ZIP package: download to temporary directory and unpack to destination folder. See below for download link(s).
Following ones are the download links for manual installation:
DosBeep Replacement Sample v. 2.0 (19/4/2020, Lars Erdmann) | Readme/What's new |
This is a source code sample for replacing the DosBeep and Dos16Beep functions to make it sound on the multimedia audio device instead of the PC speaker.
This sample replaces:
- Dos32Beep (DOSCALL.286)
- Dos16Beep (DOSCALLS.50)
Effectively it works like this:
1) ANY application (that is, either an EXE or a DLL that this EXE binds to) that uses DosBeep can be modified to instead use the DosBeep that is implemented in NEWCALLS.DLL. That also includes any DLLs delivered with OS/2.
2) What you need to do to this EXE or DLL is to overwrite the import references that import from DOSCALLS (where ordinal 286 points to the genuine DosBeep function as implemented in the kernel: it uses the PIT to create a sound via an attached PC built-in LOW-FI speaker) and instead tell the EXE or DLL to import from NEWCALLS (which uses your soundcard to play a generated PCM sine wave stream).
3) Because there is a good chance that a number of DOSCALLS routines are bound to the exe and not only DosBeep, what NEWCALLS does is to forward all unchanged functions directly from DOSCALLS to the EXE or DLL. With the one exception of "DosBeep" which NEWCALLS replaces with its own implementation (and it binds to the genuine implementation and calls that where necessary).
Let's say you have an EXE called MyApp.exe that links to a couple of functions implemented in the kernel, say, DosOpen, DosClose, DosRead, DosWrite and also DosBeep.
Run this command:
dllrname.exe /Q /N MyApp.exe DOSCALLS=NEWCALLS
After doing so, MyApp.exe will invoke the genuine kernel functions DosOpen,DosClose, DosRead,DosWrite in DOSCALLS (because those are forwarded) but it will call the implementation of DosBeep in NEWCALLS as that has been replaced.
You will have to do the "dllrname" action on every EXE and DLL where you want to have a DosBeep call replaced by the implementation in NEWCALLS.DLL. "dllrname.exe" is a tool that comes with VAC but there are free tools and serve the very same purpose.
https://hobbes.nmsu.edu/download/pub/os2/dev/util/dllrname.zip
https://hobbes.nmsu.edu/download/pub/os2/util/system/renmodul2_0_0.zip
NEWCALLS.DLL has to go somewhere in your LIBPATH or BEGINLIBPATH or ENDLIBPATH, just like for any other DLL.
Just a hint: You can only do this module renaming (DOSCALLS=NEWCALLS) if the modules have the very same name length (DOSCALLS and NEWCALLS have the same number of letters). That is a design limitation. Additionally, under OS/2, DLL names are limited to 8 characters (excluding file extension).
Additional note: I have implemented DosBeep so that PM and also VIO applications can use it without any restrictions. I even went through some hoola hoops so that error message boxes can be displayed even from a VIO app.
==PROCEDURE==
You need to modify the binary (EXE or DLL) so it can use the NEWCALLS.DLL (New DOSBeep) file instead of DOSCALL.DLL. Remember to backup the EXE or DLL before doing this.
Follow the next procedure:
First validate if the EXE uses DosBeep. If you have an exe called MyApp.Exe, then do this:
exehdr /V MyApp.exe | find "imp"
It will list all the imports. If you find something like this:
REL OFF(32) 0014 imp DOSCALLS.286
Then you know that this exe uses DosBeep.
Now modify the file by running:
dllrname /N /Q MyApp.exe DOSCALLS=NEWCALLS
You can rerun:
exehdr /V MyApp.exe | find "imp"
Now it will show this:
REL OFF(32) 0014 imp NEWCALLS.286
Now you will know that from now on, the new DosBeep Implementation will be called. It works the very same way to patch a DLL.
==SAMPLE==
There is this simple app to help you test this, file bla.c:
#define INCL_BASE
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
ULONG freq=0,dur=0;
if (argc != 3)
{
printf("bla [freq] [dur]\n");
return 1;
}
printf("Hallo!\n");
freq = strtoul(argv[1],NULL,10);
dur = strtoul(argv[2],NULL,10);
if (!freq || !dur)
{
printf("invalid arguments\n");
return 1;
}
DosBeep(freq,dur);
return 0;
}
and build it with "build.cmd".
After that you can try:
dllrname /N /Q bla.exe DOSCALLS=NEWCALLS /* this does the interesting part */
When you now run bla.exe, you can hear the Beep via my Speakers driven by Uniaud or via the Speakers connected via Audio USB, whatever device I choose as the default WAV device (the default WAV device will be used to play the sound).
By the way: use my USBAUDIO.ZIP package to have everything to work with USB audio. And it also comes with a DLL that extends the Multimedia Setup Object to allow you to simply switch the default WAV device with a mouse click. Of course that works across the board, not only for the USB audio device but also for the UNIAUD device.
==REFERENCE==
This sample is inspired by the NEWCALLS sample of "Hook code to circumvent built-in methods of OS/2" by Peter Fitzsimmons.
http://www.edm2.com/common/snippets/newc2.zip
==LICENSE==
BSD 3-Clauses
==AUTHOR==
Lars Erdmann
==LINKS==
http://www.edm2.com/common/snippets/newc2.zip
https://hobbes.nmsu.edu/download/pub/os2/dev/util/dllrname.zip
https://hobbes.nmsu.edu/download/pub/os2/util/system/renmodul2_0_0.zip |
hobbes.nmsu.edu/download/net/ftp/pub/os2/apps/mmedia/util/DosBeep_Replacement_Sample_2-0.zip | ||
DosBeep Replacement Sample (22/1/1997, Peter Fitzsimmons) | Readme/What's new |
Jan 22, 1997
What is NEWCALLS.DLL?
=====================
This is an example of how to redirect (or "hook") a function
in a dll that you don't have the code for. My example hooks
DosOpen() for PMMERGE.DLL. It works under Warp 3.0 or Warp
4.0.
How it Works
============
The exe header of PMMERGE.DLL is changed so that all
references to DOSCALLS.DLL go to NEWCALLS.DLL instead (IBM's
compiler supplies a utility called DLLRNAME to do this).
NEWCALLS.DLL contains a forwarder entry (to DOSCALLS.DLL)
for every function exept Dos32Open(). The forwarder entries
are created, by the linker (FWDSTAMP.EXE is NOT needed),
because the *.def file both IMPORTS and EXPORTS all of the
function names (except Dos32Open).
Since many of the ordinals are not referenced by name (and
are not documented), I forward them with a manufactured
name. Example:
_undoc4=DOSCALLS.4
Since the real doscalls.dll does not export a name for
ordinal 4, it should not make any difference what it is
called. The important thing is that an oridinal of 4 does
exist, in case pmmerge.dll uses it (it will use it by
ordinal number, not by name). This may seem untrustworthy,
but you have to keep in mind we are redirecting DOSCALLS
only for one specific piece of code (pmmerge.dll); and if
we get it wrong it will fail to load.
Additionally, newfwd.def IMPORTS the real Dos32Open, for
its own use, by ordinal (273), and calls it _Dos32Open().
_Dos32Open() is a private name, only seen by newcalls.dll.
_DOS32OPEN =DOSCALLS.273
Newcalls.c only has to contain code for one function,
Dos32Open(). All other functions are automatically
redirected, at load time, to the real doscalls.dll; thus
there is NO runtime performance impact, only a slight
(probably immeasurable) loadtime impact.
Why NEWCALLS.DLL?
=================
Whenever anything changes on your WPS (which can happen
simply by referencing an object), changes are made to
OS2.INI and/or OS2SYS.INI. Every so often (2 mins or so)
these changes are written to disk. Since OS/2 2.1, *.ini
files are kept completely in (swappable) memory, and are
rewritten, in their entirety, whenever updates are required
(this means the system will have to swap out memory equal to
the size of your *.ini files, swap in the memory where the
*.ini file data is, write them out, etc...this design
sucks; I liked the way os/2 1.1 through 2.0 did it better).
To further complicate things, the *.ini file data is
written out in chunks of 32k or less. My os2*.ini files are
1.2mb; that means about 40 DosWrite's.
The code for PrfOpenProfile() is in pmmerge.dll.
PrfOpenProfile() calls DosOpen with the
"OPEN_FLAGS_WRITE_THROUGH" bit on. This causes each
DosWrite() to the *.INI files to completely update all disk
structures before it returns (this is VERY slow on HPFS).
It takes a good 15-20 seconds to write my *.ini files out
(and this happens every few mins). Not only do I have an
annoyingly loud harddisk, but the performance of my PC will
sometimes crawl while the update is taking place (a priority
boost is giving to threads blocked on disk i/o).
My replacement DosOpen() masks off the
OPEN_FLAGS_WRITE_THROUGH bit, so that all operations to the
*.ini files are cached. Now my *.ini file updates are next
to instant (a second or so), and a lot quieter; file
system structures are only updated once, instead of after
each DosWrite().
I'm sure someone at IBM thought they were being "safe" by
using write-through mode. I disagree. The *.ini files are
actually written as "os2.!!!" and "os2sys.!!!", then
renamed to os2*.ini after they are on the disk. A power
failure will only cause a problem if it occurs when the
*.!!! files are renamed (regardless if you're using
newcalls.dll or not). A power failure while the ini file
data is being written to disk will cause the system to use
the older *.ini files already present. It actually makes
more sense to write the data out as fast as you can, to get
ahead of the power failure.
How to use NEWCALLS.DLL
=======================
1) Compile/link newcalls.c using IBM's compiler (the IBM
linker is key; it creates the forwarder entries from the
*.def file). Simply:
icc /Gn /O /Fenewcalls.dll newcalls.c newfwd.def os2386.lib
[see G.CMD]
2) Backup your original \os2\dll\pmmerge.dll. Run:
\ibmcpp\bin\dllrname pmmerge.dll doscalls=NewCalls /q
[see T.CMD]
(You can't use the pmmerge.dll that is active; copy it
somewhere else, modify the copy, boot to the command
line, copy new dll to \os2\dll).
This command will change all references to DOSCALLS to
NEWCALLS.DLL. This only affects pmmerge.dll. It does
not directly affect any programs that use pmmerge.dll.
Furthermore, only DosOpen (Dos32Open actually) has
replacement code -- all other entry points are forwarded
to the real doscalls. There is ZERO runtime performance
impact on forwarded calls (there is a slight LOAD time
impact, as the loader does its stuff).
3) newcalls.dll, and the modified pmmerge.dll must be on
your libpath (for example \os2\dll).
Tip: - create a new dir called \newdll.
- Place newcalls.dll, and the modified pmmerge.dll
there.
- Edit config.sys, make sure the directory \newdll
comes before \os2\dll.
- Leave your original pmmerge.dll in \os2\dll (this
will allow Service Packs to correctly upadate
your system).
- Whenever you install a fixpak (service pack),
repeat steps 2 and 3.
4) (optional). You might have noticed that newcalls.c
prints a few messages to file handle 2 (stderr). If you
want to see these, follow this procedure:
a) Edit config.sys; change "runworkplace" to
\os2\cmd.exe.
b) reboot (you will be in a PM session, with an
open command prompt window).
c) enter "pmshell 2>stderr.out". All pmshell
messages sent to stderr will go to this file, not
just messages from newcalls.dll.
d) To view the file while the shell is still
running, use "type stderr.out".
Peter Fitzsimmons,
President,
A:WARE Inc (OS/2 Contracting)
Voice: 905 858 3222
Internet: pfitz@echo-on.net
Internet: sol3@olc.gvc.com
Copyright (C) 1997, A:WARE Inc. |
www.edm2.com/common/snippets/newc2.zip | local copy |
This work is licensed under a Creative Commons Attribution 4.0 International License.
Add new comment