Problem with File Opening

So i'm trying to build this program while learning Win32API from theForge's tutorial at winprog.org, but when i click File->Open, my program just closes.

1
2
3
case ID_FILE_OPEN:
	DoFileOpen(hwnd);
break;			


This is the WndProc case for ID_FILE_OPEN, which would be the File->Open.
I couldn't paste the entire code, so here is a link for it:
https://dl.dropboxusercontent.com/u/67669537/program.txt

And here is a link to the compiled program:
https://dl.dropboxusercontent.com/u/67669537/ProgramTest.exe

Thanks in advance.
Last edited on
Try changing:
 
    ofn.lpstrFile = _T ("szFileName");
to
 
    ofn.lpstrFile = szFileName;


and
 
    LoadTextFileToEdit(hEdit, _T ("szFileName"));
to
 
    LoadTextFileToEdit(hEdit, szFileName);
It gives me errors, about LPSTR and LPWSTR. That's why i typed it like that. And also, i'm using VC++ 2010.
It gives me errors, about LPSTR and LPWSTR.
Please state what the actual errors are.

That's why i typed it like that.
Well, szFileName is the name of a variable. "szFileName" is something completely different, it's a character string, which is a constant. The program is not allowed to modify such a string. In any case it has no relationship with the similarly-named variable.

I didn't attempt to compile your entire program, I just copied the relevant function. My compiler complained that _T was not defined, so I added this:
 
#define _T(x)      x 


My version (I used a messagebox, but that was merely to verify that the rest of the code was working):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void DoFileOpen(HWND hwnd)
{
#define _T(x)      x
    OPENFILENAME ofn;
    char szFileName[MAX_PATH] = "";

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFilter = _T("Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0");
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    ofn.lpstrDefExt = _T ("txt");

    if(GetOpenFileName(&ofn))
    {
        MessageBoxA(hwnd, szFileName, "File to open:",  MB_ICONINFORMATION);
    }
#undef _T(x) 
}

closed account (z05DSL3A)
Using the _T() macro shouldn't give you any trouble.
@Chevril: your _T macro does nothing. You need to include <tchar.h> for the actual _T macro. It's the same as the TEXT() macro. IE: it makes string literals TCHAR strings.

@OP:
If you're using the TCHAR version of OPENFILENAME and GetOpenFileName, you need to use TCHARs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    OPENFILENAME ofn;  // <- TCHAR version
    TCHAR szFileName[MAX_PATH] = TEXT("");  //<- TCHAR and TEXT()

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFilter = TEXT("Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0"); // <- TEXT()
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    ofn.lpstrDefExt = TEXT("txt"); // <- TEXT()

    if(GetOpenFileName(&ofn))


Or... if you don't want to deal with TCHARs because they are retarded... use the Unicode version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    OPENFILENAMEW ofn;  // <- note the 'W' at the end for Unicode version
    wchar_t szFileName[MAX_PATH] = L"";  //<- wchar_t and L""

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(OPENFILENAMEW); // <- 'W' at end (or just use sizeof(ofn) )
    ofn.hwndOwner = hwnd;
    ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0"; // <- L"" to make it wide
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    ofn.lpstrDefExt = L"txt"; // <- L""

    if(GetOpenFileNameW(&ofn)) // <- 'W' at end to make it Unicode version 


Or... if you don't want to deal with Unicode... use the ASCII version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    OPENFILENAMEA ofn;  // <- note 'A' at end for ASCII version
    char szFileName[MAX_PATH] = "";  //<- normal char, normal string

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(OPENFILENAMEA); //<- 'A' at end (or sizeof(ofn) )
    ofn.hwndOwner = hwnd;
    ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0"; // <- normal string
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    ofn.lpstrDefExt = "txt"; // <- normal string

    if(GetOpenFileNameA(&ofn)) // <- note 'A' at end 

Last edited on
@Disch: Thank you so much, it works PERFECTLY now^^. I got confuse about Unicode stuff, is there some sort of documentation? I want to do this entirely with Unicode, if possible. I also use the TxtMessage() (the second function) to grab the first line from a "hi.txt" file and paste it in a MessageBox(), but it doesn't work with all texts, like this lenny face ( ͡° ͜ʖ ͡°). This post has a link to the latest source code.

@Chevril: Thanks for your help too, usefull info.

Thank you guys^^

Latest source code:
https://dl.dropboxusercontent.com/u/67669537/program2.txt
I got confuse about Unicode stuff


Let's start with what Unicode actually is.

Unicode is just a character set. All it does it assign unique numbers to glyphs. Examples:

A=0x41 (aka U+0041)
û=0xFB (aka U+00FB)
π=0x3C0
拢=0x62E2
etc

The numbers being assigned are referred to as "codepoints".

These codepoints conceptually range between 0-Infinity, though realistically, the range is smaller than a 32-bit variable (ie: a single 32-bit unsigned integer can represent any Unicode character).


Now there are multiple ways that these numbers can be represented. The most straightforward way would be to have a 32-bit value for each character in the string. That way 1 value = 1 character. Simple, but horrendously inefficient with space, since most characters used need only 1 or maybe occasionally 2 bytes. Because of this, there are multiple encodings. The most common are:


- UTF-8
- UTF-16
- UTF-32


UTF-32 is the simplest... and is what I said above: One 4-byte value = one character.

UTF-16 uses 2-byte values... and 99.99% of the time one 2-byte value will be one character... but for the codepoints above U+FFFF, it has to be spread across two 2-byte values.

