Help with Parsing a CSTRING from API Call

Hello Everyone,

Enumerating audio devices.

I have a group with a CSTRING as one of its fields.

The API call works fine.

However, upon sending the CSTRING to Debugview++ I don’t see the field’s value.

But if I loop through the CSTRING like:

        Loop x = 1 to SIZE(WaveOutCapsGrp.szPname)
            MyWave.Trace(WaveOutCapsGrp.szPname[x])
        END

I see the expected values.

This shouldn’t have me stumped but it does.

Unless Carriage Return is forced in DebugView options the program must add it itself to display a CSTRING.

If the first character is null, it could represent an empty value.

A CSTRING can contain other stuff after the null, but it is still considered blank.

The api you are calling is likely returning a char* (or void*) and a length. Not really a Cstring. In other words “a binary string”.

In clarion a cstring “cannot” hold binary strings because they “terminate” on the first chr(0).

For api calls like this you should use either a string (remembering to kerp track of the length) or a variable length binary string (like stringtheory).

Outputting to debugview is also problematic. Because debugview takes cstrings as input, so terminates after the first chr(0). Plus of course you wont see non-printable binary chars.

I use stringtheory PeekRam method to inspect binary values. (You can just pass in the address and length, no need to assign.)

What exact API function? MSDN will have some details about the values and types.

Paste your calling code, API Prototype and variable declarations.

If it’s regarding this structure, can you show your clarion rendition? It could be possible that there’s an alignment problem, putting a zero where it shouldn’t be.

If you can find a http://pinvoke.net/ declaration it can often be helpful in Clarion. The types are closer and constants tend to be defined

typedef struct {
  WORD      wMid;
  WORD      wPid;
  MMVERSION vDriverVersion;
  TCHAR     szPname[MAXPNAMELEN];
  DWORD     fdwSupport;
  DWORD     cDestinations;
} MIXERCAPS;

[StructLayout(LayoutKind.Sequential)]
struct MixerCaps
{
  public ushort ManufacturerID;        
  public ushort ProductId; 
  public int Version;          
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] 
  public String ProductName;
  public uint Support;
  public uint Destinations;        
  public override String ToString()
  {
 return String.Format(...);
  }
}

Is this what you’re using …

typedef struct tagWAVEOUTCAPSA {
  WORD      wMid;
  WORD      wPid;
  MMVERSION vDriverVersion;
  CHAR      szPname[MAXPNAMELEN];
  DWORD     dwFormats;
  WORD      wChannels;
  WORD      wReserved1;
  DWORD     dwSupport;
} WAVEOUTCAPSA, *PWAVEOUTCAPSA, *NPWAVEOUTCAPSA, *LPWAVEOUTCAPSA;

where MAXPNAMELEN = 32, so it’s an array of CHAR (Bytes) in which you just need to locate the ‘<0>’

Can’t you just declare either a STRING[32] or a CSTRING[32] and then just assign them thus …

Option 1 :
string_ProductName = SUB(szPname,1,INSTRING(‘<0>’,szPname,1,1))

Option 2 :
cstring_ProductName = szPname

maybe subtract one from the length to not include the null terminator ‘<0>’

string_ProductName = SUB(szPname,1,INSTRING(‘<0>’,szPname,1,1) - 1)

and then consider if there is no terminator, perhaps something like:

x  long,auto
  code
  x = instring(‘<0>’,szPname,1,1)
  case x
  of 0
    string_ProductName = szPname  ! no null terminator
  of 1 
   string_ProductName = ''
  else
   string_ProductName = szPname[1 : x-1]
  end

Yes (schoolboy error) you are correct Geoff … subtract 1 to remove the <0>

Looks like there will always be a terminator according to the docs at Jeff’s link.

1 Like

I have no problem with wave api, particulary with these structs:

tagWAVEOUTCAPSA               GROUP, TYPE
wMid                            USHORT
wPid                            USHORT
vDriverVersion                  MMVERSION
szPname                         CSTRING(MAXPNAMELEN)
dwFormats                       ULONG
wChannels                       USHORT
wReserved1                      USHORT
dwSupport                       ULONG
                              END
tagWAVEOUTCAPS2A              GROUP(tagWAVEOUTCAPSA),TYPE
ManufacturerGuid                LIKE(_GUID)
ProductGuid                     LIKE(_GUID)
NameGuid                        LIKE(_GUID)
                              END

TWaveOut.GetDevCaps           PROCEDURE(UNSIGNED pDeviceID, *typWAVEOUTCAPS pwoc)
woc2                            LIKE(tagWAVEOUTCAPS2A)
  CODE
  CLEAR(pwoc)
  SELF.nret = winmm::waveOutGetDevCapsA(pDeviceID, ADDRESS(woc2), SIZE(tagWAVEOUTCAPS2A))
  IF SELF.nret = MMSYSERR_NOERROR
    pwoc.vDriverVersion = woc2.vDriverVersion
    pwoc.szPname = woc2.szPname
    pwoc.dwFormats = woc2.dwFormats
    pwoc.wChannels = woc2.wChannels
    pwoc.dwSupport = woc2.dwSupport
    RETURN TRUE
  ELSE
    printd('waveOutGetDevCapsA(%i) error: %s [%i].', pDeviceID, SELF.GetLastError(), SELF.nret)
    RETURN FALSE
  END