GetShortPathName fails with Chinese folder on a network drive - c++

I want to get a short path name with the function GetShortPathName on a network drive, F:\, with a Chinese folder name. When I run the EXE file from that folder I don't see it is able to obtain the short path name. When I do that from the C:\ drive everything works fine.
Here is my code:
#include"stdafx.h"
#include<windows.h>
#include<tchar.h>
#include<stdio.h>
#include <iostream>
using namespace std;
#define BUFSIZE 4096
bool GetIsCaseCorrect(const WCHAR* fileName)
{
bool result = false;
// Correct case by converting to short path and back to long
WCHAR shortFileName[_MAX_PATH];
if (GetShortPathName(fileName, shortFileName, _MAX_PATH) != 0)
{
wchar_t correctFileName[_MAX_PATH];
wcout << "ShortFile " << shortFileName;
GetLongPathName(shortFileName, correctFileName, _MAX_PATH);
result = wcscmp(fileName, correctFileName) != 0;
}
return result;
}
int main() {
bool ret;
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ret = GetIsCaseCorrect(path);
getchar();
}
If I run this program the short path is not displayed on a non-system drive where folder is in Chinese.
My Windows OS is Windows 7.

As far as I know, the Windows 7 machine takes with the SMB 2.0 by default. GetShortPathNameW method is better supported with SMB 3.0.
You could follow this document below to enable SMB 3.0 with Win7.
https://support.microsoft.com/en-us/help/2696547/how-to-detect-enable-and-disable-smbv1-smbv2-and-smbv3-in-windows-and?wa=wsignin1.0%3Fwa%3Dwsignin1.0
Or you could follow this document below to check the volume state for Disable8dot3.
https://blogs.msdn.microsoft.com/winsdk/2013/10/09/getshortpathname-doesnt-return-short-path-name/
Best Regards,
Baron Bi

Related

Why does my DLL load properly when given an absolute path but not a relative path?

I have created a simple dll using /MDd flag on windows 10 using msvc 2019 compilers. The dll only contains a simple add function (like in all the tutorials). After building this library I've copied it into a test folder for explicit linking. Basically, the test passes if I give it the full absolute path to the dll but it doesn't load if I only provide the name of the dll.
Here is the test code:
//test_add.cpp
#include <windows.h>
#include "gtest/gtest.h"
TEST(test, test_add_windows) {
#if defined(_WIN32) || defined (_WIN64)
typedef int (*addPtr)(int, int);
// full path works and the test passes
HINSTANCE hinstLib = LoadLibrary(TEXT("D:\\ACrossPlatformCppLibrary\\test\\ACrossPlatformCppLibrary.dll"));
// relative path does not work: library fails to load
// HINSTANCE hinstLib = LoadLibrary(TEXT("ACrossPlatformCppLibrary.dll"));
std::cout << hinstLib << std::endl;
ASSERT_NE(hinstLib, nullptr);
auto add = (addPtr) GetProcAddress(hinstLib, "add");
ASSERT_NE(add, nullptr);
int x = 5;
int y = 6;
int answer = add(x, y);
ASSERT_EQ(answer, 11);
BOOL fFreeResult = FreeLibrary(hinstLib);
#else
ASSERT_TRUE(true);
#endif
}
And my directory tree
I figured out the answer. I ran another test from the same file to get the current directory:
TEST(test, test2) {
char *fileExt;
char szDir[256]; //dummy buffer
GetFullPathName(".", 256, szDir, &fileExt);
printf("Full path: %s\nFilename: %s", szDir, fileExt);
}
Which outputs:
Full path: D:\ACrossPlatformCppLibrary\cmake-build-debug\test
The problem was that I copied the dll into the source directory, not the build directory.

C++ RegGetValue function not returning correct Windows OS Version

I'm using the below code in a console application to get my Windows OS version:
#include <string>
#include <iostream>
#include <Windows.h>
int main()
{
DWORD dataSize = 0;
char buffer[256];
dataSize = sizeof(buffer);
RegGetValueA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"ProductName", RRF_RT_REG_SZ, 0, &buffer, &dataSize);
cout << buffer << endl;
return 0;
}
I have Windows 10 Pro installed, but the function is returning Windows 10 Enterprise. I have even manually navigated using regedit to the specified key and under "Product Name" I can see Windows 10 Pro. Here is an image of my regedit.
I ran another function RtlGetProductInfo(10, 0, 0, 0, f); and it returned the value of 0x48 for f, which according to Microsoft I have Windows 10 Enterprise Evaluation.
Now, at this point, I understand that something is messed up with my Windows installation, but why would my first code return another value than what is displayed for "ProductName" in my registry editor?
Edit: I should add that I've run the code on another Windows PC and it returns the correct version on that computer.

How to get Windows path using Qt/C++

I am trying to get the windows path using Qt and C++. The below code compiles, but not gettting the windows folder path in Qt. The same code works in Visual Studio 2010
wchar_t path[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, 0, path);
The below code change seems working:
int const bufferSize = 512;
QScopedPointer<WCHAR> dirPath(new WCHAR[bufferSize]);
ZeroMemory( dirPath.operator ->(), bufferSize);
SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, 0, dirPath.operator ->());
There isn't a Qt function to do this, but what you are asking could be achieved by reading the environtment variable WINDIR:
QStringList env_list(QProcess::systemEnvironment());
int idx = env_list.indexOf(QRegExp("^WINDIR=.*", Qt::CaseInsensitive));
if (idx > -1)
{
QStringList windir = env_list[idx].split('=');
qDebug() << "Var : " << windir[0];
qDebug() << "Path: " << windir[1];
}
Outputs:
Var : "WINDIR"
Path: "C:\WINDOWS"
QString windowsInstallPath;
#ifdef Q_WS_WIN
QDir d;
if (d.cd("%windir%"))
windowsInstallPath = d.absolutePath();
#endif
if (!windowsInstallPath.isNull())
qDebug() << windowsInstallPath;
else
qDebug() << "Not compiled for Windows";
Should work.
I think another very reasonable way to get the Windows directory would be to get it from the environment passed to the program:
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
qDebug() << env.value("windir");
https://doc.qt.io/qt-5/qprocessenvironment.html
I don't think there is a specific Qt function to do this.
The nearest is QSysinfo which tells you the windows version. However SHGetFolderPath() shoudl work in Qt just as well as any other win API call.
ps In Windows vista-> this is replaced with SHGetKnownFolderPath
Here is a one line solution:
QString winPath = QString::fromUtf8(qgetenv("windir"));
This can also be used for any environment variable. I am not sure if qgetenv is available in Qt4 but it is in Qt5.
If your application is not Terminal Services aware, you may get a different directory under TS environment. Found this out myself today, not that I've ever been bit by %windir% or %SystemRoot% or using the ShGetKnownFolderPath or GetWindowsDirectory APIs.
I've opted for using GetSystemWindowsDirectory which exists Windows 2000 and upward. Microsoft's page for the function is here.
Further explanation by Raymond Chen is here.
Finally, the code...
It's written in Delphi 6. Sorry about that :) It's what I'm coding in at the moment, but if you have code for GetWindowsDirectory in your language, then just a few copy + renames are needed as the function signatures are identical. Note: this code is ANSI (...single byte chars in Delphi 6).
function GetSystemWindowsDirectoryA(lpBuffer: PAnsiChar; uSize: UINT): UINT; stdcall; external kernel32 name 'GetSystemWindowsDirectoryA';
function GetSystemWindowsDirectory: string;
var
buf: array[0..MAX_PATH] of Char;
resultLength: Cardinal;
begin
resultLength := GetSystemWindowsDirectoryA(#buf, SizeOf(buf));
if resultLength = 0 then
RaiseLastOSError;
SetLength(Result, resultLength);
Move(buf, PChar(Result)^, resultLength);
end;

Get path to My Documents

From Visual C++, how do I get the path to the current user's My Documents folder?
Edit:
I have this:
TCHAR my_documents[MAX_PATH];
HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, my_documents);
However, result is coming back with a value of E_INVALIDARG. Any thoughts as to why this might be?
It depends on how old of a system you need compatibility with. For old systems, there's SHGetSpecialFolderPath. For somewhat newer systems, there's SHGetFolderPath. Starting with Vista, there's SHGetKnownFolderPath.
Here's some demo code that works, at least on my machine:
#include <windows.h>
#include <iostream>
#include <shlobj.h>
#pragma comment(lib, "shell32.lib")
int main() {
CHAR my_documents[MAX_PATH];
HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, my_documents);
if (result != S_OK)
std::cout << "Error: " << result << "\n";
else
std::cout << "Path: " << my_documents << "\n";
return 0;
}
Use the SHGetFolderPath Windows API function and request CSIDL_MYDOCUMENTS.
Using Visual Studio 2017 with an MFC application under Windows 10 I am using the following code snippet with SHGetKnownFolderPath function to get the current user's Documents folder:
#include <string> // include file for C++ native strings
// . . . other code.
PWSTR ppszPath; // variable to receive the path memory block pointer.
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &ppszPath);
std::wstring myPath;
if (SUCCEEDED(hr)) {
myPath = ppszPath; // make a local copy of the path
}
CoTaskMemFree(ppszPath); // free up the path memory block
Note that the documentation has this to say about the path variable usage and the path returned:
ppszPath [out]
Type: PWSTR*
When this method returns, contains the address of a pointer to a
null-terminated Unicode string that specifies the path of the known
folder. The calling process is responsible for freeing this resource
once it is no longer needed by calling CoTaskMemFree. The returned
path does not include a trailing backslash. For example, "C:\Users" is
returned rather than "C:\Users\".
For a list of the FOLDERID_ arguments possible see the MSDN article KNOWN_FOLDER_FLAG enumeration.
Note that CSIDL_PERSONAL will not return the desired folder if the user has changed the default save folder in the Win7 Documents library. To get the right folder, you need to use SHLoadLibraryFromKnownFolder to obtain the IShellLibrary for the Documents library, use IShellLibrary::GetDefaultSaveFolder to get the IShellItem for the library's default save folder, and finally use IShellItem::GetDisplayName to get the folder name.
std::string GetMyDocumentsFolderPath()
{
wchar_t Folder[1024];
HRESULT hr = SHGetFolderPathW(0, CSIDL_MYDOCUMENTS, 0, 0, Folder);
if (SUCCEEDED(hr))
{
char str[1024];
wcstombs(str, Folder, 1023);
return str;
}
else return "";
}
cout<<GetMyDocumentsFolderPath()<<endl;
how about this solution? Its working fine for me.

