Lets do some graphics: DirectDraw, Part 2

Well, the initial excitement from DDEX1 didn’t last for long. Just flipping between screens having some text ain’t much fun. I moved onto DDEX2. DDEX2 pretty much did the same thing as DDEX1 but on a background image. It drew a fancy background on the back buffer and then flipped some alternating text onto it. Nothing too exciting here, so I didn’t bother trying to make it work on the emulator.DDEX3 turned out to be interesting. It used 4 surfaces in total, a primary surface, a back buffer and two additional surface buffers. The two additional buffers were used to store the upper half and lower half of a bmp image file, respectively. And as and when the timer fired, they Blt one of these buffers onto the back buffer and then flipped the primary surface so that the current image on the back buffer could be displayed. You can check out the bmp file, the upper half says “Even Screen” on a blue background and the lower half says “Odd Screen” on a red background.As I said before, trying to run DDEX3 directly on emulator was not possible because the emulators do not support back buffers (also known as Complex surfaces, i think) and the program quit with an error message. So obviously the code had to be modified a bit, just like we did for DDEX1, to make it work. Here is how to make it work on an emulator:



First create a global BOOL variable bSingleBuffer and initialize it to FALSE. As before, comment out the part in InitApp() function where after doing GetCaps() it fails with an error message saying that the device does not support back buffers. So with that code out of the way, add the following piece of code:

    if (!(ddCaps.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) || !(ddCaps.ddsCaps.dwCaps & DDSCAPS_FLIP))    
    {
        printf(“Device does not support backbuffer or flip!\n”);         printf(“Using a normal surface (not a backbuffer)\n”);         bSingleBuffer = TRUE;

    }

    memset(&ddsd, 0, sizeof(ddsd));

    ddsd.dwSize = sizeof(ddsd);

    if(bSingleBuffer)

    {

        ddsd.dwFlags = DDSD_CAPS;

        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    }

    else

    {

        ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |

                              DDSCAPS_FLIP;

        ddsd.dwBackBufferCount = 1;

    }

    hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);

If the device does not support back buffer or flip then we set the bSingleBuffer variable to TRUE. Then create our primary surface and store the pointer to the surface in g_pDDSPrimary variable. The next part of code gets the back buffer from the primary surface using EnumAttachedSurfaces() function, that part has to be modified as below:



    if(bSingleBuffer)

    {

        ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;

        ddsd.dwWidth = 240;

        ddsd.dwHeight = 320;

        hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSBack, NULL);

        if(hRet != DD_OK)

        {

            return InitFail(hWnd, hRet, TEXT(“Failed to create normal surface”));

        }

    }

    else

    {

        // Get a pointer to the back buffer

        hRet = g_pDDSPrimary->EnumAttachedSurfaces(&g_pDDSBack, EnumFunction);

        if (hRet != DD_OK)

            return InitFail(hWnd, hRet, szEnumAttachedSurfacesFailMsg);

    }

Here, if bSingleBuffer is set, we create a normal surface, 240 pixels wide and 320 pixels high. The resolution of the emulator. Otherwise we get the back buffer from the primary surface.

Then the two off-screen surfaces are created. This part of code remains the same, the only change is,

   // Create a offscreen bitmap.

    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;

    ddsd.dwHeight = 320;

    ddsd.dwWidth = 240;

This is same as the original code, only thing I changed here are the height and width.

Next change would be in the InitSurfaces() function,

    DDCopyBitmap(g_pDDSOne, hbm, 0, 0, 240, 320);

    DDCopyBitmap(g_pDDSTwo, hbm, 0, 320, 240, 320);

Originally, the bitmap was getting copied from 0, 0, 640, 480 and 0, 480, 640, 480. I think the original program was intended for a VGA resolution device but since the emulator runs Quarter VGA, I modified the dimensions.

