Skip to main content

Problem:

Following is an example of how to Play a WAV file under 32-bit windows using the Win 32 API functions and PlaySound (old api) and PlaySound (Current API).

The PlaySound API is the recommend way, and the information below relates only to the play sound demo. The zip file constains information about the sndPlaySound demo.

Resolution:

BUILD W/RELEASE:     Net Express 4.0, All04n40.exe and 3.1, SP1 FixPacks

============

Two Net Express project files, "PlaySound.APP" and "PlaySound.OLD" are present in the project directory. The "PlaySound.APP" file is a Net Express 4.0 project file. The "PlaySound.OLD" file is a Net Express 3.1, SP1 project file. If you are working with Net Express 3.1 rename the 4.0 project file ("PlaySound.V40") and change the file extension of the 3.1 project file from "OLD" to "APP".

INTRODUCTION

==========

The "PlaySound" example uses the Win32 "PlaySound" API to play a ".Wav" file using the "Winmm.dll" file. This is the recommended API for playing ".Wav" files when using 32 bit Windows operating systems.

SOURCE FILES:

=============

Program Files      Description

-----------------     -----------------------------------------------------------

PlaySound.cbl     Example program that uses the Win32 "PlaySound" API to play a ".Wav" file

                           using the "Winmm.dll" file.

REQUIREMENTS:

==========

You must "Rebuild All" the Debug Build before attempting Animation of the example program.

You must "Rebuild All" the Release Build before attempting execution of the example program. The Release Build is a "Shared", "Dynamic", "Single-threaded", "Character" application. It should execute on your development system from a Windows or Net Express Command Prompt.

OPERATION:

========

How does this example program work?

The example consists of a single program, "PlaySound.cbl". What follows addresses details that exist in the "PlaySound" program.

Windows was created using the 'C' programming language. The Win32 SDK documentation is intended for a 'C' programmer. To hurdle this language barrier we need to discuss several issues that are fundamental to your success when using Win32 API functions.

The 'C' programming language is case sensitive. Words that are similar to COBOL verbs and data item usages are all lower case "keywords". Capitalization is used to distinguish typedef names, constant names, structure tags, structure names, and error names from regular data names, function names and standard "keywords". It is common for data names and function names to be mixed case. These mixed case names achieve the same effect as hyphenated COBOL names (PHONE-NUMBER, PhoneNumber). Because function names are mixed case, you must match exactly the case of the letters shown in the function definition when you code your call literal. Also, 32 bit Windows operating systems support both ANSI character strings and double byte character strings. You must add a suffix of "A" (for ANSI) or "W" (for double byte) to the end of your call literal when the function processes one or more character strings. Character strings are often embedded within 'C' structures (which are similar to COBOL record definitions) and the suffix is required to ensure these embedded character strings are processed correctly.

In 'C' the default for passing parameters between programs is "by value". COBOL's default is to pass parameters "by reference". Both languages can pass parameters with either technique. Passing "by value" actually passes a copy of a numeric item's value to another program. A

pointer is a numeric item. In 'C', passing a pointer "by value" is equivalent to passing a data item "by reference" in COBOL. A pointer is passed to the called program which points to where the data is located in the calling program. The difference is that in COBOL the compiler sets up the pointer and initializes it for the programmer, but in 'C' the programmer must declare and initialize the pointer.

This means that you will use "by value" whenever you must pass a numeric item to a Win32 API. In the Win32 SDK, when a structure or character string must be passed to an API function, a pointer name will appear in the syntax definition prefixed with "lp" (ie lpFilename). This prefix is short for "long pointer". Coding these prefixes is a convention, not a langauge requirement. Declaring the pointer and initializing it to an address is up to the programmer. Here's a COBOL

example:

Plan A  

     working-storage section.

     77  Filename pic x(512).

     77  lpFilename usage pointer.

     procedure division.

           move z'C:\\MYDATA\\LATEST\\GOODDATA.DAT' to Filename

           set lpFilename to address of Filename

           call winapi 'WindowsFunction'

                 using by value lpFilename

Plan B

     working-storage section

     77  Filename pic x(512).

     procedure division.

           move z'C:\\MYDATA\\LATEST\\GOODDATA.DAT' to Filename

           call winapi 'WindowsFunction'

                    using by reference Filename

Both solutions will work. Plan B appears simpler. Plan A, however, is more flexible. In Plan B, creation and initialization of the pointer are hard coded into the call statement. If you need to make multiple calls to an API with different parameters, you'll need multiple call statements. In Plan A you can reset the pointer to a different parameter definition and reuse the call statement.

Let's look at the specific details that make the PlaySound program work.

This example uses call convention 66.

       special-names.    call-convention 66 is cc66.

Call convention "66" supports dynamic DLL access. It works with all code formats (*.Int, *.Gnt and *.Obj). Call convention 74 is an alternative that works only with object code. You provide an import library name in your program's Build Settings so the Link program can resolve the reference in your program to the API function. You can determine the import library name by clicking the "Quick Info" button on the page that documents the API function. The DLL and library file names are the same. The import library name for the PlaySound API is "winmm.lib". The DLL name is "winmm.dll".

