UCONVERT.C

/**************************************************************************\ 
* uconvert.c -- convert to/from unicode using
* MulitByteToWideChar & WideCharToMulitByte
*
* Steve Firebaugh
* Microsoft Developer Support
* Copyright 1992 - 1998 Microsoft Corporation
*
\**************************************************************************/
#define UNICODE

#include <windows.h>
#include <commdlg.h>
#include "uconvert.h"
#include "install.h"






/**************************************************************************\
* Global variables.
\**************************************************************************/

HANDLE hInst;

/* declare global HWNDs for the child windows.
* They are created at WM_CREATE of the main window.
* Also used in the "View" dialogs.
*/
HWND hwndLabel0, hwndLabel1;
HWND hwndName0, hwndName1;
HWND hwndSize0, hwndSize1;
HWND hwndCodePage0, hwndCodePage1;
HWND hwndByteOrder0, hwndByteOrder1;
HWND hwndButton0, hwndButton1;

/* Global variables storing the source and destination "type" information.
*
* used to communicate between main wnd proc, and *OptionsProc.
*
* gTypeSource - stores the type interpretation of the source data
* (and implicitly the destination data.)
* TYPEUNKNOWN: indeterminant... not set. Can not do conversion.
* TYPEUNICODE: source unicode & destination giDestinationCodePage.
* TYPECODEPAGE: source giSourceCodePage & destination unicode.
*
* giSourceCodePage stores valid source code page iff gTypeSource == TRUE
* giDestinationCodePage stores valid destination code page iff gTypeSource == FALSE
*
*/
int gTypeSource;
UINT giSourceCodePage;
UINT giDestinationCodePage;

/* Pointers to the source and destination data, and the
* count of bytes in each of the buffers.
*/
#define NODATA 0
PBYTE pSourceData = NULL;
PBYTE pDestinationData = NULL;
int nBytesSource = NODATA;
int nBytesDestination = NODATA;

/* Conversion Options variables. */
DWORD gMBFlags = MB_PRECOMPOSED;
DWORD gWCFlags = 0;

char glpDefaultChar[4] = "?";
BOOL gUsedDefaultChar = FALSE;

/* Handling the Byte Order Mark (BOM).
*
* If the input file begins with a BOM, then we know it is unicode,
* we skip over the BOM and decrement the size of data by SIZEOFBOM.
*
*
* Before writing data that we know is unicode, write the szBOM string
* to the file.
*
* Notice that this means that the file sizes we show in the window
* do NOT include the BOM.
*/

char szBOM[] = "\377\376"; // 0xFF, 0xFE // leave off TEXT() macro.
char szRBOM[] = "\376\377"; // 0xFF, 0xFE // leave off TEXT() macro.
#define SIZEOFBOM 2

/* Title of main window */
TCHAR TitleMBToWC[]= TEXT("UConvert -- MultiByteToWideChar()");
TCHAR TitleWCToMB[]= TEXT("UConvert -- WideCharToMultiByte()");
TCHAR TitleUnknown[]= TEXT("UConvert.");

/* file name of the online help file */
TCHAR szHelpPathName[] = TEXT("uconvert.HLP");

/* Strings used to fill onscreen windows. */
TCHAR szBlank[] = TEXT("");

/* MessageBox() strings and flags. */
TCHAR MBTitle[30]= TEXT("");
UINT MBFlags = MB_OK | MB_ICONEXCLAMATION;


/* misc. defines affecting size and placement of child windows */
#define BORDER GetSystemMetrics (SM_CXFRAME)*4
#define WHEIGHT GetSystemMetrics (SM_CYMENU)



/**************************************************************************\
*
* function: WinMain()
*
*
\**************************************************************************/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wc;
HWND hwndMain;
HACCEL haccel;


UNREFERENCED_PARAMETER( lpCmdLine );
UNREFERENCED_PARAMETER( nCmdShow );
hInst = hInstance;


/* Check for previous instance. If none, then register class. */
if (!hPrevInstance) {

wc.style = 0;
wc.lpfnWndProc = (WNDPROC)MainWndProc;

wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, TEXT("uconvertIcon"));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject (LTGRAY_BRUSH);
wc.lpszMenuName = TEXT("uconvertMenu");
wc.lpszClassName = TEXT("uconvert");

if (!RegisterClass(&wc)) return (FALSE);

} /* class registered o.k. */


/* Create the main window. Return false if CreateWindow() fails */
hwndMain = CreateWindow(
TEXT("uconvert"),
TitleUnknown,
(WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX)) | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
512, // Big enough for most of the text.
16*WHEIGHT,
NULL, NULL, hInst, NULL);

if (!hwndMain) return (FALSE);


/* Load the accelerator table that provides clipboard support. */
haccel = LoadAccelerators (hInst, TEXT("uconvertAccel"));

LoadString(hInst,IDS_APP_WARNING,MBTitle,sizeof(MBTitle));