The final change is in the WindowProc() function under the WM_TIMER message inside the while loop. The original code simply called Flip() on the primary surface to display the image in the back buffer, and as I have been repeating like a million times, the emulator does not support flipping (: So the original code,

    hRet = g_pDDSPrimary->Flip(NULL, 0);

becomes,

if(bSingleBuffer)

    {

        //RECT src, dest;

        hRet = g_pDDSPrimary->Blt(NULL, g_pDDSBack, NULL, 0, NULL);

   

        if(hRet != DD_OK)

        {

            printf(“DDEX3: Blt on primary surface failed, errcode:0x%x\n”, GetLastError());

        }

    }

    else

    {

         hRet = g_pDDSPrimary->Flip(NULL, 0);

    }

And that is all! DDEX3 is now ready to be run on the emulator (: Notice that last time I had used the src and dest RECT’s to specify the size and location of the source and destination rectangles on the source and destination surfaces respectively. Specifying NULL for both src and dest would make use of the entire source and destination surfaces, which is exactly what we want. The remaning part of the while loop remains same.

The emulator screen now flips between the following two screens every half-second.

img1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

and

 

img2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

As you can see, I replaced the stale red and blue screens with something a bit more nice (: Well, obviously you know how to do that. Don’t you? (:

 

 

Lets make the spaceship move! DirectDraw, Part 3

Ok, time to get our hands dirty. If you have seen the Donuts sample you would have noticed the tubular spaceship. The donuts sample uses a single image, donuts.bmp, to store all the images used in the game. Well, I just cut off a portion of it to try something out. Just use any image editor and cut out only the spaceship portion of the image. Make sure that the image you cut is 320×384 in size. This will make our calculations easier as you will see later. So the image would look something like this:

Again, make sure the image you cut is 320 pixels wide and 384 pixels high.

Lets move onto some code. Most of the code remains the same as I had modified it here and here to make it work on the emulator. The only changes will be while creating the surfaces and in the UpdateFrame() function.

 

No changes in the way the primary and back buffers were created, just remember to create the back buffer with width as 240 and height as 320. We will need only one extra surface to hold the above image. Create the surface as follows:



    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;

    ddsd.dwWidth = 320;

    ddsd.dwHeight = 384;

    hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSOne, NULL);

    if (hRet != DD_OK)

        return InitFail(hWnd, hRet, szCreateSurfaceFailMsg);



Notice that the dimensions of the surface match the bitmap size, 320×384. Lets load the bitmap onto the surface now, in InitSurfaces():



    DDCopyBitmap(g_pDDSOne, hbm, 0, 0, 320, 384);



So g_pDDSOne now points to a surface that holds our bitmap. Oh and one more thing, just declare a const variable to hold the number of columns in the image,



static const int            TOTAL_COLS = 5;



Yes, you can go back and count (: Anyways, lets take a look at the UpdateFrame() function now:



static void UpdateFrame(HWND hWnd)

{

    static BYTE                 phase = 0;

    HRESULT                     hRet;

    DDBLTFX                     ddbltfx;

    RECT src, dest;

    int row = 0, col = 0;



    memset(&ddbltfx, 0, sizeof(ddbltfx));

    ddbltfx.dwSize = sizeof(ddbltfx);

    ddbltfx.dwFillColor = 0;



    ddbltfx.dwROP = SRCCOPY;



    //the destination rect is in the center of the screen

    dest.top = 128;

    dest.left = 88;

    dest.bottom = 192;

    dest.right = 152;



    //clear the back buffer (color fill with black)

    g_pDDSBack->Blt(&dest, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAITNOTBUSY, &ddbltfx);



    //calculate the src rect depending on the value of ‘phase’

    row = phase/TOTAL_COLS;

    col = phase%TOTAL_COLS;



    src.top = 64*row;

    src.left = 64*col;

    src.bottom = src.top + 64;

    src.right = src.left + 64;



    while (TRUE)

    {

        hRet = g_pDDSBack->Blt(&dest, g_pDDSOne, &src, DDBLT_ROP, &ddbltfx);

        if (hRet == DD_OK)

            break;

        if (hRet == DDERR_SURFACELOST)

        {

            hRet = RestoreAll();

            if (hRet != DD_OK)

                break;

        }

        if (hRet != DDERR_WASSTILLDRAWING)

            break;

    }



    phase = (phase+1)%30;



}

A few points first, every spaceship image in the bitmap is 64×64 in size. There are a total of 30 spaceship images, so we make the ‘phase‘ variable to run between 0 to 29 in a circular fashion, each time selecting the next image in the bitmap and wrapping around. I decided to place the spaceship in the middle of the screen, as you can see the hard coded values for dest RECT. This is not very elegant and I should really have used GetSystemMetrics() with SM_CXSCREEN and SM_CYSCREEN to calculate the position at run time, but since this is for illustration only, I’ll leave it at that. Next we clear that dest portion of the back buffer, to clear the prevous image, calculate the current image to be loaded using the value of ‘phase‘ and putting the values in the src RECT. Finally we Blt() the image onto the back buffer from g_pDDSOne surface, and this back buffer will then be Blt() onto the primary surface under WM_TIMER message in WindowProc(). Before returning we update the ‘phase‘ variable. Well, thats it! Pretty simple right. If you run this, it will display a rotating spaceship at the center of the emulator screen. You can play around with the timer,



#define TIMER_RATE          80

to control the speed at which the spaceship rotates.

 

Thats it for now! In the next post I’ll explain on how to make the rotating spaceship bounce around the screen edges. It can’t get any simpler (:

I took a small video of the rotating spaceship.

 

Lets make the spaceship move! DirectDraw, Part 4

As I had mentioned in my previous post, we’ll make the spaceship move around on the screen and bounce off when it hits the screen edges.

 

I would like to diverge a bit here. When I got the spaceship to rotate at the center of the screen, I was wondering how I can make it move around in the screen and bounce off the edges. I thought motion would be much more complicated and difficult and it would involve line equations along a particular direction, and then use geometrical concepts involving things like "The law of reflection states that the angle of incidence equals the angle of reflection." and much more. I had a discussion with a few of my colleagues about this same topic and our discussion pretty much went along the same lines. It made me apprehensive. And when I looked at Donuts’ code in detail, well, it seemed like we were trying to boil the ocean to make tea, not that you can’t do it (: I could not have ever imagined that moving an object around the screen and making it bounce off the edges perfectly could be more simpler!

 

Here is how the concept works in brief. You decide on the velocity along the x-axis and the velocity along the y-axis.  Generally, it will be the number of pixels the object moves along each axis per frame. The objects new position is calculated by adding to it the velocity along the X and Y axis. And when ever the object reaches the edges, you just negate the velocity along that direction. That’s it. So if the object is about to hit the top or bottom of the screen, just negate the velocity along the Y axis and if its about to hit the left or right edges, you negate the velocity along the X axis. The object bounces around perfectly.

 

I will be building on the code from my previous post. So I would hope that you have already read it. If you remember, each image of the spaceship in the bitmap was 64×64 in size. To keep things simple, we would make a point move along the X and Y axis and then draw our 64×64 square containing the spaceship around that point, such that the point is at the center of the image always. So our image would be about to hit a screen edge when the point is actually 32 pixels away from it.



Define the velocity globals,



//these are the objects velocities along the x and y axis

int                            dwVelX = 4;

int                            dwVelY = 5;

I have just hard coded the values, it would be better to use the Random() function to get the values, in case of a game. Keeps it a bit unpredictable.

 

The next change is to calculate the position of the dest RECT at run time, unlike the center of the screen, which we did last time. Here is the code for UpdateFrame():



static void UpdateFrame(HWND hWnd)

{

    HRESULT                     hRet;

    DDBLTFX                     ddbltfx;

    RECT src;

    int row = 0, col = 0;



    static BYTE                 phase = 0;

    static RECT dest;



    //the destination rect’s position will be calculated around a point (xPos, yPos), initially its the center of the screen

    static int xPos = 120;

    static int yPos = 160;

    static BOOL first = TRUE;



    memset(&ddbltfx, 0, sizeof(ddbltfx));

    ddbltfx.dwSize = sizeof(ddbltfx);

    ddbltfx.dwFillColor = 0;



    ddbltfx.dwROP = SRCCOPY;



    if(!first)

    {

        //clear the back buffer (color fill with black)

        g_pDDSBack->Blt(&dest, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAITNOTBUSY, &ddbltfx);

    }



    //calculate the destination rect’s position (+-32 coz the image is 64 pixels wide and high)

    dest.top = yPos – 32;

    dest.left = xPos – 32;

    dest.bottom = yPos + 32;

    dest.right = xPos + 32;



    if(first)

    {

        //clear the back buffer (color fill with black)

        g_pDDSBack->Blt(&dest, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAITNOTBUSY, &ddbltfx);

        first = FALSE;

    }



    //calculate the src rect depending on the value of ‘phase’

    row = phase/TOTAL_COLS;

    col = phase%TOTAL_COLS;



    src.top = 64*row;

    src.left = 64*col;

    src.bottom = src.top + 64;

    src.right = src.left + 64;



    while (TRUE)

    {

        hRet = g_pDDSBack->Blt(&dest, g_pDDSOne, &src, DDBLT_ROP, &ddbltfx);

        if (hRet == DD_OK)

            break;

        if (hRet == DDERR_SURFACELOST)

        {

            hRet = RestoreAll();

            if (hRet != DD_OK)

                break;

        }

        if (hRet != DDERR_WASSTILLDRAWING)

            break;

    }



    phase = (phase+1)%30;



    //now update xPos, yPos depending on the velocities

    xPos = xPos + dwVelX;

    yPos = yPos + dwVelY;



    //if any of the four boundaries is reached reset xPos or yPos and negate the velocity along that direction

    if(xPos <= 32)

    {

        xPos = 32;

        dwVelX = -dwVelX;

    }



    if(xPos >= (240-32))

    {

        xPos = 240-32;

        dwVelX = -dwVelX;

    }



    if(yPos <= 32)

    {

        yPos = 32;

        dwVelY = -dwVelY;

    }



    if(yPos >= (320-32))

    {

        yPos = 320-32;

        dwVelY = -dwVelY;

    }

}

The first thing to notice is that the dest RECT is now static. This is, of course, because we are updating the destination position relative to its previous value. Two new static variables xPos and yPos are added. These will store the co-oridnates of the point around which we will draw our image. xPos and yPos are initialized to the center of the screen. We then calculate the dest RECT using xPos and yPos by adding and subracting 32 from it. Calculate the src RECT just like before and Blt() it onto the back buffer. The next part is updating the xPos and yPos values. The values are updated by just adding the X velocity to xPos and Y velocity to yPos. Next we do bounds checking to see if our image is about to go off the screen or put in other words, about to hit a edge. If so, then we reset the position and negate the velocity along that axis. This negating has a bouncing effect. I know that I should have used GetSystemMetrics() rather than hard coding values like 240, 320 etc, but I again I am just plain lazy to do it (: And I hope you are not.

 

Here is a video of the bouncing spaceship.

 



 

Applications: Problems with static text control

Today at work, I was working on a small application with my colleague. We had dialog box with a button, an edit control and a static control. What we wanted to do was show the status of the program on the static control once the button was pressed. And the code looked something like this:

case WM_COMMAND:

{

    wmID = LOWORD(wParam);

    wmEvent = HIWORD(wParam);

    switch(wmID)

    {

        case IDM_BUTTON_GMC:

        {

            SetWindowText(GetDlgItem(hDlg, IDC_STATIC_STATUS), L"In progress..");



            /*

            Do some work

            */



            SetWindowText(GetDlgItem(hDlg, IDC_STATIC_STATUS), L"Done.");

        }

        break;

    }

    .

    .

    .

}

break;

The default text on the static control was blank. When I ran the program we noticed that the static control never displayed "In Progress..". After finishing the actual meat of the job, the static control displayed "Done". We wondered whether it was because the actual work was happening too fast, and we put a sleep of 1 second after the first SetWindowText() call, but it didn’t solve the problem. We sat wondering about why this was happening. We checked the return value of the first SetWindowText call and it was non-zero which meant the call was successful. Another colleague of ours walked by and we explained the problem to her briefly, and she stared for a moment and said, UpdateWindow. And we all went "Aww…". So we put UpdateWindow(GetDlgItem(hDlg, IDC_STATIC_STATUS)); after both the calls and it worked, as expected. Painting is one of the most costliest operation and runs at a very low priority. So no wonder the calls before weren’t updating the text immediately. Sometimes we are all so occupied with bigger things that we miss out on the small ones.

Applications: Problems with painting DialogBox

Today I was working on an application with a colleague. The application contained many dialogs, well, it was a settings related app which let the user perform and view all sort of settings on the phone. The application was medium sized with the original code very badly written. And we were supposed to clean up the code and change the look and feel of the various Dialogs used in the app. And also make sure that the same code base worked on both Standard and Professional devices, when compiled with different flags of course. We were almost through most of the things but one problem kept bothering us for a long time. One of the dialogs in the app never displayed correctly. And it was strange because it was exactly the same as other dialogs. The DialogProc of the dialog looked something like this:

BOOL CALLBACK DlgProc((HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)

{

    BOOL fRet = FALSE;

    switch(uMessage)

    {

        case WM_INITDIALOG:

        {

            /*

            Do SHInitDialog()

            */

            /*

            Do SHCreateMenuBar()

            */

            /*

            Setup controls in the dialog

            */

            return TRUE;

        }

        break;



        case WM_COMMAND:

        {

            /*

            Handle all controls here

            */

            return TRUE;

        }

        break;



        case WM_ACTIVATE:

        {

            /*

            Do some stuff

            */

        }

        break;



        case WM_PAINT:

        {

            hdc = BeginPaint(hDlg, &ps);



            EndPaint(hDlg, &ps);

        }

        break;



        case WM_CLOSE:

        {

            EndDialog(hDlg, uMessage);

        }

        break;

    }



    fRet = TRUE;



    return fRet;

}

Well, the code was a lot more messy than is shown here. Anyways, we sat wondering why only this dialog was not getting displayed properly. The background of the dialog was not at all drawn and the controls on the dialog appeared shabbily. We thought that a certain sequence of events caused the dialog to be displayed that way, so we tried calling the dialog from different places but the result still the same. We checked and compared the dialog properties of various dialogs with this one. They were insignificantly different but we still tried to make them exactly same, but still no luck yet. After about 2 hours I noticed the return value of the dialog proc and saw that it was always returning true no matter what. And I knew. How in the silly world can I miss such an important thing for this long! The return value of a dialog proc is a really important thing. Returning TRUE from the function means that you have handled the message and the OS may or may not take further action, mostly it wont. Returning FALSE would tell the OS that you have not handled the message and the OS would now handle the message and execute the default handling for that message. Why our dialog was not getting displayed correctly was that, we were returning TRUE for all messages, including WM_PAINT. So the OS thought that I have handled the paint message it does not need to take any action. And that is why there were problems in painting the dialog. We changed the return value to be TRUE or FALSE depending on whether we handled that message or not and it worked.

 

Applications: Creating a simple UI application from scratch

In this series of posts I will show how we can create a basic UI application for a Windows Mobile Professional device from scratch, without having to go through all the hidden stuff that the wizard does to your application (the stdafx’s). And there is another reason why I would want to create an application from scratch, while porting applications, which were built using VS2005, to a BSP (in a build environment) I have faced lot of annoying problems. It throws a lot of compilation errors, and its a pain to resolve them. To make the application a little more useful, the application will display all the current running processes, and you will be able to refresh the view. I am planning to use a List View control for this, instead of a stale list box for two reasons, One I have never used a list view control and two you can display icons in a list view, cool right? Anyways, I have just started with the application. More posts coming soon.

Applications: Here is what I did over the weekend, Process Viewer

Well, I was just able to finish this small project of mine on time this weekend. But unfortunately I don’t have the time enough to write in detail about it. Anyways, I created a small Process Viewer. It shows all the current running processes in the system, shows the process id, number of threads running in the process and the load address of the process. The back-end didn’t take much time as compared to the UI itself. But I am happy that I finished it on time.

 

Here are a few screen shots of my Process Viewer:

and,

In the next few posts I will write about how I accomplished this task.

Plain text, ASCII, ANSI, UNICODE, UTF-8, UTF-16 confused?

I came across this wonderful post by Joel Spolsky  on characters, character sets, ANSI, ASCII, UNICODE and much much more. I have to admit that till now I thought plain text is ASCII is ANSI, all within 8-bits. And anything that takes up 2-bytes was unicode. Well, I couldn’t have been more wrong! Here are a few excerpts from his entry:

 

"All that stuff about "plain text = ascii = characters are 8 bits" is not only wrong, it’s hopelessly wrong, and if you’re still programming that way, you’re not much better than a medical doctor who doesn’t believe in germs."

 

"Some people are under the misconception that Unicode is simply a 16-bit code where each character takes 16 bits and therefore there are 65,536 possible characters. This is not, actually, correct. It is the single most common myth about Unicode, so if you thought that, don’t feel bad."

 

Don’t miss this one. Grab a read at:

 

Better learn late than never (:

Applications: Creating a simple UI application from scratch, Part 1

Lets create a simple UI application for Win Mob 6 professional device, from scratch. When you create the project using the wizard you can see that basic code is already there. The main function, the window procedure etc. And then there’s stdafx. I never quite understood what they are there for, but I guess they must be important. But when I create the application from scratch, there will no stdafx’s. Just a plain simple file with a UI that shows up.

To start, create a new project for Win32 smart devices, (I have mentioned about this is my previous posts). At the last screen of the wizard, check the “Empty Project” checkbox. It will be unchecked by default. When you click finish, you’ll see that the project is empty! (-:

Ok, so the first thing we do is add a cpp file to our sources directory.  Lets call it FromScratch.cpp.  Add the following code, just to test if things are alright:

#include <windows.h>

#include <aygshell.h>

 

int WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,

                  LPTSTR lpCmdLine, int nCmdShow)

{

      MessageBox(NULL, L"Hi there..", L"Info", MB_OK);

      return 0;

}

Hit compile and run. You should see a message box. windows.h is the standard header file and aygshell contains UI stuff.  WinMain is the entry point of your application and takes in 4 parameters. The first, hInst, is the handle to the instance of your module. The second is the handle to the previous instance of this program if it is already running. This param is always passed as NULL, so don’t bother. lpCmdLine is the command line arguments, if any and the last param, nCmdShow, determines how the window is to be shown. Most of the times I just ignore all the params except hInst.

Lets now add some UI to it. First create the resource file, which will hold all our application’s resources. Right click on “Resource Files” and add a new item, name it FromScratch.rc. resource.h will also be created in your project. Double click the resource file and add a new dialog to the resource by using “Add resource..”. Add a new dialog for pocket pc portrait. Change the dialog properties, I renamed the title to “From Scratch”, changed the caption of the default label and changed the ID of the dialog to IDD_PPC_FROMSCRATCH. My dialog now looks like this:

Lets add a menu bar to our application. The thing with menu bar is, you need to define the menubar in a separate text file (.rc2 file for e.g.) and then include this text file in the .rc file. This is a known bug. So add a new text file to the project, call it “FromScratch.rc2” and include it in the .rc file.

This menu bar will have two entries, the left soft key as “Exit” and the right soft key as “Refresh”. I ll let you in on refresh later.

We need two string resources for “Exit” and “Refresh” and two codes which will be sent when the user clicks on them. Define the menubar in the .rc2 file:

IDR_MENU_FROMSCRATCH SHMENUBAR DISCARDABLE

BEGIN

    IDR_MENU_FROMSCRATCH,

    2,

 

    I_IMAGENONE, IDM_EXIT, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_EXIT, 0, NOMENU,

   

    I_IMAGENONE, IDM_REFRESH, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_REFRESH, 0, NOMENU,

END

 

Add the following entries in the resource.h file:

#define IDR_MENU_FROMSCRATCH              501

#define IDS_EXIT                          502

#define IDS_REFRESH                       503

#define IDM_EXIT                          504

#define IDM_REFRESH                       505

 

And add the following stringtable in the .rc file:

 

STRINGTABLE

BEING

      IDS_EXIT                "Exit"

      IDS_REFRESH             "Refresh"

END

 

I have explained all these in my previous posts, so I am not going to repeat them.

Include resource.h in FromScratch.cpp. Add aygshell.lib in Additional dependencies (go to project properties, linker -> input). As mentioned before we need aygshell.lib for SHInitDialog, SHCreateMenuBar etc.

Now all we need to do is, add the code for FromScractchDlgProc and invoke the dialog from main.

Here is the code for FromScratchDlgProc:

BOOL CALLBACK FromScratchDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)

{

      int wmID, wmEvent;

      PAINTSTRUCT ps;

      HDC hdc;

 

      switch(uMessage)

      {

            case WM_INITDIALOG:

                  {

                        SHINITDLGINFO shidi;

                        SHMENUBARINFO mbi;

 

                        memset(&shidi, 0, sizeof(shidi));

                        memset(&mbi, 0, sizeof(mbi));

 

                        shidi.dwMask = SHIDIM_FLAGS;

                        shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;

                        shidi.hDlg = hDlg;

                        SHInitDialog(&shidi);

 

                        mbi.cbSize = sizeof(mbi);

                        mbi.hwndParent = hDlg;

                        mbi.nToolBarId = IDR_MENU_FROMSCRATCH;

                        mbi.hInstRes = g_hInst;

 

                        if(!SHCreateMenuBar(&mbi))

                        {

                              printf("FromScratch: Error creating menu bar, errcode:0x%x\n", GetLastError());

                        }

                  }

                  return TRUE;

 

            case WM_COMMAND:

                  {

                        wmID = LOWORD(wParam);

                        wmEvent = HIWORD(wParam);

 

                        switch(wmID)

                        {

                              case IDM_EXIT:

                                    EndDialog(hDlg, uMessage);

                                    break;

 

                              case IDM_REFRESH:

                                    //for now

                                    EndDialog(hDlg, uMessage);

                                    break;

                        }

                  }

                  break;

                 

            case WM_PAINT:

                  {

                        hdc = BeginPaint(hDlg, &ps);

 

                        EndPaint(hDlg, &ps);

                  }

                  break;

 

      }

 

      return FALSE;

}

Again, I have explained about this in my previous posts. Basically, WM_INITDIALOG is where you do your initialization part, set the dialog as fullscreen, add the menubar etc. There are no controls yet, so the WM_COMMAND is empty. However, we need to add the handling for the two menus that we added before. Invoke this dialog from main function:

DialogBox(hInst, MAKEINTRESOURCE(IDD_PPC_FROMSCRATCH), NULL, FromScratchDlgProc);

Create a global variable to store the instance handle of the program:

HINSTANCE g_hInst;

And in WinMain, store the hInst in g_hInst. WinMain now looks like:

int WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,

                  LPTSTR lpCmdLine, int nCmdShow)

{

      //MessageBox(NULL, L"Hi there..", L"Info", MB_OK);

 

      g_hInst = hInst;

 

      DialogBox(hInst, MAKEINTRESOURCE(IDD_PPC_FROMSCRATCH), NULL, FromScratchDlgProc);

      return 0;

}

And you’re done. Here is the screenshot: