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