Create StartMenu Entry - c++

i try to create link to file in StartMenu folder, my code:
bool createStartMenuEntry(string targetPath, string name){
std::wstring stemp = s2ws(targetPath);
LPCWSTR target = stemp.c_str();
WCHAR startMenuPath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);
if (SUCCEEDED(result)) {
std::wstring linkPath = std::wstring(startMenuPath) + s2ws(name);
LPCWSTR link = linkPath.c_str();
//TEST MESSAGE!!!
MessageBox(NULL, LPCSTR(target), LPCSTR(link), MB_ICONWARNING);
CoInitialize(NULL);
IShellLinkW* shellLink = NULL;
result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink);
if (SUCCEEDED(result)) {
shellLink->SetPath(target);
//shellLink->SetDescription(L"Shortcut Description");
shellLink->SetIconLocation(target, 0);
IPersistFile* persistFile;
result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile);
if (SUCCEEDED(result)) {
result = persistFile->Save(link, TRUE);
persistFile->Release();
}
else {
return false;
}
shellLink->Release();
}
else {
return false;
}
}
else {
return false;
}
return true;
}
String to widestring conversion:
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
When I call my func like createStartMenuEntry("E:\\file.exe" , "File"), in test message I have only first letters of path and shortcut isn't created, I think, problem in unicode conversion.

There are multiple problems here:
MessageBox(NULL, LPCSTR(target), LPCSTR(link), MB_ICONWARNING); is all kinds of wrong. You should not be casting strings like this. If you are compiling without UNICODE defined, you must use MessageBoxW() to display a LPCWSTR string. You get a single character because "c:\\" as a Unicode string is 'c',0,':',0,'\\',0,0,0 in memory, and that is the same as a "c" string when treated as a narrow ANSI string.
You ignore the result of persistFile->Save()! You also ignore the results of SetPath() and SetIconLocation().
A normal user cannot write to CSIDL_COMMON_PROGRAMS, only administrators have write access to that folder, because it is shared by all users. If you are not planning to require UAC elevation, you must write to CSIDL_PROGRAMS instead.
You should not use std::string to store paths, only std::wstring and WCHAR*/LP[C]WSTR, because paths that contain certain Unicode characters cannot be represented in a narrow ANSI string.

Related

change the use of SHBrowseForFolder and struct BROWSEINFO TO - IFileOpenDialog

