I need to be able to read the target of a shortcut (a .lnk file).
I have Googled this and found numerous results that I have found to be helpful:
http://cboard.cprogramming.com/windows-programming/62962-ishelllink-getpath-dev-cplusplus.html
http://www.go4answers.com/Example/get-shortcut-target-cpp-win64-216615.aspx
http://msdn.microsoft.com/en-us/library/bb776891%28VS.85%29.aspx
http://www.codeproject.com/KB/shell/create_shortcut.aspx
Some of these web pages don't mention which header files I need, and I am unaware how to find this information out.
The code that I am currently trying to get working is this:
#include <windows.h>
#include <string>
#include <objidl.h> /* For IPersistFile */
#include <shlobj.h> /* For IShellLink */
using namespace std;
int main(void)
{
IShellLink* psl;
wchar_t* tempStr = new wchar_t[MAX_PATH];
string path = "E:\\shortcuts\\myshortcut.lnk";
HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl);
if (SUCCEEDED(hr))
{
IPersistFile* ppf;
hr = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
if (SUCCEEDED(hr))
{
hr = ppf->Load(path.c_str(), STGM_READ);
if (SUCCEEDED(hr))
{
WIN32_FIND_DATA wfd;
psl->GetPath(tempStr, MAX_PATH, &wfd, SLGP_UNCPRIORITY | SLGP_RAWPATH);
}
}
}
return 0;
}
You can probably see that this is mainly from one of the websites above, however they did not mention which headers they used, so I had a good guess (which seems to be working) at which ones to use.
Currently the errors I am getting are:
In function 'int main()':
24|error: no matching function for call to 'IPersistFile::Load(const char*, int)'
29|error: no matching function for call to 'IShellLinkA::GetPath(wchar_t*&, int, WIN32_FIND_DATA*, int)'
||=== Build finished: 2 errors, 0 warnings ===|
I was hoping that someone may be able to give me some advice on this, whether it is just pointing me to some better links or, even better, possibly explaining the above code, how to find out which headers to use and where I am going wrong, or an entirely different solution that achieves the same result.
All headers are fine, but you are using wide (wchar_t based) and 'normal' (char based) strings incorrectly: IPersistFile::Load takes a wide string while IShellLinkA::GetPath takes a normal string.
Using this should compile:
IShellLinkA* psl; //specify the ansi version explicitely
CoInitialize( 0 ); //you forgot this, needed for all COM calls to work
char* tempStr = new char[ MAX_PATH ];
std::wstring path = L"E:\\shortcuts\\myshortcut.lnk";
Also if you just want the path, you can just pass 0 instead of a pointer to a WIN32_FIND_DATA.
Related
I know there are already some questions regarding this topic, but none of them seem to be working under Windows 10.
I am trying to feed some function a path to a file/folder, which then opens this file/folder in an explorer (or better the default software regarding opening directories). If possible it then should also preselect those files.
As a reference point we could use the default argv[0] syntax.
Things i tried before:
void BrowseToFile(LPCTSTR filename)
{
// ITEMIDLIST* pidl = ILCreateFromPath(filename);
ITEMIDLIST* pidl = ILCreateFromPathW(filename);
if (pidl) {
SHOpenFolderAndSelectItems(pidl, 0, 0, 0);
ILFree(pidl);
}
}
This does compile but just does nothing when i feed any sort of path that is LPCTSTR conform.
I also tried
#include <shellapi.h>
#pragma comment(lib, "shell32")
ShellExecute(NULL, L"open", LPCWSTR(filesystem::path(std::filesystem::directory_entry(argv[0])).c_str()), NULL, NULL, SW_SHOWMINIMIZED);
, which also does compile and doesn't prone any errors but no matter what sort of path i feed it, even when I manually feed it LPCWSTR("C:\") as the path, it just doesn't do anything.
The solution that sounds the most suited would probably be an implementation of the SHOpenFolderAndSelectItems function (shlobj_core.h).
But after searching through 4 result sites of google searches, no syntax leads to a working prototype.
Maybe someone could share a solution that fits modern style C++, atleast on Windows 10 which is my targeted platform rn.
I am ofc using C++20.
EDIT
My current Code looks like
void BrowseToFile(LPCTSTR filename)
{
ITEMIDLIST* pidl = ILCreateFromPathW(filename);
if (pidl) {
CoInitializeEx(NULL, COINIT_MULTITHREADED);
SHOpenFolderAndSelectItems(pidl, 0, 0, 0);
ILFree(pidl);
}
}
int main(int argc, char* argv[])
{
BrowseToFile(LPCTSTR(argv[0]));
}
and it still doesn't work..
This question already has answers here:
Cast to LPCWSTR?
(4 answers)
Closed 3 years ago.
I'm trying to load a DLL into C++ but was getting error code 126, which I think means the DLL couldn't be found. After some poking around I changed LoadLibrary to LoadLibraryA and suddendly it worked. However, I am at a complete loss as to why. I realise that I haven't provided the dll for this code to be runable but would be greatful if somebody could provide an explaination as to why this is happening? And prehaps an example of how to get LoadLibary working.
Broken version
#include <stdio.h>
#include <windows.h>
typedef char* (*gf_getCurrentLibraryVersion) ();
int main() {
gf_getCurrentLibraryVersion getVersion;
HINSTANCE hLib = LoadLibrary((LPCWSTR)"libsbnw.dll");
if (hLib) {
getVersion = (gf_getCurrentLibraryVersion)GetProcAddress(hLib, "gf_getCurrentLibraryVersion");
printf("Version = %s\n", getVersion());
}
else {
printf("Error loading dll: %d/n", GetLastError());
}
printf("Hit any key to continue\n");
getchar();
return 0;
}
Compiles and outputs
Error loading dll: 126/nHit any key to continue
to console
Working version
#include <stdio.h>
#include <windows.h>
typedef char* (*gf_getCurrentLibraryVersion) ();
int main() {
gf_getCurrentLibraryVersion getVersion;
HINSTANCE hLib = LoadLibraryA("libsbnw.dll");
if (hLib) {
getVersion = (gf_getCurrentLibraryVersion)GetProcAddress(hLib, "gf_getCurrentLibraryVersion");
printf("Version = %s\n", getVersion());
}
else {
printf("Error loading dll: %d/n", GetLastError());
}
printf("Hit any key to continue\n");
getchar();
return 0;
}
Compiles and outputs
version is: 1.3.4
The problem with your LoadLibrary((LPCWSTR)"libsbnw.dll") call is that your build environment converts that to a LoadLibraryW call, but the way you are trying to pass a wide-character string is wrong.
As you have it, you are simply casting a const char* pointer to a const wchar_t* pointer, which won't work (for example, it will interpret the initial "li" characters as a single 16-bit character).
What you need to do is specify the string literal as a wide character constant, using the L prefix:
HINSTANCE hLib = LoadLibrary(L"libsbnw.dll");
Or, alternatively, using the TEXT() macro (which will boil down to the same, when using the UNICODE build environment):
HINSTANCE hLib = LoadLibrary(TEXT("libsbnw.dll"));
Feel free to ask for further explanation and/or clarification.
Ordinarily the compiler will try to point out when you're making a mistake. But in this case you've told it not to by adding an explicit cast to the string.
HINSTANCE hLib = LoadLibrary((LPCWSTR)"libsbnw.dll");
//^^^^^^^^^
I'm assuming you've built your app with Unicode enabled, which defines a macro converting LoadLibrary to LoadLibraryW. The parameter must be a wide-character string.
HINSTANCE hLib = LoadLibraryW(L"libsbnw.dll");
There's another macro you can use when you're not sure if the app will be compiled with Unicode or not, TEXT() or the shorter form _T(). Not recommended for modern code since needing to turn Unicode on or off hasn't been a problem in many years, just use Unicode always.
HINSTANCE hLib = LoadLibrary(TEXT("libsbnw.dll"));
I'm trying to build a functionality that copies multiple files with default windows dialog.
I found this examples from microsoft: FileOperationSample.cpp
However I get an error on this line:
IShellItemArray *psia;
hr = SHCreateShellItemArray(NULL, psfSampleSrc, c_cMaxFilesToCreate, &rgpidlChildren[0], &psia);
PITEMID_CHILD rgpidlChildren[c_cMaxFilesToCreate] = { 0 };
for (UINT i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(rgpidlChildren); i++)
{
WCHAR szSampleFileName[MAX_PATH];
hr = StringCchPrintfW(szSampleFileName, ARRAYSIZE(szSampleFileName), L"%s%u.%s", c_szSampleFileName, i, c_szSampleFileExt);
if (SUCCEEDED(hr))
{
hr = psfSampleSrc->ParseDisplayName(NULL, NULL, szSampleFileName, NULL, (PIDLIST_RELATIVE *)&rgpidlChildren[i], NULL);
}
}
if (SUCCEEDED(hr))
{
IShellItemArray *psia;
hr = SHCreateShellItemArray(NULL, psfSampleSrc, c_cMaxFilesToCreate, &rgpidlChildren[0], &psia);
The sample code compiles without any errors nor warnings with VS2017. However, if I remove #define STRICT_TYPED_ITEMIDS, I get exactly the same error.
Solution:
Make sure, your source code has #define STRICT_TYPED_ITEMIDS before including any Windows headers, ideally at the beginning of your precompiled header (stdafx.h or pch.h, depending on VS version).
Background:
If STRICT_TYPED_ITEMIDS is not defined, the following defines from the shtypes.h are enabled:
#define PITEMID_CHILD LPITEMIDLIST
#define PCUITEMID_CHILD_ARRAY LPCITEMIDLIST *
So in this case, SHCreateShellItemArray expects a pointer to LPCITEMIDLIST for the 4th parameter, but instead you pass it a pointer to LPITEMIDLIST, which is incompatible with the expected type.
Bonus OldNewThing link:
STRICT_TYPED_ITEMIDS is the shell namespace version of the STRICT macro used by USER and GDI
Yes, samples from Microsoft sometimes need to be adapted to modern headers from modern SDKs. Usually, the more recent SDK (from Visual Studio or not) you use, the stricter the compilation is.
You can just change rgpidlChildren declaration to (note the 'C', for const):
PCITEMID_CHILD rgpidlChildren[c_cMaxFilesToCreate] = { 0 };
I really need your help. I tried everything but the result is always the same, nothing. Please advise.
And this is my code
#include <windows.h>
#include <iostream>
#include <windef.h>
using namespace std;
int main (void)
{
HKEY hKey;
LONG regOpenResult;
const char PATH[] = "C:\\Users\\les\\Documents\\visual studio 2010\\Projects\\registryTester\\Debug\\registryTester.exe";
RegCreateKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0,
NULL,REG_OPTION_VOLATILE,
KEY_SET_VALUE,
NULL,
&hKey,
NULL
);
RegSetValueExW(hKey, L"regTest", 0, REG_SZ, (BYTE*)PATH, strlen(PATH));
RegCloseKey(hKey);
}
You're using the unicode version of the RegSetValueEx, but passing it a char* string. You should change the declaration of PATH to:
const wchar_t PATH[] = L"C:\\Users\\les\\Documents\\visual studio 2010\\Projects\\registryTester\\Debug\\registryTester.exe";
And use wcslen instead of strlen since it's a wchar_t string. Since it's measured in bytes (not characters) and needs to include the null terminator, the size parameter should be wcslen(PATH)*2+2.
Check the return status of each API call!!!! Use GetLastError () for each failure.
Knowing the error should lead you to resolve the problem.
Otherwise, you're flying completely blind...
Yes, PATH shouldn't be an 8-bit character string if you're calling a 16-bit Unicode API. But it's not necessarily the only problem.
And yes, "permissions" could very definitely be an issue if you're using Vista, Windows 7 or higher.
i want to read a file.. but.. when i debug my program it runs but a pop up appears and says system programming has stopped working and in the console, it`s written that Press enter to close the program. my code is ::
// System Programming.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "iostream"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hin;
HANDLE hout;
TCHAR buff[20]= {'q','2','3'};
TCHAR buff2[20]={'a','v'};
hin = CreateFile(_T("Abid.txt"),GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hin == INVALID_HANDLE_VALUE)
{
cout<<"error";
}
WriteFile(hin,buff,40,0,NULL);
CloseHandle(hin);
hout = CreateFile(_T("Abid.txt"),GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hout == INVALID_HANDLE_VALUE)
{
cout<<"error";
}
ReadFile(hout,buff2,40,0,NULL);
CloseHandle(hout);
return 0;
}
According to MSDN, lpNumberOfBytesWritten paremeter can be NULL only when the lpOverlapped parameter is not NULL. So the calls should be
DWORD nWritten;
WriteFile(hin, buff, 40, &nWritten, NULL);
and
DWORD nRead;
ReadFile(hout, buff2, 40, &nRead, NULL);
Also, rename hin and hout.
Others have already answered your question. This is about the code.
// Your code:
// System Programming.cpp : Defines the entry point for the console application.
//
Just remove that comment. It isn't true. :-) The entry point for your program is where the machine code starts executing, and with the Microsoft toolchain it's specified by the /entry linker option.
Note that Microsoft's documentation is generally confused about entry points, e.g. it has always, one way or other, documented incorrect signature for entry point.
It's one of the most infamous Microsoft documentation errors, and, given that it's persisted, in various forms, for 15 years, I think it says something (not sure exactly what, though).
// Your code:
#include "stdafx.h"
You don't need this automatically generated header. Instead use <windows.h>. A minimal way to include <windows.h> for your program would be
#undef UNICODE
#define UNICODE
#include <windows.h>
For C++ in general you'll want to also make sure that STRICT and NOMINMAX are defined before including <windows.h>. With modern tools at least STRICT is defined by default, but it doesn't hurt to make sure. Without it some of declarations won't compile with a C++ compiler, at least not without reinterpret casts, e.g. dialog procedures.
// Your code:
#include "iostream"
using namespace std;
Almost OK.
Do this:
#include <iostream>
using namespace std;
The difference is where the compiler searches for headers. With quoted name it searches in some additional places first (and that's all that the standard has to say about it). With most compilers those additional places include the directory of the including file.
// Your code:
int _tmain(int argc, _TCHAR* argv[])
Oh no! Don't do this. It's a Microsoft "feature" that helps support Windows 9.x. And it's only relevant when you're using MFC linked dynamically and you're targeting Windows 9.x; without MFC in the picture you'd just use the Microsoft Unicode layer.
Area you really targeting Windows 9.x with an app using dynamically linked MFC?
Instead, do ...
int main()
... which is standard, or use the Microsoft language extension ...
int wMain( int argc, wchar_t* argv[] )
... if you want to handle command line arguments the "easy" way.
// Your code:
{
HANDLE hin;
HANDLE hout;
TCHAR buff[20]= {'q','2','3'};
TCHAR buff2[20]={'a','v'};
The TCHAR stuff is just more of that MFC in Windows 9.x support stuff.
Apart from being totally unnecessary (presumably, you're not really targeting Windows 9.x, are you?), it hides your intention and hurts the eyes.
Did you mean ...
char buff[20] = {'q', '2', '3'};
... perhaps?
// Your code:
hin = CreateFile(_T("Abid.txt"),GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hin == INVALID_HANDLE_VALUE)
{
cout<<"error";
}
As others have mentioned, OPEN_EXISTING isn't logical when you're creating the file, and the count pointer argument can't be 0 for your usage.
When using <windows.h>, with UNICODE defined as it should be, the filename argument should be specifed as L"Abid.txt".
Cheers & hth.,
The problem is that you're passing a NULL pointer in for the lpNumberOfBytesWritten/lpNumberOfBytesread parameter. While this is an optional parameter, there's a condition:
This parameter can be NULL only when the lpOverlapped parameter is not NULL
Also, you may have the size of your buffers wrong:
WriteFile(hin,buff,40,0,NULL); // says that buff has 40 bytes
ReadFile(hout,buff2,40,0,NULL); // says that buff2 has 40 bytes
But if you're compiling for ANSI instead of UNICODE, these will only be 20 bytes in size.
You should probably use sizeof(buff) and sizeof(buff2) instead.
Assuming your initial code attempts to create the file as a new file, then you cannot use OPEN_EXISTING, you have to use OPEN_ALWAYS (or some other creational variant) on this call.
The OPEN_EXISTING usage for readback will be OK.
btw once this is fixed the WriteFile calls causes an access violation, as you are trying to write more bytes that your array contains.