/*
 * Transym OCR Demonstration program
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * This program demonstrates calling TOCR version 4.0 from C++.
 * This program demonstrates the simple processing of a single file.
 * No real attempt is made to handle errors.
 *
 * Copyright (C) 2012 Transym Computer Services Ltd.
 *
 * TOCR4.0DemoC++ Issue1
 */

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <io.h>
#include <fcntl.h>
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <malloc.h>

#pragma warning(disable : 4996)

#include "TOCRdll.h"
#include "TOCRuser.h"
#include "TOCRerrs.h"

void Example1();
void Example2();
void Example3();
void Example4();
void Example5();
void Example6();
void Example7();
void Example8();

bool OCRWait(long JobNo, TOCRJOBINFO2 JobInfo2);
bool OCRPoll(long JobNo, TOCRJOBINFO2 JobInfo2);
bool GetResults(long JobNo, TOCRRESULTS **Results);
bool GetResults(long JobNo, TOCRRESULTSEX **Results);
bool FormatResults(TOCRRESULTS *Results, char *Msg);
bool FormatResults(TOCRRESULTSEX *Results, char *Msg);
void WaitForKey();
HANDLE ConvertGlobalMemoryToMMF(HANDLE hMem);

#define SAMPLE_TIF_FILE "Sample.tif"
#define SAMPLE_BMP_FILE "Sample.bmp"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{

	Example1();	// Demonstrates how to OCR a file
	Example2();	// Demonstrates how to OCR multiple files
	Example3();	// Demonstrates how to OCR an image using a memory mapped file created by TOCR
	Example4();	// Demonstrates how to OCR an image using a memory mapped file created here
	Example5();	// Retrieve information on Job Slot usage
	Example6();	// Retrieve information on Job Slots
	Example7();	// Get images from a TWAIN compatible device
	Example8();	// Demonstrates TOCRSetConfig and TOCRGetConfig

	return 0;
} // main

// Demonstrates how to OCR a file
void Example1()
{

	TOCRJOBINFO2		JobInfo2;
	TOCRRESULTS			*Results = 0;
	long				Status;
	long				JobNo;
	char				InputFile[MAX_PATH];
	char				Msg[1024];

	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);

	memset(&JobInfo2, 0, sizeof(TOCRJOBINFO2));

	strcpy(InputFile, SAMPLE_TIF_FILE);
	JobInfo2.JobType = TOCRJOBTYPE_TIFFFILE;

	// or
	//strcpy(InputFile,SAMPLE_BMP_FILE);
	//JobInfo2.JobType = TOCRJOBTYPE_DIBFILE;

	JobInfo2.InputFile = InputFile;

	Status = TOCRInitialise(&JobNo);
	if ( Status == TOCR_OK ) {

		if ( OCRWait(JobNo, JobInfo2) ) {
		//if ( OCRPoll(JobNo, JobInfo2) ) {
			if ( GetResults(JobNo, &Results) ) {

				// Display the results

				FormatResults(Results, Msg);
				MessageBox(NULL, Msg, "Example 1", MB_TASKMODAL | MB_TOPMOST);

				free(Results);
			}
		}

		TOCRShutdown(JobNo);
	}
} // Example1()

// Demonstrates how to OCR a multiple files
void Example2()
{

	TOCRJOBINFO2		JobInfo2;
	TOCRRESULTS			*Results = 0;
	long				Status;
	long				JobNo;
	char				InputFile[MAX_PATH];
	char				Msg[1024];
	long				CountDone = 0;

	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);

	memset(&JobInfo2, 0, sizeof(TOCRJOBINFO2));

	JobInfo2.InputFile = InputFile;

	Status = TOCRInitialise(&JobNo);
	if ( Status == TOCR_OK ) {
		
		// 1st file
		strcpy(InputFile, SAMPLE_TIF_FILE);
		JobInfo2.JobType = TOCRJOBTYPE_TIFFFILE;
		if ( OCRWait(JobNo, JobInfo2) ) {
			if ( GetResults(JobNo, &Results) ) {

				// Process the results
				CountDone ++;
				free(Results);
			}
		}

		// 2nd file
		strcpy(InputFile,SAMPLE_BMP_FILE);
		JobInfo2.JobType = TOCRJOBTYPE_DIBFILE;
		if ( OCRWait(JobNo, JobInfo2) ) {
			if ( GetResults(JobNo, &Results) ) {

				// Process the results
				CountDone ++;
				free(Results);
			}
		}
			
		TOCRShutdown(JobNo);
	}

	sprintf(Msg,"%d of 2 jobs done", CountDone);
	MessageBox(NULL, Msg, "Example 2", MB_TASKMODAL | MB_TOPMOST);
} // Example2()