/* Loop getting messages and dispatching them. */
while (GetMessage(&msg, NULL, 0,0)) {
if (!TranslateAccelerator(hwndMain, haccel, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return (msg.wParam);
}




/**************************************************************************\
*
* function: MainWndProc()
*
*
* On WM_CREATE create all of the child windows.
* On WM_DESTROY make sure that all dynamically allocated memory is freed.
* On WM_PAINT, outline many of the child windows.
* On WM_COMMAND, respond to the command messages properly.
*
\**************************************************************************/
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{


/* misc. variables used in multiple messages cases. */
RECT clientrect;
RECT rect;
TCHAR buffer[50];
static TCHAR szFilter[MAX_PATH];

switch (message) {

/**********************************************************************\
* WM_CREATE
*
* Create all of the child windows used on this main window.
* Assign the HWNDs to the correct static variables.
*
\**********************************************************************/
case WM_CREATE: {
GetClientRect (hwnd, &clientrect);

/* Create Source Windows. */
CopyRect (&rect, &clientrect);
rect.right = (clientrect.right - clientrect.left) /2;
InflateRect (&rect, -2*BORDER, -BORDER);
createwindows(&rect,
hwnd,
&hwndLabel0,
&hwndName0,
&hwndSize0,
&hwndCodePage0,
&hwndByteOrder0,
&hwndButton0);

/* Create Destination Windows. */
CopyRect (&rect, &clientrect);
rect.left = (clientrect.right - clientrect.left) /2;
InflateRect (&rect, -2*BORDER, -BORDER);
createwindows(&rect,
hwnd,
&hwndLabel1,
&hwndName1,
&hwndSize1,
&hwndCodePage1,
&hwndByteOrder1,
&hwndButton1);

/* fill in window information that is different for source/destination */
SetWindowText (hwndLabel0, LoadResourceString(IDS_SOURCE));
SetWindowText (hwndLabel1, LoadResourceString(IDS_DESTINATION));

SetWindowText (hwndButton0, LoadResourceString(IDS_VIEW_SOURCE_BTN));
SetWindowText (hwndButton1, LoadResourceString(IDS_VIEW_DESTINATION_BTN));

SetWindowLong (hwndButton0, GWL_ID, BID_VIEWSOURCE );
SetWindowLong (hwndButton1, GWL_ID, BID_VIEWDESTINATION );

gTypeSource = TYPEUNKNOWN;
giSourceCodePage = GetACP(); // Just some reasonable initializer.
giDestinationCodePage = GetACP(); // Just some reasonable initializer.

/* initialize source & destination data correctly */
SendMessage (hwnd, WM_COMMAND, MID_CLEARSOURCE, 0);
SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);

/* Build up the correct filter strings for OPENFILENAME structure
* Do it here so that we only have to do it once.
*/
{
TCHAR *p;

p = szFilter;
lstrcpy (buffer,LoadResourceString(IDS_FILE_FILTER_SPEC1));
lstrcpy (p,buffer);
p += lstrlen (buffer) +1;
lstrcpy (buffer,TEXT("*.*"));
lstrcpy (p,buffer);
p += lstrlen (buffer) +1;

lstrcpy (buffer,LoadResourceString(IDS_FILE_FILTER_SPEC2));
lstrcpy (p,buffer);
p += lstrlen (buffer) +1;
lstrcpy (buffer,TEXT("*.txt"));
lstrcpy (p,buffer);
p += lstrlen (buffer) +1;

lstrcpy (buffer,LoadResourceString(IDS_FILE_FILTER_SPEC3));
lstrcpy (p,buffer);
p += lstrlen (buffer) +1;
lstrcpy (buffer,TEXT("*.utf"));
lstrcpy (p,buffer);
p += lstrlen (buffer) +1;

lstrcpy (p,TEXT("\0"));
}
} break; /* end WM_CREATE */



/**********************************************************************\
* WM_DESTROY
*
* Release the Online help, and free allocated memory if any.
\**********************************************************************/
case WM_DESTROY:
WinHelp( hwnd, szHelpPathName, (UINT) HELP_QUIT, (DWORD) NULL );
ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
ManageMemory (MMFREE, MMDESTINATION, 0, pDestinationData);
PostQuitMessage(0);
break;


/**********************************************************************\
* WM_CTLCOLOR*
*
* Set the background of the child controls to be gray here.
\**********************************************************************/
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC: {
HDC hdc;

hdc = (HDC) wParam;
SetBkMode (hdc, TRANSPARENT);
return (LRESULT)GetStockObject (LTGRAY_BRUSH);
} break;




/**********************************************************************\
* WM_PAINT
*
* Simply draw the two vertical divider lines, and 3D frame the children.
*
\**********************************************************************/
case WM_PAINT: {
HDC hdc;
PAINTSTRUCT ps;

hdc = BeginPaint(hwnd, &ps);
GetClientRect (hwnd, &clientrect);

/* draw vertical separator line in the center */
rect.left = (clientrect.right - clientrect.left ) /2 -1;
rect.top = clientrect.top;
rect.right = rect.left +1;;
rect.bottom = clientrect.bottom;
FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
SelectObject (hdc, GetStockObject (WHITE_PEN));
MoveToEx (hdc, rect.right, rect.top, NULL);
LineTo (hdc,rect.right, rect.bottom);

/* draw 3D outlines of child windows. */
framechildwindow (hdc, hwnd, hwndName0);
framechildwindow (hdc, hwnd, hwndSize0);
framechildwindow (hdc, hwnd, hwndCodePage0);
framechildwindow (hdc, hwnd, hwndByteOrder0);

framechildwindow (hdc, hwnd, hwndName1);
framechildwindow (hdc, hwnd, hwndSize1);
framechildwindow (hdc, hwnd, hwndCodePage1);
framechildwindow (hdc, hwnd, hwndByteOrder1);

/* underline the labels */
underlinechildwindow (hdc, hwnd, hwndLabel0);
underlinechildwindow (hdc, hwnd, hwndLabel1);

EndPaint (hwnd, &ps);
} break; /* end WM_PAINT */




/**********************************************************************\
* WMU_ADJUSTFORNEWSOURCE
*
* lParam - szName of source (file, clipboard, ...)
*
* global - nBytesSource
*
* "user message." Set the text of the Source windows
\**********************************************************************/
case WMU_ADJUSTFORNEWSOURCE: {
LPVOID szName;

szName = (LPVOID) lParam;

/* Set Window text appropriately */
SetWindowText (hwndName0, szName);
wsprintf (buffer, LoadResourceString(IDS_BYTES), nBytesSource);
SetWindowText (hwndSize0, buffer);
SetWindowText (hwndByteOrder0, szBlank);

/* Clear the destination data if any to avoid user confusion. */
SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);

/* Reset the "type strings" based on new gTypeSource. */
SendMessage (hwnd, WMU_SETTYPESTRINGS, 0,0);
} break;




/**********************************************************************\
* WMU_SETTYPESTRINGS
*
* "user message." Set the text of the "type" windows to reflect
* the state stored in gTypeSource and gi*CodePage.
*
\**********************************************************************/
case WMU_SETTYPESTRINGS:
switch (gTypeSource) {
case TYPEUNICODE:
SetWindowText (hwndCodePage0, TEXT("Unicode"));
wsprintf (buffer, LoadResourceString(IDS_CODE_PAGE),
giDestinationCodePage);
SetWindowText (hwndCodePage1, buffer);
SetWindowText (hwnd, TitleWCToMB);
break;
case TYPECODEPAGE:
wsprintf (buffer, LoadResourceString(IDS_CODE_PAGE),
giSourceCodePage);
SetWindowText (hwndCodePage0, buffer);
SetWindowText (hwndCodePage1, TEXT("Unicode"));
SetWindowText (hwnd, TitleMBToWC);
break;
case TYPEUNKNOWN:
SetWindowText (hwndCodePage0, szBlank);
SetWindowText (hwndCodePage1, szBlank);
SetWindowText (hwnd, TitleUnknown);
break;
} /* end switch gTypeSource */
break;


/**********************************************************************\
* WM_INITMENU
*
* Manage the enabled state of all of the menus.
* Notice that the button enabled state is taken care of in ManageMemory().
*
* In general, this is dependent upon pSourceData & pDestinationData.
* They are either NULL or non-NULL, and menu items are dependent upon
* this state.
*
* One exception is the "Paste from Clipboard menu" which is enabled
* conditional upon there being text data in the clipboard.
*
\**********************************************************************/
case WM_INITMENU:

/* Adjust the "Paste from Clipboard menu" */
OpenClipboard (hwnd);
if (IsClipboardFormatAvailable (CF_UNICODETEXT) ||
IsClipboardFormatAvailable (CF_OEMTEXT) ||
IsClipboardFormatAvailable (CF_TEXT))
EnableMenuItem (GetMenu (hwnd),MID_PASTESOURCE, MF_ENABLED);
else
EnableMenuItem (GetMenu (hwnd),MID_PASTESOURCE, MF_GRAYED);
CloseClipboard ();


/* Adjust the source data dependent menus. */
if (pSourceData != NULL) {
EnableMenuItem (GetMenu (hwnd),MID_SOURCEOPT, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_SWAPSOURCE, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_CLEARSOURCE, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_CONVERTNOW, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_CONVERSIONOPT, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_DESTINATIONOPT, MF_ENABLED);
} else {
EnableMenuItem (GetMenu (hwnd),MID_SOURCEOPT, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_SWAPSOURCE, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_CLEARSOURCE, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_CONVERTNOW, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_CONVERSIONOPT, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_DESTINATIONOPT, MF_GRAYED);
}


/* Adjust the destination data dependent menus. */
if (pDestinationData != NULL) {
EnableMenuItem (GetMenu (hwnd),MID_SAVEAS, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_SWAPDESTINATION, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_COPYDESTINATION, MF_ENABLED);
EnableMenuItem (GetMenu (hwnd),MID_CLEARDESTINATION, MF_ENABLED);
} else {
EnableMenuItem (GetMenu (hwnd),MID_SAVEAS, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_SWAPDESTINATION, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_COPYDESTINATION, MF_GRAYED);
EnableMenuItem (GetMenu (hwnd),MID_CLEARDESTINATION, MF_GRAYED);
}

break;





/**********************************************************************\
* WM_COMMAND
*
* Just switch() on the command ID. Notice that menu and button
* command messages are treated the same.
*
\**********************************************************************/
case WM_COMMAND:
switch (LOWORD(wParam)) {


/******************************************************************\
* WM_COMMAND, MID_OPEN
*
* Put up common dialog, try to open & read file.
* Fill windows with correct text & fill pSourceData.
\******************************************************************/
case MID_OPEN : {
HANDLE hFile;
DWORD nBytesRead;
TCHAR szFile[MAX_PATH],szFileTitle[MAX_PATH];

/* First set up the structure for the GetOpenFileName
* common dialog.
*/
{
OPENFILENAME OpenFileName;
/* buffers for the file names. */

wsprintf (szFile, szBlank);
wsprintf (szFileTitle, szBlank);


OpenFileName.lStructSize = sizeof(OPENFILENAME);
OpenFileName.hwndOwner = hwnd;
OpenFileName.hInstance = (HANDLE) hInst;
OpenFileName.lpstrFilter = szFilter; // built in WM_CREATE
OpenFileName.lpstrCustomFilter = NULL;
OpenFileName.nMaxCustFilter = 0L;
OpenFileName.nFilterIndex = 1L;
OpenFileName.lpstrFile = szFile;
OpenFileName.nMaxFile = MAX_PATH;
OpenFileName.lpstrFileTitle = szFileTitle;
OpenFileName.nMaxFileTitle = MAX_PATH;
OpenFileName.lpstrInitialDir = NULL;
OpenFileName.lpstrTitle = LoadResourceString(IDS_OPEN_FILE_TITLE);

OpenFileName.nFileOffset = 0;
OpenFileName.nFileExtension = 0;
OpenFileName.lpstrDefExt = NULL;

OpenFileName.lCustData = 0;
OpenFileName.lpfnHook = NULL;
OpenFileName.lpTemplateName = NULL;

OpenFileName.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;

if (!GetOpenFileName(&OpenFileName)) return 0;
}


/* User has filled in the file information.
* Try to open that file for reading.
*/
hFile = CreateFile(szFile,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
MessageBox (hwnd, LoadResourceString(IDS_OPEN_FILE_FAILED),
MBTitle, MBFlags);
return 0;
}


/* make sure file is not too big... i.e. > 2^32
* If it is OK, write the file size in hwndSize0
*/
{
BY_HANDLE_FILE_INFORMATION bhfi;

GetFileInformationByHandle (hFile, &bhfi);
if (bhfi.nFileSizeHigh != 0) {
MessageBox (hwnd, LoadResourceString(IDS_FILE_TOO_BIG),
MBTitle, MBFlags);
CloseHandle (hFile);
return 0;
}

nBytesSource= bhfi.nFileSizeLow;

}

/* Allocate space for string, including potential UNICODE_NULL */
pSourceData = ManageMemory (MMALLOC, MMSOURCE, nBytesSource +2, pSourceData);
if (pSourceData == NULL) {
CloseHandle (hFile);
return 0;
}

if (nBytesSource < SIZEOFBOM) {
gTypeSource = TYPEUNKNOWN;
goto no_bom;
}

/* first read two bytes and look for BOM */
if (!ReadFile (hFile, pSourceData,SIZEOFBOM, &nBytesRead, NULL)) {
MessageBox (hwnd, LoadResourceString(IDS_READFILE_FAILED),
MBTitle, MBFlags);
CloseHandle (hFile);
pSourceData = ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
return 0;
}



/* If file begins with BOM, then we know the type,
* we'll decrement the number of bytes by two,
* and read the rest of the data.
*/
if (IsBOM (pSourceData)) {
gTypeSource = TYPEUNICODE;
nBytesSource -=SIZEOFBOM;

/* If file begins with Reverse BOM, then we know the type,
* we'll decrement the number of bytes by two,
* and read the rest of the data, and post a message so
* that we know to swap the order later.
*/
} else if (IsRBOM (pSourceData)) {
gTypeSource = TYPEUNICODE;
nBytesSource -=SIZEOFBOM;
MessageBox (hwnd, LoadResourceString(IDS_SWAPPING_BYTE_ORDER),
MBTitle, MBFlags);
PostMessage (hwnd, WM_COMMAND, MID_SWAPSOURCE, 0);

/* Oops, does not begin with BOM.
* Reset file pointer, and read data.
*/
} else {
gTypeSource = TYPEUNKNOWN;
SetFilePointer (hFile, -SIZEOFBOM, NULL, FILE_CURRENT);
}

no_bom:


/* try to read all of it into memory */
if (!ReadFile (hFile, pSourceData,nBytesSource, &nBytesRead, NULL)) {
MessageBox (hwnd, LoadResourceString(IDS_READFILE_FAILED),
MBTitle, MBFlags);
CloseHandle (hFile);
pSourceData = ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
return 0;
}

CloseHandle (hFile);

/* If we don't know the file type at this point,
* try to determine if it is unicode.
*/
if (gTypeSource == TYPEUNKNOWN) {
if (IsUnicode (pSourceData)) {
gTypeSource = TYPEUNICODE;
pSourceData[nBytesSource] = 0; // UNICODE_NULL
pSourceData[nBytesSource+1] = 0;
} else {
gTypeSource = TYPECODEPAGE;
pSourceData[nBytesSource] = 0;
}
}

SendMessage (hwnd, WMU_ADJUSTFORNEWSOURCE, 0, (LPARAM)szFile);



} break; /* end case MID_OPEN */



/******************************************************************\
* WM_COMMAND, MID_SAVEAS
*
* Put up common dialog, try to open file, and write data to it.
\******************************************************************/
case MID_SAVEAS: {
HANDLE hFile;
DWORD nBytesRead;
TCHAR szFile[MAX_PATH],szFileTitle[MAX_PATH];

if (nBytesDestination == NODATA ) {
MessageBox (hwnd, LoadResourceString(IDS_NOTEXT_TO_SAVE),
MBTitle, MBFlags);
return 0;
}


/* Set up the structure for the GetSaveFileName
* common dialog.
*/
{
OPENFILENAME OpenFileName;
/* buffers for the file names. */

wsprintf (szFile, szBlank);
wsprintf (szFileTitle, szBlank);

OpenFileName.lStructSize = sizeof(OPENFILENAME);
OpenFileName.hwndOwner = hwnd;
OpenFileName.hInstance = (HANDLE) hInst;
OpenFileName.lpstrFilter = szFilter;
OpenFileName.lpstrCustomFilter = NULL;
OpenFileName.nMaxCustFilter = 0L;
OpenFileName.nFilterIndex = 1L;
OpenFileName.lpstrFile = szFile;
OpenFileName.nMaxFile = MAX_PATH;
OpenFileName.lpstrFileTitle = szFileTitle;
OpenFileName.nMaxFileTitle = MAX_PATH;
OpenFileName.lpstrInitialDir = NULL;
OpenFileName.lpstrTitle = LoadResourceString(IDS_SAVE_AS_TITLE);

OpenFileName.nFileOffset = 0;
OpenFileName.nFileExtension = 0;
OpenFileName.lpstrDefExt = NULL;

OpenFileName.lCustData = 0;
OpenFileName.lpfnHook = NULL;
OpenFileName.lpTemplateName = NULL;

OpenFileName.Flags = OFN_HIDEREADONLY;

if (!GetSaveFileName(&OpenFileName)) return 0;
}


/* User has filled in the file information.
* Try to open that file for writing.
*/
hFile = CreateFile(szFile,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);

if (hFile == INVALID_HANDLE_VALUE) {
MessageBox (hwnd, LoadResourceString(IDS_CREATEFILE_FAILED),
MBTitle, MBFlags);
return 0;
}


/* if destination is unicode, try to write BOM first.
* unless the bytes have been swapped
* (criterion: hwndByteOrder contains text)
* in which case, write a Reverse Byte Order Mark.
*/
if (gTypeSource == TYPECODEPAGE) {
if (GetWindowTextLength (hwndByteOrder1) == 0) {

if (!WriteFile (hFile, szBOM, SIZEOFBOM, &nBytesRead, NULL)) {
MessageBox (hwnd, LoadResourceString(IDS_WRITEFILE_FAILED),
MBTitle, MBFlags);
CloseHandle (hFile);
return 0;
}

}else {
if (!WriteFile (hFile, szRBOM, SIZEOFBOM, &nBytesRead, NULL)) {
MessageBox (hwnd, LoadResourceString(IDS_WRITEFILE_FAILED),
MBTitle, MBFlags);
CloseHandle (hFile);
return 0;
}

}
}


/* try to write all of it into memory */
if (!WriteFile (hFile, pDestinationData,nBytesDestination, &nBytesRead, NULL)) {
MessageBox (hwnd, LoadResourceString(IDS_WRITEFILE_FAILED),
MBTitle, MBFlags);
CloseHandle (hFile);
return 0;
}

SetWindowText (hwndName1, szFile);
CloseHandle (hFile);

} break;


/**********************************************************************\
* WM_COMMAND, MID_PASTESOURCE
*
* Paste the clipboard's prefered data format into the source.
* Fills pSourceData.
\**********************************************************************/
case MID_PASTESOURCE: {
UINT iFormat;
PVOID pData;

OpenClipboard (hwnd);

iFormat = 0;
while (iFormat = EnumClipboardFormats(iFormat))
if ((iFormat == CF_UNICODETEXT) || (iFormat == CF_OEMTEXT) || (iFormat == CF_TEXT)) {

HGLOBAL hMem;

hMem = GetClipboardData (iFormat);
pData = GlobalLock(hMem);

switch (iFormat) {
case CF_UNICODETEXT:
nBytesSource = lstrlenW (pData) *2;
pSourceData= ManageMemory (MMALLOC, MMSOURCE, nBytesSource+2, pSourceData);
lstrcpyW ((LPVOID)pSourceData, pData);
gTypeSource = TYPEUNICODE;
break;

case CF_OEMTEXT:
nBytesSource = lstrlenA (pData);
pSourceData= ManageMemory (MMALLOC, MMSOURCE, nBytesSource+1, pSourceData);
lstrcpyA (pSourceData, pData);
gTypeSource = TYPECODEPAGE;
giSourceCodePage = GetOEMCP();
break;

case CF_TEXT:
nBytesSource = lstrlenA (pData);
pSourceData= ManageMemory (MMALLOC, MMSOURCE, nBytesSource+1, pSourceData);
lstrcpyA (pSourceData, pData);
gTypeSource = TYPECODEPAGE;
giSourceCodePage = GetACP();
break;

default: break; // shouldn't get here
} /* end switch (iFormat) */

SendMessage (hwnd, WMU_ADJUSTFORNEWSOURCE, 0,

(LPARAM)LoadResourceString(IDS_FROM_CLIPBOARD)); 

GlobalUnlock(hMem);
break; /* break out of while loop. */
} /* end if iFormat */


CloseClipboard ();

} break;



/**********************************************************************\
* WM_COMMAND, MID_COPYDESTINATION
*
* Copy destination data to the clipboard.
\**********************************************************************/
case MID_COPYDESTINATION:
{
HGLOBAL hMem;
if (pDestinationData == NULL) return FALSE;

if (gTypeSource != TYPEUNICODE) {
if (!(hMem = GlobalAlloc(
GMEM_MOVEABLE | GMEM_DDESHARE,
(lstrlenW((LPWSTR)pDestinationData)+1) * 2))) {
return FALSE;
}
lstrcpyW(GlobalLock(hMem), (LPWSTR)pDestinationData);
GlobalUnlock(hMem);
} else {
if (!(hMem = GlobalAlloc(
GMEM_MOVEABLE | GMEM_DDESHARE,
lstrlenA(pDestinationData)+1))) {
return FALSE;
}
lstrcpyA(GlobalLock(hMem), pDestinationData);
GlobalUnlock(hMem);
}

OpenClipboard (hwnd);
EmptyClipboard();

/* if source NOT unicode, then destination is, else look at dest CP */
if (gTypeSource != TYPEUNICODE)
SetClipboardData (CF_UNICODETEXT, hMem);
else if (giDestinationCodePage == GetOEMCP())
SetClipboardData (CF_OEMTEXT, hMem);
else
SetClipboardData (CF_TEXT, hMem);


CloseClipboard ();

break;
}



/******************************************************************\
* WM_COMMAND, MID_CONVERTNOW
*
* This is where the conversion actually takes place.
* In either case, make the call twice. Once to determine how
* much memory is needed, allocate space, and then make the call again.
*
* If conversion succeeds, it fills pDestinationData.
\******************************************************************/
case MID_CONVERTNOW: {
int nBytesNeeded, nWCharNeeded, nWCharSource;


if (nBytesSource == NODATA ) {
MessageBox (hwnd, LoadResourceString(IDS_LOAD_SOURCE_FILE),
MBTitle, MBFlags);
return 0;
}


/* Converting UNICODE -> giDestinationCodePage*/
if (gTypeSource == TYPEUNICODE) {

nWCharSource = nBytesSource/2;

/* Query the number of bytes required to store the Dest string */
nBytesNeeded = WideCharToMultiByte(giDestinationCodePage, gWCFlags,
(LPWSTR)pSourceData, nWCharSource,
NULL, 0,
glpDefaultChar, &gUsedDefaultChar);

/* Allocate the required amount of space */

if (nBytesNeeded == 0) {
MessageBox (hwnd, LoadResourceString(IDS_FIRSTCALL_FAILED),
MBTitle, MBFlags);
break;
}

/* We need more 1 byte for '\0' */
pDestinationData= ManageMemory (MMALLOC, MMDESTINATION, nBytesNeeded + 2, pDestinationData);

/* Do the conversion */
nBytesDestination = WideCharToMultiByte(giDestinationCodePage, gWCFlags,
(LPWSTR)pSourceData, nWCharSource,
pDestinationData, nBytesNeeded, glpDefaultChar, &gUsedDefaultChar);
if (nBytesNeeded == 0) {
MessageBox (hwnd, LoadResourceString(IDS_FIRSTCALL_FAILED),
MBTitle, MBFlags);
break;
}
*(LPSTR)((LPSTR)pDestinationData + nBytesNeeded) = '\0';
}


/* converting giSourceCodePage -> UNICODE */
else if (gTypeSource == TYPECODEPAGE) {

/* Query the number of WChar required to store the Dest string */
nWCharNeeded = MultiByteToWideChar(giSourceCodePage, gMBFlags,
pSourceData, nBytesSource, NULL, 0 );

/* Allocate the required amount of space */

/* We need more 2 bytes for '\0' */
pDestinationData= ManageMemory (MMALLOC, MMDESTINATION, (nWCharNeeded+1)*2, pDestinationData);

/* Do the conversion */
nWCharNeeded = MultiByteToWideChar(giSourceCodePage, gMBFlags,
pSourceData, nBytesSource,
(LPWSTR)pDestinationData, nWCharNeeded);

*(LPWSTR)((LPWSTR)pDestinationData + nWCharNeeded) = L'\0';

/* MultiByteToWideChar returns # WCHAR, so multiply by 2 */
nBytesDestination = 2*nWCharNeeded ;
} else {
MessageBox (hwnd, LoadResourceString(IDS_SOURCE_TYPE_UNKNOWN),
MBTitle, MBFlags);
return 0;
}


/* code common to all conversions... */
SetWindowText (hwndName1, LoadResourceString(IDS_DATA_NOT_SAVED));
wsprintf (buffer, LoadResourceString(IDS_BYTES), nBytesDestination);
SetWindowText (hwndSize1, buffer);
SetWindowText (hwndByteOrder1, szBlank);


/* Throw up "Save as" dialog to help the user along.
* They can always <esc> if need be.
*/
SendMessage (hwnd, WM_COMMAND, MID_SAVEAS, 0);

} break; /* end case BID_CONVERT */



/******************************************************************\
* WM_COMMAND, BID_VIEWSOURCE
*
\******************************************************************/
case BID_VIEWSOURCE:
if (gTypeSource == TYPEUNICODE)
DialogBoxW (hInst, L"ShowTextDlg", hwnd, (DLGPROC)ViewSourceProc);
else
DialogBoxA (hInst, "ShowTextDlg", hwnd, (DLGPROC)ViewSourceProc);
break;

/******************************************************************\
* WM_COMMAND, BID_VIEWDESTINATION
*
\******************************************************************/
case BID_VIEWDESTINATION:
if (gTypeSource == TYPEUNICODE)
DialogBoxA (hInst, "ShowTextDlg", hwnd, (DLGPROC)ViewDestinationProc);
else
DialogBoxW (hInst, L"ShowTextDlg", hwnd, (DLGPROC)ViewDestinationProc);
break;



/******************************************************************\
* WM_COMMAND, MID_SOURCEOPT
*
* Allows user to change interpretation options for the source data.
*
* Put up appropriate dialog box, and reset window text in response.
\******************************************************************/
case MID_SOURCEOPT:
if (DialogBox (hInst, TEXT("DataOptionsDlg"), hwnd, (DLGPROC)SourceOptionsProc)) {
SendMessage (hwnd, WMU_SETTYPESTRINGS, 0,0);
SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);
}
break;

/******************************************************************\
* WM_COMMAND, MID_DESTINATIONOPT
*
* Allows user to change options for destination data.
*
* Put up appropriate dialog box, and reset window text in response.
\******************************************************************/
case MID_DESTINATIONOPT:
if (DialogBox (hInst, TEXT("DataOptionsDlg"), hwnd, (DLGPROC)DestinationOptionsProc)) {
SendMessage (hwnd, WMU_SETTYPESTRINGS, 0,0);
SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);
}
break;

/******************************************************************\
* WM_COMMAND, MID_CONVERSIONOPT
*
\******************************************************************/
case MID_CONVERSIONOPT:
if (DialogBox (hInst, TEXT("ConversionOptionsDlg"), hwnd, (DLGPROC)ConversionOptionsProc)) {
SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);
}
break;



/******************************************************************\
* WM_COMMAND, MID_SWAPSOURCE
*
* Allows user to reverse byte order of data.
*
\******************************************************************/
case MID_SWAPSOURCE: {
int i, end;
BYTE temp;

if (pSourceData == NULL) return FALSE;

end = nBytesSource - 2;
for (i = 0; i<= end; i+=2) {
temp = pSourceData[i];
pSourceData[i] = pSourceData[i+1];
pSourceData[i+1] = temp;
}

if (GetWindowTextLength (hwndByteOrder0) == 0)
SetWindowText (hwndByteOrder0,
LoadResourceString(IDS_BYTE_ORDER_REVERSED));
else
SetWindowText (hwndByteOrder0, szBlank);

/* Since source is different, invalidate Destination data. */
SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);

} break;



/******************************************************************\
* WM_COMMAND, MID_SWAPDESTINATION
*
* Allows user to reverse byte order of data.
*
\******************************************************************/
case MID_SWAPDESTINATION: {
int i, end;
BYTE temp;

if (pDestinationData == NULL) return FALSE;

end = nBytesDestination - 2;
for (i = 0; i<= end; i+=2) {
temp = pDestinationData[i];
pDestinationData[i] = pDestinationData[i+1];
pDestinationData[i+1] = temp;
}

if (GetWindowTextLength (hwndByteOrder1) == 0)
SetWindowText (hwndByteOrder1,
LoadResourceString(IDS_BYTE_ORDER_REVERSED));
else
SetWindowText (hwndByteOrder1, szBlank);

} break;


