Beginner question.

When the winmain function is set up like below does the HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow basically just initialize classes / variables?

1
2
3
4
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
   //Code Here
}



I'm also a little confused by the function declaration. I've never seen any function with two types before the functions name. Ie. the int WINAPI Winmain. I'm assuming Winmain is the function name and int WINAPI are types.
Last edited on
A Google search got me this from MSDN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx
It describes all the parameters and the return value.

The WINAPI part after the int but before the function name is a "calling convention" descriptor. You should try using Google for more information on what WINAPI is defined to mean and what calling convention it stands for.
closed account (N36fSL3A)
That's actually a #define. It's so your program knows to access a DLL.

I think...
Last edited on
Thank you so much Zhuge. I wasn't sure what was going on there (or even what to call it) but I think I understand it a little better now.

This article here seemed incredibly descriptive about what goes on with a calling convention.

http://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified

It would seem If i want to know more about how it works I'll have to read on assembly language though. Maybe after I figure out how to set up and use GUI's I can read up on that.


I guess the compiler automatically interprets this example

1
2
3
int main() {
   //code here
}


to look like this

1
2
3
int __cdecl main(){
   //code here
}


I ran a simple program that way and it compiled fine. So these calling conventions are how you would mix different language code together in an IDE?


edit.. I also tried replacing WINAPI with __stdcall and everything seems to work fine!
Last edited on
__cdecl is the standard calling convention of C/C++ if the calling convention isn't specified.

If you replace WINAPI or CALLBACK with __stdcall and try to compile x64 I don't believe it will work. If I were you I'd not substitute there macros (#defines). Its OK for learning purposes, which appears to be what you are doing.
I'll stick with the normal way of writing the windows function.

IE int WINAPI Winmain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

I haven't read much about the difference between 64 bit and 32 bit programming but my system is 64 bit and when I replaced just WINAPI with __stdcall the program still ran fine. I haven't tried replacing CALLBACK but I was mostly trying to learn why the winmain function was written that way.

I didn't know what a calling convention was and now it makes sense to me. It bothers me to use a piece of code that I don't at least understand on a basic level so I was trying to figure it out.

Thanks again. This website almost always has the answer :)
Last edited on
You are doing good Garion to question things as you are. Most folks don't seem to it seems.

For awhile I followed the same path you did, in that I started substituting __stdcall for WINAPI and CALLBACK. Like I said and you found out, that works in a 32 bit environment. If you are using the Code::Blocks development environment and you went with the stock install, I believe that's still using the MinGW 32 bit compiler. If you are using Visual Studio you can configure that environment in various project setup options to use either the x86 (32 bit) or x64 (64 bit) compiler. If you are running a 32 bit operating system your main choice is just 32 bit (even if the actual hardware is 64 bit). However, on 64 bit operating systems you can compile both x86 and x64 binaries.

I have some tutorial material here that might help you getting started ...

http://www.jose.it-berater.org/smfforum/index.php?topic=3389.0

Also, the Forger's Win32 Tutorials are often recommended. Back in the days when Windows was fairly young (that's a long time ago I realize - not sure they even had electiicity back then, and I think most folks used outhouses and dynasaurs roamed the earth), people learned Windows Api coding using books (I know - an antiquated method of learning). The most widely used book was Charles Petzold's Programming Windows. The reason I'm mentioning this is that he explained what all those funny symbols were that you are questioning, such as WINAPI, CALLBACK. And all the others too, that will likely be your next questions, such as WPARAM, LPARAM, et. el., ad infinitum.
I haven't read much about the difference between 64 bit and 32 bit programming but my system is 64 bit and when I replaced just WINAPI with __stdcall the program still ran fine.


It doesn't matter if the operating system is 64 bit, it matters is your executable is compiled as 64 bit or as 32 bit.
Modoran, I'm feeling like a fool now. I read what you said and just went back and tried some of the test code I used when I first began experimenting with x64, and now the very same code that wouldn't compile using __stdcall for WINAPI or CALLBACK is now compiling. To be very precise, the issue I had was compiling as x64 on 64 bit Windows 7. The latest Microsoft compiler I have is VC9 from VS 2008 Pro. Apparently the install of that on x86 Win XP doesn't install the 64 bit compilers. But it did when I installed on 64 bit Win 7. So long about 2010 I figured I'd try experimenting with x64 just for general knowledge even though I don't need x64 yet. The first thing I found out when I jumped through Visual Studio's hoops in 'Configuration Manager' to create an x64 build was it couldn't tolerate my substitutions of __stdcall for WINAPI or CALLBACK in any of my myriads of template programs where I had done that. I did a few minutes of research on the issue and seem to recall reading something to the effect that x64 only uses or tolerates one calling convention, and if I recall correctly, that would be __stdcall. And however Microsoft set up the includes for the compilers, it didn't want to see __stdcall substituted for WINAPI or CALLBACK as I had been doing kind of 'on a lark' for a couple of years, for really the same reasons the original poster mentioned doing it, i.e., just because it would work. So I'm wrong here in what I stated. I'm very sorry I posted erroneous information.