// Demonstrates how to OCR an image using a memory mapped file created by TOCR
void Example3()
{
	TOCRJOBINFO2		JobInfo2;
	TOCRRESULTSEX		*Results = 0;
	long				Status;
	long				JobNo;
	HANDLE				hMMF = 0;
	char				Msg[1024];


	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);

	memset(&JobInfo2, 0, sizeof(TOCRJOBINFO2));

	Status = TOCRInitialise(&JobNo);
	if ( Status == TOCR_OK ) {
		Status = TOCRConvertFormat(JobNo, SAMPLE_TIF_FILE, TOCRCONVERTFORMAT_TIFFFILE, &hMMF, TOCRCONVERTFORMAT_MMFILEHANDLE, 0);
		if ( Status == TOCR_OK ) {

			JobInfo2.JobType = TOCRJOBTYPE_MMFILEHANDLE;
			JobInfo2.hMMF = hMMF;
			if ( OCRWait(JobNo, JobInfo2) ) {
				if ( GetResults(JobNo, &Results) ) {

					FormatResults(Results, Msg);
					MessageBox(NULL, Msg, "Example 3", MB_TASKMODAL | MB_TOPMOST);

					free(Results);
				}
			}
			CloseHandle(hMMF);
		}
		TOCRShutdown(JobNo);
	}

} // Example3()

// Demonstrates how to OCR an image using a memory mapped file created here
void Example4()
{
	TOCRJOBINFO2		JobInfo2;
	TOCRRESULTS			*Results = 0;
	long				Status;
	long				JobNo;
	int					file = -1;
	HANDLE				hMap = NULL;
	void				*View = NULL;
	HANDLE				hMMF = NULL;
	long				numbytes;
	BITMAPFILEHEADER	bf;
	char				Msg[1024];

	// Create a memory mapped file from a DIB file
	// The contents of a memory mapped file for TOCR are everything in the bitmap file except
	// the bitmap file header.

	file = _open(SAMPLE_TIF_FILE, _O_RDONLY | _O_SEQUENTIAL | _O_BINARY);
	if ( file != -1 ) {

		// read the bitmap file header to find the size of the bitmap
		if ( _read(file, &bf, sizeof(bf)) > 0 ) {
			numbytes = bf.bfSize - sizeof(bf);
			hMMF = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, numbytes, "any unique name");
			if ( hMMF != NULL ) {
				View = (void *)MapViewOfFile(hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);
				if ( View != NULL ) {

					// read the rest of the bitmap into the memory mapped file
					if ( _read(file, View, numbytes) > 0 ) {
						UnmapViewOfFile(View);
						View = NULL;
						_close(file);
						file = -1;
						JobInfo2.hMMF = hMap;
						// Do not close the handle to the memory mapped file here - otherwise the
						// object will be destroyed before the OCR engine can read it
					} else {
						UnmapViewOfFile(View);
						CloseHandle(hMMF);
						_close(file);
						return;
					}
				} else {
					CloseHandle(hMMF);
					_close(file);
					return;
				}
			} else {
				_close(file);
				return;
			}
		} else {
			_close(file);
		   return;
		}
	} else {
	   return;
	}

	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);

	memset(&JobInfo2, 0, sizeof(TOCRJOBINFO2));

	Status = TOCRInitialise(&JobNo);
	if ( Status == TOCR_OK ) {
		Status = TOCRConvertFormat(JobNo, SAMPLE_TIF_FILE, TOCRCONVERTFORMAT_TIFFFILE, &hMMF, TOCRCONVERTFORMAT_MMFILEHANDLE, 0);
		if ( Status == TOCR_OK ) {

			JobInfo2.JobType = TOCRJOBTYPE_MMFILEHANDLE;
			JobInfo2.hMMF = hMMF;
			if ( OCRWait(JobNo, JobInfo2) ) {
				if ( GetResults(JobNo, &Results) ) {

					FormatResults(Results, Msg);
					MessageBox(NULL, Msg, "Example 4", MB_TASKMODAL | MB_TOPMOST);

					free(Results);
				}
			}
		}
		TOCRShutdown(JobNo);
	}

	CloseHandle(hMMF);

} // Example4()