UTF-8 is probably used most often. It uses 1-byte values... and for normal English text, that will mean that one byte = one character. However for codepoints above U+007F it has to use more bytes. Ultimately UTF-8 can use up to 4 bytes per character.


So UTF-8 is the most space efficient... but is also the most "variable" since multiple-byte characters are extremely common for everything except basic English.


Got it? Good. ;)
Now let's move onto the Windows mess.


(note the info I give below is specific to Windows, and the size of these data types and what they mean may differ on other platforms)




WinAPI is cursed with having to support a legacy program base, so all this stuff is especially confusing on Windows.


Nearly all WinAPI functions and structs (those which take strings) come in 3 forms:

TCHAR - the "normal" function. IE: MessageBox
char - the 'A' function. IE: MessageBoxA
wchar_t - the 'W' function. IE: MessageBoxW


'char's are just a simple 8-bit character you're probably used to dealing with.
'wchar_t's are the same thing, but are 16-bits which allow them to hold a wider range of characters.
'TCHAR' is a retarded typedef that could be either char or wchar_t. Its inconsistency makes it very difficult to use properly... and the weak typing of C/C++ makes it very easy to misuse on accident. I recommend avoiding it.


Windows treats wchar_t's as a UTF-16 encoded string. And Windows does everything 'under the hood' using UTF-16. Therefore the Wide version of these functions/structs is the most straight-forward way to deal with Windows and have support for the Unicode character set.


Unfortunately... Windows does not treat chars as UTF-8. Again, this is due to legacy reasons. Instead, it has to use the user's locale setting to expand non-ascii characters. So it uses a different character set.


The TCHAR thing... don't get me started. It's a disaster.


I want to do this entirely with Unicode, if possible. I also use the TxtMessage() (the second function) to grab the first line from a "hi.txt" file and paste it in a MessageBox(), but it doesn't work with all texts, like this lenny face ( ͡° ͜ʖ ͡°).


You certainly can print that lenny face with MessageBoxW(). You just have to make sure your text is UTF-16 encoded.

Which begs the question... how is your text file encoded?. You will need to know this before you can convert that encoding to UTF-16. It's probably UTF-8.... but double check. What editor are you using to make the file, and how are you saving it?



Windows supplies a function to convert char strings to UTF-16 strings called MultiByteToWideChar:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd319072%28v=vs.85%29.aspx

Usage example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const char* narrow = "A string you want to convert to UTF-16";
wchar_t wide[100];  // a buffer large enough to hold the wide version of that string

MultiByteToWideChar(
    CP_UTF8,  // tell it we want to convert from UTF-8 to UTF-16
    0,   // some extra flags (we don't really care about any of these)
    narrow, // the string we want to convert
    -1,  // the length of that string (we can use -1 to autodetect, since the string is null terminated)
    wide, // the buffer to put our generated UTF-16 string
    100   // the size of our buffer
);

// at this point.. 'wide' contains the UTF-16 encoded string.  So we can send it to
//  MessageBoxW:

MessageBoxW( mywnd, wide, L"Alert!", MB_OK );
So now, i'm having problems using getline() to pass the text into const char* narrow, here is the function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void TxtMessage()
{
	ifstream inFile ("hi.txt");
	string line,text;
	if (inFile.is_open())
	{
		while (getline(inFile,line));
		{
			text += line + "\n";
		}
		inFile.close();
	}
	const char* narrow = text.c_str();
	//LPCWSTR lpline = wline.c_str();
	MessageBox(NULL,  (narrow), L"^^",MB_OK|MB_SYSTEMMODAL);
}


The error is exactly: Error: argument of type "const char*" is incompatible with parameter of type "LPCWSTR"

I believe there might be a more productive way to get the text in the file but i'm not sure.

EDIT: I just realized how much i skipped, probably because i'm tired, let me try again, i'll be back in a few minutes.
Last edited on
So i did it as explained and no more errors showed up, until running... When i opened tried it, visual studio gave me an error and the program froze.

Here's the error:
Run-Time Check Failure #3 - The variable 'narrow' is being used without being initialized.

Here is the function:
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
void TxtMessage()
{
	ifstream inFile ("hi.txt");
	string line,text;
	if (inFile.is_open())
	{
		while (getline(inFile,line));
		{
			text += line + "\n";
		}
		inFile.close();
	}
	const char* narrow;
	wchar_t wide[100];
	MultiByteToWideChar(
		CP_UTF8,
		0,
		narrow,
		-1,
		wide,
		100
	);
	narrow = text.c_str();
	//LPCWSTR lpline = wline.c_str();
	MessageBox(NULL,  (wide), L"^^",MB_OK|MB_SYSTEMMODAL);
}


EDIT: I feel tired and i think i'll be going to bed. I'll check this thread again tomorrow.
Last edited on
Line 13: You create your narrow pointer, but it doesn't point to anything.
Line 15: narrow still doesn't point to anything. How is MultiByteToWideChar supposed to know what string you want to convert?

You probably meant to put line 23 above line 15.


Though if you have a string, you can get rid of narrow entirely by just passing the c_str to MultiByteToWideChar directly:

1
2
3
4
5
6
7
8
9
10
11
    // get rid of 'narrow' 
    // and while your at it, get rid of tabs and convert them to spaces.  (soapbox)
	wchar_t wide[100];
	MultiByteToWideChar(
		CP_UTF8,
		0,
		text.c_str(),  // <- give it the string data directly
		-1,
		wide,
		100
	);
Hey thanks, it works perfectly now ^^.
Topic archived. No new replies allowed.