如何得到工作目录?

我想在工作目录中创建一个文件(可执行文件正在其中运行)。

我的代码:

LPTSTR NPath = NULL;
DWORD a = GetCurrentDirectory(MAX_PATH,NPath);
HANDLE hNewFile = CreateFile(NPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

我在 GetCurrentDirectory()得到例外。

为什么我会有例外?

382887 次浏览

GetCurrentDirectory does not allocate space for the result, it's up to you to do that.

TCHAR NPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH, NPath);

Also, take a look at Boost.Filesystem library if you want to do this the C++ way.

I would recommend reading a book on C++ before you go any further, as it would be helpful to get a firmer footing. Accelerated C++ by Koenig and Moo is excellent.

To get the executable path use GetModuleFileName:

TCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName( NULL, buffer, MAX_PATH );

Here's a C++ function that gets the directory without the file name:

#include <windows.h>
#include <string>
#include <iostream>


std::wstring ExePath() {
TCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName( NULL, buffer, MAX_PATH );
std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/");
return std::wstring(buffer).substr(0, pos);
}


int main() {
std::cout << "my directory is " << ExePath() << "\n";
}

You should provide a valid buffer placeholder. that is:

TCHAR s[100];
DWORD a = GetCurrentDirectory(100, s);

GetCurrentDirectory() gets the current directory which is where the exe is invoked from. To get the location of the exe, use GetModuleFileName(NULL ...). if you have the handle to the exe, or you can derive it from GetCommandLine() if you don't.

As Mr. Butterworth points out, you don't need a handle.

To find the directory where your executable is, you can use:

TCHAR szFilePath[_MAX_PATH];
::GetModuleFileName(NULL, szFilePath, _MAX_PATH);

Please don't forget to initialize your buffers to something before utilizing them. And just as important, give your string buffers space for the ending null

TCHAR path[MAX_PATH+1] = L"";
DWORD len = GetCurrentDirectory(MAX_PATH, path);

Reference

IMHO here are some improvements to anon's answer.

#include <windows.h>
#include <string>
#include <iostream>


std::string GetExeFileName()
{
char buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
return std::string(buffer);
}


std::string GetExePath()
{
std::string f = GetExeFileName();
return f.substr(0, f.find_last_of( "\\/" ));
}
WCHAR path[MAX_PATH] = {0};
GetModuleFileName(NULL, path, MAX_PATH);
PathRemoveFileSpec(path);
#include <windows.h>
using namespace std;


// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
const unsigned long maxDir = 260;
char currentDir[maxDir];
GetCurrentDirectory(maxDir, currentDir);
return string(currentDir);
}

You can remove the filename from GetModuleFileName() with more elegant way:

TCHAR fullPath[MAX_PATH];
TCHAR driveLetter[3];
TCHAR directory[MAX_PATH];
TCHAR FinalPath[MAX_PATH];
GetModuleFileName(NULL, fullPath, MAX_PATH);
_splitpath(fullPath, driveLetter, directory, NULL, NULL);
sprintf(FinalPath, "%s%s",driveLetter, directory);

Hope it helps!

String^ exePath = Application::ExecutablePath;<br>
MessageBox::Show(exePath);

Code snippets from my CAE project with unicode development environment:

/// @brief Gets current module file path.
std::string getModuleFilePath() {
TCHAR buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
CT2CA pszPath(buffer);
std::string path(pszPath);
std::string::size_type pos = path.find_last_of("\\/");
return path.substr( 0, pos);
}

Just use the templete CA2CAEX or CA2AEX which calls the internal API ::MultiByteToWideChar or ::WideCharToMultiByte

#include <iostream>
#include <stdio.h>
#include <dirent.h>


std::string current_working_directory()
{
char* cwd = _getcwd( 0, 0 ) ; // **** microsoft specific ****
std::string working_directory(cwd) ;
std::free(cwd) ;
return working_directory ;
}


int main(){
std::cout << "i am now in " << current_working_directory() << endl;
}

I failed to use GetModuleFileName correctly. I found this work very well. just tested on Windows, not yet try on Linux :)

Why does nobody here consider using this simple code?

TCHAR szDir[MAX_PATH] = { 0 };


GetModuleFileName(NULL, szDir, MAX_PATH);
szDir[std::string(szDir).find_last_of("\\/")] = 0;

or even simpler

TCHAR szDir[MAX_PATH] = { 0 };
TCHAR* szEnd = nullptr;
GetModuleFileName(NULL, szDir, MAX_PATH);
szEnd = _tcsrchr(szDir, '\\');
*szEnd = 0;

The question is not clear whether the current working directory is wanted or the path of the directory containing the executable.

Most answers seem to answer the latter.

But for the former, and for the second part of the question of creating the file, the C++17 standard now incorporates the filesystem library which simplifies this a lot:

#include <filesystem>
#include <iostream>


std::filesystem::path cwd = std::filesystem::current_path() / "filename.txt";
std::ofstream file(cwd.string());
file.close();

This fetches the current working directory, adds the filename to the path and creates an empty file. Note that the path object takes care of os dependent path handling, so cwd.string() returns an os dependent path string. Neato.

An easy way to do this is:

int main(int argc, char * argv[]){
std::cout << argv[0];
std::cin.get();
}

argv[] is pretty much an array containing arguments you ran the .exe with, but the first one is always a path to the executable. If I build this the console shows: C:\Users\Ulisse\source\repos\altcmd\Debug\currentdir.exe

If you are using the Poco library, it's a one liner and it should work on all platforms I think.

Poco::Path::current()

I guess, that the easiest way to locate the current directory is to cut it from command line args.

#include <string>
#include <iostream>


int main(int argc, char* argv[])
{
std::string cur_dir(argv[0]);
int pos = cur_dir.find_last_of("/\\");


std::cout << "path: " << cur_dir.substr(0, pos) << std::endl;
std::cout << "file: " << cur_dir.substr(pos+1) << std::endl;
return 0;
}

You may know that every program gets its executable name as first command line argument. So you can use this.

if you don't want to use std, you can use this code:

char * ExePath()
{
static char buffer[MAX_PATH] = { 0 };
GetModuleFileName( NULL, buffer, MAX_PATH );
char * LastSlash = strrchr(buffer, '\\');
if(LastSlash == NULL)
LastSlash = strrchr(buffer, '/');
    

buffer[LastSlash-buffer] = 0;
return buffer;
}

I simply use getcwd() method for that purpose in Windows, and it works pretty well. The code portion is like following:

char cwd[256];
getcwd(cwd, 256);
string cwd_str = string(cwd);

The <unistd.h> library has to be added though.

In Windows console, you can use the system command CD (Current Directory):

std::cout << "Current Directory = ";
system("cd"); // to see the current executable directory