However, at this point I'm wondering what happened a few years ago when it simply wouldn't compile with __stdcall substituted for WINAPI and CALLBACK. And I mean, it wouldn't. I fought with it as we all do when something like that happens and we're not sure what's going on. The only way it would compile is if I put WINAPI and CALLBACK where I put __stdcall.

And just now I compiled two seperate programs invoking the x64 compiler - one in the Visual Studio IDE and another on the command line invoking the x64 compiler. And both now compiled clean.

I even checked the dates on the include files the compiler was using to see if in any updates Microsoft might have changed some include files, but unless I'm looking at the wrong ones, they haven't been changed. So I'm really, really, wondering what's going on here. Anybody have any ideas?

I use the MinGW x64 compiler a lot. I'm gonna ck and see if that works without the WINAPI, CALLBACK macro. Ever since I had that first 'run in' with the compilers and those macro substitutions, I went back to WINAPI and CALLBACK in my code. If __stdcall works and it looks like it does, I might go back to using that. I like the pretty blue text my editors use to highlight __stdcall instead of the drab looking WINAPI or CALLBACK:)

Here is one of my test programs followed with the console output from VC9's compile and MinGW TDM-GCC-64 ...



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Main.h
#ifndef Main_h
#define Main_h

#define dim(x) (sizeof(x) / sizeof(x[0]))

struct WndEventArgs
{
 HWND                         hWnd;
 WPARAM                       wParam;
 LPARAM                       lParam;
 HINSTANCE                    hIns;
};

long fnWndProc_OnCreate       (WndEventArgs& Wea);
long fnWndProc_OnDestroy      (WndEventArgs& Wea);

struct EVENTHANDLER
{
 unsigned int                 iMsg;
 long                         (*fnPtr)(WndEventArgs&);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                  fnWndProc_OnCreate},
 {WM_DESTROY,                 fnWndProc_OnDestroy}
};
#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//Main.cpp
// cl Main.cpp Kernel32.lib User32.lib Gdi32.lib /FeForm1_x64 /O1 /Os /MT /GA
// g++ Main.cpp -oForm1_MinGW64.exe -mwindows -m64 -s -Os
#include <windows.h>
#include <tchar.h>
#include "Form1.h"


long fnWndProc_OnCreate(WndEventArgs& Wea)
{
 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
 return 0;
}


long fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 PostQuitMessage(0);
 return 0;
}



LRESULT __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

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


int __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Form1");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=NULL;                             wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,320,305,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//Command Line Output
Setting environment for using Microsoft Visual Studio 2008 Beta2 x64 tools.

c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC>cd\

c:\>CD C:\Code\VStudio\VC++9\64_Bit\Form1

C:\Code\VStudio\VC++9\64_Bit\Form1>g++ Main.cpp -oForm1_MinGW64.exe -mwindows -m64 -s -Os

C:\Code\VStudio\VC++9\64_Bit\Form1>dir
 Volume in drive C is OSDisk
 Volume Serial Number is 3E79-B713

 Directory of C:\Code\VStudio\VC++9\64_Bit\Form1

01/24/2014  10:05 AM    <DIR>          .
01/24/2014  10:05 AM    <DIR>          ..
01/17/2013  04:13 PM               655 Form1.h
01/24/2014  10:05 AM            17,408 Form1_MinGW64.exe
01/24/2014  10:05 AM             1,708 Main.cpp
01/24/2014  08:41 AM            39,424 Main.exe
01/24/2014  08:41 AM             2,965 Main.obj
01/24/2014  09:29 AM               652 SomeDefines.txt
               6 File(s)         62,812 bytes
               2 Dir(s)  126,916,378,624 bytes free

C:\Code\VStudio\VC++9\64_Bit\Form1>Form1_MinGW64

C:\Code\VStudio\VC++9\64_Bit\Form1>cl Main.cpp Kernel32.lib User32.lib Gdi32.lib /FeForm1_x64 /O1 /Os /MT /GA
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Main.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form1_x64.exe
Main.obj
Kernel32.lib
User32.lib
Gdi32.lib

C:\Code\VStudio\VC++9\64_Bit\Form1>dir
 Volume in drive C is OSDisk
 Volume Serial Number is 3E79-B713

 Directory of C:\Code\VStudio\VC++9\64_Bit\Form1

