• Forum
  • Lounge
  • Windows 10, Visual Studio 2015 and the W

 
Windows 10, Visual Studio 2015 and the Win32 API

closed account (E0p9LyTq)
<rant on>

Several months ago I upgraded to Visual Studio 2015, after years of mucking around with VS 6. Long before I upgraded from Win98 to Win 7/Win 10.

I decided to review the old stand-by Win32 API guide, "Programming Windows, 5th Edition" by Petzold.

The first few chapters, with really easy and simple code examples, compiled/tested without a problem.

When the examples began using C memory methods (calloc, for instance) VS2015 compiled the code without any errors, but the apps failed to run.

Well, they ran but there was no output. No window, the app started and ended without even a hint there was a crash. Yet there were crash/error system files generated. What the FRACK!

Compile the same code using TDM-GCC 4.9.2 and it works! What the DOUBLE FRACK!

Apparently VS doesn't "like" the C memory management functions.

After a lot of frustrated head-banging and curses directed at VS I looked at the section of code that was deceptively indicating that "ntdll.dll" was at fault (that was the cause of the crash according to the VS built-in debugger).

Petzold used calloc to read the string contents of a selected list box. Now why didn't he use the Win32 API list box messages?

I know MS has deprecated the C string functions, but even the C memory functions can be problematical? GRRRRRRR!

Maybe time to poke around online through the SDK to see if there are more "modern" ways to mash around with the Win32 API.

</rant off>

Thanks for listening. ;)
Can you post some example code for that? Anyways, malloc should work.

This: x = calloc(4,sizeof(int)); is equivalent to this: x = malloc(4*sizeof(int));
closed account (E0p9LyTq)
I was more ranting than seeking help. ;)

S G H wrote:
Can you post some example code for that?


Well, you "axed" for it (Petzold's original source):

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*----------------------------------------
   ENVIRON.C -- Environment List Box
                (c) Charles Petzold, 1998
  ----------------------------------------*/

#include <windows.h>

#define ID_LIST     1
#define ID_TEXT     2

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("Environ") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("Environment List Box"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

void FillListBox (HWND hwndList) 
{
     int     iLength ;
     TCHAR * pVarBlock, * pVarBeg, * pVarEnd, * pVarName ;

     pVarBlock = GetEnvironmentStrings () ;  // Get pointer to environment block

     while (*pVarBlock)
     {
          if (*pVarBlock != '=')   // Skip variable names beginning with '='
          {
               pVarBeg = pVarBlock ;              // Beginning of variable name
               while (*pVarBlock++ != '=') ;      // Scan until '='
               pVarEnd = pVarBlock - 1 ;          // Points to '=' sign
               iLength = pVarEnd - pVarBeg ;      // Length of variable name

                    // Allocate memory for the variable name and terminating
                    // zero. Copy the variable name and append a zero.

               pVarName = calloc (iLength + 1, sizeof (TCHAR)) ;
               CopyMemory (pVarName, pVarBeg, iLength * sizeof (TCHAR)) ;
               pVarName[iLength] = '\0' ;

                    // Put the variable name in the list box and free memory.

               SendMessage (hwndList, LB_ADDSTRING, 0, (LPARAM) pVarName) ;
               free (pVarName) ;
          }
          while (*pVarBlock++ != '\0') ;     // Scan until terminating zero
     }
     FreeEnvironmentStrings (pVarBlock) ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HWND  hwndList, hwndText ;
     int          iIndex, iLength, cxChar, cyChar ;
     TCHAR      * pVarName, * pVarValue ;

     switch (message)
     {
     case WM_CREATE :
          cxChar = LOWORD (GetDialogBaseUnits ()) ;
          cyChar = HIWORD (GetDialogBaseUnits ()) ;

               // Create listbox and static text windows.
          
          hwndList = CreateWindow (TEXT ("listbox"), NULL,
                              WS_CHILD | WS_VISIBLE | LBS_STANDARD,
                              cxChar, cyChar * 3,
                              cxChar * 16 + GetSystemMetrics (SM_CXVSCROLL),
                              cyChar * 5,
                              hwnd, (HMENU) ID_LIST,
                              (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),
                              NULL) ;
          
          hwndText = CreateWindow (TEXT ("static"), NULL,
                              WS_CHILD | WS_VISIBLE | SS_LEFT,
                              cxChar, cyChar, 
                              GetSystemMetrics (SM_CXSCREEN), cyChar,
                              hwnd, (HMENU) ID_TEXT,
                              (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),
                              NULL) ;

          FillListBox (hwndList) ;
          return 0 ;
          
     case WM_SETFOCUS :
          SetFocus (hwndList) ;
          return 0 ;
          
     case WM_COMMAND :
          if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_SELCHANGE)
          {
                    // Get current selection.

               iIndex  = SendMessage (hwndList, LB_GETCURSEL, 0, 0) ;
               iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0) + 1 ;
               pVarName = calloc (iLength, sizeof (TCHAR)) ;
               SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) pVarName) ;

                    // Get environment string.

               iLength = GetEnvironmentVariable (pVarName, NULL, 0) ;
               pVarValue = calloc (iLength, sizeof (TCHAR)) ;
               GetEnvironmentVariable (pVarName, pVarValue, iLength) ;

                    // Show it in window.
               
               SetWindowText (hwndText, pVarValue) ;
               free (pVarName) ;
               free (pVarValue) ;
          }
          return 0 ;

     case WM_DESTROY :
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
closed account (E0p9LyTq)
BTW, here's the exact error VS2015 throws up when debugging:

Unhandled exception at 0x7787C7A9 (ntdll.dll) in Win32Project1.exe: 0xC0000374: A heap has been corrupted (parameters: 0x778A8890).


Really weird is after VS2015 hits the exception there is an option to continue. Continue and the app runs just fine, as it does when compiled with TDM-GCC 4.9.2.

Am I 100% certain it is the C memory functions that are the cause of the crash? NO!

It could be the C string functions as well, MS replaced those functions with "safer" Windows string functions in the <strsafe.h> header.

At this point all I know is compiling the exact same source in VS2015 crashes, and doesn't crash when compiled with TDM-GCC 4.9.2.

The frustration is fading, the intrigue of tracking down how to make the older Petzold code be more "modern" is on the rise. This is becoming fun, flailing around and searching SDK documentation.
Last edited on
I mean that's code from 1998. Seems there is something wrong with the class that is being registered. If you do CreateWindow(NULL, ...) it doesn't crash anymore. Anyways why are you using Win32? It is pretty awful and I wouldn't use it unless you had to, like if you were making your own crossplatform GUI api.
The exception seems to come from the call to FreeEnvironmentStrings in FillListBox.
Saving the pointer returned from GetEnvironmentStrings and freeing that seems to prevent the exception. I'm not a Win32 guy so I don't know if this is the correct way to handle this issue.
closed account (E0p9LyTq)
@naraku9333, If there was an error with the code it wouldn't matter which compiler used, the app should fail.

The code as written worked with VS 6.0 and works with TDM-GCC 4.9.2.

Something changed with how Visual Studio builds Win32 API apps between VS 6.0 and VS 2015.

A lot has changed with the API since 1998 and now. Now I get to poke around the online SDK documentation to see if I can update the code to work.

@gooestuf,

Why do I use the Win32 SDK? Intellectual curiosity. A hobby.

You consider it horrible, I don't. It might be old, but it still works.
If there was an error with the code it wouldn't matter which compiler used, the app should fail.
If the heap is corrupted, the behavior of the program is undefined. The app may crash, or it may not, or it may crash only some times. Or demons may fly out your nose. All these behaviors are permitted by the standards.

Something changed with how Visual Studio builds Win32 API apps between VS 6.0 and VS 2015.
Most likely the 2016 CRT is more sensitive to heap corruption than the old 6.0 CRT. This is a good thing. Erroneous code should fail fast.
It's also possible that the code was relying on an invalid assumption.
Last edited on
> If there was an error with the code it wouldn't matter which compiler used, the app should fail.

Undefined behaviour is undefined behaviour; it need not be caught or diagnosed.

1
2
3
4
5
6
7
8
9
10
     pVarBlock = GetEnvironmentStrings () ;  // Get pointer to environment block

     while (*pVarBlock)
     {
          // ...
          while (*pVarBlock++ != '=') ;      // Scan until '='
          
          while (*pVarBlock++ != '\0') ;     // Scan until terminating zero
     }
     FreeEnvironmentStrings (pVarBlock) ;

The pointer passed to FreeEnvironmentStrings must be a pointer that was returned from a call to GetEnvironmentStrings
You also may have an issue with older code and internal string representations.

Code from 1998 be broken due to trying to pass non-unicode strings to unicode functions. Try changing the settings in the project.
Topic archived. No new replies allowed.