/**********************************************************************\
* WM_COMMAND, MID_CLEARDESTINATION
*
* Clear the destination information. May cause data to be lost.
\**********************************************************************/
case MID_CLEARDESTINATION:
SetWindowText (hwndSize1, szBlank);
SetWindowText (hwndName1, szBlank);
SetWindowText (hwndByteOrder1, szBlank);
pDestinationData= ManageMemory (MMFREE, MMDESTINATION, 0, pDestinationData);
break;


/**********************************************************************\
* WM_COMMAND, MID_CLEARSOURCE
*
* Clear the SOURCE information. May cause data to be lost.
\**********************************************************************/
case MID_CLEARSOURCE:
SetWindowText (hwndSize0, szBlank);
SetWindowText (hwndName0, szBlank);
SetWindowText (hwndByteOrder0, szBlank);
pSourceData= ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
break;





/******************************************************************\
* WM_COMMAND, MID_INSTALLTABLES
*
\******************************************************************/
case MID_INSTALLTABLES:
DialogBox (hInst, TEXT("InstallTableDlg"), hwnd, (DLGPROC)InstallTableProc);
break;




/* Simply call WinHelp to display the OnLine help file. */
case MID_HELP:
WinHelp( hwnd, szHelpPathName, HELP_INDEX, (DWORD) NULL );
break;


