【问题标题】:c++ win32 console app adding a "choose file" dialog box using windows apic++ win32控制台应用程序使用windows api添加“选择文件”对话框
【发布时间】:2017-03-29 22:45:34
【问题描述】:

我正在尝试向我的项目添加一个选择文件对话框,该对话框现在只能接受用户输入的文件名。

我做了一些搜索,似乎带有 GetOpenFileName 函数的 windows API 是我最简单的方法。但是,当我从 MSDN 或其他网站复制和粘贴示例代码时,我遇到了一些构建错误。

我使用的是visual studio 2017。我使用的示例代码来自http://www.cplusplus.com/forum/windows/169960/

#include <iostream>

#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>

int main()
{
  char filename[ MAX_PATH ];

  OPENFILENAME ofn;
    ZeroMemory( &filename, sizeof( filename ) );
    ZeroMemory( &ofn,      sizeof( ofn ) );
    ofn.lStructSize  = sizeof( ofn );
    ofn.hwndOwner    = NULL;  // If you have a window to center over, put its HANDLE here
    ofn.lpstrFilter  = "Text Files\0*.txt\0Any File\0*.*\0";
    ofn.lpstrFile    = filename;
    ofn.nMaxFile     = MAX_PATH;
    ofn.lpstrTitle   = "Select a File, yo!";
    ofn.Flags        = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;

  if (GetOpenFileNameA( &ofn ))
  {
    std::cout << "You chose the file \"" << filename << "\"\n";
  }
  else
  {
    // All this stuff below is to tell you exactly how you messed up above. 
    // Once you've got that fixed, you can often (not always!) reduce it to a 'user cancelled' assumption.
    switch (CommDlgExtendedError())
    {
      case CDERR_DIALOGFAILURE   : std::cout << "CDERR_DIALOGFAILURE\n";   break;
      case CDERR_FINDRESFAILURE  : std::cout << "CDERR_FINDRESFAILURE\n";  break;
      case CDERR_INITIALIZATION  : std::cout << "CDERR_INITIALIZATION\n";  break;
      case CDERR_LOADRESFAILURE  : std::cout << "CDERR_LOADRESFAILURE\n";  break;
      case CDERR_LOADSTRFAILURE  : std::cout << "CDERR_LOADSTRFAILURE\n";  break;
      case CDERR_LOCKRESFAILURE  : std::cout << "CDERR_LOCKRESFAILURE\n";  break;
      case CDERR_MEMALLOCFAILURE : std::cout << "CDERR_MEMALLOCFAILURE\n"; break;
      case CDERR_MEMLOCKFAILURE  : std::cout << "CDERR_MEMLOCKFAILURE\n";  break;
      case CDERR_NOHINSTANCE     : std::cout << "CDERR_NOHINSTANCE\n";     break;
      case CDERR_NOHOOK          : std::cout << "CDERR_NOHOOK\n";          break;
      case CDERR_NOTEMPLATE      : std::cout << "CDERR_NOTEMPLATE\n";      break;
      case CDERR_STRUCTSIZE      : std::cout << "CDERR_STRUCTSIZE\n";      break;
      case FNERR_BUFFERTOOSMALL  : std::cout << "FNERR_BUFFERTOOSMALL\n";  break;
      case FNERR_INVALIDFILENAME : std::cout << "FNERR_INVALIDFILENAME\n"; break;
      case FNERR_SUBCLASSFAILURE : std::cout << "FNERR_SUBCLASSFAILURE\n"; break;
      default                    : std::cout << "You cancelled.\n";
    }
  }
}

当我复制并粘贴到 vs 时,它显示以下内容:

Severity    Code    Description Project File    Line    Suppression State
Error   C2440   '=': cannot convert from 'char [260]' to 'LPWSTR'   ConsoleApplication1 c:\users\xfan0\documents\visual studio 2017\projects\consoleapplication1\consoleapplication1\consoleapplication1.cpp    18  
Severity    Code    Description Project File    Line    Suppression State
Error   C2440   '=': cannot convert from 'const char [19]' to 'LPCWSTR' ConsoleApplication1 c:\users\xfan0\documents\visual studio 2017\projects\consoleapplication1\consoleapplication1\consoleapplication1.cpp    20  
Severity    Code    Description Project File    Line    Suppression State
Error   C2664   'BOOL GetOpenFileNameA(LPOPENFILENAMEA)': cannot convert argument 1 from 'OPENFILENAME *' to 'LPOPENFILENAMEA'  ConsoleApplication1 c:\users\xfan0\documents\visual studio 2017\projects\consoleapplication1\consoleapplication1\consoleapplication1.cpp    23  

Severity    Code    Description Project File    Line    Suppression State
Error (active)  E0167   argument of type "OPENFILENAME *" is incompatible with parameter of type "LPOPENFILENAMEA"  ConsoleApplication1 c:\Users\XFAN0\Documents\Visual Studio 2017\Projects\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp    23  

