Visual Studio Notes

From Minor Miracle Software
Jump to: navigation, search

Visual Studio Notes
Assorted notes on Microsoft Visual Studio.[1]

Projects that create and use DLLs

Microsoft Walkthrough[2]
Be aware that mixing x86 and x64 projects and DLLs will produce link errors, but not notify you the cause was differing architectures.

Build Problems

  • Solution build is always "out of date".

If the solution was moved to a new folder the old paths are in the *.tlog files. Delete them and rebuild.

  • _WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h)

Put #include <SDKDDKVer.h> as first line in "StdAfx.h".

  • Change Active Configuration from Debug to Release.

Menu->BUILD->Configuration Manager->Active solution Configuration. Select Release.

Linking error: afxnmcd.lib(wincore2.obj) : error LNK2005
In your static library go to the STDAFX.H and comment out following preprocessor macro:
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS
  • Multiple Pre and Post Build Events[3]

Separate multiple events with a newline.

  • WINDOWS.H included twice in an MFC solution.

Usually "Winsock.h" is loaded somewhere. Add #include <afxsock.h> // MFC socket extensions to stdafx.h.

  • Launch time crash while loading m_hIcon in Dlg.cpp constructor.
//	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MAINFRAME));

RegEx in Visual Studio

In C# best to use the built-in regex. In VC++ use something like this:

  CString recv_cs;
  int64_t siz=recv(fd,bufp,1,0);      // Try to read one character.
  char get_c = *bufp;
  std::string str;
  std::regex ex ( "[a-zA-Z0-9_ ]"); // AlphaNumeric, numbers, underline and whitespace
  str = get_c;
  if( std::regex_match( str, ex ) ) {
      recv_cs += get_c; // recv_cs.Append does not work for a char.
  }

Class

  • Get the object's class name. CString name_cs = typeid(data).name();

Memory Leaks

Visual Leak Detector for VC++ v2.5.1[4]
Decent leak detector. Has problems with class destructors. It doesn't recognize them.
Install and open vld.h then comment out this:

  • Only works on x64 systems.
/*
#ifdef __AFXWIN_H__
#error[VLD COMPILE ERROR] '#include <vld.h>' should appear before '#include <afxwin.h>' in file stdafx.h
#endif
*/

Additional Include directory:

C:\Program Files (x86)\Visual Leak Detector\include

Additional Library directory:

C:\Program Files (x86)\Visual Leak Detector\lib\Win32

Place #include "vld.h" in the stdafx.h file as the first executable line.
In the vld.ini file set:

AggregateDuplicates = yes

Add vld include and lib paths to the project. Exit VS. Open VS. Then compile and run. You might have to do this several times to wipe out old memory entries.

  • File copy during build

Use a command like:

copy /Y $(SolutionDir)RIL\Lib\<name>.* $(TargetDir)<name>.*

Because VS reads it as one command, not many separated by ";".

Visual Studio IDEs

VS2010 to VS2012

  • Open the solution in VS2012 then save the *.sln file as.
  • Properties, Change Toolset to v100 from v110

VS2019

  • MFC Console. Select Windows Desktop Wizard with the tags; C++, Windows, Desktop, Console, and Library.

Visual Studio Data Structures

Main Article: Visual_Studio_Notes_Lists
Main Article: Visual_Studio_Notes_Stacks

Error Messages

Main Article: Visual_Studio_Notes_Errors

  1. _WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h)
    1. Put '#include <SDKDDKVer.h>' first in the header file.

Folder Selection

MFC, use CFolderPickerDialog.

  • Does a folder exist?

CreateDirectory returns a true on creating a directory or false otherwise.

	// Can't create the folder, can't create the file either.
	if (!CreateDirectory( DirectoryPath,NULL)) {
	  if (GetLastError() == ERROR_ALREADY_EXISTS) {
		// directory already exists
		  ErrorExit( "CreateDirectory: Folder already exits. " );
	  } else {
		// creation failed due to some other reason
		  ErrorExit( "CreateDirectory: Failed for other reason. " );
	  }
	}

