Passing a SAFEARRAY of bytes to load function of MSXML - c++

If any kind soul out there please go through following source and tell me why MSXML "load" function fails to load this XML.
Here i'm trying to load a UTF-8 encoded XML using MSXML parser's "load" function. And i have a BSTR [UTF-16 encoded] as an argument, so i'm trying to convert it, into a SAFEARRAY of bytes so that i can passed it into the "load" function of MSXML. But the issue is load function failed to load this XML. If anyone could provide a solution to i would be really grateful.
#include <windows.h>
#include <objsafe.h>
#include <objbase.h>
#include <atlbase.h>
#include <string>
#include <comutil.h>
#include <msxml2.h>
#include <iostream>
using namespace std;
#define STATUS_SUCCESS 0
#define STATUS_FAIL -1
long LoadXmlData(BSTR xmlDoc)
{
HRESULT hr = S_OK;
CComPtr <IXMLDOMDocument> xmlDomDoc = NULL;
CComPtr <IXMLDOMElement> docRoot = NULL;
VARIANT_BOOL isParseSucess = FALSE;
CoInitialize(NULL);
hr = xmlDomDoc.CoCreateInstance(__uuidof(DOMDocument30));
if (FAILED(hr))
{
return STATUS_FAIL;
}
BYTE HUGEP *pByte;
int len = WideCharToMultiByte(CP_UTF8, 0, xmlDoc, -1, NULL, 0, NULL, NULL);
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].cElements = len;
rgsabound[0].lLbound = 0;
SAFEARRAY* psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
if (psa != NULL)
{
hr = SafeArrayAccessData(psa, (void HUGEP**)&pByte);
if (!FAILED(hr))
{
if (len > 0)
{
WideCharToMultiByte(CP_UTF8, 0, xmlDoc, -1, (LPSTR)&pByte[0], len, NULL, NULL);
//cout << "Converted Byte Array: " << pByte << endl << endl;
}
else
{
return STATUS_FAIL;
}
SafeArrayUnaccessData(psa);
}
}
VARIANT v;
VariantInit(&v);
V_VT(&v) = VT_ARRAY | VT_UI1;
V_ARRAY(&v) = psa;
hr = xmlDomDoc->load(v, &isParseSucess);
//hr = xmlDomDoc->loadXML(xmlDoc, &isParseSucess); //can't use this function because XML is encoded in UTF-8
if (FAILED(hr) || (!isParseSucess))
{
return STATUS_FAIL;
}
else
{
return STATUS_SUCCESS;
}
}
int main()
{
BSTR xmlDoc = SysAllocString(L"<?xml version=\"1.0\" encoding=\"UTF-8\"?> <response> </response> ");
long ret = LoadXmlData(xmlDoc);
if (ret == STATUS_SUCCESS)
{
cout << "MSXML: loading the XML succeeded";
}
else
{
cout << "MSXML: loading the XML failed";
}
//string str;
//getline(cin, str);
return 0;
}
ps: If anyone try to compile this source you may get a link error first time, add comsuppw.lib as a linker dependency in VS settings. And XML is UTF-8 encoded, so i can't use "LoadXML" function in MSXML.

XML files are UTF-8 but there is no conversion needed here because Windows functions will automatically convert between UTF-16 and UTF-8 when needed (unless input/output is BYTE*...)
BSTR needs cleanup. You can use CComBSTR(str) which has automatic cleanup.
You can use const wchar_t* to pass strings to your own functions. BSTR is required for COM etc.
As noted in comments, IXMLDOMDocument::load expects a file name as input. Use IXMLDOMDocument::loadXML in this case.
#include <iostream>
#include <windows.h>
#include <atlbase.h>
#include <msxml2.h>
long LoadXmlData(const wchar_t* content)
{
HRESULT hr = S_FALSE;
CComPtr<IXMLDOMDocument> doc = NULL;
CComPtr<IXMLDOMElement> docRoot = NULL;
hr = doc.CoCreateInstance(__uuidof(DOMDocument30));
if(FAILED(hr))
return S_FALSE;
VARIANT_BOOL is_success = FALSE;
CComBSTR arg(content);
hr = doc->loadXML(arg, &is_success);
//if you are loading from a file:
//hr = doc->load(CComVariant(L"c:\\test\\test.xml"), &is_success);
if(FAILED(hr) || !is_success)
return S_FALSE;
//if save is needed:
//doc->save(CComVariant(L"c:\\test\\test.xml"));
return S_OK;
}
int main()
{
CoInitialize(NULL);
//ελληνική γλώσσα Greek text for testing
CComBSTR data(L"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response>ελληνική γλώσσα</response>");
long ret = LoadXmlData(data);
if(ret == S_OK)
std::cout << "MSXML: loading the XML succeeded\n";
else
std::cout << "MSXML: loading the XML failed\n";
CoUninitialize();
return 0;
}

Related

Exception thrown: read access violation. **pDSSearch** was nullptr