You use a "set procedure-pointer-item to entry non-numeric-literal" statement to load or access the "winmm.dll" file where the PlaySound API is located.

     77  winmmPtr                             procedure-pointer.

     procedure division.

          set winmmPtr to entry 'winmm'

You can test the procedure-pointer-item for a null value to determine if the set statement has been successful. When the item is not null the DLL has been located in memory or loaded from your hard drive.

          if winmmPtr = NULL

               display 'Unable to load winmm.dll.'

               stop run

          end-if

The PlaySound API has three parameters and a return value. In the SDK documentation the function is defined as follows:

     BOOL PlaySound(

          LPCSTR pszSound,

          HMODULE hmod,

          DWORD fdwSound

     );

"BOOL" is a typedef for the value returned by the PlaySound API. Return values are 32 bit numeric values which can be defined with "pic xxxx comp-5". If you have the Win32 SDK installed, you can search the 'C' header files (*.h) located in drive:\\MSTOOLS\\include looking for a match to "BOOL;". Several files may match, "Wtypes.h" is the most likely candidate. Open the file in Notepad and search for "BOOL;". You should locate this 'C' typedef:

     typedef long BOOL;

A "long" is a 32 bit numeric item.

          LPCSTR lpszSound,

"LPCSTR" is another typedef that can be translated into "long pointer character string". Note also that the parameter name "pszSound" has a prefix "psz" that can be translated into "pointer string null terminated". The "p" can be dropped when defining the data variable, but the "sz" is worth retaining to remind us that the character string is null terminated.

          HMODULE hmod,

"HMODULE" is a typedef for a "handle" which is another 32 bit numeric value that can be defined with "pic xxxx comp-5". We'll define this item and assign it a null value because of this note in the

documentation:

     hmod

          Handle of the executable file that contains the resource to be loaded. This parameter must

          be NULL unless SND_RESOURCE is specified in fdwSound.

This "handle" would be for an executable file (*.EXE or *.DLL) where the sound file has been linked into the executable. We're not going to be working with such a resource and we'll not be using the SND_RESOURCE flag.

          DWORD fdwSound

"DWORD", another typedef, is an abbreiation for "double word" which can be defined with "pic xxxx comp-5". The "fdwSound" parameter name also has a "fdw" designation which can be interpreted to mean "flag double word". This prefix is retained when the item is defined to serve as a reminder.

I've included a subset of the flag definitions that are documented in the SDK. You can find them by searching your Win32 SDK 'C' header files (*.h) located in drive:\\MSTOOLS\\include looking for "SND_SYNC". Only one file, MMSYSTEM.h, will provide a match.

     #define SND_SYNC  0x0000  /* play synchronously (default) */

The other constants will be nearby.

Here are the constants for the flags converted to COBOL:

     78  SND-ASYNC                               value  1.

     78  SND-LOOP                                value  8.

     78  SND-MEMORY                              value  4.

     78  SND-NODEFAULT                           value  2.

     78  SND-NOSTOP                              value 16.

     78  SND-SYNC                                value  0.

And here is how fdwSound gets initialized:

          move SND-SYNC to fdwSound

This flag setting causes PlaySound to play the specified sound synchronously. In other words, it plays the entire sound and then returns to the calling program.

Below are the COBOL parameter definitions the PlaySound API requires and the call statement needed to access the function. Note that an "A" has been added to the end of the function name to handle the character string that we are passing to the API. Note that hMod and fdwSound are passed "by value". Note also that szSound is passed "by reference". This leaves initializing a pointer and passing it "by value" up to the COBOL compiler.

     77  APIReturn       pic xxxx    comp-5.

     77  fdwSound        pic xxxx    comp-5.

     77  hMod            pic xxxx    comp-5      value h'00'.

     77  szSound         pic x(11)               value z'chimes.wav'.

     77  winmmPtr                    procedure-pointer.

          call Win32 'PlaySoundA' using

                         by reference szSound

                         by value     hMod

                         by value     fdwSound

               returning    APIReturn    end-call

These notes regarding return values are worth reading:

     Return Values

          Returns TRUE if successful or FALSE otherwise.

In COBOL a return value of zero is considered "OK" or good. A non-zero return value is considered "Not OK" or bad and the numeric value is usually an error number.

In 'C" a return value of zero is considered "Not OK" or bad. In Windows you have to call another API, "GetLastError" to determine what has gone wrong. A non-zero return value is considered "OK" or good and the numeric value is usually a "1", a "-1", a pointer value or a handle.

The "Remarks" for this API are also of interest:

     Remarks

          The sound specified by pszSound must fit into available physical memory and be playable by

          an installed waveform-audio device driver. PlaySound searches the following directories for

          sound  files: the current directory; the Windows directory; the Windows system directory;

          directories listed in the PATH environment variable; and the list of directories mapped in a

          network. For more information about the directory search order, see the documentation for

          the OpenFile function If it cannot find the specified sound, PlaySound uses the default

          system event sound entry instead. If the function can find neither the system default entry

          nor the default sound, it makes no sound and returns FALSE.

This completes the description of how the example program works.

==========================================================

Keywords: demonstration, sample, example, demo, win32 api, play32.zip

demo.ex

demo.ne

Attachments:

play32.zip

Old KB# 4185