1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // glink_win32.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gconvert.h"
23 #include "glink.h"
24 #include "gstr.h"
25 #include "gfile.h"
26 #include <stdexcept>
27 #include <string>
28 #include <sstream>
29 #include <shlwapi.h>
30 #include <shlobj.h>
31 #include <windows.h>
32 
33 template <typename I>
34 struct GComPtr
35 {
36 	I * m_p ;
GComPtrGComPtr37 	GComPtr() : m_p(NULL) {}
GComPtrGComPtr38 	explicit GComPtr( I * p ) : m_p(p) {}
~GComPtrGComPtr39 	~GComPtr() { if(m_p) m_p->Release() ; }
getGComPtr40 	I * get() { return m_p ; }
getGComPtr41 	const I * get() const { return m_p ; }
vpGComPtr42 	void ** vp() { return (void**) &m_p ; }
43 	private: GComPtr(const GComPtr<I> &) ;
44 	private: void operator=(const GComPtr<I> &) ;
45 } ;
46 
47 struct bstr
48 {
49 	private: BSTR m_p ;
bstrbstr50 	public: explicit bstr( const std::string & s )
51 	{
52 		std::wstring ws ;
53 		G::Convert::convert( ws , s ) ;
54 		m_p = SysAllocString( ws.c_str() ) ;
55 	}
~bstrbstr56 	public: ~bstr() { SysFreeString(m_p) ; }
pbstr57 	public: BSTR p() { return m_p ; }
58 	private: bstr( const bstr & ) ;
59 	private: void operator=( const bstr & ) ;
60 } ;
61 
62 class GLinkImp
63 {
64 public:
65 	GLinkImp( const G::Path & target_path , const std::string & name , const std::string & description ,
66 		const G::Path & working_dir , const G::Strings & args , const G::Path & icon_source , GLink::Show show ) ;
67 	static std::string filename( const std::string & ) ;
68 	void saveAs( const G::Path & link_path ) ;
69 
70 private:
71 	GLinkImp( const GLinkImp & ) ;
72 	void operator=( const GLinkImp & ) ;
73 	static void check( HRESULT , const char * ) ;
74 	void createInstance() ;
75 	void qi() ;
76 	void setTargetPath( const G::Path & ) ;
77 	void setDescription( const std::string & s ) ;
78 	void setWorkingDir( const G::Path & ) ;
79 	void setArgs( const G::Strings & ) ;
80 	void setIcon( const G::Path & ) ;
81 	void setShow( int ) ;
82 
83 private:
84 	GComPtr<IShellLink> m_ilink ;
85 	GComPtr<IPersistFile> m_ipf ;
86 } ;
87 
GLinkImp(const G::Path & target_path,const std::string &,const std::string & description,const G::Path & working_dir,const G::Strings & args,const G::Path & icon_source,GLink::Show show_enum)88 GLinkImp::GLinkImp( const G::Path & target_path , const std::string & , const std::string & description ,
89 	const G::Path & working_dir , const G::Strings & args , const G::Path & icon_source , GLink::Show show_enum )
90 {
91 	createInstance() ;
92 	setTargetPath( target_path ) ;
93 	if( ! description.empty() ) setDescription( description ) ;
94 	if( ! working_dir.str().empty() ) setWorkingDir( working_dir ) ;
95 	if( ! args.empty() ) setArgs( args ) ;
96 	if( icon_source != G::Path() ) setIcon( icon_source ) ;
97 	if( show_enum == GLink::Show_Hide ) setShow( SW_HIDE ) ;
98 	qi() ;
99 }
100 
filename(const std::string & name_in)101 std::string GLinkImp::filename( const std::string & name_in )
102 {
103 	return name_in + ".lnk" ;
104 }
105 
check(HRESULT hr,const char * op)106 void GLinkImp::check( HRESULT hr , const char * op )
107 {
108 	if( FAILED(hr) )
109 	{
110 		std::ostringstream ss ;
111 		ss << "com error: " << op << ": " << std::hex << hr ;
112 		if( hr == E_ACCESSDENIED ) ss << " (access denied)" ;
113 		throw GLink::SaveError( ss.str() ) ;
114 	}
115 }
116 
createInstance()117 void GLinkImp::createInstance()
118 {
119 	HRESULT hr = CoCreateInstance( CLSID_ShellLink , NULL , CLSCTX_INPROC_SERVER , IID_IShellLink , m_ilink.vp() ) ;
120 	check( hr , "createInstance" ) ;
121 }
122 
qi()123 void GLinkImp::qi()
124 {
125 	HRESULT hr = m_ilink.get()->QueryInterface( IID_IPersistFile , m_ipf.vp() ) ;
126 	check( hr , "qi" ) ;
127 }
128 
setTargetPath(const G::Path & target_path)129 void GLinkImp::setTargetPath( const G::Path & target_path )
130 {
131 	std::basic_string<TCHAR> arg ;
132 	G::Convert::convert( arg , target_path.str() ) ;
133 	HRESULT hr = m_ilink.get()->SetPath( arg.c_str() ) ;
134 	check( hr , "SetPath" ) ;
135 }
136 
setWorkingDir(const G::Path & working_dir)137 void GLinkImp::setWorkingDir( const G::Path & working_dir )
138 {
139 	std::basic_string<TCHAR> arg ;
140 	G::Convert::convert( arg , working_dir.str() ) ;
141 	HRESULT hr = m_ilink.get()->SetWorkingDirectory( arg.c_str() ) ;
142 	check( hr , "SetWorkingDirectory" ) ;
143 }
144 
setDescription(const std::string & s)145 void GLinkImp::setDescription( const std::string & s )
146 {
147 	std::basic_string<TCHAR> arg ;
148 	G::Convert::convert( arg , s ) ;
149 	HRESULT hr = m_ilink.get()->SetDescription( arg.c_str() ) ;
150 	check( hr , "SetDescription" ) ;
151 }
152 
setArgs(const G::Strings & args)153 void GLinkImp::setArgs( const G::Strings & args )
154 {
155 	std::ostringstream ss ;
156 	const char * sep = "" ;
157 	for( G::Strings::const_iterator p = args.begin() ; p != args.end() ; ++p )
158 	{
159 		std::string s = *p ;
160 		const char * qq = "" ;
161 		if( s.find(' ') != std::string::npos )
162 		{
163 			G::Str::replaceAll( s , "\"" , "\\\"" ) ; // windows is too stupid for this to work :-<
164 			qq = "\"" ;
165 		}
166 		ss << sep << qq << s << qq ;
167 		sep = " " ;
168 	}
169 
170 	std::basic_string<TCHAR> arg ;
171 	G::Convert::convert( arg , ss.str() ) ;
172 	HRESULT hr = m_ilink.get()->SetArguments( arg.c_str() ) ;
173 	check( hr , "SetArguments" ) ;
174 }
175 
setIcon(const G::Path & icon_source)176 void GLinkImp::setIcon( const G::Path & icon_source )
177 {
178 	std::basic_string<TCHAR> arg ;
179 	G::Convert::convert( arg , icon_source.str() ) ;
180 	HRESULT hr = m_ilink.get()->SetIconLocation( arg.c_str() , 0U ) ;
181 	check( hr , "SetIconLocation" ) ;
182 }
183 
setShow(int show)184 void GLinkImp::setShow( int show )
185 {
186 	HRESULT hr = m_ilink.get()->SetShowCmd( show ) ;
187 	check( hr , "SetShowCmd" ) ;
188 }
189 
saveAs(const G::Path & link_path)190 void GLinkImp::saveAs( const G::Path & link_path )
191 {
192 	HRESULT hr = m_ipf.get()->Save( bstr(link_path.str()).p() , TRUE ) ;
193 	check( hr , "Save" ) ;
194 }
195 
196 // ==
197 
GLink(const G::Path & target_path,const std::string & name,const std::string & description,const G::Path & working_dir,const G::Strings & args,const G::Path & icon_source,Show show,const std::string &,const std::string &,const std::string &)198 GLink::GLink( const G::Path & target_path , const std::string & name , const std::string & description ,
199 	const G::Path & working_dir , const G::Strings & args , const G::Path & icon_source , Show show ,
200 	const std::string & , const std::string & , const std::string & ) :
201 		m_imp( new GLinkImp(target_path,name,description,working_dir,args,icon_source,show) )
202 {
203 }
204 
filename(const std::string & name_in)205 std::string GLink::filename( const std::string & name_in )
206 {
207 	return GLinkImp::filename( name_in ) ;
208 }
209 
saveAs(const G::Path & link_path)210 void GLink::saveAs( const G::Path & link_path )
211 {
212 	m_imp->saveAs( link_path ) ;
213 }
214 
~GLink()215 GLink::~GLink()
216 {
217 	delete m_imp ;
218 }
219 
remove(const G::Path & link_path)220 bool GLink::remove( const G::Path & link_path )
221 {
222 	return G::File::remove( link_path , G::File::NoThrow() ) ;
223 }
224 
225 /// \file glink_win32.cpp
226