Getting the Volume Serial Number using Borland C++ 4.5 (16-Bit)

I am trying to write a dll in C++ that will return the Volume Serial Number of any drive (or at least Drive C:).  I am not sure exactly how to do this.  I'm sure there is a pretty easy way, but I am new at C and have very little time to learn it.  Could someone send me source for a simple 16-bit DLL that will return the Volume Serial Number?
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Could you tell us some information concerning your environment? (Win 3.1x, Win95/NT). Do you absolutly need a 16-bit dll?
ehilderAuthor Commented:
The application that will use the DLL is designed in Visual D-Base.  The application will be run on all platforms of Windows. (3.1x, Win95 and WinNt)  Since it is possible that the application will be run on Windows 3.1 without Win32s, I would prefer a 16-bit DLL.  If this is absolutely impossible, I could recommend that the application be run only in 32-bit mode.
The following function will return you the volume name and serial number of any drive.

static      void      Get_Volume_Info(int Disk, char *Vol, char *SSN)
  unsigned      char buf[1024];
  int           i;

  strcpy(Vol, "");
  strcpy(SSN, "");

  i = biosdisk(2, Disk, Disk >= 0x80 ? 1 : 0, 0, 1, 1, &buf);
  if(i == 0)
#pragma warn -ucp
    strncpy(Vol, buf+0x2B, 11);
    Vol[11] = '\0';
    sprintf(SSN, "%02X%02X-%02X%02X", buf[0x2a], buf[0x29],       buf[0x28], buf[0x27]);
#pragma warn +ucp

Exploring SQL Server 2016: Fundamentals

Learn the fundamentals of Microsoft SQL Server, a relational database management system that stores and retrieves data when requested by other software applications.