I'm trying to code a C++ program to print the distinguishedName of an user in active directory. The program compiles without errors, however instantly crashes on start up. I admit that I don't know c++ very well, and I believe that the problem is somewhere there.
#include <iostream>
#include <jni.h>
#include<string.h>
#include "com_library_ldap_JNIHelper.h"
#include <iads.h>
#include <adshlp.h>
#include "activeds.h"
#include <windows.h>
#include <stdlib.h>
#include <atlbase.h>
using namespace std;
int main()
{
string username = "Admin10";
string password = "password#10";
wchar_t wName[20];
mbstowcs(wName, username.c_str(), username.length());
wchar_t wpwd[20];
mbstowcs(wpwd, password.c_str(), password.length());
//HRESULT hr = S_OK; // COM result variable
HRESULT hr;
ADS_SEARCH_COLUMN col; // COL for iterations
LPWSTR szUsername = wName; // user name
LPWSTR szPassword = wpwd; // password
// Interface Pointers.
IDirectorySearch* pDSSearch=NULL;
// Initialize COM.
//CoInitialize(0);
::CoInitialize(NULL);
// Add code to securely retrieve the user name and password or
// leave both as NULL to use the default security context.
// Open a connection with server.
hr = ADsOpenObject(L"LDAP://10.0.14.74/OU=Vimal,DC=test,DC=local",
szUsername,
szPassword,
ADS_SECURE_AUTHENTICATION,
IID_IDirectorySearch,
(void**)&pDSSearch);
string des = "description";
string name = "Name";
string dn = "distinguishedname";
string propertycheck = "(&(objectClass=user)(cn=Admin10))";
wchar_t wdes[20];
mbstowcs(wdes, des.c_str(), des.length());
LPWSTR szDes = wdes;
wchar_t wname[20];
mbstowcs(wname, name.c_str(), name.length());
LPWSTR szName = wname;
wchar_t wdn[20];
mbstowcs(wdn, dn.c_str(), dn.length());
LPWSTR szDN = wdn;
wchar_t wpc[20];
mbstowcs(wpc, propertycheck.c_str(), propertycheck.length());
LPWSTR szPC = wpc;
LPWSTR pszAttr[] = { szDes, szName, szDN };
ADS_SEARCH_HANDLE hSearch;
DWORD dwCount = 0;
DWORD dwAttrNameSize = sizeof(pszAttr) / sizeof(LPWSTR);
// Search for all objects with the 'cn' property that start with c.
hr = pDSSearch->ExecuteSearch(szPC, pszAttr, dwAttrNameSize, &hSearch);
LPWSTR pszColumn;
while (pDSSearch->GetNextRow(hSearch) != S_ADS_NOMORE_ROWS)
{
// Get the property.
hr = pDSSearch->GetColumn(hSearch, szDN, &col); // want user distinguishedname
// If this object supports this attribute, display it.
if (SUCCEEDED(hr))
{
if (col.dwADsType == ADSTYPE_CASE_IGNORE_STRING)
wprintf(L"The description property:%s\r\n", col.pADsValues->CaseIgnoreString);
pDSSearch->FreeColumn(&col);
}
else
puts("description property NOT available");
puts("------------------------------------------------");
dwCount++;
}
pDSSearch->CloseSearchHandle(hSearch);
pDSSearch->Release();
CoUninitialize();
return 0;
}
Below I add the screenshot of an error displaying in visual studio:
enter image description here
Exception thrown: read access violation.
pDSSearch was nullptr.
The above is the error message I got, but I have no idea how to trace it. Thanks in advance.
Now I find the solution to my problem. I would changed the datatype of username and password variable which is passed to ADsOpenObject function and some more changes... Below I add the complete code for the solution.
#include <iostream>
#include <jni.h>
#include<string.h>
#include <iads.h>
#include <adshlp.h>
#include "activeds.h"
#include <windows.h>
#include <stdlib.h>
#include <atlbase.h>
using namespace std;
int main()
{
string username = "username";
string password = "password";
string resDN="";
std::wstring stemp = std::wstring(username.begin(), username.end());
LPCWSTR lpszUserName = stemp.c_str();
std::wstring stemp1 = std::wstring(password.begin(), password.end());
LPCWSTR lpszPassword = stemp1.c_str();
ADS_SEARCH_COLUMN col; // COL for iterations
HRESULT hr;
string uType;
string check;
bool res = false;
// Interface Pointers.
IDirectorySearch* pDSSearch = NULL;
::CoInitialize(NULL);
hr = ADsOpenObject(L"LDAP://10.0.124.714/OU=Vimal,DC=test,DC=local",
lpszUserName,
lpszPassword,
ADS_SECURE_AUTHENTICATION,
IID_IDirectorySearch,
(void**)&pDSSearch);
std::cout << hr;
if (!SUCCEEDED(hr))
{
std::cout << "Not working!!!";
return 0;
}
if (SUCCEEDED(hr))
{
wstring des = L"description";
wstring name = L"Name";
wstring dn = L"distinguishedname";
wstring propertycheck = L"(&(objectClass=user)(cn=Vimaluser1))";
std::wstring sdes = std::wstring(des.begin(), des.end());
LPCWSTR lpszDes = sdes.c_str();
std::wstring sname = std::wstring(name.begin(), name.end());
LPCWSTR lpszName = sname.c_str();
std::wstring sdn = std::wstring(dn.begin(), dn.end());
LPCWSTR lpszDN = sdn.c_str();
std::wstring sprop = std::wstring(propertycheck.begin(), propertycheck.end());
LPCWSTR lpszProp = sprop.c_str();
LPCWSTR pszAttr[] = { lpszDes, lpszName, lpszDN };
ADS_SEARCH_HANDLE hSearch;
DWORD dwCount = 0;
DWORD dwAttrNameSize = sizeof(pszAttr) / sizeof(LPCWSTR);
hr = pDSSearch->ExecuteSearch((LPWSTR)lpszProp, (LPWSTR*)pszAttr, dwAttrNameSize, &hSearch);
LPCWSTR pszColumn;
while (pDSSearch->GetNextRow(hSearch) != S_ADS_NOMORE_ROWS)
{
// Get the property.
hr = pDSSearch->GetColumn(hSearch, (LPWSTR)lpszDN, &col); // want user distinguishedname
// If this object supports this attribute, display it.
if (SUCCEEDED(hr))
{
if (col.dwADsType == ADSTYPE_DN_STRING) {
wprintf(L"The distinguishedName property:%s\r\n", col.pADsValues->DNString);
//resDN += col.pADsValues->DNString;
}
else
std::cout << "dn not found";
pDSSearch->FreeColumn(&col);
}
else
puts("distinguishedName property NOT available");
dwCount++;
}
uType = "Success";
res = true;
pDSSearch->CloseSearchHandle(hSearch);
pDSSearch->Release();
CoUninitialize();
}
if (res == false)
uType = "Login details not found...";
std::cout << uType;
return 0;
}

Windows SAPI error : 'GetVersionExA': was declared deprecated