until now- in order to open a dialog box I used SHBrowseForFolder function. It causes some bugs and I was told to use IFileOpenDialog.
here is the code I need to replace:
bool wvFM::SelectFileSystemObjectDialogTree(const WCDialogCreationOptions& in_options,
WCDialogReply& out_Reply)
{
AUTO_FUNC_DEBUG;
DWORD osErr = NO_ERROR;
WTErr wtErr = eNoErr;
out_Reply.accept = false;
if(in_options.m_flags[WCDialogCreationOptions::eSelectFolder]) {
BROWSEINFO bi = {0};
bi.hwndOwner = (HWND)in_options.m_owner;
bi.pidlRoot = NULL; // TBD by callback BrowseCallbackProc
bi.lpszTitle = in_options.m_windowTitle.c_str();
bi.ulFlags = BIF_USENEWUI; // to enable pasting path
bi.lpfn = BrowseCallbackProc; // for initial dir option
WTPathString initialLocationPathString(
in_options.m_InitialDir.GetNativePathString());
bi.lParam = in_options.m_InitialDir.IsValid()
? (LPARAM)initialLocationPathString.c_str()
: NULL;
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
// returns focus to the internal window of the plug
::SetFocus((HWND)in_options.m_owner);
if(pidl != NULL) {
TCHAR szPath[MAX_PATH];
SHGetPathFromIDList(pidl, szPath);
out_Reply.m_filePathRef = wvFM::WCStPath((char*)szPath);
out_Reply.accept = true;
IMalloc* imalloc = 0;
if(SUCCEEDED(SHGetMalloc(&imalloc))) {
imalloc->Free(pidl);
imalloc->Release();
}
}
return true;
}
I am not so sure of how to make that conversion.
Thanks to anyone who will help!
You are clearly using a 3rd party library with some kind of custom string type, but you did not provide any details about what that string type actually is. I'm guessing that string type is char-based, given that you are type-casting the result of SHGetPathFromIDList() to char* when assigning it to out_Reply.m_filePathRef, so you are likely compiling your project with UNICODE undefined so that TCHAR maps to char and not wchar_t. In which case, SHBrowseForFolder() would be calling SHBrowseForFolderA() and not SHBrowseForFolderW().
IFileOpenDialog does not deal with ANSI strings at all, only UNICODE strings, so you will have to convert back and forth between your 3rd party string type and Windows UTF-16 strings as needed.
The conversion of SHBrowseForFolder() to IFileOpenDialog would look roughly like the following (where YourStringType in toWString() is the 3rd party string type - make whatever adjustments you need where indicated):
std::wstring toWString(const YourStringType &str)
{
const char pStr = ... pointer to str's characters ...;
int sLen = ... length of str in chars, not counting the null terminator ...;
std::wstring ws;
int wLen = MultiByteToWideChar(CP_ACP, 0, pStr, slen, NULL, 0);
if (wLen > 0)
{
ws.resize(wLen);
MultiByteToWideChar(CP_ACP, 0, pStr, sLen, ws.data(), wLen);
}
return ws;
}
std::string toString(const wchar_t *ws)
{
std::string s;
int wLen = lstrlenW(s);
int sLen = WideCharToMultiByte(CP_ACP, 0, ws, wLen, NULL, 0, NULL, NULL);
if (sLen > 0)
{
s.resize(sLen);
WideCharToMultiByte(CP_ACP, 0, ws, wLen, s.data(), sLen, NULL, NULL);
}
return s;
}
IShellItem* toShellItem(const YourStringType &path)
{
IShellItem *pItem = NULL;
if (FAILED(SHCreateItemFromParsingName(toWString(path).c_str(), NULL, IID_PPV_ARGS(&pItem))))
pItem = NULL;
return pItem;
}
bool wvFM::SelectFileSystemObjectDialogTree(const WCDialogCreationOptions& in_options,
WCDialogReply& out_Reply)
{
AUTO_FUNC_DEBUG;
out_Reply.accept = false;
if (in_options.m_flags[WCDialogCreationOptions::eSelectFolder]) {
IFileOpenDialog *pFileOpen = NULL;
if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pFileOpen))))
return false;
FILEOPENDIALOGOPTIONS opts = 0;
pFileOpen->GetOptions(&opts);
pFileOpen->SetTitle(toWString(in_options.m_windowTitle).c_str());
pFileOpen->SetOptions(opts | FOS_PICKFOLDERS);
IShellItem *pItem = toShellItem(in_options.m_InitialDir);
if (pItem)
{
pFileOpen->SetFolder(pItem);
pItem->Release();
}
HWND hwndOwner = (HWND) in_options.m_owner;
HRESULT hr = pFileOpen->Show(hwndOwner);
::SetFocus(hwndOwner);
if (SUCCEEDED(hr))
{
if (SUCCEEDED(pFileOpen->GetResult(&pItem)))
{
PWSTR pszFilePath;
if (SUCCEEDED(pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath)))
{
out_Reply.m_filePathRef = wvFM::WCStPath(toString(pszFilePath).c_str());
out_Reply.accept = true;
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFileOpen->Release();
return true;
}
...
}

LogonUserW logon fails for a non-ascii username

