I want Create a shortcut to my application in startup . I used visual c++ 2010 and windows 7.
HRESULT CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc){
HRESULT hres;
IShellLink* psl;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH);
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
return hres;
}
But, when I was performing not creating. in line :
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
get error. and not performing Remains my program.
The returned HRESULT does not just indicate success and failure. In the case of failure, the HRESULT tells you why the call failed. When you encounter a failure, you need to decode the HRESULT to find out why the call failed.
In this case the call is so simple that there is just one obvious failure mode. You did not initialize COM. With probability very close to 1, the value returned by the call to CoCreateInstance is CO_E_FIRST, with value 0x800401f0, which indicates that COM has not been initialized.
You'll want to add a call to CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) at startup, and match it with a call to CoUninitialize() when you are done with COM.
For what it is worth, I think that your function would be better if all of its parameters were wide strings. A function like this should not be worrying itself with such conversions. If you need to make such conversions, then include some helper methods in your code to perform them. You can add a function to convert from std::string to std::wstring and that should be all you ever need.
You need to initialize COM before performing your tasks:
HRESULT CreateLink(LPCSTR lpszPathObj, LPCSTR lpszPathLink, LPCSTR lpszDesc){
HRESULT hres;
IShellLink* psl;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH);
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
return hres;
}
int main()
{
CoInitialize(0); // Initialize COM
CreateLink("C:\\hello.lnk", "C:\\hello.obj", "hello");
CoUninitialize();
return 0;
}
Also: watch out for LPCSTR/LPCWSTR (wide) conversions. The hres parameter contains additional information in case you were wondering what's wrong with your code.
Related
I have this function to create a file-shortcut in Win32 :
// prototype
HRESULT CreateLink(LPCSTR lpszPathObj, LPCSTR lpszPathLink, LPCSTR lpszDesc);
When I call it as bellow :
StrCpyA(dst_file, argv[2]);
StrCpyA(src_file, argv[4]);
// concat ".lnk" to destination path
StrCatA(dst_file, ".lnk");
HRESULT res = CreateLink(src_file, dst_file, LINK_DESC);
It generate the specific short-cut file And then filename is as entered filename in argv[4] like this :
e.g. : file_shortcut.exe -src 1.bmp -dst 123
But the real filename in file properties is a unicode name :
⸱浢p
Even I used MultiByteToWideChar() to convert dest filename to WCHAR[] :
INT wch_size = MultiByteToWideChar(CP_UTF8, 0, lpszPathLink, -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, lpszPathLink, -1, wsz, wch_size);
In this function I'm used of IShellLink and IPersistFile interfaces as fllowing :
// function implementation
HRESULT CreateLink(LPCSTR lpszPathObj, LPCSTR lpszPathLink, LPCSTR lpszDesc)
{
HRESULT hres;
IShellLinkA* psl;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
CoInitialize(NULL);
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH] = {0};
// Ensure that the string is Unicode.
INT wch_size = MultiByteToWideChar(CP_UTF8, 0, lpszPathLink, -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, lpszPathLink, -1, wsz, wch_size);
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
return hres;
}
Any suggested?
I got it :
Because I defined IShellLinkA* psl; interface as ANSI And I have to to use of IID_IShellLinkA instead of IID_IShellLink in bellow function :
// mismatch with IShellLinkA
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
// correct way used of IID_IShellLinkA instead of IID_IShellLink
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, (LPVOID*)&psl);
I have found some samples with C# and VBS for WMI instance deletion, however I need this implemented with C++.
My sample code:
CoInitialize(NULL);
HRESULT hRes;
//Obtain the initial locator to WMI
CComPtr<IWbemLocator> pLoc = NULL;
hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*) &pLoc);
if(FAILED(hRes))
return 1;
//Connect to WMI through the IWbemLocator::ConnectServer method
CComPtr<IWbemServices> pSvc = NULL;
//Connect to the root namespace with the current user and obtain pointer pSvc to make IWbemServices calls.
hRes = pLoc->ConnectServer(L"ROOT\\SUBSCRIPTION", NULL, NULL, 0, NULL, 0, 0, &pSvc);
if(FAILED(hRes))
return 1;
hRes = pSvc->DeleteInstance(
L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'",
WBEM_FLAG_RETURN_IMMEDIATELY, NULL, NULL);
return 0;
According to that I've found here and here, my code should work. I surely have CommandLineEventConsumer named {709782F3-E860-488E-BD8A-89FBC8C1495C}
And my code fails on IWbemServices::DeleteInstance, error code 0x80041008 (One of the parameters to the call is not correct).
I would appreciate if someone spot mistake in my code. Or maybe some privileges are required to do that?
The first parameter to IWbemServices::DeleteInstance is a BSTR. A BSTR is different from a UTF-16 encoded C-style string in that it stores an explicit length argument. Even though BSTR is of type wchar_t*, you cannot pass a plain string literal in place of a BSTR.
To create a BSTR from a string literal you need to call SysAllocString:
BSTR objPath = ::SysAllocString(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
hRes = pSvc->DeleteInstance(
objPath,
WBEM_FLAG_RETURN_IMMEDIATELY, NULL, NULL);
::SysFreeString(objPath);
Alternatively, since you are already using ATL for CComPtr you could use CComBSTR to make your life easier:
CComBSTR objPath(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
hRes = pSvc->DeleteInstance(
objPath,
WBEM_FLAG_RETURN_IMMEDIATELY, NULL, NULL);
Note: IWbemLocator::ConnectServer also needs BSTRs as parameters. The sample provided on the documentation page does pass a plain C-style string, so maybe the IWbemLocator interface is more forgiving when presented invalid parameters.
I have found two solutions:
1.Remove WBEM_FLAG_RETURN_IMMEDIATELY flag.
_bstr_t objPath(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
hRes = pSvc->DeleteInstance(objPath, 0, NULL, NULL);
2.Pass IWbemCallResult for result.
_bstr_t objPath(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
CComPtr<IWbemCallResult> pRes = NULL;
hRes = pSvc->DeleteInstance(objPath, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pRes);
Didn't investigate a lot, but it works both ways. Looks like specs aren't 100% correct.
I would like to know how to obtain the path to the start menu folder on Windows and then create a shortcut to a path that might contain non-ASCII characters.
Here is the solution. It uses Qt but it's also possible without. Then just use std::wstring instead of QString. For concatenating the paths and filenames you will then have to use string operations instead of using QDir.
#include <shlobj.h>
bool createStartMenuEntry(QString targetPath) {
targetPath = QDir::toNativeSeparators(targetPath);
WCHAR startMenuPath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);
if (SUCCEEDED(result)) {
QString linkPath = QDir(QString::fromWCharArray(startMenuPath)).absoluteFilePath("Shortcut Name.lnk");
CoInitialize(NULL);
IShellLinkW* shellLink = NULL;
result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink);
if (SUCCEEDED(result)) {
shellLink->SetPath(targetPath.toStdWString().c_str());
shellLink->SetDescription(L"Shortcut Description");
shellLink->SetIconLocation(targetPath.toStdWString().c_str(), 0);
IPersistFile* persistFile;
result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile);
if (SUCCEEDED(result)) {
result = persistFile->Save(linkPath.toStdWString().c_str(), TRUE);
persistFile->Release();
} else {
return false;
}
shellLink->Release();
} else {
return false;
}
} else {
return false;
}
return true;
}
Thats the part that obtains the location of the start-menu folder:
WCHAR startMenuPath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);
The rest is then creation of the shortcut. Exchange shortcut name and description for your desired values.
Same idea as accepted answer but Visual Studio method.
Usage:
CString sProgramsPath = getenv("PROGRAMDATA");
CString sShortcutPath = sProgramsPath += "\\Microsoft\\Windows\\Start Menu\\Programs\\SHORTCUT_NAME.lnk";
// (that's .LNK)
CreateLink("C:\\target_file_path\\target_file_name.exe",
"sShortcutPath",
"C:\\target_file_path\\,
"Shortcut Description");
Function:
/*============================================================================*/
// CreateLink - Uses the Shell's IShellLink and IPersistFile interfaces
// to create and store a shortcut to the specified object.
//
// Returns the result of calling the member functions of the interfaces.
//
// Parameters:
// lpszPathObj - Address of a buffer that contains the path of the object,
// including the file name.
// lpszPathLink - Address of a buffer that contains the path where the
// Shell link is to be stored, including the file name.
// lpszPath - Working directory of target Obj file
// lpszDesc - Address of a buffer that contains a description of the
// Shell link, stored in the Comment field of the link
// properties.
HRESULT CreateLink(
LPCSTR lpszPathObj,
LPCSTR lpszPathLink,
LPCSTR lpszPath,
LPCSTR lpszDesc )
/*============================================================================*/
{
IShellLink* psl = NULL;
HRESULT hres = CoInitialize(NULL);
if (!SUCCEEDED(hres))
LOGASSERT(FALSE);
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
psl->SetWorkingDirectory(lpszPath);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
if (!SUCCEEDED(hres))
LOGASSERT(FALSE);
ppf->Release();
}
psl->Release();
}
CoUninitialize();
return hres;
}
I want to find target path of a shortcut in startmenu folder ,
I know that should use from shell link component object model ,
But in my test for some shortcuts it shows: "windows\installer\{guid}\x.exe" and does not show program files folder for it , and for other shortcut works fine,
How can i find target path for these products.
this is the function i use:
HRESULT TargetShortcut::ResolveIt(HWND hwnd, LPCTSTR lpszLinkFile, LPTSTR lpszPath, int iPathBufferSize)
{
HRESULT hres;
if (lpszPath == NULL)
return E_INVALIDARG;
*lpszPath = 0;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
IShellLink* __psl = NULL;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
// Get a pointer to the IPersistFile interface.
IPersistFile* ppf = NULL;
hres = __psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
if (SUCCEEDED(hres))
{
// Add code here to check return value from MultiByteWideChar
// for success.
// Load the shortcut.
#ifdef _UNICODE
hres = ppf->Load(lpszLinkFile, STGM_READ);
#else
WCHAR wsz[MAX_PATH] = {0};
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH);
hres = ppf->Load(wsz, STGM_READ);
#endif
if (SUCCEEDED(hres))
{
// Resolve the link.
hres = __psl->Resolve(hwnd, 0);
if (SUCCEEDED(hres))
{
// Get the path to the link target.
TCHAR szGotPath[MAX_PATH] = {0};
hres = __psl->GetPath(szGotPath, _countof(szGotPath), NULL, SLGP_SHORTPATH);
if (SUCCEEDED(hres))
{
hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
}
}
}
// Release the pointer to the IPersistFile interface.
ppf->Release();
}
// Release the pointer to the IShellLink interface.
__psl->Release();
}
return hres;
}
and this an answer for a shortcut :
C:\Windows\Installer{53FA9A9F-3C19-4D43-AD6B-DEF365D469BA}
Try first this code:
#include "msi.h"
#pragma comment (lib, "msi")
...
TCHAR Path[MAX_PATH];
Path[0] = '\0';
TCHAR pszComponentCode[MAX_FEATURE_CHARS+1];
TCHAR pszProductCode[MAX_FEATURE_CHARS+1];
pszComponentCode[0] = _T('\0');
pszProductCode[0] = _T('\0');
if ( MsiGetShortcutTarget(pszLinkPathName, pszProductCode, NULL, pszComponentCode) == ERROR_SUCCESS)
{
DWORD dw = MAX_PATH;
UINT ret = MsiGetComponentPath(pszProductCode, pszComponentCode, Path, &dw);
//Path now contains path to EXE
}
else
{
//process regular LNK
}
Then in ELSE part you can call code to resolve regular LNK
I am trying to save a shortcut to my application in the startup folder. It all compiles, but it fails to actually save the game. The error seems to occur at hres = ppf->Save(wsz, TRUE);, where hres is set to -2147024891. If that means something specific, I haven't discovered what yet. My code is copied almost verbatim from MSDN, so I'm pretty confused why it isn't working. Perhaps I don't have permission to save a shortcut to the startup folder? Then again, I am also fairly new to all this, so it might be some basic error I am making. I am copying in all my #includes as well in case that is the problem.
Edit:
First, to avoid confusion, this is CLI based C++.
Checking hres for errors is just part of the MDSN code. This is really almost the exact same code from the website example. I have put in breakpoints, which is how I know that hres becomes -2147024891 right after the line hres = ppf->Save(wsz, TRUE); is run.
In case these are wrong, mediaMaestroLocation is set to "C:\Users\Keith\Documents\Visual Studio 2012\Projects\MediaMaestro\Debug\MediaMaestro.exe" and startupDestination is "C:\Users\Keith\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup". While the exe location looks great, I wonder if it matters that there isn't a \ after the destination folder path. I would have checked it already, but I need to spend a couple minutes figure out how to do it first.
#include <windows.h>
#include <string>
#include <stdio.h>
#include <shobjidl.h>
#include <shlobj.h>
#include "objbase.h"
#include <objidl.h>
#include <shlguid.h>
#include <winnls.h>
#using <System.dll>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Windows::Forms;
char startupDestination[MAX_PATH];
char mediaMaestroLocation[MAX_PATH];
DWORD nChars = 0;
BOOL yChars = 0;
HRESULT CreateLink()
{
CoInitializeEx( NULL, 0 );
HRESULT hres = 0;
IShellLink* psl;
if (SUCCEEDED(hres))
{
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(mediaMaestroLocation);
psl->SetDescription("Media Maestro");
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, startupDestination, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
}
CoUninitialize();
return hres;
}
Here is the click event in the UI that calls the function:
void settingsLaunchOnStart_Click( Object^ Sender, EventArgs^ e )
{
if (settingsLaunchOnStart->Checked == false)
{
HRESULT r;
nChars = GetModuleFileName( NULL, mediaMaestroLocation, sizeof(mediaMaestroLocation) );
yChars = SHGetFolderPath( NULL, CSIDL_STARTUP, NULL, SHGFP_TYPE_CURRENT, startupDestination);
r = CreateLink();
}
else if (settingsLaunchOnStart->Checked == true)
{
//code to remove the shortcut
}
}
Is there something I am missing?
It turns out that it wasn't enough to name the output folder path, I had to name the file and extension as well. It seems strange to me, considering I don't think I have seen a single other example doing this. Anyway, here is my updated working code:
HRESULT CreateLink()
{
CoInitializeEx( NULL, 0 );
HRESULT hres = 0;
IShellLink* psl;
if (SUCCEEDED(hres))
{
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLink, (LPVOID*)&psl); //CLSCTX_ALL CLSCTX_INPROC_SERVER (void**)&psl (LPVOID*)&psl
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(mediaMaestroLocation);
psl->SetDescription(L"Media Maestro");
psl->SetIconLocation(mediaMaestroLocation, 0);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); //(void**)&psl (LPVOID*)&ppf
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Save the link by calling IPersistFile::Save.
hres = _wmakepath_s( wsz, _MAX_PATH, NULL, startupDestination,
L"MediaMaestro", L"lnk" );
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
}
CoUninitialize();
return hres;
}
The addition of _wmakepath_s lets me append the name of my program and its extension onto the filepath I got from SHGetFolderPath. Once I feed that into the IPersistFile interface it saves as it should.
you have only initialized hres to 0 and then ur checking if it succeded ? u are never realling declaring it somewhere, and -2147024891 probably means that the variable is not initialized yet.
a wild guess is that it never even reahes the: hres = ppf->Save(wsz, TRUE); line and therefore it is not initialized :P try putting out some breakpoints when debugging and maybe use some watches to peek into variables :)
Best regards.