So all I want in life is to have a program where I can say "Hey Computer" and it responds with "Hello". So I set myself upon the task and after some research produced the code below yet whenever I try to compile it through Visual Studio 2017 on Windows 10 I get this error: 'GetVersionExA': was declared deprecated but I don't understand because I don't call that function anywhere in my code.
#include <sphelper.h>
#include <sapi.h>
#include <iostream>
#include <string>
#include <vector>
#include <locale>
const ULONGLONG grammarId = 0;
const wchar_t* ruleName1 = L"ruleName1";
int start_listening(const std::string& word);
ISpRecoGrammar* init_grammar(ISpRecoContext* recoContext, const std::string& command);
void get_text(ISpRecoContext* reco_context);
void check_result(const HRESULT& result);
ISpVoice * pVoice = NULL;
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
inline std::wstring s2w(const std::string &s, const std::locale &loc = std::locale())
{
typedef std::ctype<wchar_t> wchar_facet;
std::wstring return_value;
if (s.empty())
{
return return_value;
}
if (std::has_facet<wchar_facet>(loc))
{
std::vector<wchar_t> to(s.size() + 2, 0);
std::vector<wchar_t>::pointer toPtr = &to[0];
const wchar_facet &facet = std::use_facet<wchar_facet>(loc);
if (0 != facet.widen(s.c_str(), s.c_str() + s.size(), toPtr))
{
return_value = to.data();
}
}
return return_value;
}
int main(int argc, char** argv)
{
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
std::string hello = "hello";
start_listening("Hey computer");
hr = pVoice->Speak(s2w(hello).c_str(), 0, NULL);
return EXIT_SUCCESS;
}
// This function exits when the word passed as parameter is said by the user
int start_listening(const std::string& word)
{
// Initialize COM library
if (FAILED(::CoInitialize(nullptr))) {
return EXIT_FAILURE;
}
std::cout << "You should start Windows Recognition" << std::endl;
std::cout << "Just say \"" << word << "\"" << std::endl;
HRESULT hr;
ISpRecognizer* recognizer;
hr = CoCreateInstance(CLSID_SpSharedRecognizer,
nullptr, CLSCTX_ALL, IID_ISpRecognizer,
reinterpret_cast<void**>(&recognizer));
check_result(hr);
ISpRecoContext* recoContext;
hr = recognizer->CreateRecoContext(&recoContext);
check_result(hr);
// Disable context
hr = recoContext->Pause(0);
check_result(hr);
ISpRecoGrammar* recoGrammar = init_grammar(recoContext, word);
hr = recoContext->SetNotifyWin32Event();
check_result(hr);
HANDLE handleEvent;
handleEvent = recoContext->GetNotifyEventHandle();
if (handleEvent == INVALID_HANDLE_VALUE) {
check_result(E_FAIL);
}
ULONGLONG interest;
interest = SPFEI(SPEI_RECOGNITION);
hr = recoContext->SetInterest(interest, interest);
check_result(hr);
// Activate Grammar
hr = recoGrammar->SetRuleState(ruleName1, 0, SPRS_ACTIVE);
check_result(hr);
// Enable context
hr = recoContext->Resume(0);
check_result(hr);
// Wait for reco
HANDLE handles[1];
handles[0] = handleEvent;
WaitForMultipleObjects(1, handles, FALSE, INFINITE);
get_text(recoContext);
std::cout << "Hello user" << std::endl;
recoGrammar->Release();
::CoUninitialize();
system("PAUSE");
return EXIT_SUCCESS;
}
/**
* Create and initialize the Grammar.
* Create a rule for the grammar.
* Add word to the grammar.
*/
ISpRecoGrammar* init_grammar(ISpRecoContext* recoContext, const std::string& command)
{
HRESULT hr;
SPSTATEHANDLE sate;
ISpRecoGrammar* recoGrammar;
hr = recoContext->CreateGrammar(grammarId, &recoGrammar);
check_result(hr);
WORD langId = MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH);
hr = recoGrammar->ResetGrammar(langId);
check_result(hr);
// TODO: Catch error and use default langId => GetUserDefaultUILanguage()
// Create rules
hr = recoGrammar->GetRule(ruleName1, 0, SPRAF_TopLevel | SPRAF_Active, true, &sate);
check_result(hr);
// Add a word
const std::wstring commandWstr = std::wstring(command.begin(), command.end());
hr = recoGrammar->AddWordTransition(sate, NULL, commandWstr.c_str(), L" ", SPWT_LEXICAL, 1, nullptr);
check_result(hr);
// Commit changes
hr = recoGrammar->Commit(0);
check_result(hr);
return recoGrammar;
}
void get_text(ISpRecoContext* reco_context)
{
const ULONG maxEvents = 10;
SPEVENT events[maxEvents];
ULONG eventCount;
HRESULT hr;
hr = reco_context->GetEvents(maxEvents, events, &eventCount);
// Warning hr equal S_FALSE if everything is OK
// but eventCount < requestedEventCount
if (!(hr == S_OK || hr == S_FALSE)) {
check_result(hr);
}
ISpRecoResult* recoResult;
recoResult = reinterpret_cast<ISpRecoResult*>(events[0].lParam);
wchar_t* text;
hr = recoResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, FALSE, &text, NULL);
check_result(hr);
CoTaskMemFree(text);
}
void check_result(const HRESULT& result)
{
if (result == S_OK) {
return;
}
std::string message;
switch (result) {
case E_INVALIDARG:
message = "One or more arguments are invalids.";
case E_ACCESSDENIED:
message = "Acces Denied.";
case E_NOINTERFACE:
message = "Interface does not exist.";
case E_NOTIMPL:
message = "Not implemented method.";
case E_OUTOFMEMORY:
message = "Out of memory.";
case E_POINTER:
message = "Invalid pointer.";
case E_UNEXPECTED:
message = "Unexpecter error.";
case E_FAIL:
message = "Failure";
default:
message = "Unknown : " + std::to_string(result);
}
throw std::exception(message.c_str());
}
It is suppressable with a pragma:
#pragma warning(disable:4996)
#include <sphelper.h>
#pragma warning(default: 4996)
GetVersionEx is being used by the header sphelper.h which you're including. It's using it to check that the function SpGetDescription() is running on Vista or later. You could probably work around this issue by targeting the 8.1 SDK version instead of 10. However, it's bad that the shipped MS SAPI API in the Windows 10 SDK is using functions which are deprecated in Windows 10.. I'd say this is a MS issue.
Alternately this would work:
#define FKG_FORCED_USAGE 1
#include <sphelper.h>
#undef FKG_FORCED_USAGE