ehilderAuthor Commented:
I know that I am new to C, but I was just wondering...  How exactly do you use a DOS ONLY function in a Windows DLL?
I answered a similar question in Microsoft Windows Programming Topic Area. (4/14/97  10 Accessing HDD serial # <PAQ>)

For 32-bit:
The Win32 function GetVolumeInformation() returns this info.

                   BOOL GetVolumeInformation(
                       LPCTSTR lpRootPathName, // address of root directory of the file system
                       LPTSTR lpVolumeNameBuffer, // address of name of the volume
                       DWORD nVolumeNameSize, // length of lpVolumeNameBuffer
                       LPDWORD lpVolumeSerialNumber, // address of volume serial number
                       LPDWORD lpMaximumComponentLength, // address of system's maximum filename
                       LPDWORD lpFileSystemFlags, // address of file system flags
                       LPTSTR lpFileSystemNameBuffer, // address of name of file system
                       DWORD nFileSystemNameSize // length of lpFileSystemNameBuffer

For 16-bit:
I copied an article from Visaul C++ KBase here.

                  Retrieving a Disk Volume Serial Number from C

                  PSS ID Number: Q69223
                  Article last modified on 01-24-1995
                  5.10 6.00 6.00a 6.00ax 7.00 | 1.00 1.50
                  MS-DOS | WINDOWS

                  The information in this article applies to:
                   - The C Run-time (CRT) included with:
                      - Microsoft C for MS-DOS, versions 5.1, 6.0, 6.0a, and 6.0ax
                      - Microsoft C/C++ for MS-DOS, version 7.0
                      - Microsoft Visual C++ for Windows, versions 1.0 and 1.5
                  Beginning with MS-DOS version 4.0, a semi-random 32-bit binary
                  identification number (ID) is assigned to each disk that MS-DOS
                  formats. The volume serial number (or ID) is stored at offset 27H to
                  2AH in the boot sector of each disk.
                  NOTE: code compiled with Visual C++ version 1.5 may yield the following
                  message from Windows NT:
                     An applicaion has attempted to directly access the hard disk, which
                     cannot be supported. This may cause the application to function
                  It provides Terminate and Ignore buttons. If the user is logged on with
                  Administrative privileges, this will succeed for a FAT partition, else it
                  fails for a FAT partition. It always fails for an NTFS partition. After
                  clicking Terminate or Ignore the program returns with the error message
                  that has been coded (error on int 25).
                  MORE INFORMATION
                  The following program illustrates how to retrieve this information:
                  /* */
                  /* This program reads the volume serial number (or ID) from */
                  /* the boot sector of a specified disk. The DOS interrupt 25 */
                  /* Absolute Disk Read is used to read in the boot sector. */
                  /* */
                  /* Note: The volume ID is only implemented from MS-DOS 4.0 */
                  /* and later. */
                  /* */
                  /* The output consists of the OEM name and version of the */
                  /* disk-formatting program (stored at offset 03H to 0AH in the */
                  /* boot sector), the disk volume label, and the disk-volume */
                  /* serial number. */
                  /* */
                  #include <stdio.h>
                  #include <stdlib.h>
                  #include <string.h>
                  #include <dos.h>
                  #include <conio.h>
                  char bootsector[1024];
                  char volume[12];
                  char ver[9];
                  char block[10];
                  void main(void)
                     int ax, _far *p, drive;
                     struct find_t fileinfo;
                     char filename[13], _far *myvar, _far *q;
                     union REGS inregs, outregs;
                     struct SREGS segregs;
                     printf("Enter drive number (0=A,1=B,2=C, ...): ");
                     drive = getche() - '0';
                     /* Parameter block for int 25H */
                     /* Bytes Description */
                     /* ------- ----------- */
                     /* 00H-03H 32-bit sector number */
                     /* 04H-05H Number of sectors to read */
                     /* 06H-07H Offset of buffer */
                     /* 08H-09H Segment of buffer */
                     block[0] = block[1] = block[2] = block[3] = 0;
                     block[4] = 1;
                     block[5] = 0;
                     myvar = bootsector;
                     p = (int *)&block[6];
                     *p = FP_OFF(myvar);
                     p = (int *)&block[8];
                     *p = FP_SEG(myvar);
                     q = block;
            = (char)drive;
            = -1;
                     inregs.x.bx = FP_OFF(q);
                     segregs.ds = FP_SEG(q);
                     ax = int86x(0x25, &inregs, &outregs, &segregs);
                     /*** Error routine ***/
                     if (outregs.x.cflag)
                      printf("\n\nerror on int 25\n");
                      printf("this is AX:%04X", ax);
                     /*** Output ***/
                     printf("\n\nDrive %c\n-------\n\n", drive +'A');
                     strncpy(ver, &bootsector[3], 8);
                     printf("OEM name and version: %s\n", ver);
                     /* Use _dos_findfirst for the volume label */
                     filename[0] = (char)(drive + 'A');
                     filename[1] = '\0';
                     strcat(filename, ":\\*.*");
                     if(!_dos_findfirst(filename, _A_VOLID, &fileinfo))
                     printf("Volume name : %s\n",;
                     /* Before printing serial number, check if version >= 4.x */
                     if ((ver[6] == '.') && (ver[5] >= '4') && (ver[5] <= '9'))
                      printf("Serial number : %02X%02X-%02X%02X\n\n",
                      (unsigned char) bootsector[0x2a],
                      (unsigned char) bootsector[0x29],
                      (unsigned char) bootsector[0x28],
                      (unsigned char) bootsector[0x27]);
                  Additional reference words: kbinf 5.10 6.00 6.00a 6.00ax 7.00 1.00 1.50
                  KBCategory: kbprg
                  KBSubcategory: CRTIss
                  Copyright Microsoft Corporation 1995.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ehilderAuthor Commented:
The 16-Bit example works great, but I do need a little more information.  I am increasing the points to 150.

I am new to C++ and I was wondering if you could post an example using the GetVolumeInformation() function in Win32.  Also, is there a way to tell if Windows is running 16-bit or 32-bit.  
The reasoning is that this DLL needs to run in 16-bit and 32-bit and on Windows NT.

If I can tell that Windows is running in 32-bit, I can branch from the 16-bit routine to the 32-bit routine.  Even if they are in two DLL's.  Can you test for 32-bit mode in a 16-bit DLL???  If so, this is how I would like to work it.

Does this make sense?
The following is the example using GetVolumeInformation.

CHAR szVolumeName[MAX_PATH], szFSName[MAX_PATH];
      DWORD dwVolSerialNum,
      ::GetVolumeInformation("c:\\",  // "d:\\" for d:, NULL for current directory

CHAR szBuf[256];
                   "szVolumeName = %s\n"
                   "dwVolSerialNum = %lX\n"
                   "dwMaxCompLen = %lu\n"
                     "dwFSFlags = %lu\n"
                     "szFSName = %s\n",
      ::MessageBox(NULL, szBuf, "GetVolumeInformation", MB_OK);

If your program is 32-bit, it must be running on a 32-bit platform (including Win32s on Windows 3.1, you can check it with GetVersionEx function). A 16-bit platform cannot run a 32-bit program.

If your program is 16-bit, it may be running on a 16-bit platform or a 32-bit platform. You can check the version with GetVersion function in a 16-bit program. For Windows 3.1, it returns version 3.1. For Windows 95, it returns version 3.95. I have not tried it on Windows NT. However, you cannot call a 32-bit DLL from a 16-bit program or DLL although you can run a 32-bit EXE from a 16-bit program or DLL.

So, in your case, you might need to provide two version: one is 16-bit, another is 32-bit.

Sorry, I think I made a mistake. I said "you cannot call a 32-bit DLL from a 16-bit program or DLL". In fact, there is a way to do so. Generic Thunks allow a 16-bit Windows-based application to load and call a Win32-based DLL on Windows NT and Windows 95. See the following Win32 SDK documentation:
Win32 SDK/Programming Guides and Tools/Programming Techniques/Generic Thunks.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.