我目前正在开发一个带有 C++
windows.h
内置 GUI 库的文本编辑器。
每次我尝试保存文件或打开文件并将其显示到窗口时,它都会返回错误,或者打开文件对话框失败。错误是
Exception: Segmentation Fault
,并且它总是发生在 GetOpenFileName();
线上。此外,如果它确实有效,FATAL ERROR: Couldn't display file
和 FATAL ERROR: Couldn't write file
总是会发生。我使用的是 MINGW MSYS 的 g++,所以也许我的编译器不好?
另外,我见过有人使用
L"your text"
,但是当我这样做时它会返回错误并且无法编译,所以我求助于_T("your text")
。这是因为没有启用 UNICODE。
顺便说一句,我的 VS Code Intellisense 告诉我,我在没有错误的地方出现了错误,并且程序编译正确(错误不在我打开/保存文件的位置)。
附注我还没有真正找到任何关于我正在尝试做的事情的文档。
这是我现在的代码:
#include <windows.h>
#include <commctrl.h>
#include <shellapi.h>
#include <tchar.h>
#define ID_BUTTON_1 1
#define ID_BUTTON_2 2
#define ID_BUTTON_3 3
#define ID_BUTTON_4 4
#define ID_BUTTON_5 5
#define ID_BUTTON_6 6
#define ID_BUTTON_7 7
#define ID_BUTTON_8 8
#define ID_BUTTON_9 9
#define ID_FILE_NEW 1000
#define ID_FILE_OPEN 1010
#define ID_FILE_SAVE 1020
#define ID_FILE_QUIT 1030
//Global Variables
//Main window class name
static TCHAR szWindowClass[] = _T("Desktop Application");
//Main window title bar text
static TCHAR szTitle[] = _T("C++ GUI Application");
//Instance handle for Win32 API calls
HINSTANCE hInst;
HWND hWnd;
HWND hWndFileText;
int hWndWidth = 600;
int hWndHeight = 400;
//Forward declarations of functions included in this code
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow) {
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(wcex.hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL,
_T("Call to RegisterClassEx Failed!"),
_T("Error"),
MB_ICONERROR);
return 1;
}
//Store instance handle in our global variable
hInst = hInstance;
HMENU hFileMenu = CreateMenu();
HMENU hMenuBar = CreateMenu();
AppendMenu(hFileMenu, MF_STRING, ID_FILE_NEW, _T("New"));
AppendMenu(hFileMenu, MF_STRING, ID_FILE_OPEN, _T("Open"));
AppendMenu(hFileMenu, MF_STRING, ID_FILE_SAVE, _T("Save"));
AppendMenu(hFileMenu, MF_STRING, ID_FILE_QUIT, _T("Quit"));
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFileMenu, _T("File"));
hWnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW, //Extended window style
szWindowClass, //Name of application
szTitle, //Title bar text
WS_OVERLAPPEDWINDOW, //window characteristics
CW_USEDEFAULT, //Default Position x
CW_USEDEFAULT, //Default Position y
hWndWidth, //Window size (width)
hWndHeight, //Window size (height)
NULL, //Window parent
hMenuBar, //Menu bar
hInstance, //first parameter from WinMain
NULL
);
if (!hWnd) {
MessageBox(NULL,
_T("Call to CreateWindowEx Failed!"),
_T("Error"),
MB_ICONERROR);
return 1;
}
HWND hWndFileText = CreateWindow(
_T("EDIT"),
_T("File text goes here"),
WS_VISIBLE | WS_CHILD | ES_LEFT | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE,
5, 30, hWndWidth - 5, hWndHeight - 30,
hWnd, NULL, hInst, NULL);
if (!hWndFileText) {
MessageBox(hWnd,
_T("Call to create texbox failed!"),
_T("Error"),
MB_ICONERROR);
return 1;
}
ShowWindow(hWnd, nCmdShow); //Value returned from CreateWindowEx,
//fourth WinMain parameter
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//WndProc (window process)
//
//Processes messages for the main window
//
//WM_PAINT - paints the main window
//WM_DESTROY - posts a quit message
char fileData[100];
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc; //handle to device context (hdc)
TCHAR greeting[] = _T("Hello, world!"); //Text for window
switch (message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
//This is where stuff is put in the window
//like text, buttons, etc.
//For now, I'm putting a simple hello, world
//at the top left corner of the window
TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
EndPaint(hWnd, &ps); //releases device context and ends paint request
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_FILE_QUIT:
DestroyWindow(hWnd);
break;
case ID_FILE_OPEN:
{
LPDWORD bytesRead = 0;
OPENFILENAME fileName; //Dialog box structure
HANDLE fileHandle;
char szfileName[260]; //Max length of file name
char* fileReadBuffer; //Place to put text read from file
DWORD fileSize;
ZeroMemory(&fileName, sizeof(fileName));
fileName.lStructSize = sizeof(fileName);
fileName.hwndOwner = hWnd;
fileName.lpstrFile = szfileName;
fileName.nMaxFile = sizeof(szfileName);
fileName.lpstrFile[0] = '\0';
fileName.lpstrFilter = _T("Text (.txt)\0*.TXT\0");
fileName.nFilterIndex = 1;
fileName.lpstrFileTitle = NULL;
fileName.nMaxFileTitle = 0;
fileName.lpstrInitialDir = NULL;
fileName.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&fileName)) {
fileHandle = CreateFile(fileName.lpstrFile,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else {
MessageBox(hWnd, _T("Couldn't Open File!"), _T("ERROR"), MB_ICONERROR);
break;
}
fileSize = GetFileSize(fileHandle, NULL);
fileReadBuffer = (char*)GlobalAlloc(GPTR, (fileSize+1));
if (!ReadFile(fileHandle, (LPVOID)fileReadBuffer, fileSize, bytesRead, NULL)) {
MessageBox(hWnd, _T("Fatal Error: Couldn't initialize file read"), _T("ERROR"), MB_ICONERROR);
}
else {
MessageBox(hWnd, _T("File Read"), _T("Success"), MB_OK);
}
SetWindowText(hWndFileText, _T("Attempting to display file"));
fileReadBuffer[fileSize] = '\0';
if (!SetWindowText(hWndFileText, fileReadBuffer)) { //This never works
MessageBox(hWnd, _T("Fatal Error: Couldn't display file"), _T("ERROR"), MB_ICONERROR);
}
else {
MessageBox(hWnd, _T("File displayed"), _T("Success"), MB_OK);
}
CloseHandle(fileHandle);
break;
}
case ID_FILE_SAVE:
{
LPDWORD bytesWritten;
OPENFILENAME fileName; //Dialog box structure
HANDLE fileHandle;
char szfileName[260]; //Max length of file name
char fileWriteBuffer[100]; //Place to put text from textbox
fileName.lStructSize = sizeof(fileName);
fileName.hwndOwner = hWnd;
fileName.lpstrFile = szfileName;
fileName.nMaxFile = sizeof(szfileName);
fileName.lpstrFile[0] = '\0';
fileName.lpstrFilter = _T("Text (.txt)\0*.TXT\0");
fileName.nFilterIndex = 1;
fileName.lpstrFileTitle = NULL;
fileName.nMaxFileTitle = 0;
fileName.lpstrInitialDir = NULL;
fileName.Flags = OFN_EXPLORER;
if (GetOpenFileName(&fileName)) { //This is where error occurs
fileHandle = CreateFile(fileName.lpstrFile,
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else {
MessageBox(hWnd, _T("Couldn't Open File For Saving!"), _T("ERROR"), MB_ICONERROR);
break;
}
GetWindowText(hWndFileText, fileWriteBuffer, 99);
if(!WriteFile(fileHandle, fileWriteBuffer, sizeof(fileWriteBuffer), bytesWritten, NULL)){ //This never works
MessageBox(hWnd, _T("FATAL ERROR: Couln't write to file!"), _T("ERROR"), MB_ICONERROR);
}
else {
MessageBox(hWnd, _T("File saved successfully!"), _T("Success"), MB_OK);
}
}
default:
break;
}
break;
case WM_CREATE:
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
我知道我不需要第二个和第三个包含,我只是没有删除它们。按钮 1-9 是稍后使用的,我还没有做到这一点,但它们不应该引起问题。
我查看了一堆有关如何使用
CreateFile()
、ReadFile()
和 WriteFile()
函数的文档,这就是我到目前为止所得到的,但似乎都不起作用。
另外,我不知道如何初始化保存文件对话框,所以它是两者的打开文件对话框(但由于某种原因,保存功能中打开文件对话框的窗口标题是
A[*62]<
或其他一些废话(垃圾?))。
请不要发表任何类似“这对于初学者来说太难了”之类的评论,因为这对于其他有类似问题的人来说并不是很有帮助。
这是完整的源代码,所以如果你想尝试一下,你应该能够按原样编译它。
fileName.lpstrFilter = _T("Text (.txt)\0*.TXT\0");
不可否认,文档包含成对的以 null 结尾的过滤器字符串的缓冲区。缓冲区中的最后一个字符串必须以两个 NULL 字符终止。
如果我没记错的话(并且基于我刚刚检查的一些旧代码),在终止最后对的第二个字符串的空字符之后,实际上需要一对空字符(即一对空字符串)。所以缓冲区应该以三个空字符结束。
GetOpenFileName 和 GetSaveFileName 对话框非常古老且粗糙。通用项目对话框提供了更新的界面。在某些方面,它们更难使用,因为您需要了解一些有关 COM 编程的知识。一旦你克服了这个障碍,它们就有点难以使用了,因为有设置每个选项的方法,所以你可以获得更多的类型安全性。