How do you get the NamePropertyId of a UIAutomationElement by hovering the cursor?

I am trying to build my own screen reader using UIAutomation. I want my program to return the NameProperty of the element that is pointed by my cursor
This is what I have done so far; this is just sample code anyway:
#include <iostream>
#include <windows.h>
#include <UIAutomation.h>
const int MAX_WND_TEXT = 60;
IUIAutomation *automation = NULL;
BOOL InitializeUIAutomation(IUIAutomation **pAutomation)
{
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)pAutomation);
return (SUCCEEDED(hr));
}
int main()
{
POINT p;
IUIAutomationElement *elem;
wchar_t wndName[MAX_WND_TEXT];
BOOL stat = InitializeUIAutomation(&automation);
while (true)
{
if (stat)
{
GetCursorPos(&p);
HRESULT hr = automation->ElementFromPoint(p, &elem);
if (SUCCEEDED(hr))
{
HRESULT hr = elem->GetCurrentPropertyValue(UIA_NamePropertyId,
(VARIANT*)wndName);
if (SUCCEEDED(hr))
std::cout << wndName << std::endl;
else
wndName[0] = '\0';
}
else
std::cout << "No element selected." << std::endl;
Sleep(100);
elem->Release();
}
}
automation->Release();
CoUninitialize();
return 0;
}
Now the problem is I can't make it to print the values I wanted. The program just output a specific hex number. And also I'm still a beginner in UIAutomation so I am still lost.
Can you help me or give me tips how to solve my problem?
Solved my problem using this code.
#include <iostream>
#include <string>
#include <Windows.h>
#include <UIAutomation.h>
BOOL InitializeUIAutomation(IUIAutomation **automation)
{
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL,
CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation),
(void**)automation);
return (SUCCEEDED(hr));
}
int main()
{
IUIAutomation *automation = NULL;
IUIAutomationElement *elem = NULL;
BOOL stat = InitializeUIAutomation(&automation);
POINT mousePt;
BSTR elemName = NULL;
if (stat)
{
while(true)
{
GetCursorPos(&mousePt);
HRESULT hr = automation->ElementFromPoint(mousePt, &elem);
if (SUCCEEDED(hr) && elem != NULL)
{
elem->get_CurrentName(&elemName);
std::wstring ws(elemName, SysStringLen(elemName));
std::wcout << ws << std::endl;
}
SysFreeString(elemName);
elem->Release();
Sleep(200);
}
}
automation->Release();
CoUninitialize();
return 0;
}
The hex numbers printed was the BSTR header after all. Solve my problem by converting BSTR to wstring.

0x00000010 Error reading characters of string. PROPVARIANT structure

Here's how I obtain the PROPVARIANT structure with WASAPI API related functions:
//Pointer for stored audio stream
IAudioClient *iac = NULL;
//Endpoint device selection
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice;
IMMDeviceCollection *pCollection = NULL;
CoInitialize(NULL);
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);
//Create vector of IMMDevices
UINT endpointCount = NULL;
(*pCollection).GetCount(&endpointCount);
std::vector<IMMDevice**> IMMDevicePP; //IMMDevice seems to contain all endpoint devices, so why have a collection here?
for (UINT i = 0; i < (endpointCount); i++)
{
IMMDevice* pp = NULL;
(*pCollection).Item(i, &pp);
IMMDevicePP.assign(1, &pp);
}
UINT IMMDeviceCount = IMMDevicePP.size();
//Enumerate Properties of IMMDevices
std::vector<IPropertyStore*> IMMDeviceProperties;
for (int k = 0; k < IMMDeviceCount; k++) {
IPropertyStore* prop = NULL;
(**IMMDevicePP[k]).OpenPropertyStore(STGM_READ, &prop);
IMMDeviceProperties.assign(1, prop);
}
UINT PropertyStoreCount = IMMDeviceProperties.size();
//Find name property of device
std::vector<PROPVARIANT*> properties;
for (int i = 0; i < PropertyStoreCount; i++) {
DWORD propCount = 1;
HRESULT countResult = (*IMMDeviceProperties[i]).GetCount(&propCount);
if (countResult == S_OK) { }
else {
int x = 5;
}
for (int p = 0; p < propCount; p++) {
PROPERTYKEY key;
HRESULT keyResult = (*IMMDeviceProperties[i]).GetAt(p, &key);
HRESULT getAT;
PROPVARIANT propVari;
HRESULT propVariResult = (*IMMDeviceProperties[i]).GetValue(key, &propVari);
propVari.vt = VT_LPWSTR;
LPWSTR test = propVari.pwszVal;
//char pwszValTest;
//strcpy(&pwszValTest, propVari.pwszVal);
//WCHAR friendlyName = *propVari.pwszVal;
properties.assign(1, &propVari);
}
}
All HRESULT's return S_OK.
The resulting PROPVARIANT struct renders correctly at first glance. However, when inspecting further with VS's property watch all of the string type properties return the error reflected in the title of this question. So when I attempt to retrieve the name of my Audio Endpoint Device which is contained the the pwszVal property of my PROPVARIANT struct like so:
LPWSTR test = propVari.pwszVal;
I am unable to retrieve the desired data. I have tried copying the string with various converter methods to no avail. I know this error is on a ton of questions but I can't seem to crack this error.
Here's the doc for PROPVARIANT and its corresponding properties:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa380072(v=vs.85).aspx
In this documentation it states that "PROPVARIANT member vt is set to VT_LPWSTR" VT_LPWSTR is an enum type and corresponds to the value 31. Whereas VT_BLOB corresponds to the value 65. My vt member is being set to VT_BLOB or 65 instead of 31 or VT_LPWSTR. Why is this so? This is contradictory to the value stated in this documentation:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370812(v=vs.85).aspx
Manually setting the vt member also does not change/fix the string reading error:
propVari.vt = VT_LPWSTR;
The PKEY_Device_FriendlyName is what I'm essentially after. Any help/tips is much appreciated.
You are not filling your vectors correctly. You are storing memory addresses of local variables, not the actual items that variables refer to.
And worse, you are using std::vector::assign() to add items. assign() replaces the entire contents of a vector with the specified value. If you have multiple devices in a collection, you will not end up with a vector of multiple devices. You should be using push_back() instead of assign().
You are making those mistakes with all of your vectors.
On a side note, you should use the -> operator instead of using (*). when calling methods of the objects. It will make the code cleaner and easier to read.
Try this instead:
//Endpoint device selection
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pCollection = NULL;
CoInitialize(NULL);
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);
pEnumerator->Release();
//Create vector of IMMDevices
std::vector<IMMDevice*> IMMDevice;
UINT endpointCount = 0;
hr = pCollection->GetCount(&endpointCount);
if (hr == S_OK) {
IMMDevice.reserve(endpointCount);
for (UINT i = 0; i < endpointCount; ++i) {
IMMDevice *pDevice = NULL;
hr = pCollection->Item(i, &pDevice);
if (hr == S_OK) {
IMMDevice.push_back(pDevice);
}
}
}
UINT IMMDeviceCount = IMMDevice.size();
pCollection->Release();
//Enumerate Properties of IMMDevices
std::vector<IPropertyStore*> IMMDeviceProperties;
IMMDeviceProperties.reserve(IMMDeviceCount);
for (int k = 0; k < IMMDeviceCount; k++) {
IPropertyStore* prop = NULL;
hr = IMMDevice[k]->OpenPropertyStore(STGM_READ, &prop);
if (hr == S_OK) {
IMMDeviceProperties.push_back(prop);
}
}
UINT PropertyStoreCount = IMMDeviceProperties.size();
//Find name property of devices
std::vector<std::wstring> MMDeviceFriendlyNames;
MMDeviceFriendlyNames.reserve(IMMDeviceCount);
for (int i = 0; i < PropertyStoreCount; i++) {
PROPVARIANT propVari;
PropVariantInit(&propVari);
hr = IMMDeviceProperties[i]->GetValue(PKEY_Device_FriendlyName, &propVari);
if (hr == S_OK) {
MMDeviceFriendlyNames.push_back(propVari.pwszVal);
PropVariantClear(&propVari);
}
}
// use vectors as needed...
for (UINT i = 0; i < PropertyStoreCount; ++i) {
IMMDeviceProperties[i]->Release();
}
for (UINT i = 0; i < IMMDeviceCount; ++i) {
IMMDevice[i]->Release();
}
The following code, based upon yours but without the obfuscating vectors appears to work fine. In runnin g it I get "FriendlyName: Speakers / HP (IDT High Definition Audio CODEC)" which seems correct here for this laptop.
When working with COM and without some kind of smart pointer, be really careful to release all the pointers. And always check all the results. COM calls can fail for all kinds of reasons.
#define WINVER _WIN32_WINNT_VISTA
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define STRICT
#include <windows.h>
#include <ole2.h>
#include <mmdeviceapi.h>
#include <propsys.h>
#include <propvarutil.h>
#include <stdio.h>
#include <Functiondiscoverykeys_devpkey.h>
#pragma comment(lib, "ole32")
#pragma comment(lib, "propsys")
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
static HRESULT
DumpDeviceProperties(IMMDevice *pDevice)
{
IPropertyStore *pStore = NULL;
HRESULT hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
if (SUCCEEDED(hr))
{
PROPVARIANT prop;
PropVariantInit(&prop);
hr = pStore->GetValue(PKEY_Device_FriendlyName, &prop);
if (SUCCEEDED(hr))
{
if (IsPropVariantString(prop))
wprintf(L"FriendlyName: %s\n", PropVariantToStringWithDefault(prop, L"(missing)"));
else
hr = E_UNEXPECTED;
}
PropVariantClear(&prop);
pStore->Release();
}
return hr;
}
int
wmain(int argc, WCHAR *argv[])
{
HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
IMMDeviceEnumerator *pEnumerator = NULL;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void **>(&pEnumerator));
if (SUCCEEDED(hr))
{
IMMDeviceCollection *pCollection = NULL;
hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);
if (SUCCEEDED(hr))
{
UINT cEndpoints = 0;
hr = pCollection->GetCount(&cEndpoints);
if (SUCCEEDED(hr))
{
for (UINT n = 0; SUCCEEDED(hr) && n < cEndpoints; ++n)
{
IMMDevice *pDevice = NULL;
hr = pCollection->Item(n, &pDevice);
if (SUCCEEDED(hr))
{
hr = DumpDeviceProperties(pDevice);
pDevice->Release();
}
}
}
pCollection->Release();
}
pEnumerator->Release();
}
CoUninitialize();
}
return SUCCEEDED(hr) ? 0 : 1;
}
Compiled using: cl -nologo -MDd -Zi -W3 -Od lsdevices.cpp with MSVC 2013.
Code that I made, for people that wonder on this page like I did:
You also need this policyconfig.h file
#include <windows.h>
#include <ole2.h>
#include <ShellAPI.h>
#include <olectl.h>
#include <mmdeviceapi.h>
#include <propsys.h>
#include <propvarutil.h>
#include <stdio.h>
#include <Functiondiscoverykeys_devpkey.h>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <atlstr.h>
#include <atlcore.h>
#include "Policyconfig.h"
#include "Propidl.h"
#include "Functiondiscoverykeys_devpkey.h"
#pragma comment(lib, "ole32")
#pragma comment(lib, "propsys")
using namespace std;
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
class DeviceProps {
public:
string id;
string name;
string dll;
int iconID = 0;
bool isActive = false;
};
static HRESULT getDeviceProperty(IMMDevice* pDevice, DeviceProps* output)
{
IPropertyStore* pStore = NULL;
HRESULT hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
if (SUCCEEDED(hr))
{
PROPVARIANT prop;
PropVariantInit(&prop);
hr = pStore->GetValue(PKEY_Device_FriendlyName, &prop);
if (SUCCEEDED(hr))
{
if (IsPropVariantString(prop))
{
std::wstring wstr(PropVariantToStringWithDefault(prop, L"missing"));
std::string str(wstr.begin(), wstr.end());
output->name = str.c_str();
}
else
hr = E_UNEXPECTED;
}
hr = pStore->GetValue(PKEY_DeviceClass_IconPath, &prop);
if (SUCCEEDED(hr))
{
if (IsPropVariantString(prop))
{
PCWSTR propValue = PropVariantToStringWithDefault(prop, L"missing,0");
std::wstring propW(propValue);
std::string cPropValue(propW.begin(), propW.end());
vector<string> strings;
istringstream f(cPropValue);
string s;
while (getline(f, s, ',')) {
strings.push_back(s);
}
string location = strings[0];
string id = strings[1];
output->dll = location;
output->iconID = stoi(id);
}
else
hr = E_UNEXPECTED;
}
PropVariantClear(&prop);
pStore->Release();
}
return hr;
}
std::vector<DeviceProps> EnumAudioDevices(EDataFlow deviceType = eRender)
{
std::vector<DeviceProps> output;
HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
IMMDeviceEnumerator* pEnumerator = NULL;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&pEnumerator));
if (SUCCEEDED(hr))
{
IMMDevice* pActive = NULL;
pEnumerator->GetDefaultAudioEndpoint(deviceType , eMultimedia, &pActive);
DeviceProps activeDevice;
getDeviceProperty(pActive, &activeDevice);
LPWSTR aid;
pActive->GetId(&aid);
std::wstring aid2(aid);
std::string aidS(aid2.begin(), aid2.end());
activeDevice.id = aidS;
//output.push_back(activeDevice);
pActive->Release();
IMMDeviceCollection* pCollection = NULL;
hr = pEnumerator->EnumAudioEndpoints(deviceType , DEVICE_STATE_ACTIVE, &pCollection);
if (SUCCEEDED(hr))
{
UINT cEndpoints = 0;
hr = pCollection->GetCount(&cEndpoints);
if (SUCCEEDED(hr))
{
for (UINT n = 0; SUCCEEDED(hr) && n < cEndpoints; ++n)
{
IMMDevice* pDevice = NULL;
hr = pCollection->Item(n, &pDevice);
if (SUCCEEDED(hr))
{
DeviceProps device;
hr = getDeviceProperty(pDevice, &device);
LPWSTR id;
pDevice->GetId(&id);
std::wstring id2(id);
std::string idS(id2.begin(), id2.end());
device.id = idS;
if (device.id == activeDevice.id)
device.isActive = true;
output.push_back(device);
pDevice->Release();
}
}
}
pCollection->Release();
}
pEnumerator->Release();
}
//CoUninitialize();
}
return output;
}
static HRESULT setDefaultDevice(string id)
{
string:wstring devID(id.begin(), id.end());
IPolicyConfigVista* pPolicyConfig;
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient),
NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig);
if (SUCCEEDED(hr))
{
hr = pPolicyConfig->SetDefaultEndpoint(devID.c_str(), eConsole);
hr = pPolicyConfig->SetDefaultEndpoint(devID.c_str(), eMultimedia);
hr = pPolicyConfig->SetDefaultEndpoint(devID.c_str(), eCommunications);
pPolicyConfig->Release();
}
return hr;
}
static int switchDefaultDevice(EDataFlow deviceType = eRender)
{
std::vector<DeviceProps> result = EnumAudioDevices(deviceType);
if (!result.empty())
{
std::string activateID("");
for (const auto& device : result)
{
if (activateID== "x") {
activateID = device.id;
break;
}
if (device.isActive) activateID= "x";
}
if (activateID == "x" || activateID == "") activateID = result[0].id;
setDefaultDevice(activateID);
return 1;
}
return 0;
}
int wmain(int argc, WCHAR* argv[])
{
std::vector<DeviceProps> result = EnumAudioDevices(eRender);
for (const auto& device : result)
{
std::cout << (device.isActive ? "ACTIVE:" : "") << "Name: " << device.name << " DLL: " << device.dll << " (#" << device.iconID << ")" << "\n" << device.id << "\n";
}
switchDefaultDevice(eRender);
}

Getting actual file name (with proper casing) on Windows

Windows file system is case insensitive. How, given a file/folder name (e.g. "somefile"), I get the actual name of that file/folder (e.g. it should return "SomeFile" if Explorer displays it so)?
Some ways I know, all of which seem quite backwards:
Given the full path, search for each folder on the path (via FindFirstFile). This gives proper cased results of each folder. At the last step, search for the file itself.
Get filename from handle (as in MSDN example). This requires opening a file, creating file mapping, getting it's name, parsing device names etc. Pretty convoluted. And it does not work for folders or zero-size files.
Am I missing some obvious WinAPI call? The simplest ones, like GetActualPathName() or GetFullPathName() return the name using casing that was passed in (e.g. returns "program files" if that was passed in, even if it should be "Program Files").
I'm looking for a native solution (not .NET one).
And hereby I answer my own question, based on original answer from cspirz.
Here's a function that given absolute, relative or network path, will return the path with upper/lower case as it would be displayed on Windows. If some component of the path does not exist, it will return the passed in path from that point.
It is quite involved because it tries to handle network paths and other edge cases. It operates on wide character strings and uses std::wstring. Yes, in theory Unicode TCHAR could be not the same as wchar_t; that is an exercise for the reader :)
std::wstring GetActualPathName( const wchar_t* path )
{
// This is quite involved, but the meat is SHGetFileInfo
const wchar_t kSeparator = L'\\';
// copy input string because we'll be temporary modifying it in place
size_t length = wcslen(path);
wchar_t buffer[MAX_PATH];
memcpy( buffer, path, (length+1) * sizeof(path[0]) );
size_t i = 0;
std::wstring result;
// for network paths (\\server\share\RestOfPath), getting the display
// name mangles it into unusable form (e.g. "\\server\share" turns
// into "share on server (server)"). So detect this case and just skip
// up to two path components
if( length >= 2 && buffer[0] == kSeparator && buffer[1] == kSeparator )
{
int skippedCount = 0;
i = 2; // start after '\\'
while( i < length && skippedCount < 2 )
{
if( buffer[i] == kSeparator )
++skippedCount;
++i;
}
result.append( buffer, i );
}
// for drive names, just add it uppercased
else if( length >= 2 && buffer[1] == L':' )
{
result += towupper(buffer[0]);
result += L':';
if( length >= 3 && buffer[2] == kSeparator )
{
result += kSeparator;
i = 3; // start after drive, colon and separator
}
else
{
i = 2; // start after drive and colon
}
}
size_t lastComponentStart = i;
bool addSeparator = false;
while( i < length )
{
// skip until path separator
while( i < length && buffer[i] != kSeparator )
++i;
if( addSeparator )
result += kSeparator;
// if we found path separator, get real filename of this
// last path name component
bool foundSeparator = (i < length);
buffer[i] = 0;
SHFILEINFOW info;
// nuke the path separator so that we get real name of current path component
info.szDisplayName[0] = 0;
if( SHGetFileInfoW( buffer, 0, &info, sizeof(info), SHGFI_DISPLAYNAME ) )
{
result += info.szDisplayName;
}
else
{
// most likely file does not exist.
// So just append original path name component.
result.append( buffer + lastComponentStart, i - lastComponentStart );
}
// restore path separator that we might have nuked before
if( foundSeparator )
buffer[i] = kSeparator;
++i;
lastComponentStart = i;
addSeparator = true;
}
return result;
}
Again, thanks to cspirz for pointing me to SHGetFileInfo.
Have you tried using SHGetFileInfo?
There is another solution. First call GetShortPathName() and then GetLongPathName(). Guess what character case will be used then? ;-)
Just found that the Scripting.FileSystemObject suggested by #bugmagnet 10 years ago is a treasure. Unlike my old method, it works on Absolute Path, Relative Path, UNC Path and Very Long Path (path longer than MAX_PATH). Shame on me for not testing his method earlier.
For future reference, I would like to present this code which can be compiled in both C and C++ mode. In C++ mode, the code will use STL and ATL. In C mode, you can clearly see how everything is working behind the scene.
#include <Windows.h>
#include <objbase.h>
#include <conio.h> // for _getch()
#ifndef __cplusplus
# include <stdio.h>
#define SafeFree(p, fn) \
if (p) { fn(p); (p) = NULL; }
#define SafeFreeCOM(p) \
if (p) { (p)->lpVtbl->Release(p); (p) = NULL; }
static HRESULT CorrectPathCasing2(
LPCWSTR const pszSrc, LPWSTR *ppszDst)
{
DWORD const clsCtx = CLSCTX_INPROC_SERVER;
LCID const lcid = LOCALE_USER_DEFAULT;
LPCWSTR const pszProgId = L"Scripting.FileSystemObject";
LPCWSTR const pszMethod = L"GetAbsolutePathName";
HRESULT hr = 0;
CLSID clsid = { 0 };
IDispatch *pDisp = NULL;
DISPID dispid = 0;
VARIANT vtSrc = { VT_BSTR };
VARIANT vtDst = { VT_BSTR };
DISPPARAMS params = { 0 };
SIZE_T cbDst = 0;
LPWSTR pszDst = NULL;
// CoCreateInstance<IDispatch>(pszProgId, &pDisp)
hr = CLSIDFromProgID(pszProgId, &clsid);
if (FAILED(hr)) goto eof;
hr = CoCreateInstance(&clsid, NULL, clsCtx,
&IID_IDispatch, (void**)&pDisp);
if (FAILED(hr)) goto eof;
if (!pDisp) {
hr = E_UNEXPECTED; goto eof;
}
// Variant<BSTR> vtSrc(pszSrc), vtDst;
// vtDst = pDisp->InvokeMethod( pDisp->GetIDOfName(pszMethod), vtSrc );
hr = pDisp->lpVtbl->GetIDsOfNames(pDisp, NULL,
(LPOLESTR*)&pszMethod, 1, lcid, &dispid);
if (FAILED(hr)) goto eof;
vtSrc.bstrVal = SysAllocString(pszSrc);
if (!vtSrc.bstrVal) {
hr = E_OUTOFMEMORY; goto eof;
}
params.rgvarg = &vtSrc;
params.cArgs = 1;
hr = pDisp->lpVtbl->Invoke(pDisp, dispid, NULL, lcid,
DISPATCH_METHOD, &params, &vtDst, NULL, NULL);
if (FAILED(hr)) goto eof;
if (!vtDst.bstrVal) {
hr = E_UNEXPECTED; goto eof;
}
// *ppszDst = AllocWStrCopyBStrFrom(vtDst.bstrVal);
cbDst = SysStringByteLen(vtDst.bstrVal);
pszDst = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, cbDst + sizeof(WCHAR));
if (!pszDst) {
hr = E_OUTOFMEMORY; goto eof;
}
CopyMemory(pszDst, vtDst.bstrVal, cbDst);
*ppszDst = pszDst;
eof:
SafeFree(vtDst.bstrVal, SysFreeString);
SafeFree(vtSrc.bstrVal, SysFreeString);
SafeFreeCOM(pDisp);
return hr;
}
static void Cout(char const *psz)
{
printf("%s", psz);
}
static void CoutErr(HRESULT hr)
{
printf("Error HRESULT 0x%.8X!\n", hr);
}
static void Test(LPCWSTR pszPath)
{
LPWSTR pszRet = NULL;
HRESULT hr = CorrectPathCasing2(pszPath, &pszRet);
if (FAILED(hr)) {
wprintf(L"Input: <%s>\n", pszPath);
CoutErr(hr);
}
else {
wprintf(L"Was: <%s>\nNow: <%s>\n", pszPath, pszRet);
HeapFree(GetProcessHeap(), 0, pszRet);
}
}
#else // Use C++ STL and ATL
# include <iostream>
# include <iomanip>
# include <string>
# include <atlbase.h>
static HRESULT CorrectPathCasing2(
std::wstring const &srcPath,
std::wstring &dstPath)
{
HRESULT hr = 0;
CComPtr<IDispatch> disp;
hr = disp.CoCreateInstance(L"Scripting.FileSystemObject");
if (FAILED(hr)) return hr;
CComVariant src(srcPath.c_str()), dst;
hr = disp.Invoke1(L"GetAbsolutePathName", &src, &dst);
if (FAILED(hr)) return hr;
SIZE_T cch = SysStringLen(dst.bstrVal);
dstPath = std::wstring(dst.bstrVal, cch);
return hr;
}
static void Cout(char const *psz)
{
std::cout << psz;
}
static void CoutErr(HRESULT hr)
{
std::wcout
<< std::hex << std::setfill(L'0') << std::setw(8)
<< "Error HRESULT 0x" << hr << "\n";
}
static void Test(std::wstring const &path)
{
std::wstring output;
HRESULT hr = CorrectPathCasing2(path, output);
if (FAILED(hr)) {
std::wcout << L"Input: <" << path << ">\n";
CoutErr(hr);
}
else {
std::wcout << L"Was: <" << path << ">\n"
<< "Now: <" << output << ">\n";
}
}
#endif
static void TestRoutine(void)
{
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
Cout("CoInitialize failed!\n");
CoutErr(hr);
return;
}
Cout("\n[ Absolute Path ]\n");
Test(L"c:\\uSers\\RayMai\\docuMENTs");
Test(L"C:\\WINDOWS\\SYSTEM32");
Cout("\n[ Relative Path ]\n");
Test(L".");
Test(L"..");
Test(L"\\");
Cout("\n[ UNC Path ]\n");
Test(L"\\\\VMWARE-HOST\\SHARED FOLDERS\\D\\PROGRAMS INSTALLER");
Cout("\n[ Very Long Path ]\n");
Test(L"\\\\?\\C:\\VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
L"VERYVERYVERYLOOOOOOOONGFOLDERNAME");
Cout("\n!! Worth Nothing Behavior !!\n");
Test(L"");
Test(L"1234notexist");
Test(L"C:\\bad\\PATH");
CoUninitialize();
}
int main(void)
{
TestRoutine();
_getch();
return 0;
}
Screenshot:
Old Answer:
I found that FindFirstFile() will return the proper casing file name (last part of path) in fd.cFileName. If we pass c:\winDOWs\exPLORER.exe as first parameter to FindFirstFile(), the fd.cFileName would be explorer.exe like this:
If we replace the last part of path with fd.cFileName, we will get the last part right; the path would become c:\winDOWs\explorer.exe.
Assuming the path is always absolute path (no change in text length), we can just apply this 'algorithm' to every part of path (except the drive letter part).
Talk is cheap, here is the code:
#include <windows.h>
#include <stdio.h>
/*
c:\windows\windowsupdate.log --> c:\windows\WindowsUpdate.log
*/
static HRESULT MyProcessLastPart(LPTSTR szPath)
{
HRESULT hr = 0;
HANDLE hFind = NULL;
WIN32_FIND_DATA fd = {0};
TCHAR *p = NULL, *q = NULL;
/* thePart = GetCorrectCasingFileName(thePath); */
hFind = FindFirstFile(szPath, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
hFind = NULL; goto eof;
}
/* thePath = thePath.ReplaceLast(thePart); */
for (p = szPath; *p; ++p);
for (q = fd.cFileName; *q; ++q, --p);
for (q = fd.cFileName; *p = *q; ++p, ++q);
eof:
if (hFind) { FindClose(hFind); }
return hr;
}
/*
Important! 'szPath' should be absolute path only.
MUST NOT SPECIFY relative path or UNC or short file name.
*/
EXTERN_C
HRESULT __stdcall
CorrectPathCasing(
LPTSTR szPath)
{
HRESULT hr = 0;
TCHAR *p = NULL;
if (GetFileAttributes(szPath) == -1) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
for (p = szPath; *p; ++p)
{
if (*p == '\\' || *p == '/')
{
TCHAR slashChar = *p;
if (p[-1] == ':') /* p[-2] is drive letter */
{
p[-2] = toupper(p[-2]);
continue;
}
*p = '\0';
hr = MyProcessLastPart(szPath);
*p = slashChar;
if (FAILED(hr)) goto eof;
}
}
hr = MyProcessLastPart(szPath);
eof:
return hr;
}
int main()
{
TCHAR szPath[] = TEXT("c:\\windows\\EXPLORER.exe");
HRESULT hr = CorrectPathCasing(szPath);
if (SUCCEEDED(hr))
{
MessageBox(NULL, szPath, TEXT("Test"), MB_ICONINFORMATION);
}
return 0;
}
Advantages:
The code works on every version of Windows since Windows 95.
Basic error-handling.
Highest performance possible. FindFirstFile() is very fast, direct buffer manipulation makes it even faster.
Just C and pure WinAPI. Small executable size.
Disadvantages:
Only absolute path is supported, other are undefined behavior.
Not sure if it is relying on undocumented behavior.
The code might be too raw too much DIY for some people. Might get you flamed.
Reason behind the code style:
I use goto for error-handling because I was used to it (goto is very handy for error-handling in C). I use for loop to perform functions like strcpy and strchr on-the-fly because I want to be certain what was actually executed.
Okay, this is VBScript, but even so I'd suggest using the Scripting.FileSystemObject object
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim f
Set f = fso.GetFile("C:\testfile.dat") 'actually named "testFILE.dAt"
wscript.echo f.Name
The response I get is from this snippet is
testFILE.dAt
Hope that at least points you in the right direction.
FindFirstFileNameW will work with a few drawbacks:
it doesn't work on UNC paths
it strips the drive letter so you need to add it back
if there are more than one hard link to your file you need to identify the right one
After a quick test, GetLongPathName() does what you want.