/* No-op Window procedure to simply display the dialog box. */
case MID_ABOUT:
ShellAbout (hwnd, TEXT("UConvert"), NULL, LoadIcon (hInst, TEXT("uconvertIcon")));
break;

/* Just close the window. */
case MID_EXIT:
PostMessage (hwnd, WM_CLOSE, 0,0);
break;




} /* end switch (LOWORD(wParam)) */
break; /* end WM_COMMAND */



default: break;
} /* end switch */

return (DefWindowProc(hwnd, message, wParam, lParam));
}





/**************************************************************************\
*
* function: IsUnicode()
*
* HACK... eventually use a proper function for IsUnicode
* Use function from unipad?
*
\**************************************************************************/
BOOL IsUnicode (PBYTE pb)
{
return (IsBOM (pb));
}



/**************************************************************************\
*
* function: IsBOM()
*
* true iff pb points to a Byte Order Mark.
*
\**************************************************************************/
BOOL IsBOM (PBYTE pb)
{
if ((*pb == 0xFF) & (*(pb+1) == 0xFE)) // BOM
return TRUE;
else
return FALSE;
}


/**************************************************************************\
*
* function: IsRBOM()
*
* true iff pb points to a reversed Byte Order Mark.
*
\**************************************************************************/
BOOL IsRBOM (PBYTE pb)
{
if ((*pb == 0xFE) & (*(pb+1) == 0xFF)) // RBOM
return TRUE;
else
return FALSE;
}