// Retrieve information on Job Slot usage
void Example5()
{
	long				Status;
	long				NumSlots;
	long				*JobSlotInf;
	char				Msg[1024];
	char				Line[80];

	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);
	
	// uncomment to see effect on usage
	//long				JobNo;
	//Status = TOCRInitialise(&JobNo);

	NumSlots = TOCRGetJobDBInfo(0);
	if ( NumSlots > 0 ) {
		JobSlotInf= (long *)malloc(NumSlots * sizeof(long));
		if ( JobSlotInf ) {
			Status = TOCRGetJobDBInfo(JobSlotInf);
			if (Status == TOCR_OK) {
				strcpy(Msg, "Slot usage is\n");
				for (long SlotNo = 0; SlotNo < NumSlots; SlotNo++) {
					sprintf(Line, "\nSlot %d is ", SlotNo);
					strcat(Msg, Line);
					switch (JobSlotInf[SlotNo])
					{
						case TOCRJOBSLOT_FREE:
							strcat(Msg, "free");
							break;
						case TOCRJOBSLOT_OWNEDBYYOU:
							strcat(Msg, "owned by you");
							break;
						case TOCRJOBSLOT_BLOCKEDBYYOU:
							strcat(Msg, "blocked by you");
							break;
						case TOCRJOBSLOT_OWNEDBYOTHER:
							strcat(Msg, "owned by another process");
							break;
						case TOCRJOBSLOT_BLOCKEDBYOTHER:
							strcat(Msg, "blocked by another process");
							break;
					}
				}
				MessageBox(NULL, Msg, "Example 5", MB_TASKMODAL | MB_TOPMOST);
			}
			free(JobSlotInf);
		} else
			MessageBox(NULL, "Failed to malloc for JobSlotInf", "Example 5", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
	} else 
		MessageBox(NULL, "No job slots", "Example 5", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);

	//TOCRShutdown(JobNo);


} // Example5()

// Retrieve information on Job Slots
void Example6()
{
	long				Status;
	long				NumSlots;
	unsigned char		Licence[20];
	long				Volume;
	long				Time;
	long				Remaining;
	long				Features;
	long				JobNo;
	char				Msg[1024];
	char				Line[80];

	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);

	// comment to see effect on usage
	Status = TOCRInitialise(&JobNo);

	NumSlots = TOCRGetJobDBInfo(0);
	if ( NumSlots > 0 ) {
		strcpy(Msg, "Slot usage is\n");
		for (long SlotNo = 0; SlotNo < NumSlots; SlotNo++) {
			sprintf(Line, "\nSlot %d is ", SlotNo);
			strcat(Msg, Line);
			Status = TOCRGetLicenceInfoEx(SlotNo, (char *)Licence, &Volume, &Time, &Remaining, &Features);
			if ( Status == TOCR_OK ) {
				sprintf(Line, " %s", Licence);
				strcat(Msg, Line);
				switch (Features)
				{
					case TOCRLICENCE_STANDARD:
						strcat(Msg, " STANDARD licence");
						break;
					case TOCRLICENCE_EURO:
						if ( strcmp((char *)Licence, "154C-43BA-9421-C925") == 0 )
							strcat(Msg, " EURO TRIAL licence");
						else 
							strcat(Msg, " EURO licence");
						break;
					case TOCRLICENCE_EUROUPGRADE:
						strcat(Msg, " EURO UPGRADE licence");
						break;
					case TOCRLICENCE_V3SE:
						if ( strcmp((char *)Licence, "2E72-2B35-643A-0851") == 0 )
							strcat(Msg, " V3 SE TRIAL licence");
						else 
							strcat(Msg, " V3 licence");
						break;
					case TOCRLICENCE_V3SEUPGRADE:
						strcat(Msg, " V1/2 UPGRADE to V3 SE licence");
						break;
					case TOCRLICENCE_V3PRO:
						strcat(Msg, " V3 Pro/V4 licence");
						break;
					case TOCRLICENCE_V3PROUPGRADE:
						strcat(Msg, " V1/2 UPGRADE to V3 Pro/V4 licence");
						break;
					case TOCRLICENCE_V3SEPROUPGRADE:
						strcat(Msg, " V3 SE UPGRADE to V3 Pro/V4 licence");
						break;
				}
				if ( Volume != 0 || Time != 0 ) {
					sprintf(Line, " %d", Remaining);
					strcat(Msg, Line);
					if ( Time != 0 )
						strcat(Msg, " days");
					else
						strcat(Msg, " A4 pages");
					strcat(Msg, " remaining on licence");
				}
			} else
				strcat(Msg, "Failed to get information");	
		}
		MessageBox(NULL, Msg, "Example 6", MB_TASKMODAL | MB_TOPMOST);
	} else 
		MessageBox(NULL, "No job slots", "Example 6", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);

	TOCRShutdown(JobNo);

} // Example6()

// Get images from a TWAIN compatible device
void Example7()
{
	TOCRJOBINFO2		JobInfo2;
	TOCRRESULTS			*Results = 0;
	long				JobNo;
	long				Status;
	long				NumImages = 0;
	long				CntImages = 0;
	HANDLE				*hMems;
	HANDLE				hMMF = 0;
	char				Msg[1024];

	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);

	memset(&JobInfo2, 0, sizeof(TOCRJOBINFO2));

	Status = TOCRInitialise(&JobNo);

	Status = TOCRTWAINSelectDS(); // select the TWAIN device
	if ( Status == TOCR_OK) {
		Status = TOCRTWAINShowUI(1);
		Status = TOCRTWAINAcquire(&NumImages);
		if ( NumImages > 0 ) {
			hMems = (HANDLE *)malloc(NumImages * sizeof (HANDLE));
			if ( hMems ) {
				memset(hMems, 0, NumImages * sizeof(HANDLE));
				Status = TOCRTWAINGetImages(hMems);
				for (long ImgNo = 0; ImgNo < NumImages; ImgNo++) {
					if ( hMems[ImgNo] ) {
						// convert the memory block to a Memory Mapped File
						hMMF = ConvertGlobalMemoryToMMF(hMems[ImgNo]);
						// free the global memory block to save space
						GlobalFree(hMems[ImgNo]);

						if ( hMMF ) {
// uncomment to see OCRed results
/*
							JobInfo2.JobType = TOCRJOBTYPE_MMFILEHANDLE;
							JobInfo2.hMMF = hMMF;
							if ( OCRWait(JobNo, JobInfo2) ) {
								if ( GetResults(JobNo, &Results) ) {
									if ( FormatResults(Results, Msg) ) {
										FormatResults(Results, Msg);
										MessageBox(NULL, Msg, "Example 7", MB_TASKMODAL | MB_TOPMOST);
										free(Results);
									}
								}
							}
*/
							CntImages ++;
							CloseHandle(hMMF);
						}

					}
				}
				free(hMems);
			}
		}
	}
	TOCRShutdown(JobNo);

	sprintf(Msg, "%d images successfully acquired\n", CntImages);
	MessageBox(NULL, Msg, "Example 7", MB_TASKMODAL | MB_TOPMOST);

} // Example7()


// Demonstrates TOCRSetConfig and TOCRGetConfig
void Example8()
{
	long				JobNo;
	long				Value;
	char				Msg[1024];
	char				Line[250];

	// Override the INI file settings for all new jobs
	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_MSGBOX);
	TOCRSetConfig(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_SRV_ERRORMODE, TOCRERRORMODE_MSGBOX);

	TOCRGetConfigStr(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_LOGFILE, Line);
	sprintf(Msg, "Default Log file name %s", Line);
	MessageBox(NULL, Msg, "Example 8", MB_TASKMODAL | MB_TOPMOST);

	TOCRSetConfigStr(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_LOGFILE, "Loggederrs.lis");
	TOCRGetConfigStr(TOCRCONFIG_DEFAULTJOB, TOCRCONFIG_LOGFILE, Line);
	sprintf(Msg, "New default Log file name %s", Line);
	MessageBox(NULL, Msg, "Example 8", MB_TASKMODAL | MB_TOPMOST);

	TOCRInitialise(&JobNo);
	TOCRSetConfig(JobNo, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_NONE);

	TOCRGetConfig(JobNo, TOCRCONFIG_DLL_ERRORMODE, &Value);
	sprintf(Msg, "Job %d DLL error mode %d", JobNo, Value);
	MessageBox(NULL, Msg, "Example 8", MB_TASKMODAL | MB_TOPMOST);

	TOCRGetConfig(JobNo, TOCRCONFIG_SRV_ERRORMODE, &Value);
	sprintf(Msg, "Job %d Service error mode %d", JobNo, Value);
	MessageBox(NULL, Msg, "Example 8", MB_TASKMODAL | MB_TOPMOST);

	// Cause an error - then check Loggederrs.lis
	TOCRSetConfig(JobNo, TOCRCONFIG_DLL_ERRORMODE, TOCRERRORMODE_LOG);
	TOCRSetConfig(JobNo, 1000, TOCRERRORMODE_LOG);

	TOCRShutdown(JobNo);

} // Example8()