Code Analysis

  1. C6284: Object passed as... Passing CString but the functions want string. Use
    1. str.Format ("%s Configuration", (LPCSTR) m_name); from str.Format ("%s Configuration", m_name);

CMFCButton

  • An image will appear left to the text.
    • Adding an image to a text button pushes the text to the right.

CStringArray

  • Always remember that CStringArray has NO copy constructor.

CFont

  • Has no copy-constructor. Pass LOGFONT structs instead.

Data Types

C++[5] [6]

C++
__int8 nSmall;      // Declares 8-bit integer   -128 to 127
__int16 nMedium;    // Declares 16-bit integer  -32,768 to 32,767
__int32 nLarge;     // Declares 32-bit integer  -2,147,483,648 to 2,147,483,647
__int64 nHuge;      // Declares 64-bit integer -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

Division

Integer division is modulus. The following code:

int a_int =  270208992;
int b_int =  436270230;

int c_int = a_int / b_int;
float d_float = a_int / b_int;

Sets "d_float" to 0.0, not 0.6193615182039811.

Cast the division to float sets "d_float" to 0.6193615182039811.

int a_int =  270208992;
int b_int =  436270230;

int c_int = a_int / b_int;
float d_float = (float) a_int / b_int;

Network Programming

  1. Visual Studio Network Programming

TabControl

VC++ TabControl in MFC. Decent tutorial here[7].
1 Create an MFC project. Select "Dialog Based".
2 In the dialog window drag the "Tab Control" tool from the tool box over and set the size. The tab control is empty. Time to flesh it out.
3 Select the tab control. Right-click and "Add Variable". Add a CWnd* and call it m_pwndShow.
4 Select the tab control. Right-click and "Add Event Handler". The wizard will create something like "OnTcnSelchangeTab1". This will execute when a tab is touched.
5 Select the tab control. Right-click and "Add Variable". Add a CTabCtrl variable, m_Tab in this example.
6 Go to "OnInitDialog()" and add initialization code for the tabs.

m_Tab.InsertItem( 0, "Tab One", 0); // First Tab 
m_Tab.InsertItem( 1, "Tab Two", 1); // Second Tab, etc.

7 Select the Main dialog window, right-click and "Add Class". Name it "ConeDialog" with CDialogEx for the base class. This will show up in *Dlg.h under m_Tab. In the header file make sure IDD points to the correct dialog IDD.
Set the properties as:

  • Style = Child
  • Border = None

8 Create a variable with the class created above.
9 Select the main dialog and create a CWnd* variable, m_pwndShow. This will display the tab dialogs.
9 Back to OnInitDialog:

// Fetch the m_Tab rectangle
CRect rect;
m_Tab.GetClientRect( &rect );

// Create the tab dialogs in reverse order. 
m_TwoDialog.Create( IDD_DIALOG2, &m_Tab );
m_TwoDialog.SetWindowPos( NULL, 5, 25, rect.Width() - 10, rect.Height() - 30, SWP_SHOWWINDOW | SWP_NOZORDER);

m_OneDialog.Create( IDD_DIALOG1, &m_Tab );
m_OneDialog.SetWindowPos( NULL, 5, 25, rect.Width() - 10, rect.Height() - 30, SWP_SHOWWINDOW | SWP_NOZORDER);
// etc.

// Hide all tabs except for the first one.
m_configTabDialog.ShowWindow( SW_HIDE );
// Set the display for the first one.
m_pwndShow = &m_OneDialog;
m_pwndShow->ShowWindow( SW_SHOW );

10 In OnTcnSelchangeTab1 add the following:


	// TODO: Add your control notification handler code here
	if( m_pwndShow != NULL )
	{
		m_pwndShow->ShowWindow( SW_HIDE );
		m_pwndShow = NULL;
	}
	int index = m_Tab.GetCurSel();

	switch(index) {
	case 0:
		m_OneDialog.ShowWindow( SW_SHOW );
		m_pwndShow = &m_OneDialog;
		break;
	case 1:
		m_TwoDialog.ShowWindow( SW_SHOW );
		m_pwndShow = &m_TwoDialog;
		break;

	}

Text Control

  • To insert a blank line use "\r\n".

Time libraries

    • CTime
    • CTimeSpan
    • COleDateTime
    • COleDateTimeSpan
  • Shifting focus to the next edit control
// Activates on an entry to StartDOY

	CWnd *Start_wPtr;
	CString Start_cs;

	Start_wPtr = GetDlgItem ( IDC_START_DOY );

	Start_wPtr->GetWindowTextA( Start_cs );

// After entering a 3 digit DOY shift the focus to StartHour
	if( Start_cs.GetLength() == 3 ) {
		CWnd *CWnd_wPtr = GetDlgItem ( IDC_START_HOUR );
		CWnd_wPtr->SetFocus();
	}

  • Class Name
CAge a(21);
CRuntimeClass* prt = a.GetRuntimeClass();
ASSERT(strcmp(prt->m_lpszClassName, "CAge")  == 0);   

void StartZero() 
{
	CEdit *Start_wPtr = NULL;

	Start_wPtr = (CEdit * ) GetDlgItem (IDC_START_YEAR);
	Start_wPtr->SetSel( 0, 255 );
	Start_wPtr->ReplaceSel("0");

	Start_wPtr = (CEdit * ) GetDlgItem (IDC_START_DOY);
	Start_wPtr->SetSel( 0, 255 );
	Start_wPtr->ReplaceSel("0");

	Start_wPtr = (CEdit * ) GetDlgItem (IDC_START_HOUR);
	Start_wPtr->SetSel( 0, 255 );
	Start_wPtr->ReplaceSel("0");

	Start_wPtr = (CEdit * ) GetDlgItem (IDC_START_MINUTES);
	Start_wPtr->SetSel( 0, 255 );
	Start_wPtr->ReplaceSel("0");

	Start_wPtr = (CEdit * ) GetDlgItem (IDC_START_SECONDS);
	Start_wPtr->SetSel( 0, 255 );
	Start_wPtr->ReplaceSel("0");
}

  • Path to empty_console debug
C:\Users\Software Engineering\Documents\Visual Studio 2010\Projects\empty_console\Debug
  • CString
  1. Raw initialization is "";
  • Daylight Savings Time

Use GetTimeZoneInformation[8] with a TIME_ZONE_INFORMATION[9] struck. The result is:
0 = TIME_ZONE_ID_UNKNOWN
1 = TIME_ZONE_ID_STANDARD
2 = TIME_ZONE_ID_DAYLIGHT
Code example:

TIME_ZONE_INFORMATION TimeZoneInformation = {0};
DWORD TZ_result = GetTimeZoneInformation( &TimeZoneInformation );
printf("\n TZ_result %d \n", TZ_result );

Dialog Boxes

  • MFC Dialog Boxes cannot be layered on upon the other. Use a Tab Control.
  • MFC Group Boxes are for show only. No way to enclose the controls therein into a class.

Modal dialog boxes, like MessageBox and AfxMessageBox, are modal and suspend execution until the user input responds to the dialog.

-- Default messagebox with variable message
MessageBox( NULL,
	(LPCSTR) "Inside Loop \r\n" + cmd2,
	NULL,
	MB_ICONWARNING
	);

or

AfxMessageBox("Are you sure you want to Set the Recorder time based on this PC's clock?", MB_ICONQUESTION | MB_YESNO);
CString str;
str.Format("my string %d", nYrIntValue);
AfxMessageBox(str);

Buttons

To create a button with changeable colors use an "MFC Button Control" in the dialog window and set it to:

  • Client Edge to true.
  • Caption to a space. It won't take a blank.
  • Style to No Borders
  • Visible to True