I have C++ code that tries to authenticate a local user to windows:
BOOL result = ::LogonUserW(localAdminUserName_.c_str(), L".", localAdminPassword_.c_str(),
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT);
This works fine for ASCII character-set based usernames.
But doesn't work for a user named, say userああ
If I print the variable localAdminUserName_ in the log, It's printing the username just fine.
Is .c_str() messing it up somehow?
Should I encode the username/password in someway before making this API call?
The following console application I made to test this scenario, is working fine!
_declspec(dllimport)
BOOL
__stdcall
LogonUserW(
__in LPCWSTR lpszUsername,
__in_opt LPCWSTR lpszDomain,
__in LPCWSTR lpszPassword,
__in DWORD dwLogonType,
__in DWORD dwLogonProvider,
__deref_out PHANDLE phToken
);
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hToken = NULL;
BOOL returnValue = LogonUserW(
L"userああ",
L".",
L"pa$$w0rd",
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
&hToken);
if (returnValue == false) {
std::cout<<"Error!";
} else {
std::cout<<"Success!";
}
return 0;
}
Problem got solved after the original string is converted to multi byte string and then used the MultiByteToWideChar method in windows.h to convert it to wide char:
//convert the credentials to a multi-byte string first
std::string MbLocalAdminUserName = MbString(localAdminUserName_.c_str());
std::string MbLocalAdminPassowrd = MbString(localAdminPassowrd_.c_str());
//convert this multi-byte format to wide char that windows is expecting
//username
int len_MbLocalAdminUserName = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminUserName = new wchar_t[len_MbLocalAdminUserName + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str(), -1, WcLocalAdminUserName, len_MbLocalAdminUserName + 1);
//password
int len_MbLocalAdminPassowrd = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminPassowrd = new wchar_t[len_MbLocalAdminPassowrd + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str(), -1, WcLocalAdminPassowrd, len_MbLocalAdminPassowrd + 1);
BOOL result = ::LogonUser(WcLocalAdminUserName, L".", WcLocalAdminPassowrd,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken);
delete[] WcLocalAdminUserName;
delete[] WcLocalAdminPassowrd;
Where MbString is :
MbString::MbString(const wchar_t* src)
: buf_(0) {
const size_t count = 1 + sizeof(wchar_t) * wcslen(src);
buf_ = new char[count];
// The 3rd parameter specifies the size of multi-bytes string buffer.
wcstombs(buf_, src, count);
}

store a variable that holds user input data in sapi5 speak function

