paul
paul

Reputation: 39

trouble manipulating wchar

I'm new in c++. I'm trying to list files in dir. I'm using unicode. The problem is not listing files but treat string and paths with wchar*, I'm going mad. Here's my test code:

#define UNICODE 1
#include <stdio.h>
#include <windows.h>
#include <wchar.h>


int wmain(int argc,wchar_t **argv){
    if (argc > 1){
        wchar_t* path=argv[1];
        wchar_t* pwc;
        int last_occurence;
        pwc=wcsrchr(path,L'\\');  
        last_occurence = pwc-path+1;
        int len = wcslen(path);
        if (last_occurence == len){
            //slash
        }else{
            //no slash
            wcscat(path,L"\\");
        }
        wcscat(path,L"*");

        WIN32_FIND_DATA FindData;
        HANDLE hSearch;
        hSearch = FindFirstFile(path , &FindData);
        if(hSearch == INVALID_HANDLE_VALUE){
            return -1;
        }else{
            // *** PROBLEM STARTS HERE
            wchar_t* filePath=NULL;
            do{
                wcscpy(filePath,path);
                wcscat(filePath,FindData.cFileName);
                wprintf(L"Path %s\n",filePath);
                memset(filePath, '\0', wcslen(filePath));
            // *** PROBLEM ENDS HERE
            }while( FindNextFile(hSearch, &FindData) > 0 );
            int ret = FindClose(hSearch);
            return 0;
        }
    }
}

When I run the compiled app, it stops responding. What I would like to do is print the path I pass to my app (c:\dir1\dir2) and append the files in it (file1,file2) as follows:

c:\dir1\dir2\file1

c:\dir1\dir2\file2

How to solve this problem? There are best methods to do something like this? I would remain with wchar not std string if possible

Upvotes: 0

Views: 338

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 597051

There are a few issues with your code.

  • you are concatenating "\\*" to the memory that argv[1] points to, which is bad. You need to change path from a wchar_t* pointer to an wchar_t[] array, and then wcscpy the argv[1] data into it.

  • you are not allocating any memory for filePath, so wcscpy() and wcscat() are writing to invalid memory. You need to change filePath to a wchar_t[] array as well, and then wcscpy/wcscat the path data into it.

  • you are also not ignoring the concatenated * when combining the path and cFileNames values together.

  • you don't need the memset() at all (especially since you are giving it the wrong byte count anyway).

Try something more like this instead:

#define UNICODE 1
#include <stdio.h>
#include <windows.h>
#include <wchar.h>

int wmain(int argc, wchar_t **argv)
{
    if (argc < 2)
    {
        wprintf(L"Usage: \"%s\" path\n", argv[0]);
        return -1;
    }

    int len = wcslen(argv[1]);
    if (len >= MAX_PATH)
    {
        wprintf(L"Path is too long\n");
        return -1;
    }

    wchar_t path[MAX_PATH+1] = {};
    wcscpy(path, argv[1]);
    if ((len > 0) && (path[len-1] != L'\\'))
        wcscat(path, L"\\");

    wchar_t searchMask[MAX_PATH+2] = {};
    wcscpy(searchMask, path);
    wcscat(searchMask, L"*");

    WIN32_FIND_DATA FindData;
    HANDLE hSearch = FindFirstFileW(searchMask, &FindData);
    if (hSearch == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() != ERROR_FILE_NOT_FOUND)
        {
            wprintf(L"Error looking for first file\n");
            return -1;
        }
        wprintf(L"No files found\n");
    }
    else
    {
        wchar_t filePath[MAX_PATH*2];
        do
        {
            wcscpy(filePath, path);
            wcscat(filePath, FindData.cFileName);
            wprintf(L"Path %s\n", filePath);
        }
        while (FindNextFileW(hSearch, &FindData));

        if (GetLastError() != ERROR_NO_MORE_FILES)
        {
            FindClose(hSearch);
            wprintf(L"Error looking for next file\n");
            return -1;
        }

        FindClose(hSearch);
    }

    return 0;
}

Though, you really should be using the std::unique_ptr and std::wstring classes and let them manage memory/resources for you. Using C library functions is not helping you learn C++:

#define UNICODE 1
#include <windows.h>
#include <wchar.h>

#include <iostream>
#include <string>
#include <memory>

struct FindDeleter
{
    typedef HANDLE pointer;

    void operator()(HANDLE h)
    {
        if(h != INVALID_HANDLE_VALUE)
            FindClose(h);
    }
};

int wmain(int argc, wchar_t **argv)
{
    if (argc < 2)
    {
        std::wcerr << L"Usage: \"" << argv[0] << L"\" path" << std::endl;
        return -1;
    }

    std::wstring path = argv[1];
    if ((!path.empty()) && (path[path.length()-1] != L'\\'))
        path += L'\\';

    WIN32_FIND_DATA FindData;
    std::unique_ptr<HANDLE, FindDeleter> hSearch(FindFirstFileW((path + L"*").c_str(), &FindData));
    if (hSearch.get() == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() != ERROR_FILE_NOT_FOUND)
        {
            std::wcerr << L"Error looking for first file" << std::endl;
            return -1;
        }
        std::wcout << L"No files found" << std::endl;
    }
    else
    {
        do
        {
            std::wstring filePath = path + FindData.cFileName;
            std::wcout << L"Path " << filePath << std::endl;
        }
        while (FindNextFileW(hSearch.get(), &FindData));

        if (GetLastError() != ERROR_NO_MORE_FILES)
        {
            std::wcerr << L"Error looking for next file" << std::endl;
            return -1;
        }
    }

    return 0;
}

Or, if you are not using a C++11 compiler:

#define UNICODE 1
#include <windows.h>
#include <wchar.h>

#include <iostream>
#include <string>

class FindHandle
{
private:
    HANDLE m_hFind;

public:
    FindHandle(HANDLE hFind) : m_hFind(hFind) {}

    ~FindHandle()
    {
        if (m_hFind != INVALID_HANDLE_VALUE)
            FindClose(m_hFind);
    }

    HANDLE get() { return m_hFind; }
};

int wmain(int argc, wchar_t **argv)
{
    if (argc < 2)
    {
        std::wcerr << L"Usage: \"" << argv[0] << L"\" path" << std::endl;
        return -1;
    }

    std::wstring path = argv[1];
    if ((!path.empty()) && (path[path.length()-1] != L'\\'))
        path += L'\\';

    WIN32_FIND_DATA FindData;
    FindHandle hSearch(FindFirstFileW((path + L"*").c_str(), &FindData));
    if (hSearch.get() == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() != ERROR_FILE_NOT_FOUND)
        {
            std::wcerr << L"Error looking for first file" << std::endl;
            return -1;
        }
        std::wcout << L"No files found" << std::endl;
    }
    else
    {
        do
        {
            std::wstring filePath = path + FindData.cFileName;
            std::wcout << L"Path " << filePath << std::endl;
        }
        while (FindNextFileW(hSearch.get(), &FindData));

        if (GetLastError() != ERROR_NO_MORE_FILES)
        {
            std::wcerr << L"Error looking for next file" << std::endl;
            return -1;
        }
    }

    return 0;
}

Upvotes: 1

Related Questions