Handling two bytes as a double-byte character

┌──────────────────────────────────────────────────────────────────────┐
│A double-byte character always needs to be handled by a two-byte unit.│
└──────────────────────────────────────────────────────────────────────┘

A double-byte character always needs to be handled by a two-byte unit. You should not bisect a double-byte character and should not mistake the second byte of a double-byte character for a single-byte character, because the second byte of a double-byte character can take any hexadecimal value except 00H. If a double-byte character is bisected, you are not able to tell whether it is a single-byte character or a part of a double-byte character. You need to take this rule into consideration when you program routines for truncation, substring, insertion, substitution and comparison.

We show embodiment of this rule, using a routine to compare two strings of our example program in Comparing two strings (source: CUSTDLG.C). szText[] is a target string to be compared in the example. szKey[] is a keyword string to find. To make sure double-byte characters of szText[] are treated by a double-byte unit, the example always starts comparing two strings, szText[] and szKey[], from a character-boundary. The pointer to show a position at which the comparison begins points either a single-byte character or the first byte of a double-byte character. That is, it always starts comparing two strings at either a single-byte character or the first byte of a DBCS character. It never does so at a second-byte of a DBCS character. In turn, the first byte or the second byte of a DBCS character of szText[] is never mistaken for a SBCS character.

Comparingtwostrings( source :CUSTDLG . C )
...

/**********************************************************************/
/*    Compare a customer name with a given keyword using a simple     */
/*    string-matching algorithm.                                      */
/**********************************************************************/
SHORT MatchCustName(CHAR szText[], CHAR szKey[]) {

  #define DBCS_PLUS  2
  #define SBCS_PLUS  1
  USHORT  sMoveStart, i, j;

  if (isDBCS1st(szText[0]) == DBCS_1ST)  sMoveStart = DBCS_PLUS;
  else                                   sMoveStart = SBCS_PLUS;
  i=j=0;
  while((szText[i] != '\0') && (szKey[j] != '\0')) {
    if (szText[i] == szKey[j]) {
      i++; j++;
    } else {
      /*------------------------------------------------------------*/
      /*   If a value of the i-th byte of the target string is not  */
      /*   the same as a value of the j-th byte of the keyword,     */
      /*   move the pointer of the target string by 1 byte or       */
      /*   2 byte unit (sMoveStart) so that it points the next      */
      /*   character.                                               */
      /*------------------------------------------------------------*/
      i = i - j + sMoveStart;  j=0;
      if (isDBCS1st(szText[i]) ==  DBCS_1ST)  sMoveStart = DBCS_PLUS;
      else                                    sMoveStart = SBCS_PLUS;
    }
  }
  if (szKey[j] == '\0') return MATCH;

  return UN_MATCH;
}
┌──────────────────────────────────────────────────────────────────────┐
│Using WinNextChar, WinPrevChar and WinDrawText APIs, you can handle   │
│DBCS properly without large effort.                                   │
└──────────────────────────────────────────────────────────────────────┘

The easiest way to achieve proper DBCS string manipulation is to use system-supplied APIs,WinNextChar and WinPrevChar Those APIs are DBCS-enabled and have the ability to handle DBCS automatically. Using the APIs, You can find a character in a string regardless of SBCS or DBCS.

WinDrawText is also a convenient API for drawing DBCS at multiple lines. When DT_WORDBREAK is set as ON, WinDrawText breaks a DBCS string at a proper position so that DBCS is handled by a two-byte unit. It doesn't yield any invalid characters on displaying a string, which may be composed of single-byte and double-byte characters, at multiple lines.
Drawing DBCS (source: MISCWIN)

   ...
    static HWND hwndMenu;
    static HPS hps;
    static RECTL rcl;

    LONG lStrLength,
         lTtlDrawn,
         lDrawn,
         lYcoord;
    ULONG stringID[NUM_EXMPL] = {IDS_TEXT1, IDS_TEXT2, IDS_TEXT3,
                                 IDS_TEXT4, IDS_TEXT5};
    CHAR szExmplNo[NUM_EXMPL][10] = {"Example 1", "Example 2", "Example 3",
                                     "Example 4", "Example 5"};
    static CHAR szExmpl[NUM_EXMPL][256];
    static LONG lCharWidth,
                lCharHeight;
    static BOOL flWBreak = FALSE;

    USHORT i;

   ...

          case ID_BREAKON:    /* On */
            /* Alternate ON-OFF status of flWBreak */
            flWBreak = (BOOL)(flWBreak == DT_WORDBREAK) ?
                       (USHORT)0 : DT_WORDBREAK;

   ...

              lStrLength = (LONG)strlen(szExmpl[i]);

              /* WinDrawText until all the characters in the string are
                 drawn.  (If flWBreak = OFF, the following for-loop is
                 executed only once) */
              for (lTtlDrawn = 0; lTtlDrawn != lStrLength;
                                                     rcl.yTop -= lCharHeight){
                lDrawn = WinDrawText(hps, lStrLength - lTtlDrawn,
                              szExmpl[i] + lTtlDrawn, &rcl, 0L, 0L,
                              flWBreak | DT_TOP | DT_LEFT | DT_TEXTATTRS);
                if (lDrawn)
                  lTtlDrawn += lDrawn;
                else
                  break;
              }
   ...


[Back: Processing DBCS strings]
[Next: Preparing for DBCS output]