comparison src/helper.cpp @ 40:40c6936c2fc9 errorcode

Return numerical error codes, so the caller can apply i18n on display; distinguish explicitly the common win32 architecture problems
author Chris Cannam
date Wed, 29 Aug 2018 17:40:22 +0100
parents a43d7a2867d2
children 49946b02414e
comparison
equal deleted inserted replaced
38:a43d7a2867d2 40:40c6936c2fc9
17 * 17 *
18 * Output line for successful load of library libname.so: 18 * Output line for successful load of library libname.so:
19 * SUCCESS|/path/to/libname.so| 19 * SUCCESS|/path/to/libname.so|
20 * 20 *
21 * Output line for failed load of library libname.so: 21 * Output line for failed load of library libname.so:
22 * FAILURE|/path/to/libname.so|Reason for failure if available 22 * FAILURE|/path/to/libname.so|Error message [failureCode]
23 *
24 * or:
25 * FAILURE|/path/to/libname.so|[failureCode]
26 *
27 * where the error message is an optional system-level message, such
28 * as may be returned from strerror or similar (which should be in the
29 * native language for the system ready to show the user), and the
30 * failureCode in square brackets is a mandatory number corresponding
31 * to one of the PluginCandidates::FailureCode values (requiring
32 * conversion to a translated string by the client).
23 * 33 *
24 * Although this program was written for use with Vamp audio analysis 34 * Although this program was written for use with Vamp audio analysis
25 * plugins, it also works with other plugin formats. The program has 35 * plugins, it also works with other plugin formats. The program has
26 * some hardcoded knowledge of Vamp, LADSPA, and DSSI plugins, but it 36 * some hardcoded knowledge of Vamp, LADSPA, and DSSI plugins, but it
27 * can be used with any plugins that involve loading DLLs and looking 37 * can be used with any plugins that involve loading DLLs and looking
62 dealings in this Software without prior written authorization. 72 dealings in this Software without prior written authorization.
63 */ 73 */
64 74
65 #include "../version.h" 75 #include "../version.h"
66 76
77 #include "../checker/checkcode.h"
78
67 static const char programName[] = "vamp-plugin-load-checker"; 79 static const char programName[] = "vamp-plugin-load-checker";
68 80
69 #ifdef _WIN32 81 #ifdef _WIN32
70 #include <windows.h> 82 #include <windows.h>
71 #include <process.h> 83 #include <process.h>
76 88
77 #ifdef _WIN32 89 #ifdef _WIN32
78 #ifndef UNICODE 90 #ifndef UNICODE
79 #error "This must be compiled with UNICODE defined" 91 #error "This must be compiled with UNICODE defined"
80 #endif 92 #endif
93
81 static std::string lastLibraryName = ""; 94 static std::string lastLibraryName = "";
95
82 static HMODULE LoadLibraryUTF8(std::string name) { 96 static HMODULE LoadLibraryUTF8(std::string name) {
83 lastLibraryName = name; 97 lastLibraryName = name;
84 int n = name.size(); 98 int n = name.size();
85 int wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, 0, 0); 99 int wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, 0, 0);
86 wchar_t *wname = new wchar_t[wn+1]; 100 wchar_t *wname = new wchar_t[wn+1];
88 wname[wn] = L'\0'; 102 wname[wn] = L'\0';
89 HMODULE h = LoadLibraryW(wname); 103 HMODULE h = LoadLibraryW(wname);
90 delete[] wname; 104 delete[] wname;
91 return h; 105 return h;
92 } 106 }
107
93 static std::string GetErrorText() { 108 static std::string GetErrorText() {
94 DWORD err = GetLastError(); 109 DWORD err = GetLastError();
95 wchar_t *buffer; 110 wchar_t *buffer;
96 FormatMessageW( 111 FormatMessageW(
97 FORMAT_MESSAGE_ALLOCATE_BUFFER | 112 FORMAT_MESSAGE_ALLOCATE_BUFFER |
98 FORMAT_MESSAGE_FROM_SYSTEM | 113 FORMAT_MESSAGE_FROM_SYSTEM |
99 FORMAT_MESSAGE_IGNORE_INSERTS, 114 FORMAT_MESSAGE_IGNORE_INSERTS,
100 NULL, 115 NULL,
101 err, 116 err,
102 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 117 MAKELANGID(LANG_USER_DEFAULT, SUBLANG_USER_DEFAULT),
103 (LPWSTR) &buffer, 118 (LPWSTR) &buffer,
104 0, NULL ); 119 0, NULL );
105 int wn = wcslen(buffer); 120 int wn = wcslen(buffer);
106 int n = WideCharToMultiByte(CP_UTF8, 0, buffer, wn, 0, 0, 0, 0); 121 int n = WideCharToMultiByte(CP_UTF8, 0, buffer, wn, 0, 0, 0, 0);
107 if (n < 0) { 122 if (n < 0) {
124 if (pos != std::string::npos && lastLibraryName != "") { 139 if (pos != std::string::npos && lastLibraryName != "") {
125 s.replace(pos, 2, lastLibraryName); 140 s.replace(pos, 2, lastLibraryName);
126 } 141 }
127 return s; 142 return s;
128 } 143 }
144
129 #define DLOPEN(a,b) LoadLibraryUTF8(a) 145 #define DLOPEN(a,b) LoadLibraryUTF8(a)
130 #define DLSYM(a,b) (void *)GetProcAddress((HINSTANCE)(a),(b).c_str()) 146 #define DLSYM(a,b) (void *)GetProcAddress((HINSTANCE)(a),(b).c_str())
131 #define DLCLOSE(a) (!FreeLibrary((HINSTANCE)(a))) 147 #define DLCLOSE(a) (!FreeLibrary((HINSTANCE)(a)))
132 #define DLERROR() (GetErrorText()) 148 #define DLERROR() (GetErrorText())
149
133 #else 150 #else
151
134 #include <dlfcn.h> 152 #include <dlfcn.h>
135 #define DLOPEN(a,b) dlopen((a).c_str(),(b)) 153 #define DLOPEN(a,b) dlopen((a).c_str(),(b))
136 #define DLSYM(a,b) dlsym((a),(b).c_str()) 154 #define DLSYM(a,b) dlsym((a),(b).c_str())
137 #define DLCLOSE(a) dlclose((a)) 155 #define DLCLOSE(a) dlclose((a))
138 #define DLERROR() dlerror() 156 #define DLERROR() dlerror()
157
139 #endif 158 #endif
140 159
141 //#include <unistd.h> 160 //#include <unistd.h>
142 161
143 using namespace std; 162 using namespace std;
144 163
145 string error() 164 string error()
146 { 165 {
147 string e = DLERROR(); 166 return DLERROR();
148 if (e == "") return "(unknown error)"; 167 }
149 else return e; 168
150 } 169 struct Result {
151 170 PluginCheckCode code;
152 string checkLADSPAStyleDescriptorFn(void *f) 171 string message;
172 };
173
174 Result checkLADSPAStyleDescriptorFn(void *f)
153 { 175 {
154 typedef const void *(*DFn)(unsigned long); 176 typedef const void *(*DFn)(unsigned long);
155 DFn fn = DFn(f); 177 DFn fn = DFn(f);
156 unsigned long index = 0; 178 unsigned long index = 0;
157 while (fn(index)) ++index; 179 while (fn(index)) ++index;
158 if (index == 0) return "Library contains no plugins"; 180 if (index == 0) return { PluginCheckCode::FAIL_NO_PLUGINS, "" };
159 return ""; 181 return { PluginCheckCode::SUCCESS, "" };
160 } 182 }
161 183
162 string checkVampDescriptorFn(void *f) 184 Result checkVampDescriptorFn(void *f)
163 { 185 {
164 typedef const void *(*DFn)(unsigned int, unsigned int); 186 typedef const void *(*DFn)(unsigned int, unsigned int);
165 DFn fn = DFn(f); 187 DFn fn = DFn(f);
166 unsigned int index = 0; 188 unsigned int index = 0;
167 while (fn(2, index)) ++index; 189 while (fn(2, index)) ++index;
168 if (index == 0) return "Library contains no plugins"; 190 if (index == 0) return { PluginCheckCode::FAIL_NO_PLUGINS, "" };
169 return ""; 191 return { PluginCheckCode::SUCCESS, "" };
170 } 192 }
171 193
172 string check(string soname, string descriptor) 194 Result check(string soname, string descriptor)
173 { 195 {
174 void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); 196 void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL);
175 if (!handle) { 197 if (!handle) {
176 return "Unable to open plugin library: " + error(); 198 PluginCheckCode code = PluginCheckCode::FAIL_NOT_LOADABLE;
177 } 199 string message = error();
178 200 #ifdef _WIN32
179 string msg = ""; 201 DWORD err = GetLastError();
202 if (err == ERROR_BAD_EXE_FORMAT) {
203 code = PluginCheckCode::FAIL_WRONG_ARCHITECTURE;
204 } else if (err == ERROR_MOD_NOT_FOUND) {
205 code = PluginCheckCode::FAIL_DEPENDENCY_MISSING;
206 }
207 #endif
208 return { code, message };
209 }
210
211 Result result { PluginCheckCode::SUCCESS, "" };
180 212
181 void *fn = DLSYM(handle, descriptor); 213 void *fn = DLSYM(handle, descriptor);
182 if (!fn) { 214 if (!fn) {
183 msg = "Failed to find plugin descriptor " + descriptor + 215 result = { PluginCheckCode::FAIL_DESCRIPTOR_MISSING, error() };
184 " in library: " + error();
185 } else if (descriptor == "ladspa_descriptor") { 216 } else if (descriptor == "ladspa_descriptor") {
186 msg = checkLADSPAStyleDescriptorFn(fn); 217 result = checkLADSPAStyleDescriptorFn(fn);
187 } else if (descriptor == "dssi_descriptor") { 218 } else if (descriptor == "dssi_descriptor") {
188 msg = checkLADSPAStyleDescriptorFn(fn); 219 result = checkLADSPAStyleDescriptorFn(fn);
189 } else if (descriptor == "vampGetPluginDescriptor") { 220 } else if (descriptor == "vampGetPluginDescriptor") {
190 msg = checkVampDescriptorFn(fn); 221 result = checkVampDescriptorFn(fn);
191 } else { 222 } else {
192 cerr << "Note: no descriptor logic known for descriptor function \"" 223 cerr << "Note: no descriptor logic known for descriptor function \""
193 << descriptor << "\"; not actually calling it" << endl; 224 << descriptor << "\"; not actually calling it" << endl;
194 } 225 }
195 226
196 DLCLOSE(handle); 227 DLCLOSE(handle);
197 228
198 return msg; 229 return result;
199 } 230 }
200 231
201 int main(int argc, char **argv) 232 int main(int argc, char **argv)
202 { 233 {
203 bool allGood = true; 234 bool allGood = true;
208 if (argc > 1) { 239 if (argc > 1) {
209 string opt = argv[1]; 240 string opt = argv[1];
210 if (opt == "-?" || opt == "-h" || opt == "--help") { 241 if (opt == "-?" || opt == "-h" || opt == "--help") {
211 showUsage = true; 242 showUsage = true;
212 } else if (opt == "-v" || opt == "--version") { 243 } else if (opt == "-v" || opt == "--version") {
213 cout << CHECKER_VERSION << endl; 244 cout << CHECKER_COMPATIBILITY_VERSION << endl;
214 return 0; 245 return 0;
215 } 246 }
216 } 247 }
217 248
218 if (argc != 2 || showUsage) { 249 if (argc != 2 || showUsage) {
237 // fail anyway. 268 // fail anyway.
238 SetErrorMode(SEM_FAILCRITICALERRORS); 269 SetErrorMode(SEM_FAILCRITICALERRORS);
239 #endif 270 #endif
240 271
241 while (getline(cin, soname)) { 272 while (getline(cin, soname)) {
242 string report = check(soname, descriptor); 273 Result result = check(soname, descriptor);
243 if (report != "") { 274 if (result.code == PluginCheckCode::SUCCESS) {
244 cout << "FAILURE|" << soname << "|" << report << endl; 275 cout << "SUCCESS|" << soname << "|" << endl;
276 } else {
277 if (result.message == "") {
278 cout << "FAILURE|" << soname
279 << "|[" << int(result.code) << "]" << endl;
280 } else {
281 cout << "FAILURE|" << soname
282 << "|" << result.message << " ["
283 << int(result.code) << "]" << endl;
284 }
245 allGood = false; 285 allGood = false;
246 } else {
247 cout << "SUCCESS|" << soname << "|" << endl;
248 } 286 }
249 } 287 }
250 288
251 return allGood ? 0 : 1; 289 return allGood ? 0 : 1;
252 } 290 }