In OnInitDialog or another initialization method fetch the pointer and set the color.

CMFCButton  * pButton = (CMFCButton*)GetDlgItem(IDC_MFCBUTTON1);
pButton->SetFaceColor(RGB(0,100,0),true);

Close(x) button

  • Close(x) button uses IDCANCEL, which is a default, you don't have to create it.
  • In Dlg.h create:

afx_msg void OnBnClickedCancel(); // make this public

  • In Dlg.cpp create:
   BEGIN_MESSAGE_MAP( , )

ON_BN_CLICKED(IDCANCEL, &CSlavingDlg::OnBnClickedCancel)

   END_MESSAGE_MAP()
  • Define the method
void CSlavingDlg::OnBnClickedCancel() {
	OnBnClickedQuit();
}

CStatic

  • To change text: CStatic m_RecordCount.SetWindowText( "shadow" );
  • To change background color:

CString

  • C6284 bug: During analysis printf will report that CString is incompatible with "%s". But it still works perfectly. Use "(LPCTSTR) CString" to avoid the error.
  • #include <atlstr.h>
  • Does not have a copy-constructor or "=" operator.
  • The debugger displays the text in double quotes. For Example, "BLOOPER" is BLOOPER. When there is a double-double quote, ""BLOOPER"", those really are extra quotes.
  • printf - use printf("%S", test_cs.GetString() );
  • sprintf - to knit strings together.
char buff[32];
	sprintf_s( buff, "%hhu.%hhu.%hhu.%hhu", test_ip.b1, test_ip.b2, test_ip.b3, test_ip.b4);

  • CString and printf "%s". Sometimes using printf("%s", CString ); will produce a warning. Instead use printf("%s", CString.GetBuffer() );
  • Find method. Use below.
if (a.Find (b) != -1)

CString Formats

CString to CTime conversion

CString m_startTime;

m_startTime.Format ( _T("%04d:%03d:%02d:%02d:%02d.000"), m_startYear, m_startDoy, m_startHours, m_startMinutes, m_startSeconds);

CString and sscanf_s

Use swscanf_s, as in:

swscanf_s ( ca, _T("%d-%d:%d:%d"), &then.tm_yday, &then.tm_hour, &then.tm_min, &then.tm_sec);

CString tokenize because Microsoft was too lame to write it.
The CStringArray must be passed by reference because Microsoft was too lame to write a proper copy-constructor.
See Visual Studio C++ Libraries CharString for an implementation.

CString to Integer

CString cs = _T("278");
int test_int = _wtoi( cs ); // For wide, i.e., 16 bit characters
int test_int = atoi( cs ); // For average, i.e., 8 bit characters
// Test a string for all numeric
	// trim whitespace
	cs.Trim();
	// length test, must have leading zeros
	if( cs.GetLength() != 3 )
		return false;

	long result_l = -1;
	char *stopstring = nullptr;
	 
	CT2A ascii_num( cs ); // convert to a char string
	// strtol ignores whitespace front and back.
	// returns 0 if no conversion can be performed
	// returns LONG_MAX or LONG_MIN
	// in an alphanumeric string, stopstring points to
	//  the first non-number characters.
	result_l = strtol( ascii_num, &stopstring, 10 );
	int stopstring_len = strlen( stopstring );
	// If alphanumeric, return false
	if( stopstring_len > 0 )
		return false;


Integer to CString

CString cs;
cs.Format( "%d", 5 );

const char * to CString

CString except_cs = CString ( e, strlen( e ) );

except_cs.Append( e );

CString to const char *

Wide character
// Convert using the local code page
CString str(_T("Hello, world!"));
CT2A ascii(str);
TRACE(_T("ASCII: %S\n"), ascii.m_psz);

// Convert to UTF8
CString str(_T("Some Unicode goodness"));
CT2A ascii(str, CP_UTF8);
TRACE(_T("UTF8: %S\n"), ascii.m_psz);