/**************************************************************************\
*
* function: framechildwindow()
*
* Simply draw a 3D frame around child window.
*
\**************************************************************************/
VOID framechildwindow (HDC hdc, HWND hwndParent, HWND hwndChild)
{
RECT rect;

GetWindowRect (hwndChild, &rect);

/* minor hack... assumes RECT is two points, right field starting first */
ScreenToClient (hwndParent, (LPPOINT)&rect);
ScreenToClient (hwndParent, (LPPOINT)&(rect.right));

InflateRect (&rect, 1, 1);
FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
InflateRect (&rect, -1, -1);
SelectObject (hdc, GetStockObject (WHITE_PEN));
MoveToEx (hdc, rect.right, rect.top, NULL);
LineTo (hdc,rect.right, rect.bottom);
LineTo (hdc,rect.left, rect.bottom);

return;
}


/**************************************************************************\
*
* function: underlinechildwindow()
*
* Underline child window.
*
\**************************************************************************/
VOID underlinechildwindow (HDC hdc, HWND hwndParent, HWND hwndChild)
{
RECT rect;

GetWindowRect (hwndChild, &rect);

/* minor hack... assumes RECT is two points, right field starting first */
ScreenToClient (hwndParent, (LPPOINT)&rect);
ScreenToClient (hwndParent, (LPPOINT)&(rect.right));

InflateRect (&rect, 1, 1);
rect.top = rect.bottom-1;
FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
SelectObject (hdc, GetStockObject (WHITE_PEN));
MoveToEx (hdc, rect.right, rect.bottom, NULL);
LineTo (hdc,rect.left, rect.bottom);

return;
}