试图搜索但没有找到我的运气:(

【问题讨论】:

  • 您的构建默认需要宽字符串,但您使用的是 MBCS 字符串。您可以在调用 Windows API 时更改代码以使用宽字符串,也可以更改项目的设置(项目属性 -> 常规 -> 字符集 -> 使用多字节字符集)。
  • 在某些地方,您抓取的示例代码明确调用 MBCS 版本(例如,GetOpenFileNameA),但它并没有始终如一地这样做(例如,OPENFILENAME 默认为 OPENFILENAMEW 而不是OPENFILENAMEA)。在现代 Windows 程序中,首选使用 API 的宽字符串版本。 MBCS 功能主要是为了向后兼容,它有局限性。
  • 酷。实际上,我尝试了该设置,但是当我测试另一个示例时,它不起作用大声笑。
  • 不太清楚你的第二点,但是……我猜还有很多东西要研究。还是谢谢你。
  • 使用OPENFILENAMEA 而不是OPENFILENAME

标签: c++ user-interface visual-studio-2017 windows-api-code-pack getopenfilename


【解决方案1】:

微软通常提倡的方法是使用他们的“通用文本”宏,所以你的字符串文字看起来像这样:

ofn.lpstrFilter = _T("Text Files\0*.txt\0Any File\0*.*\0");
ofn.lpstrTitle = _T("Select a File, yo!");

这样,您可以构建窄或宽字符串(后者通过定义UNICODE_UNICODE)。对于窄字符构建,_T 将映射为空,对于宽字符构建,将映射到 L,因此您会自动获得适合您构建方式的字符串。

要使用它,您需要包含&lt;tchar.h&gt;

例如:

#include <iostream>
#include <tchar.h>

#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>

int main()
{
  char filename[ MAX_PATH ];

  OPENFILENAME ofn;
    ZeroMemory( &filename, sizeof( filename ) );
    ZeroMemory( &ofn,      sizeof( ofn ) );
    ofn.lStructSize  = sizeof( ofn );
    ofn.hwndOwner    = NULL;  // If you have a window to center over, put its HANDLE here
    ofn.lpstrFilter  = _T("Text Files\0*.txt\0Any File\0*.*\0");
    ofn.lpstrFile    = filename;
    ofn.nMaxFile     = MAX_PATH;
    ofn.lpstrTitle   = _T("Select a File, yo!");
    ofn.Flags        = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;

  if (GetOpenFileName( &ofn ))
  {
    std::cout << "You chose the file \"" << filename << "\"\n";
  }
  else
  {
    // All this stuff below is to tell you exactly how you messed up above. 
    // Once you've got that fixed, you can often (not always!) reduce it to a 'user cancelled' assumption.
    switch (CommDlgExtendedError())
    {
      case CDERR_DIALOGFAILURE   : std::cout << "CDERR_DIALOGFAILURE\n";   break;
      case CDERR_FINDRESFAILURE  : std::cout << "CDERR_FINDRESFAILURE\n";  break;
      case CDERR_INITIALIZATION  : std::cout << "CDERR_INITIALIZATION\n";  break;
      case CDERR_LOADRESFAILURE  : std::cout << "CDERR_LOADRESFAILURE\n";  break;
      case CDERR_LOADSTRFAILURE  : std::cout << "CDERR_LOADSTRFAILURE\n";  break;
      case CDERR_LOCKRESFAILURE  : std::cout << "CDERR_LOCKRESFAILURE\n";  break;
      case CDERR_MEMALLOCFAILURE : std::cout << "CDERR_MEMALLOCFAILURE\n"; break;
      case CDERR_MEMLOCKFAILURE  : std::cout << "CDERR_MEMLOCKFAILURE\n";  break;
      case CDERR_NOHINSTANCE     : std::cout << "CDERR_NOHINSTANCE\n";     break;
      case CDERR_NOHOOK          : std::cout << "CDERR_NOHOOK\n";          break;
      case CDERR_NOTEMPLATE      : std::cout << "CDERR_NOTEMPLATE\n";      break;
      case CDERR_STRUCTSIZE      : std::cout << "CDERR_STRUCTSIZE\n";      break;
      case FNERR_BUFFERTOOSMALL  : std::cout << "FNERR_BUFFERTOOSMALL\n";  break;
      case FNERR_INVALIDFILENAME : std::cout << "FNERR_INVALIDFILENAME\n"; break;
      case FNERR_SUBCLASSFAILURE : std::cout << "FNERR_SUBCLASSFAILURE\n"; break;
      default                    : std::cout << "You cancelled.\n";
    }
  }
}

要获得“保存”文件名,只需将 GetOpenFilename 更改为 GetSaveFilename。不过,您可能在 OPENFILENAME 中传递的标志有一些差异——例如,在打开文件时传递 OFN_FILEMUSTEXIST 是很常见的,但在保存时几乎不想传递一个文件。

【讨论】:

  • 保存文件对话框,有没有像“OPENFILENAME”这样的简单方法?
  • @XiaofengFan:GetSaveFilename
  • 有什么可以学习的例子吗?我已经检查了 MSDN 的文档,但给出的示例也与其他内容一样。所以我无法理解给出的例子(在这个阶段)。非常感谢 GetSaveFileName 使用的一些可执行示例。
猜你喜欢
  • 1970-01-01
  • 2011-01-28
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多