// Convert to Thai code page
CString str(_T("Some Thai text"));
CT2A ascii(str, 874);
TRACE(_T("Thai: %S\n"), ascii.m_psz);

Regular character

char strPass[256];
strcpy_s( strPass, strCommand.GetLength(), CStringA(strCommand).GetString() );

CString: Find one char and delete it

Delete the last comma.

	CString buf_cs;
	buf_cs = "Hello Word,";
    Console::WriteLine( buf_cs );
	if( buf_cs.GetAt( buf_cs.GetLength() - 1 ) == ',')
		buf_cs.Delete( buf_cs.GetLength() -1, 1);

CString to std::string and back

CString filename = CString("file_name");
std::string string_s((LPCTSTR) filename);
std::string str ("str_one");
CString cs (str.c_str() );

CStringList

  • The equal operator, "=", does not work. But should.
    • Pass as & in a parameter
  • To copy a CStringList use below.
	CStringList list;
	CStringList listA;

	list.AddHead( "One" );
	list.AddHead( "Two" );
	list.AddHead( "Three" );
	POSITION pos;

	for( pos = list.GetHeadPosition(); pos != NULL; )
	{
		CString s = list.GetNext( pos );
		printf( "list: %s\n", s );
		listA.AddTail( s );
	}

	for( pos = listA.GetHeadPosition(); pos != NULL; )
	{
		CString s = listA.GetNext( pos );
		printf( "listA: %s\n", s );
	}

  • Loop through CStringList, Destructive
while( csList.GetCount() > 0 ) {
  CString temp_cs = csList.GetHead();
  // do something
  csList.RemoveHead();
}

  • Loop through CStringList, Nondestructive
CStringList list;
POSITION pos;

for( pos = list.GetHeadPosition(); pos != NULL; )
{
  CString s = list.GetNext( pos );
  // do something with CString s.
}

Threads

Main Article: Visual Studio Threads
Thread programming with Microsoft MFC C++.

Thread programming _beginthreadex vs. CreateThread.

  • If programming under VC++ OR using CRT inside the thread use _beginthreadex since it is C/C++ friendly.
  • If programming under C# use CreateThread.

Note: CreateThread does not have a memory leak since about VS2010.

Time

Simplest method for timing a loop in Win32, found thus far.

#include <math.h>
    double waited = 0.0;    // How long we've waited in seconds.
    int sleeptime = 500; // 500 milliseconds. 0.005 a second

    while ( <condition is true>)
    {
        if ( fmod( waited, 1 ) == 0)         // Message once every 1 second.
           <do something>;
         else if (waited >= 3.0 ) // waited 3 seconds
           <do something>;
        ::Sleep( sleeptime );  // Sleep a little while.
	waited += 0.005;      // increment waited
    }

Time/milliseconds on Windows

Keeping time on Win32 can be accomplished with chrono or SYSTEMTIME struct. SYSTEMTIME is easier because it can be formatted with COleDateTime and keeping the millisecond resolution in the SYSTEMTIME struct.

Using Win32/SYSTEMTIME/GetSystemTime() gives the same resolution as chrono at the millisecond level. Visual Studio Notes Millisecond Time Test verified this.

Millisecond Time Test
Used the code below to test millisecond accuracy with chron and SYSTEMTIME calls. Both returned 140ms.

// Time2012.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <chrono>

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>


// long operation to time
long long fib(long long n) {
  if (n < 2) {
    return n;
  } else {
    return fib(n-1) + fib(n-2);
  }
}
int main(int argc, char* argv[])
{


	// struct to request the time
	SYSTEMTIME oSytemTime;
  // getting the system time (Greenwich Mean Time); GetLocalTime () is possible too
	GetSystemTime (&oSytemTime);
	// get now for chrono
	auto start_time = std::chrono::high_resolution_clock::now();

     long long input = 32;
	 long long result = fib(input);
	// struct to request the time
	SYSTEMTIME oSytemTime_End;
  // getting the stop time
	GetSystemTime (&oSytemTime_End);
  auto end_time = std::chrono::high_resolution_clock::now();
  // chron difference
  auto time = end_time - start_time;
  // SYSTEMTIME difference
  int systime_diff = oSytemTime_End.wMilliseconds - oSytemTime.wMilliseconds;

  std::cout << "result = " << result << '\n';
  // printing the time with milliseconds
    std::cout << " chrono took " <<  time/std::chrono::milliseconds(1) << "ms to run.\n";

	printf(" SYSTEMTIME took: %d ms\n", systime_diff );

        getchar();
	return 0;
}