/**************************************************************************\
*
* function: createwindows()
*
* Create the child windows and pass the handles back in parameters.
* Each Window is created relative to (inside of) prect.
* top is a spacial pointer to the Y coordinate of the next window.
*
\**************************************************************************/
VOID createwindows(PRECT prect,
HWND hwndParent,
HWND* hwndLabel,
HWND* hwndName,
HWND* hwndSize,
HWND* hwndCodePage,
HWND* hwndByteOrder,
HWND* hwndButton)
{
int top;

top = prect->top;
*hwndLabel = CreateWindow(
TEXT("STATIC"),
szBlank,
WS_CHILD | WS_VISIBLE | SS_CENTER,
prect->left,
top,
prect->right - prect->left,
WHEIGHT,
hwndParent, NULL, hInst, 0);

top += WHEIGHT*5/2;
*hwndName = CreateWindow(
TEXT("STATIC"),
szBlank,
WS_CHILD | WS_VISIBLE | SS_RIGHT,
prect->left,
top,
prect->right - prect->left,
WHEIGHT,
hwndParent, NULL, hInst, 0);

top += WHEIGHT*2;
*hwndSize = CreateWindow(
TEXT("STATIC"),
szBlank,
WS_CHILD | WS_VISIBLE | SS_LEFT,
prect->left,
top,
(prect->right - prect->left) *3/4,
WHEIGHT,
hwndParent, NULL, hInst, 0);

top += WHEIGHT*2;
*hwndCodePage = CreateWindow(
TEXT("STATIC"),
szBlank,
WS_CHILD | WS_VISIBLE | SS_LEFT,
prect->left,
top,
(prect->right - prect->left) *3/4,
WHEIGHT,
hwndParent, NULL, hInst, 0);

top += WHEIGHT*2;
*hwndByteOrder = CreateWindow(
TEXT("STATIC"),
szBlank,
WS_CHILD | WS_VISIBLE | SS_LEFT,
prect->left,
top,
(prect->right - prect->left) *3/4,
WHEIGHT,
hwndParent, NULL, hInst, 0);

top += WHEIGHT*2;
*hwndButton = CreateWindow(
TEXT("BUTTON"),
TEXT("ViewText"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
prect->left,
top,
prect->right - prect->left,
WHEIGHT*9/7,
hwndParent, NULL, hInst, 0);

return;
}


/**************************************************************************\
*
* function: ManageMemory()
*
* Do all memory management here for the source and destination pointers.
* We also enable/disable the "View..." buttons to reflect existence of data.
*
*
* PARAMETERS
*
* message : {MMALLOC, MMFREE}
* Alloc when requested by MMALLOC, and free the existing, passed in, pointer.
* Free when requested by MMFREE.
* sourcedestination : {MMSOURCE, MMDESTINATION}
* nBytes - number to alloc on MMALLOC messages.
* p - old pointer to be freed.
*
*
* GLOBALS
*
* hwndButton0, hwndButton1
*
\**************************************************************************/
LPVOID ManageMemory (UINT message, UINT sourcedestination, DWORD nBytes, LPVOID p)
{
switch (message) {
case MMFREE :
if (sourcedestination == MMSOURCE)
EnableWindow (hwndButton0, FALSE);
else if (sourcedestination == MMDESTINATION)
EnableWindow (hwndButton1, FALSE);

if (p != NULL) GlobalFree (GlobalHandle (p));
return NULL;
break;

case MMALLOC :
if (sourcedestination == MMSOURCE)
EnableWindow (hwndButton0, TRUE);
else if (sourcedestination == MMDESTINATION)
EnableWindow (hwndButton1, TRUE);

if (p != NULL) GlobalFree (GlobalHandle (p));
p = (LPVOID) GlobalAlloc (GPTR, nBytes);
return p;
break;

} /* end switch (message) */
return NULL;
}


/**************************************************************************\
*
* function: LoadResourceString()
*
* Loads a resource string from string table and returns a pointer
* to the string.
*
* PARAMETERS: wID - resource string id
*
\**************************************************************************/
LPTSTR LoadResourceString(UINT wID)
{
static TCHAR szBuf[512];

LoadString((HANDLE)GetModuleHandle(NULL),wID,szBuf,sizeof(szBuf));
return szBuf;
}