I am developing an application that uses the Microsoft SAPI5 speech engine. However, I have hit a wall. I've been trying to use the data from the variable that stores the input from the user so the TTS engine can repeat it. But the sapi5 speech function does not allow strings, wstrings or other types except from LPCWSTR which from my research is a pointer to a wide character string, so shouldn't it accept wstrings?
Here's some example code from msdn :
#include <stdafx.h>
#include <sapi.h>
int main(int argc, char* argv[])
{
ISpVoice * pVoice = NULL;
if (FAILED(::CoInitialize(NULL)))
return FALSE;
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice;);
if( SUCCEEDED( hr ) )
{
hr = pVoice->Speak(L"Hello world", 0, NULL);
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
return TRUE;
}
So lets say for example i have this piece of code :
...
wstring text;
if( SUCCEEDED( hr ) )
{
wcin >> text;
hr = pVoice->Speak(text, SPF_IS_XML, NULL);
pVoice->Release();
pVoice = NULL;
}
...
But this does not work. How would I go about storing a variable that allows a LPCWSTR type?
I'm kind of new to c++ and this is the first time I've had this sort of problem, so it's very new to me.
I saw that the OP on this topic has the exact same problem : https://stackoverflow.com/questions/12292790/how-do-i-use-variables-in-sapi-tts
After around 2 hours of solid research, i found an article on msdn on converting a string to LPCWSTR format. The code is below :
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
I then included this code into my project, and then created a function parameter called LPCWSTR Repeat for the TTS engine initialization function ( So that the converted string can be used in the TTS function, and the engine would say the contents of the converted string ) .
static int TTS_Speak_Dialogue(LPCWSTR Repeat)
{
// Set Sapi5 voice properties
ISpVoice * pVoice = NULL;
if (FAILED(::CoInitialize(NULL)))
return FALSE;
// Create new instance of Sapi5 once initialized in COM
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if( SUCCEEDED( hr ) )
{
hr = pVoice->Speak(Repeat, SPF_IS_XML, NULL);
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
return TRUE;
}
Then i created another function to manage the conversion and manage the user input so that the TTS engine can repeat it.
static void convert_string()
{
// Declare a string type variable
string UserInput;
// Now get input from user
cout << "Get the TTS to repeat Input : ";
cin >> UserInput;
// Convert string to LPCWSTR!
std::wstring stemp = s2ws(UserInput);
// UserInput string now converted to result
LPCWSTR result = stemp.c_str();
// Call the TTS engine function and use the converted string
TTS_Speak_Dialogue(result);
}
I hope my answer helps people who are having the same problem as I had.
I would have explained my answer in more detail but I'm in need of some sleep, so please accept my sincere apologies :-) .

string to PCWSTR -> strange return(C++)

i'm really new to C++-programming and i've got an a problem with writing into a xml document.
I'm using a slightly changed example of xml outputter from msdn (http://msdn.microsoft.com/en-us/library/ms766497(VS.85).aspx).
HRESULT CreateAndAddTestMethodNode(string name)
{
HRESULT hr = S_OK;
IXMLDOMElement* pElement = NULL;
CHK_HR(CreateAndAddElementNode(pXMLDom, L"method", L"\n\t", pClass, &pMethod));
CHK_HR(CreateAndAddAttributeNode(pXMLDom, L"name", stringToPCWSTR(name), pMethod));
//more Attribute Nodes (deleted for better overview ;) )
CleanUp:
SAFE_RELEASE(pMethod);
return hr
}
I'm giving a string to CreateAndAddTestMethodNode which convert it with stringtopcwstr to a pcwstr, or should do it.
//convert string to pcwstr
PCWSTR stringToPCWSTR (const std::string& str)
{
int len;
int slength = (int)str.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, str.c_str(), slength, buf, len);
std::wstring result(buf);
delete[] buf;
PCWSTR pResult = result.c_str();
return pResult;
}
But it only returns something like
"0x00bb9908 "ﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮ" which causes an access violation in one of the next methods.
It would be really great if someone could give me clue where i did the failure.
Thank You.
The result of c_str() gets destroyed along with the result string (when it goes out of scope). You will need to explicitly allocate memory for it.
You could make the return type for stringToPCWSTR as a const reference to PCWSTR i.e. const PCWSTR&

utf-8 to/from utf-16 problem

I based these two conversion functions and an answer on StackOverflow, but converting back-and-forth doesn't work:
std::wstring MultiByteToWideString(const char* szSrc)
{
unsigned int iSizeOfStr = MultiByteToWideChar(CP_ACP, 0, szSrc, -1, NULL, 0);
wchar_t* wszTgt = new wchar_t[iSizeOfStr];
if(!wszTgt) assert(0);
MultiByteToWideChar(CP_ACP, 0, szSrc, -1, wszTgt, iSizeOfStr);
std::wstring wstr(wszTgt);
delete(wszTgt);
return(wstr);
}
std::string WideStringToMultiByte(const wchar_t* wszSrc)
{
int iSizeOfStr = WideCharToMultiByte(CP_ACP, 0, wszSrc, -1, NULL, 0, NULL, NULL);
char* szTgt = new char[iSizeOfStr];
if(!szTgt) return(NULL);
WideCharToMultiByte(CP_ACP, 0, wszSrc, -1, szTgt, iSizeOfStr, NULL, NULL);
std::string str(szTgt);
delete(szTgt);
return(str);
}
[...]
// はてなブ in utf-16
wchar_t wTestUTF16[] = L"\u306f\u3066\u306a\u30d6\u306f\u306f";
// shows the text correctly
::MessageBoxW(NULL, wTestUTF16, L"Message", MB_OK);
// convert to UTF8, and back to UTF-16
std::string strUTF8 = WideStringToMultiByte(wTestUTF16);
std::wstring wstrUTF16 = MultiByteToWideString(strUTF8.c_str());
// this doesn't show the proper text. Should be same as first message box
::MessageBoxW(NULL, wstrUTF16.c_str(), L"Message", MB_OK);
Check the docs for WideCharToMultiByte(). CP_ACP converts using the current system code page. That's a very lossy one. You want CP_UTF8.