01/24/2014  10:09 AM    <DIR>          .
01/24/2014  10:09 AM    <DIR>          ..
01/17/2013  04:13 PM               655 Form1.h
01/24/2014  10:05 AM            17,408 Form1_MinGW64.exe
01/24/2014  10:09 AM            39,424 Form1_x64.exe
01/24/2014  10:09 AM             1,709 Main.cpp
01/24/2014  08:41 AM            39,424 Main.exe
01/24/2014  10:09 AM             3,367 Main.obj
01/24/2014  09:29 AM               652 SomeDefines.txt
               7 File(s)        102,639 bytes
               2 Dir(s)  126,916,321,280 bytes free

C:\Code\VStudio\VC++9\64_Bit\Form1>Form1_x64

C:\Code\VStudio\VC++9\64_Bit\Form1>
*/


Yes, Visual Studio compiler ignores __stdcall on X64 platforms, though not all compilers do and you really shouldn't rely on this behaviour:
On ARM and x64 processors, __stdcall is accepted and ignored by the compiler; on ARM and x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.

http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx
This has been my big amazement for the day. I've a sinking feeling I'll never know why the compiler wouldn't accept that substitution several years back, but now it does. I can't reproduce the problem.

However, severasl weeks ago I had another interesting run in with this issue that cost me several hours. I was converting my ActiveX Grid Control which I had written in PowerBASIC over to C++. I was wanting to have it work both 32 bit and 64 bit. I had a call of EnumChildWindows() in it where the PowerBASIC EnumChildProc CALLBACK looked like so ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Function EnumGridProc _                                        ' The purpose of this code here is to solve a typical grid problem of
( _                                                            ' having an edit control stuck in a grid cell where the text in the
  Byval hWnd As Long, _                                        ' edit control hasn't yet been written to the underlying grid storage,
  Byval lParam As Dword _                                      ' and the user clicks a button or something, and processing occurs
) As Long                                                      ' without the user's new edit being used.  In other words, some way
  If GetClassLong(hWnd,%GCL_WNDPROC)=lParam Then               ' had to be found to guarantee that user edits are immediately written...
     #If %Def(%DEBUG)
     Print #fp, "    Called EnumGridProc() - ", hWnd, lParam
     #EndIf
     Local pGridData As GridData Ptr                           ' ..to the grid's storage when the user clicks elsewhere in the same grid
     pGridData=GetWindowLong(hWnd,0)                           ' as an edit was made, or any other grids on the user's Form.  So this
     Call IGrid_FlushData(@pGridData.pComObj)                  ' code here runs in response to a user's WM_LBUTTONDOWS in any grid on
  End If                                                       ' the Form.  The WM_LBUTTONDOWN will occur in fnCellProc() just below.
  Function=%True                                               ' See Call EnumChildWindows(). The way it works is that the address of
End Function                                                   ' the grid's WndProc is passed into this procedure through the lParam ...


Function fnCellProc _                                          ' ... parameter, and each hWnd generated by the enumerator of the whole
( _                                                            ' parent Form is checked against that WndProc address.  If a match is
  ByVal hCell As Long, _                                       ' made, then we know we'ge found a "Grid" object.  In that case use its
  ByVal wMsg As Long, _                                        ' hWnd to call IGrid_FlushData(), which causes the BSTR data in the edit
  ByVal wParam As Long, _                                      ' control to be written to the grid's storage buffer.
  ByVal lParam As Long _ 


This is how that translated to C++ ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL EnumGridProc(HWND hWnd, LPARAM lParam)
{
 #ifdef MYDEBUG
 _ftprintf(fp,_T("  Called EnumGridProc() - %u\t%u\n"),hWnd,lParam);
 #endif
 if(GetClassLongPtr(hWnd,GCLP_WNDPROC)==lParam)
 {
    #ifdef MYDEBUG
    _ftprintf(fp,_T("\n  Made A Match! - %u\t%u\n\n"),hWnd,lParam);
    #endif
    GridData* pGridData=(GridData*)GetWindowLongPtr(hWnd,0);
    pGridData->pComObj->FlushData();
 }

 return TRUE;
}


So ... that worked perfect in 32 bit PowerBASIC and perfect in 64 bit C++. But crashed hard in 32 bit C++!!! Now figure that!?! Intuitively, it seemed to me that 32 bit C++ isn't very different from 32 bit PowerBASIC, and those two shouldn't give me any trouble cuz I understand them so well. And I knew there wasn't anything wrong with the PowerBASIC code cuz I'd been using it for years. I figured that if anything I'd have trouble with 64 bit - not 32 bit.

So after several hours of pulling my hair out here's what finally dawned on me. Just like __cdecl is the standard calling convention of C/C++, __stdcall is standard with PowerBASIC. That's why there is no calling convention in front of its EnumChicdProc shown above, i.e., it defaults to __stdcall. I forgot to put the CALLBACK specifier in front of the function when I converted it to C, and it worked perfectly on my first compile when doing 64 bit because its ignored there anyway. Then when I tested my code 32 bit CRASHO!

One of my more memorable bugs which kind of helped me along the 64 bit road!

great post...very helpful..
Topic archived. No new replies allowed.