bool OCRWait(long JobNo, TOCRJOBINFO2 JobInfo2)
{
	long				Status;
	long				JobStatus;
	long				ErrorMode;
	char				Msg[TOCRJOBMSGLENGTH];

	Status = TOCRDoJob2(JobNo, &JobInfo2);
	if (Status == TOCR_OK) {
		Status = TOCRWaitForJob(JobNo, &JobStatus);
	}
	
	if (Status == TOCR_OK && JobStatus == TOCRJOBSTATUS_DONE)
	{
		return true;
	} else {
		// If something hass gone wrong display a message
		// (Check that the OCR engine hasn't already displayed a message)
		TOCRGetConfig(JobNo, TOCRCONFIG_DLL_ERRORMODE, &ErrorMode);
		if ( ErrorMode == TOCRERRORMODE_NONE ) {
			TOCRGetJobStatusMsg(JobNo, Msg);
			MessageBox(NULL, Msg, "OCRWait", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
		}
		return false;
	}
} // OCRWait()

bool OCRPoll(long JobNo, TOCRJOBINFO2 JobInfo2)
{
	long				Status;
	long				Jobstatus;
	long				ErrorMode;
	char				Msg[TOCRJOBMSGLENGTH];

	Status = TOCRDoJob2(JobNo, &JobInfo2);
	if (Status == TOCR_OK) {
		do {
			Sleep(100);
			Status = TOCRGetJobStatus(JobNo, &Jobstatus);
			if ( Status != TOCR_OK ) {
				sprintf(Msg, "OCRPoll failed - %d\n", Status);
				MessageBox(NULL, Msg, "OCRPoll", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
				return false;
			}
		} while ( !Jobstatus );
	}
	
	if (Status == TOCR_OK && Jobstatus == TOCRJOBSTATUS_DONE)
	{
		return true;
	} else {
		// If something hass gone wrong display a message
		// (Check that the OCR engine hasn't already displayed a message)
		TOCRGetConfig(JobNo, TOCRCONFIG_DLL_ERRORMODE, &ErrorMode);
		if ( ErrorMode == TOCRERRORMODE_NONE ) {
			TOCRGetJobStatusMsg(JobNo, Msg);
			MessageBox(NULL, Msg, "OCRPoll", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
		}
		return false;
	}
} // OCRPoll()

// Get the results from TOCR
bool getresults(long JobNo, long mode, void **Results)
{
	long				Status;
	long				ResultsInf;
	char				Msg[TOCRJOBMSGLENGTH];


	Status = TOCRGetJobResultsEx(JobNo, mode, &ResultsInf, 0);
	if ( Status != TOCR_OK ) {
		sprintf(Msg, "TOCRGetJobResultsEx failed - %d\n", Status);
		MessageBox(NULL, Msg, "getresults", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
		return false;

	}
	if ( ResultsInf > 0 ) {
		// Allocate memory for results

		*Results = (void *)malloc(ResultsInf * sizeof(unsigned char));
		if ( *Results ) {

			// Retrieve the results

			Status = TOCRGetJobResultsEx(JobNo, mode, &ResultsInf, *Results);
			if ( Status != TOCR_OK ) {
				sprintf(Msg, "TOCRGetJobResultsEx failed - %d\n", Status);
				MessageBox(NULL, Msg, "getresults", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
				free(*Results);
				*Results = 0;
				return false;
			}
		} else {
			MessageBox(NULL, "Failed to allocate memory for results\n", "getresults", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
			return false;
		}
	} else {
		MessageBox(NULL, "No results found\n", "getresults", MB_TASKMODAL | MB_TOPMOST | MB_ICONSTOP);
	}
	
	return true;
} // getresults()

// Get normal results
bool GetResults(long JobNo, TOCRRESULTS **Results)
{
	return getresults(JobNo, TOCRGETRESULTS_NORMAL, (void **)Results);
} // GetResults()

// Get extended results
bool GetResults(long JobNo, TOCRRESULTSEX **Results)
{
	return getresults(JobNo, TOCRGETRESULTS_EXTENDED, (void **)Results);
} // GetResults()


// Convert results to a string
bool FormatResults(TOCRRESULTS *Results, char *Msg)
{
	long			ItemNo;
	long			APos = 0;
	bool			Status = false;

	if ( Results->Hdr.NumItems > 0 ) {
		for (ItemNo = 0; ItemNo < Results->Hdr.NumItems; ItemNo ++ ) {
			if ( Results->Item[ItemNo].OCRCha == '\r' )
				Msg[APos] = '\n';
			else
				Msg[APos] = (char)Results->Item[ItemNo].OCRCha;
			APos ++;
		}
		Msg[APos] = 0;
		Status = true;
	}

	return Status;

} // FormatResults()

// Convert extended results to a string
bool FormatResults(TOCRRESULTSEX *Results, char *Msg)
{
	long			ItemNo;
	long			APos = 0;
	bool			Status = false;

	if ( Results->Hdr.NumItems > 0 ) {
		for (ItemNo = 0; ItemNo < Results->Hdr.NumItems; ItemNo ++ ) {
			if ( Results->Item[ItemNo].OCRCha == '\r' )
				Msg[APos] = '\n';
			else
				Msg[APos] = (char)Results->Item[ItemNo].OCRCha;
			APos ++;
		}
		Msg[APos] = 0;
		Status = true;
	}

	return Status;
} // FormatResults()


HANDLE ConvertGlobalMemoryToMMF(HANDLE hMem)
{
	void				*View = 0;
	HANDLE				hMMF = NULL;
	void				*ucImg = NULL;
	long				Status;

	Status = TOCR_OK + 1;

	hMMF = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)GlobalSize((void *)hMem), 0);
	if ( hMMF != NULL ) {
		View = (void *)MapViewOfFile(hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if ( View != NULL ) {
			ucImg = GlobalLock((void *)hMem);
			if ( ucImg ) {
				memcpy(View, ucImg, GlobalSize((void *)hMem) * sizeof(unsigned char));
				GlobalUnlock((void *)hMem);
				Status = TOCR_OK;
			}
			UnmapViewOfFile(View);
		}
	}

	if ( Status == TOCR_OK )
		return hMMF;
	else {
		if ( hMMF )
			CloseHandle(hMMF);
		return NULL;
	}

} // ConvertGlbalMemoryToMMF()