C++ Windows - How to get process path from its PID

How can I retrieve a process's fully-qualified path from its PID using C++ on Windows?
Call OpenProcess to get a handle to the process associated with your PID. Once you have a handle to the process, call GetModuleFileNameEx to get its fully-qualified path. Don't forget to call CloseHandle when you're finished using the process handle.
Here's a sample program that performs the required calls (replace 1234 with your PID):
#include <windows.h>
#include <psapi.h> // For access to GetModuleFileNameEx
#include <tchar.h>
#include <iostream>
using namespace std;
#ifdef _UNICODE
#define tcout wcout
#define tcerr wcerr
#else
#define tcout cout
#define tcerr cerr
#endif
int _tmain(int argc, TCHAR * argv[])
{
HANDLE processHandle = NULL;
TCHAR filename[MAX_PATH];
processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, 1234);
if (processHandle != NULL) {
if (GetModuleFileNameEx(processHandle, NULL, filename, MAX_PATH) == 0) {
tcerr << "Failed to get module filename." << endl;
} else {
tcout << "Module filename is: " << filename << endl;
}
CloseHandle(processHandle);
} else {
tcerr << "Failed to open process." << endl;
}
return 0;
}
Some notes to Emerick Rogul's solution:
Don't forget to add 'psapi.lib' to linker (additional dependencies).
I also changed PROCESS_ALL_ACCESS to PROCESS_QUERY_INFORMATION | PROCESS_VM_READ because I got:
Failed to open process.
If it's compiled as a 32 bit application it will fail to get the name of 64 bit processes ("Failed to get module filename.")
I didn't have very much luck with GetModuleFileNameEx and QueryFullProcessImageName is only available on Vista or higher. I was however able to get the path for a process by using GetProcessImageFilename. It returns the windows kernel path but you can use QueryDosDevice to compare the device path returned by GetProcessImageFilename with its proper drive path.
This page shows how to normalize an windows kernel path returned by GetProcessImageFilename (see NormalizeNTPath function):
http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/c48bcfb3-5326-479b-8c95-81dc742292ab/
Have you tried QueryFullProcessImageName?
Sometimes GetModuleFileNameEx returns the 299 error code (I don't know why)
The only method that works for all versions of Windows, including XP is in Nathan Moinvaziri answer:
check the provided url:
Windows API to Get a Full Process Path