Thread: Recreating the classic 16-bit Notepad application

  1. #1
    Registered User
    Join Date
    Jun 2009
    Posts
    93

    Recreating the classic 16-bit Notepad application

    Over the past several weeks (in my limited spare time) I decided to recreate the classic Windows 3.1 Notepad application as a winter project to kill time. I mainly under took the endeaver to learn more about C language (since I'm a beginner), and its practical use in real world (old) Windows environment. Yeah I know that what I'm referring to is really dated, but remember it's just a fun over the winter project....Besides, I sure use the application enough (newer version of coarse), so why not find out "what makes it tick". Well, I recreated the whole application and as expected there are a few things I couldn't quite figure out; hense the whole idea behind this post. Before I go and ask specific questions, or post any code I need to know if it's ok with the moderator(s) of the board. I know board rule #6 might be in question here even though Microsoft already made there statement regarding this particular application and it's source code (aka the leaked source code from early 2000). So, if the moderator(s) feels that this is not a good idea then please delete this thread.

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    I'm not a moderator but I don't see a problem posting small sections of code that are giving you problems... So, specifically what's giving you a hard time...

  3. #3
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Isn't it a bit late to be asking permission since you've already posted parts of its disassembly? The rules haven't changed since then, so if that wasn't nuked why do you think posting bits of your own code will be?

  4. #4
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    "adeyblue", what your saying is correct, but I never mentioned a specific application....I made sure I was vague.

    Well, here's the first question. Notepad uses a function called "CheckSave()" to see if the user wants to save the edit control contents. Within the function there is check (i guess) to see if the edit control contents belongs to an existing file name, and whether that file name has a root directory in its path, or full path. In the code snippit below you can see my attempt at accomplish this task, but I don't think it's correct. I know ansi characters "\\" would be represented by 0x5C5C....

    Code:
    /* ** if notepad is dirty, check to see if user wants to save contents */
    BOOL FAR PASCAL CheckSave (BOOL fSysModal)
    {
    
        /* .... */
    
        if (SendMessage (hwndEdit, EM_GETMODIFY, 0, 0L))
        {
            if (fUntitled)
                pszFileName = szUntitled;
    
            else if (*(LPWORD)szFileName+2 == 0x5C5C)
                pszFileName = szFileName+2;
            else
                pszFileName = szFileName;
    
            mdResult = AlertBox (hwndNP, szNN, szSCBC, pszFileName,
            (WORD)((fSysModal ? MB_SYSTEMMODAL :
                                MB_APPLMODAL)|MB_YESNOCANCEL|MB_ICONEXCLAMATION));
    
        /* .... */
    }

  5. #5
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Hate to break it to you but you can't assign strings using = in C... Use strcpy() or memcpy().

    Also the 0x5c5c is far more clearly presented as '\\' (which produces a single slash not a double,btw). For this you need to use either strchr() or strcmp(). The only place in the Windows operating system where double slashes are valid is as the first two characters of a UNC network name. Everywhere else that is an invalid path.

    However... If you are opening the file name with the windows open file dialog box or through file association, you will always have the absolute path to the file. Both invisibly resolve the full path name for you.

  6. #6
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    pszFileName is a char pointer which I didn't state in the code I posted. Regarding the 0x5C5C thing....My first approach was using the normal '\\' method for a file path delimiter(?), but it did not yield the results I saw within their application when viewed in disassembly. They've got something like this in disassembly:

    Code:
    cmp  word ptr [07EC], 5C5C
    In addition, the disassembly reference to szFileName is 07EA. Likewise, the IDA version of the same tid-bit is just slightly different.

    Code:
    cmp  word_542C, 5C5Ch
    IDA also refers to szFileName as 7EAh.

    Oops, forgot to add that 07EA shows the full path (i.e. c:\x\1.txt). Where as 07EC and word_542C show the following (i.e. \x\1.txt).
    Last edited by TAZIN; 01-27-2011 at 05:59 PM.

  7. #7
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    Ok, here's another problem area I ran into which has to do with the search functions for the application. More specifically it evolves around function calls: RevCaseScan(), and the inline assembly function BackCheck(). Currently I have the BackCheck() function declared to return a LPSTR yet within the code I specify no return value. I have it this currently for two reasons. First is to mimic what I see within their code, and secondly to actually be able to compile the code and have it work as intended. My question is; is there some way I can have the inline assembler function BackCheck() set to return void and still have "lpLast" as a pointer to the function call as show in RevCaseScan()?

    Code:
    /* search forward or backward in the edit control text for the given pattern */
    /* It is the responsibility of the caller to set the cursor                  */
    
    VOID FAR PASCAL Search (char * szKey)
    {
        HCURSOR   hPrevCursor;
        char     *pStart, *pMatch;
        DWORD     dwResult;
        WORD      StartIndex, LineNum, EndIndex;
        WORD      SelStart, SelEnd;
        int       i;
        HANDLE    hEText;           /* handle to edit text */
    
    
        if (!*szKey)
            return;
    
        hPrevCursor = SetCursor(hWaitCursor);
        dwResult = SendMessage (hwndEdit, EM_GETSEL, 0, 0);
        SelStart = LOWORD(dwResult);
        SelEnd   = HIWORD(dwResult);
    
        /*
         * get pointer to edit control text to search
         */
    
        hEText = (HANDLE) SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L);
        pStart = (char *) LocalLock(hEText);
    
        if (fReverse)
        {
            /* Get current line number */
            LineNum = (WORD)SendMessage (hwndEdit, EM_LINEFROMCHAR, SelStart, 0L);
            /* Get index to start of the line */
            StartIndex = (WORD)SendMessage (hwndEdit, EM_LINEINDEX, LineNum, 0L);
            /* Set upper limit for search text */
            EndIndex = SelStart;
            pMatch = NULL;
    
            /* Search line by line, from LineNum to 0 */
            for (i = LineNum; i >= 0; i--)
            {
                if (fCase)
                    (LPSTR)pMatch = RevCaseScan(pStart+StartIndex, pStart+EndIndex, szKey);
                else
                    (LPSTR)pMatch = ReverseScan(pStart+StartIndex, pStart+EndIndex, szKey);
    
                if (pMatch)
                    break;
                /* current StartIndex is the upper limit for the next search */
                EndIndex = StartIndex;
    
                if (i-1 >= 0)
                    /* Get start of the next line */
                    StartIndex = (WORD)SendMessage (hwndEdit, EM_LINEINDEX, i-1, 0L);
            }
        }
        else
        {
            if (fCase)
                (LPSTR)pMatch = FwdCaseScan(pStart + SelEnd, szKey);
            else
                (LPSTR)pMatch = ForwardScan (pStart + SelEnd, szKey);
        }
    
        LocalUnlock(hEText);
        SetCursor(hPrevCursor);
    
        if (pMatch == NULL)
        {
            /* alert user on not finding any text */
            AlertBox (hDlgFind ? hDlgFind : hwndNP,
                      szNN,
                      szCFS,
                      szSearch,
                      MB_APPLMODAL | MB_OK | MB_ICONASTERISK);
        }
        else
        {
            SelStart = pMatch - pStart;
            SendMessage (hwndEdit, EM_SETSEL, 0, MAKELONG(SelStart, SelStart+lstrlen(szKey)));
        }
    
        return;
    }
    
    
    /* ** Backward Search case sensitive. */
    LPSTR NEAR PASCAL RevCaseScan(
        LPSTR   lpSource,
        LPSTR   lpLast,
        LPSTR   lpSearch)
    {
        int     iLen;
        char    cch;
    
    
        iLen = lstrlen(lpSearch);
        cch = *lpSearch;
    
        if (!lpLast)
            lpLast = lpSource + lstrlen(lpSource);
    
        do
        {
            if (lpLast == lpSource)
                return (NULL);
    
            --lpLast;
    
            if (!(lpLast = BackCheck(lpLast, (PSTR)(lpLast+1 - lpSource), cch)))
                break;
    
            if (DoCaseSensitiveSearch(lpLast, lpSearch, iLen) != 0)
                continue;
    
            if (!(lpLast == LocateText(lpSource, lpLast - lpSource)))
                continue;
            else
                break;
    
        } while (TRUE);
    
        return (lpLast);
    }
    
    
    /* ** Backwards Search case sensitive helper function. */
    LPSTR NEAR PASCAL BackCheck(
        LPSTR   lpEnd,
        PSTR    pText,
        char    szCh)
    {
        _asm
        {
            mov  cx,pText
            jcxz OutS1
    
            les di,lpEnd
            mov al,szCh       /* search char */
            std
            repne scasb
            jnz OutS1
    
            inc di
            mov dx,es
            mov ax,di
            jmp OutS2
    
      OutS1:
            xor ax,ax
            xor dx,dx
      OutS2:
            cld
        }
    }

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Ok... you just walked right past me... My knowledge of X86 assembler is right next to 0...

    Maybe someone else can chip in....

  9. #9
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    I hear what your saying "CommonTator", since I'm a recent graduate from "HelloWorld" so any programming to me is a bit out of my league!
    Well, the more I look into the question I posted about the inline asm function BackCheck() the more I tend to believe they (Microsoft) did the same thing as I posted. It may have to do with the old 286 processors and the handling of double-word variables and registers....Or perhaps they relied on the default behavor of the compiler (as I did) to actually return a LPSTR (since it's declared in the prototype, and the inline code fills the proper registers) even though I didn't write a return. It does seem to match up exactly to what I see within their code. Anyway, it's simple enough to add an "official" return to the BackCheck() function as shown below.

    Code:
    /* ** Backwards Search case sensitive helper function. */
    LPSTR NEAR PASCAL BackCheck(
        LPSTR   lpEnd,
        PSTR    pText,
        char    szCh)
    {
        _asm
        {
            mov  cx,pText     /* source string with index */
            jcxz OutS1        /* jump if cx (pText) is 0 */
    
            les di,lpEnd      /* destination string */
            mov al,szCh       /* search char */
            std               /* set direction flag */
            repne scasb       /* scan string lpEnd for occurances of al (szCh) */
            jnz OutS1         /* jump if not equal or not zero */
    
            inc di            /* increment lpEnd */
            mov dx,es         /* our results hiword */
            mov ax,di         /* our results loword */
            jmp OutS2         /* were done; get out */
    
      OutS1:
            xor ax,ax         /* reset 0 */
            xor dx,dx         /* reset 0 */
      OutS2:
            cld                         /* clear direction flag */
            mov word ptr lpEnd, ax      /* put loword results in lpEnd */
            mov word ptr lpEnd+2, dx    /* put hiword results in lpEnd */
        }
        return (lpEnd);
    }

  10. #10
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    My next question/problem area is one that I briefly addressed in a different thread which I never really sorted out. Maybe now that there's a more complete picture to work with it will help. Ok, the question evolves around "*lpszDateType " array of pointers to strings, "iDateFmt ", the loop found in "InitLocale() " referring to both these items, and the evaluation of "iDateFmt " found in "GetTimeDate() ". Rather than ask a specific question at this time, maybe it would be beneficial for someone to outline step by step exactly what is going on with this mess I created regarding the variables I mentioned above. For some reason the preview of my post is not showing one of the variables "sSep" denoted as a pointer correctly....Must be a conflict between the board and this particular combo of letters. The good news is that it has nothing to do with my question.

    Code:
    char chPageText[6][PT_LEN];    /* Strings to hold PageSetup items.      */
    char szDec[2];                 /* Character separator.                  */
    char sAMPM[2][6];              /* Strings to hold AM PM items for time. */
    int iDate;                     /* Date format.                          */
    int iTime;                     /* 12 hour or 24 hour time format.       */
    int iTLzero;                   /* Display leading zero with time?       */
    int sDate;                     /* Date separator.                       */
    int sTime;                     /* Time separator.                       */
    int iDateFmt;                  /* Formatted date picture.               */
    
    /* Possible formatted date pictures. */
    char *lpszDateType[8][3] = {"M/d/yy", "d/M/yy", "yy/M/d",
                                "M/dd/yy", "dd/M/yy", "yy/M/dd",
                                "MM/d/yy", "d/MM/yy", "yy/MM/d",
                                "MM/dd/yy", "dd/MM/yy", "yy/MM/dd",
                                "M/d/yyyy", "d/M/yyyy", "yyyy/M/d",
                                "M/dd/yyyy", "dd/M/yyyy", "yyyy/M/dd",
                                "MM/d/yyyy", "d/MM/yyyy", "yyyy/MM/d",
                                "MM/dd/yyyy", "dd/MM/yyyy", "yyyy/MM/dd"};
    
    
    /* ** Get Locale info from the Registry, and initialize global vars  */
    void FAR PASCAL InitLocale (void)
    {
        static char szIntl[] = "intl";
        static char szShortDate[11] = "d/M/yy";
        char    szDateForm[11];
        char    sSep[2];
        char    *pch;
        int     i, j;
    
    
        iDate = GetProfileInt(szIntl, "iDate", 0);
        GetProfileString(szIntl, "sDate", "/", sSep, sizeof(sSep));
        sDate = * sSep;
        GetProfileString(szIntl, "sShortDate", "d/M/yy",
                         szShortDate, sizeof(szShortDate));
        lstrcpy (szDateForm, szShortDate);
    
        if (sDate != '/')
            for (pch = szDateForm; *pch != '\0'; *pch++)
            {
                if (*pch != 'M' && *pch != 'd' && *pch != 'y')
                    *pch = '/';
            }
    
        iDateFmt = 0;
    
        /* Compare system date picture against acceptable possibilities. */
        for (i = 0; i < 8; i++)
        {
            if (lstrcmpi(lpszDateType[i][iDate], szDateForm) != 0)
            {
                continue;
            }
            else
            {
                iDateFmt = (WORD)lpszDateType[i][0];
                break;
            }
        }
    
        GetProfileString(szIntl, "s1159", "AM", sAMPM[0], 6);
        GetProfileString(szIntl, "s2359", "PM", sAMPM[1], 6);
        iTime = GetProfileInt(szIntl, "iTime", 0);
        GetProfileString(szIntl, "sTime", ":", sSep, sizeof(sSep));
        sTime = * sSep;
        iTLzero = GetProfileInt(szIntl, "iTLzero", 0);
        * sSep = *szDec;
        GetProfileString(szIntl, "sDecimal", ".", szDec, sizeof(szDec));
    
        /* Check RC margin strings for decimal value. */
        for (j = 2; j < 6; j++)
        {
            for (i = 0; i < lstrlen(chPageText[j - HEADER]); i++) 
            {
                if (chPageText[j - HEADER][i] == * sSep)
                {
                    chPageText[j - HEADER][i] = *szDec;
                }
            }
        }
    
    }
    
    
    /* ** Builds the date and time strings */
    VOID NEAR PASCAL GetTimeDate(
        BYTE    *lpTimeStr,
        BYTE    *lpDateStr)
    {
        BYTE    szMonth[3];
        BYTE    szDay[3];
        char    szYear[5];
        BYTE    szHour[3];
        BOOL    bAMPM;
    
    
        bAMPM = FALSE;
    
        if (lpTimeStr != 0)
        {
            HIBYTE(nTimeOrDate) = 0x2C;   /* get time */
            _TimeDate();
            if (HIBYTE(nAsmHour) >= 12)
                bAMPM = TRUE;
    
            /* 12-hour clock */
            if (iTime == 0)
            {
                if (HIBYTE(nAsmHour) == 0)   /* midnight */
                {
                    HIBYTE(nAsmHour) = 12;
                }
                else if (HIBYTE(nAsmHour) > 12)
                {
                    HIBYTE(nAsmHour) -= 12;
                }
            }
    
            /* leading zero */
            if (iTLzero != 0)
            {
                wsprintf(szHour, "%02d", HIBYTE(nAsmHour));
            }
            else
            {
                wsprintf(szHour, "%d", HIBYTE(nAsmHour));
            }
    
        wsprintf(lpTimeStr, "%s%c%02d%s", (LPSTR)szHour, sTime, LOBYTE(nAsmHour),
                 bAMPM ? (LPSTR)sAMPM[1] : (LPSTR)sAMPM[0]);
        }
    
        if (lpDateStr != 0)
        {
            HIBYTE(nTimeOrDate) = 0x2A;   /* get date */
            _TimeDate();
    
            if (iDateFmt & 1)
            {
                wsprintf(szMonth, "%02d", HIBYTE(nDateStuff));
            }
            else
            {
                wsprintf(szMonth, "%d", HIBYTE(nDateStuff));
            }
    
            if (iDateFmt & 2)
            {
                wsprintf(szDay, "%02d", LOBYTE(nDateStuff));
            }
            else
            {
                wsprintf(szDay, "%d", LOBYTE(nDateStuff));
            }
    
            wsprintf(szYear, "%04d", nAsmHour);
    
            if ((iDateFmt & 4) == 0)
            {
                *szYear   = szYear[2];
                szYear[1] = szYear[3];
                szYear[2] = '\0';
            }
    
            /* If domestic. */
            if (iDate == 0)
            {
                wsprintf((LPSTR)lpDateStr, "%s%c%s%c%s", (LPSTR)szMonth, sDate,
                         (LPSTR)szDay, sDate, (LPSTR)szYear);
            }
            else
            {
                /* If international. */
                if (iDate == 1)
                {
                    wsprintf((LPSTR)lpDateStr, "%s%c%s%c%s", (LPSTR)szDay, sDate,
                             (LPSTR)szMonth, sDate, (LPSTR)szYear);
                }
                else
                {
                    wsprintf((LPSTR)lpDateStr, "%s%c%s%c%s", (LPSTR)szYear, sDate,
                             (LPSTR)szMonth, sDate, (LPSTR)szDay);
                }
            }
        }
    
        return;
    }

  11. #11
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    Wow! Kinda surprised no one hazzard a guess. Ok, lets fore go the loop found in InitLocale() and concentrate on the bitwise stuff for iDateFmt found in function GetTimeDate(). Lets say iDateFmt holds the equivelent of "M/d/yy"....Does the bit checking for iDateFmt seem correct?

    Code:
            if (iDateFmt & 1)
            {
                wsprintf(szMonth, "%02d", HIBYTE(nDateStuff));
            }
            else
            {
                wsprintf(szMonth, "%d", HIBYTE(nDateStuff));
            }
    
            if (iDateFmt & 2)
            {
                wsprintf(szDay, "%02d", LOBYTE(nDateStuff));
            }
            else
            {
                wsprintf(szDay, "%d", LOBYTE(nDateStuff));
            }
    
            wsprintf(szYear, "%04d", nAsmHour);
    
            if ((iDateFmt & 4) == 0)
            {
                *szYear   = szYear[2];
                szYear[1] = szYear[3];
                szYear[2] = '\0';
            }

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Why don't you just use an existing control in MFC to do this? I'm also pretty sure CodeGuru has a control that does what Notepad does and much more in their library of controls.

    In C# or managed C++ you could use the RTF control and write about 4 lines of code to get it all done and working. I'm also pretty sure there is a C++ ActiveX or OCX RTF control available.

  13. #13
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    Thanks for the reply Bubba. I know there are different, newer, and far more efficient ways to accomplish these tasks I've asked about. I guess one of my guidelines while undertaking this silly project was to try and replicate the original (as closely as possible); learn, and try to understand why they did things the way they did. I hope that kinda made sense..

  14. #14
    Registered User
    Join Date
    Jun 2009
    Posts
    93
    Ok, zero for three so far, but I live by the saying, "nothing ventured; nothing gained" so why not try my final question. This has do do with copying the contents of a 2-d character array to an LPSTR via a for loop. My question is: While still using the for loop as show is there a better way represent LPSTR szHeadFoot than shown?

    Code:
    #define RIGHT  2
    
    int     xCharPage;                  /* number of chars that can print on line */
    LPSTR   szHeadFoot;                 /* string for header or trailer      */
        char         chBuff[RIGHT+1][MAX_PATH];  /* string for header or trailer */
        int          nIndex[RIGHT+1];  /* current lengths of (left,center,right) */
        int          foo;
        int          iLen;             /* length of strings */
    
    
        iLen = xCharPage - nIndex[RIGHT];
        for (foo = 0; foo < nIndex[RIGHT]; foo++)
        {
            szHeadFoot[iLen+foo] = chBuff[RIGHT][foo];
        }

  15. #15
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Frankly, I think you're overthinking this by a couple of parsecs...

    Here's a little one I wrote... and it's Unicode...
    Code:
    /* 
    
    Tiny Unicode Editor Example
    
    */
    // for the compiler
    #define UNICODE
    #define _UNICODE
    #define WIN32_DEFAULT_LIBS
    #define WIN32_LEAN_AND_MEAN
    #define _WIN32_WINNT 0x0502
    #define _X86_
    
    // Windows headers
    #include <windows.h>
    #include <commdlg.h>
    
    //  PellesC headers
    #include <stdlib.h>
    #include <wchar.h>
    
    #define BOM_FLAG 0xFFFE
    
    
    // Window handles
    HWND      Wind[5];  
    HINSTANCE Inst;
    TCHAR     FileName[MAX_PATH]; // filename when opened
    BOOL      RevBytes = 0;
    
    
    // save a file
    void SaveToFile(void)
      { OPENFILENAME  ofn;        // filename struct
        HANDLE        fh;         // handle for opening files
        DWORD         fs;         // size of the data
        PTCHAR        fd;         // pointer to data
        // get the filename
        memset(&ofn,0,sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner    = Wind[0];
        ofn.hInstance    = Inst;
        ofn.lpstrFilter  = _T("All Files\0*.*\0\0");    
        ofn.lpstrFile    = FileName;
        ofn.nMaxFile     = MAX_PATH;
        ofn.lpstrTitle   = L"Save your work";
        ofn.Flags        = OFN_NONETWORKBUTTON |
                           OFN_HIDEREADONLY |
                           OFN_NOTESTFILECREATE |
                           OFN_OVERWRITEPROMPT;
        if (!GetSaveFileName(&ofn))
          return;
        // get unicode adjusted file size from edit control
        fs = (SendMessage(Wind[4],WM_GETTEXTLENGTH,0,0) * sizeof(TCHAR)); 
        if (fs < 1)
          return; 
        // create text buffer
        fd = malloc(fs);
        // get the text from the control
        SendMessage(Wind[4],WM_GETTEXT,fs,(LPARAM)fd);
        // open the file
        fh = CreateFile(FileName,GENERIC_WRITE,0,NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_NORMAL |
                                FILE_FLAG_WRITE_THROUGH,NULL);
        // save the file
        if (fh != INVALID_HANDLE_VALUE)
          { WriteFile(fh,fd,fs,&fs,NULL);
            CloseHandle(fh); }
        free(fd); }
    
    
    
    // open a file
    void OpenFromFile(void)
      { OPENFILENAME  ofn;        // filename struct
        HANDLE        fh;         // handle for opening files
        DWORD         fs;         // size of the data
        PTCHAR        fd;         // pointer to data
        // get the filename
        memset(&ofn,0,sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner    = Wind[0];
        ofn.hInstance    = Inst;
        ofn.lpstrFilter  = L"All Files\0*.*\0\0";    
        ofn.lpstrFile    = FileName;
        ofn.nMaxFile     = MAX_PATH;
        ofn.lpstrTitle   = L"Open a file";
        ofn.Flags        = OFN_NONETWORKBUTTON |
                           OFN_HIDEREADONLY |
                           OFN_NOTESTFILECREATE |
                           OFN_OVERWRITEPROMPT;
        if (!GetOpenFileName(&ofn))
          return;
        // open the file 
        fh = CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,NULL);
        if (fh == INVALID_HANDLE_VALUE)
          { MessageBox(Wind[0],L"Error opening the file",L"OOPS!",0);
            return; }
        // get the file size
        fs = GetFileSize(fh,NULL) + sizeof(TCHAR);
        // create text buffer
        fd = malloc(fs);
        // clear to 0
        memset(fd,0,fs);
        // read from disk  
        ReadFile(fh,fd,fs,&fs,NULL);
        // close the file
        CloseHandle(fh);
        // put the text in the control
        SendMessage(Wind[4],WM_SETTEXT,fs,(LPARAM)fd);
        free(fd); }
    
    
    
    // resize the window
    VOID ResizeWindow(LPARAM lParm)
      { MoveWindow(Wind[4],60,2,LOWORD(lParm) - 62,HIWORD(lParm) - 4,1); }
    
    
    
    // Message Loop
    LRESULT CALLBACK MsgProc(HWND wnd,UINT msg,WPARAM wparm,LPARAM lparm)
      { switch (msg)
          { case WM_COMMAND :
              switch (LOWORD(wparm))
                { case 1001 :
                    OpenFromFile();
                    return 0;
                  case 1002 :
                    SaveToFile();
                    return 0;
                  case 1003 :
                    PostMessage(Wind[0],WM_CLOSE,0,0);
                    return 0;
                  default :
                    return DefWindowProc(wnd,msg,wparm,lparm); }
            case WM_SIZE  :
              ResizeWindow(lparm);
              return 0;
            case WM_CLOSE :         // close window
              DestroyWindow(Wind[0]);  
              return 0;
            case WM_DESTROY :       // NC Exit button
              PostQuitMessage(0); 
              return 0; 
            default :
              return DefWindowProc(wnd,msg,wparm,lparm); } }
    
    
    
    // create the window
    VOID CreateMainWindow(void)
      { WNDCLASS  wc;
        // register App Class
        memset(&wc,0,sizeof(wc));
        wc.style          = CS_CLASSDC;
        wc.hInstance      = Inst;
        wc.hCursor        = LoadCursor(NULL,IDC_ARROW);
        wc.hbrBackground  = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
        wc.lpfnWndProc    = &MsgProc;
        wc.lpszClassName  = L"TINY_UNICODE";
        RegisterClass(&wc);
    
        // create the main window
        Wind[0] = CreateWindowEx( WS_EX_CONTROLPARENT,
                        L"TINY_UNICODE",L"Tiny Unicode Editor",
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,0,500,300,NULL,NULL,Inst,NULL);
        // buttons
        Wind[1] = CreateWindow(L"BUTTON",L"Open",
                        WS_CHILD | WS_VISIBLE | BS_FLAT,
                        2,2,50,25,Wind[0],(HMENU) 1001,Inst,NULL);
        Wind[2] = CreateWindow(L"BUTTON",L"Save",
                        WS_CHILD | WS_VISIBLE | BS_FLAT,
                        2,30,50,25,Wind[0],(HMENU) 1002,Inst,NULL);
        Wind[3] = CreateWindow(L"BUTTON",L"Quit",
                        WS_CHILD | WS_VISIBLE | BS_FLAT,
                        2,60,50,25,Wind[0],(HMENU) 1003,Inst,NULL);
        // edit window
        Wind[4] = CreateWindowEx(WS_EX_CLIENTEDGE,L"EDIT",NULL,
                        WS_CHILD | WS_VISIBLE |
                        ES_MULTILINE,
                        60,2,200,200,Wind[0],NULL,Inst,NULL);  
        UpdateWindow(Wind[0]);
        ShowWindow(Wind[0],SW_SHOWNORMAL);    }
    
    
    
             
    // Program Entry Procedure
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE pinst, LPSTR cmdl, int show)
      { MSG wmsg;
        // save instance handle
        Inst =    hinst;    
        // make the window
        CreateMainWindow();
        // dispatch window messages
        while (GetMessage(&wmsg,NULL,0,0))
          { TranslateMessage(&wmsg);
            DispatchMessage(&wmsg); }
    
        return 0; }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. 16 bit or 32 bit
    By Juganoo in forum C Programming
    Replies: 9
    Last Post: 12-19-2002, 07:24 AM
  2. I need a bit of help with exectuting my application!!
    By civix in forum C++ Programming
    Replies: 3
    Last Post: 07-20-2002, 11:34 PM
  3. 32 bit or 16 ???
    By GiraffeMan in forum C Programming
    Replies: 11
    Last Post: 04-24-2002, 12:56 PM
  4. 16 Bit Compiler
    By Jperensky in forum C Programming
    Replies: 5
    Last Post: 03-29-2002, 03:08 PM
  5. 16 bit colors
    By morbuz in forum Game Programming
    Replies: 13
    Last Post: 11-10-2001, 01:49 AM