如何从GetOpenFileNameA返回一个字符串

时间:2016-07-11 14:19:36

标签: c windows winapi

这是一个在Windows中打开文件对话框并返回文件名为

的字符串的函数
#include <windows.h>
#include <commdlg.h>
#include <string.h>
char* openFileDlg(char FileTypes[]);
char* openFileDlg(char FileTypes[]){
    OPENFILENAME ofn;
    char szFile[260];
    HWND hwnd;
    HANDLE hf;
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = szFile;
    ofn.lpstrFile[0] = '\0';
    ofn.nMaxFile = sizeof(szFile);
    strcpy(ofn.lpstrFilter,FileTypes);
    ofn.nFilterIndex = 1;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
    if(GetOpenFileNameA(&ofn)){
        char *toReturn;
        sprintf(toReturn,"%s",ofn.lpstrFile);
        return toReturn;
    }
    else{
        return NULL;
    }
}

当我调用此函数并打开文件时,进程结束并返回值3(这意味着存在错误)。如何执行此函数以返回包含所选文件路径的字符串?

编辑:我已将代码更改为此代码,但仍然无效:

#include <windows.h>
#include <commdlg.h>
#include <string.h>
void openFileDlg(char *toReturn[],char FileTypes[]);
void openFileDlg(char *toReturn[],char FileTypes[]){
    OPENFILENAME ofn;
    /*
    Code for the settings of the GetOpenFileNameA, irrelevant in this question.
    If you really need to know what's here, look at the code above.
    */
    if(GetOpenFileNameA(&ofn)){
        strcpy(*toReturn,ofn.lpstrFile);
    }
    else{
        sprintf(*toReturn,"");
    }
}

我还应该说,如果我按下打开文件对话框中的取消按钮而不是选择文件,它可以正常工作。经过一些测试后,我发现导致错误的是strcpy(*toReturn,ofn.lpstrFile);行。

3 个答案:

答案 0 :(得分:5)

指针变量toReturn并不指向任何地方,在没有初始化的情况下以任何方式使用它(即使其指向某个有效且足够大的地方)将导致未定义的行为

你真的有两个解决方案:

  1. 动态分配内存并返回指向该内存的指针。这当然要求调用者在完成操作后释放内存。

  2. 让函数接受另外两个参数:指向缓冲区的指针和缓冲区的长度。然后将字符串复制到该缓冲区中,并返回一个布尔值&#34; true&#34;或&#34;假&#34;成功/失败状态。

  3. 我推荐第二号解决方案。

    在不相关的说明中,您无需使用昂贵的sprintf函数,只需strcpy(或strncpy,如果您使用第二种解决方案)会做的。

    在这两种情况下,您还必须记住C中的字符串的实际长度比例如1。 strlen报告终止'\0'字符。

答案 1 :(得分:3)

通常,如果要在C中返回字符串,我将使用以下方法之一:

1)传入字符串缓冲区以便写入:

int openFileDlg(char FileTypes[], char* toReturn, int bufLen) {
    /* ... */
    snprintf(toReturn, bufLen, /* what you want to print */);
    return ERROR; // status-code
} 

/* ... */

char errorBuf[80];
int result;

result = openFileDlg(..., errorBuf, sizeof(errorBuf));

2)分配内存,希望调用者释放它:

char* openFileDlg(char FileTypes[]) {
    /* ... */
    char *toReturn = malloc(/* big enough */);
    sprintf(toReturn, /* what you want to print */);
    return toReturn;
} 

/* ... */
char* error = openFileDlg(...);
if (error) {
    /* ... */
    free(error);
}
个人而言,我更喜欢(1)因为它更安全。选项(2)对函数的API更好,但如果忘记释放返回的缓冲区,则存在内存泄漏的风险。在一个更大的项目中(特别是有多人参与其中),这是一个非常现实的风险。

(我意识到这和Joachim的答案几乎一样,但是当我写作时,他的表现很快)

答案 2 :(得分:1)

您没有为返回值分配内存。如果你知道ofn.lpstrFile的长度,你可以这样做:

    char *toReturn = malloc( (sizeOfLpstrFile + 1) * sizeof(char)) ;
    sprintf(toReturn,"%s",ofn.lpstrFile);
    return toReturn;

我仍然认为这是一个坏主意,因为调用函数必须释放从界面中看不明显的内存。