Time and Date Formats

Time and Date Formats

The time and date now are: %A, %B %d, %Y, %H:%M:%S
The time and date now are: Friday, October 05, 2018, 17:10:52

The date now are: %A, %B %d, %Y
The date now are: Friday, October 05, 2018

Julian Day, Hour, Minute, Second: %j:%H:%M:%S
Julian Day, Hour, Minute, Second: 278:17:10:52

Year, Julian Day, Hour, Minute, Second: %Y:%j:%H:%M:%S
Year, Julian Day, Hour, Minute, Second: 2018:278:17:10:52

Since Epoch began. Total days: %D, hours: %H, mins: %M, secs: %S
Since Epoch began. Total days: 43378, hours: 17, mins: 09, secs: 52

time_command format: %j-%H:%M:%S
time_command format: 278-17:10:52

date_command format: %Y-%m-%d
date_command format: 2018-10-05

Time Format Specification

Specifier Replaced by Example
%a Abbreviated weekday name Thu
%A Full weekday name Thursday
%b Abbreviated month name Aug
%B Full month name August
%c Date and time representation Thu Aug 23 14:55:02 2001
%C Year divided by 100 and truncated to integer (00-99) 20
%d Day of the month, zero-padded (01-31) 23
%D Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
%e Day of the month, space-padded ( 1-31) 23
%F Short YYYY-MM-DD date, equivalent to %Y-%m-%d 2001-08-23
%g Week-based year, last two digits (00-99) 01
%G Week-based year 2001
%h Abbreviated month name (same as %b) Aug
%H Hour in 24h format (00-23) 14
%I Hour in 12h format (01-12) 02
%j Day of the year (001-366) 235
%m Month as a decimal number (01-12) 08
%M Minute (00-59) 55
%n New-line character ('\n')
%p AM or PM designation PM
%r 12-hour clock time 02:55:02 pm
%R 24-hour HH:MM time, equivalent to %H:%M 14:55
%S Second (00-61) 02
%t Horizontal-tab character ('\t')
%T ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S 14:55:02
%u ISO 8601 weekday as number with Monday as 1 (1-7) 4
%U Week number with the first Sunday as the first day of week one (00-53) 33
%V ISO 8601 week number (01-53) 34
%w Weekday as a decimal number with Sunday as 0 (0-6) 4
%W Week number with the first Monday as the first day of week one (00-53) 34
%x Date representation 08/23/01
%X Time representation 14:55:02
%y Year, last two digits (00-99) 01
%Y Year 2001
%z ISO 8601 offset from UTC in timezone (1 minute=1, 1 hour=100)
If timezone cannot be determined, no characters +100
%Z Timezone name or abbreviation
If timezone cannot be determined, no characters CDT
%% A % sign %


  • COleDateTime

Verification is month 1 based, not zero based as in the documentation.
The fix is:
tTrigger = COleDateTime( m_triggerYear, month_int + 1, day_int, m_triggerHours, m_triggerMinutes, m_triggerSeconds );
Not the +1 in the month field.

CRichEditCtrl

Initializing is a bit different.
1. In Constructor() add.
AfxInitRichEdit2(); 2. In OnInitDialog() add.
m_AcuTelPort.SetEventMask(m_AcuTelPort.GetEventMask() | ENM_CHANGE);


Internal Links

Parent Article: Main Page