Audacity 3.2.0
module_win32.cpp
Go to the documentation of this file.
1//-----------------------------------------------------------------------------
2// Project : VST SDK
3//
4// Category : Helpers
5// Filename : public.sdk/source/vst/hosting/module_win32.cpp
6// Created by : Steinberg, 08/2016
7// Description : hosting module classes (win32 implementation)
8//
9//-----------------------------------------------------------------------------
10// LICENSE
11// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved
12//-----------------------------------------------------------------------------
13// Redistribution and use in source and binary forms, with or without modification,
14// are permitted provided that the following conditions are met:
15//
16// * Redistributions of source code must retain the above copyright notice,
17// this list of conditions and the following disclaimer.
18// * Redistributions in binary form must reproduce the above copyright notice,
19// this list of conditions and the following disclaimer in the documentation
20// and/or other materials provided with the distribution.
21// * Neither the name of the Steinberg Media Technologies nor the names of its
22// contributors may be used to endorse or promote products derived from this
23// software without specific prior written permission.
24//
25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34// OF THE POSSIBILITY OF SUCH DAMAGE.
35//-----------------------------------------------------------------------------
36
37#include "public.sdk/source/vst/utility/optional.h"
38#include "public.sdk/source/vst/utility/stringconvert.h"
39#include "public.sdk/source/vst/hosting/module.h"
40
41#include <windows.h>
42#include <shlobj.h>
43
44#include <algorithm>
45#include <iostream>
46
47#if _HAS_CXX17 && defined(_MSC_VER)
48#if __has_include(<filesystem>)
49#define USE_FILESYSTEM 1
50#elif __has_include(<experimental/filesystem>)
51#define USE_FILESYSTEM 0
52#endif
53#else
54#define USE_FILESYSTEM 0
55#endif
56
57#if USE_FILESYSTEM == 1
58#include <filesystem>
59namespace filesystem = std::filesystem;
60#else
61// The <experimental/filesystem> header is deprecated. It is superseded by the C++17 <filesystem>
62// header. You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence the
63// warning, otherwise the build will fail in VS2020 16.3.0
64#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
65#include <experimental/filesystem>
66namespace filesystem = std::experimental::filesystem;
67#endif
68
69#pragma comment(lib, "Shell32")
70
71//------------------------------------------------------------------------
72extern "C" {
73using InitModuleFunc = bool (PLUGIN_API*) ();
74using ExitModuleFunc = bool (PLUGIN_API*) ();
75}
76
77template <typename T>
78std::string ConvertToUTF8(const T* str)
79{
80 static_assert(sizeof(T) == sizeof(Steinberg::Vst::TChar));
81 return VST3::StringConvert::convert(
82 static_cast<const Steinberg::Vst::TChar*>(static_cast<const void*>(str)));
83}
84
85//------------------------------------------------------------------------
86namespace VST3 {
87namespace Hosting {
88
89//------------------------------------------------------------------------
90namespace {
91
92#if SMTG_OS_WINDOWS_ARM
93#define USE_OLE 0
94#if SMTG_PLATFORM_64
95constexpr auto architectureString = "arm_64-win";
96#else
97constexpr auto architectureString = "arm-win";
98#endif
99#else
100#define USE_OLE !USE_FILESYSTEM
101#if SMTG_PLATFORM_64
102constexpr auto architectureString = "x86_64-win";
103#else
104constexpr auto architectureString = "x86-win";
105#endif
106#endif
107
108#if USE_OLE
109//------------------------------------------------------------------------
110struct Ole
111{
112 static Ole& instance ()
113 {
114 static Ole gInstance;
115 return gInstance;
116 }
117
118private:
119 Ole () { OleInitialize (nullptr); }
120 ~Ole () { OleUninitialize (); }
121};
122#endif // USE_OLE
123
124//------------------------------------------------------------------------
125class VST3_API Win32Module : public Module
126{
127public:
128 template <typename T>
129 T getFunctionPointer (const char* name)
130 {
131 return reinterpret_cast<T> (GetProcAddress (mModule, name));
132 }
133
134 ~Win32Module () override
135 {
136 factory = PluginFactory (nullptr);
137
138 if (mModule)
139 {
140 // ExitDll is optional
141 if (auto dllExit = getFunctionPointer<ExitModuleFunc> ("ExitDll"))
142 dllExit ();
143
144 FreeLibrary ((HMODULE)mModule);
145 }
146 }
147
148 bool load (const std::string& inPath, std::string& errorDescription) override
149 {
150 auto wideStr = StringConvert::convert (inPath);
151 mModule = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
152 if (!mModule)
153 {
154 filesystem::path p (inPath);
155 auto filename = p.filename ();
156 p /= "Contents";
158 p /= filename;
159 wideStr = StringConvert::convert (p.string ());
160 mModule = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
161 if (!mModule)
162 {
163 auto lastError = GetLastError ();
164 LPVOID lpMessageBuffer;
165 FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
166 nullptr, lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
167 (LPSTR)&lpMessageBuffer, 0, nullptr);
168 errorDescription = "LoadLibray failed: " + std::string ((char*)lpMessageBuffer);
169 LocalFree (lpMessageBuffer);
170
171 return false;
172 }
173 }
174 auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
175 if (!factoryProc)
176 {
177 errorDescription = "The dll does not export the required 'GetPluginFactory' function";
178 return false;
179 }
180 // InitDll is optional
181 auto dllEntry = getFunctionPointer<InitModuleFunc> ("InitDll");
182 if (dllEntry && !dllEntry ())
183 {
184 errorDescription = "Calling 'InitDll' failed";
185 return false;
186 }
187 auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (owned (factoryProc ()));
188 if (!f)
189 {
190 errorDescription = "Calling 'GetPluginFactory' returned nullptr";
191 return false;
192 }
193 factory = PluginFactory (f);
194 return true;
195 }
196
197 HINSTANCE mModule {nullptr};
198};
199
200//------------------------------------------------------------------------
201bool checkVST3Package (filesystem::path& p)
202{
203 auto filename = p.filename ();
204 p /= "Contents";
206 p /= filename;
207 auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (p.c_str ()), GENERIC_READ, FILE_SHARE_READ,
208 nullptr, OPEN_EXISTING, 0, nullptr);
209 if (hFile != INVALID_HANDLE_VALUE)
210 {
211 CloseHandle (hFile);
212 return true;
213 }
214 return false;
215}
216
217//------------------------------------------------------------------------
218bool isFolderSymbolicLink (const filesystem::path& p)
219{
220#if USE_FILESYSTEM
221 if (/*filesystem::exists (p) &&*/ filesystem::is_symlink (p))
222 return true;
223#else
224 std::wstring wString = p.generic_wstring ();
225 auto attrib = GetFileAttributesW (reinterpret_cast<LPCWSTR> (wString.c_str ()));
226 if (attrib & FILE_ATTRIBUTE_REPARSE_POINT)
227 {
228 auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.c_str ()), GENERIC_READ,
229 FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
230 if (hFile == INVALID_HANDLE_VALUE)
231 return true;
232 else
233 CloseHandle (hFile);
234 }
235#endif
236 return false;
237}
238
239//------------------------------------------------------------------------
240Optional<std::string> getKnownFolder (REFKNOWNFOLDERID folderID)
241{
242 PWSTR wideStr {};
243 if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr)))
244 return {};
245 return ConvertToUTF8(wideStr);
246}
247
248//------------------------------------------------------------------------
249VST3::Optional<filesystem::path> resolveShellLink (const filesystem::path& p)
250{
251#if USE_FILESYSTEM
252 return {filesystem::read_symlink (p)};
253#else
254#if USE_OLE
255 Ole::instance ();
256
257 IShellLink* shellLink = nullptr;
258 if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
259 IID_IShellLink, reinterpret_cast<LPVOID*> (&shellLink))))
260 return {};
261
262 IPersistFile* persistFile = nullptr;
263 if (!SUCCEEDED (
264 shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast<void**> (&persistFile))))
265 return {};
266
267 if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ)))
268 return {};
269
270 if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500))))
271 return {};
272
273 WCHAR resolvedPath[MAX_PATH];
274 if (!SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, nullptr, SLGP_SHORTPATH)))
275 return {};
276
277 std::wstring longPath;
278 longPath.resize (MAX_PATH);
279 auto numChars =
280 GetLongPathNameW (resolvedPath, const_cast<wchar_t*> (longPath.data ()), MAX_PATH);
281 if (!numChars)
282 return {};
283 longPath.resize (numChars);
284
285 persistFile->Release ();
286 shellLink->Release ();
287
288 return {filesystem::path (longPath)};
289#else
290 // TODO for ARM
291 return {};
292#endif
293#endif
294}
295
296//------------------------------------------------------------------------
297void findFilesWithExt (const filesystem::path& path, const std::string& ext,
298 Module::PathList& pathList, bool recursive = true)
299{
300 for (auto& p : filesystem::directory_iterator (path))
301 {
302#if USE_FILESYSTEM
303 filesystem::path finalPath (p);
304 if (isFolderSymbolicLink (p))
305 {
306 if (auto res = resolveShellLink (p))
307 {
308 finalPath = *res;
309 if (!filesystem::exists (finalPath))
310 continue;
311 }
312 else
313 continue;
314 }
315 const auto& cpExt = finalPath.extension ();
316 if (cpExt == ext)
317 {
318 filesystem::path vstPath (finalPath);
319 if (checkVST3Package (vstPath))
320 {
321 pathList.push_back (vstPath.generic_string ());
322 continue;
323 }
324 }
325
326 if (filesystem::is_directory (finalPath))
327 {
328 if (recursive)
329 findFilesWithExt (finalPath, ext, pathList, recursive);
330 }
331 else if (cpExt == ext)
332 pathList.push_back (finalPath.generic_string ());
333#else
334 const auto& cp = p.path ();
335 const auto& cpExt = cp.extension ();
336 if (cpExt == ext)
337 {
338 if ((p.status ().type () == filesystem::file_type::directory) ||
340 {
341 filesystem::path finalPath (p);
342 if (checkVST3Package (finalPath))
343 {
344 pathList.push_back (finalPath.generic_u8string ());
345 continue;
346 }
347 findFilesWithExt (cp, ext, pathList, recursive);
348 }
349 else
350 pathList.push_back (cp.generic_u8string ());
351 }
352 else if (recursive)
353 {
354 if (p.status ().type () == filesystem::file_type::directory)
355 {
356 findFilesWithExt (cp, ext, pathList, recursive);
357 }
358 else if (cpExt == ".lnk")
359 {
360 if (auto resolvedLink = resolveShellLink (cp))
361 {
362 if (resolvedLink->extension () == ext)
363 {
364 if (filesystem::is_directory (*resolvedLink) ||
365 isFolderSymbolicLink (*resolvedLink))
366 {
367 filesystem::path finalPath (*resolvedLink);
368 if (checkVST3Package (finalPath))
369 {
370 pathList.push_back (finalPath.generic_u8string ());
371 continue;
372 }
373 findFilesWithExt (*resolvedLink, ext, pathList, recursive);
374 }
375 else
376 pathList.push_back (resolvedLink->generic_u8string ());
377 }
378 else if (filesystem::is_directory (*resolvedLink))
379 {
380 const auto& str = resolvedLink->generic_u8string ();
381 if (cp.generic_u8string ().compare (0, str.size (), str.data (),
382 str.size ()) != 0)
383 findFilesWithExt (*resolvedLink, ext, pathList, recursive);
384 }
385 }
386 }
387 }
388#endif
389 }
390}
391
392//------------------------------------------------------------------------
393void findModules (const filesystem::path& path, Module::PathList& pathList)
394{
395 if (filesystem::exists (path))
396 findFilesWithExt (path, ".vst3", pathList);
397}
398
399//------------------------------------------------------------------------
401 const std::string& modulePath)
402{
403 filesystem::path path (modulePath);
404
405 path = path.parent_path ();
406 if (path.filename () != architectureString)
407 return {};
408 path = path.parent_path ();
409 if (path.filename () != "Contents")
410 return {};
411
412 return Optional<filesystem::path> {std::move (path)};
413}
414
415//------------------------------------------------------------------------
416} // anonymous
417
418//------------------------------------------------------------------------
419Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
420{
421 auto _module = std::make_shared<Win32Module> ();
422 if (_module->load (path, errorDescription))
423 {
424 _module->path = path;
425 auto it = std::find_if (path.rbegin (), path.rend (),
426 [] (const std::string::value_type& c) { return c == '/'; });
427 if (it != path.rend ())
428 _module->name = {it.base (), path.end ()};
429 return _module;
430 }
431 return nullptr;
432}
433
434//------------------------------------------------------------------------
435Module::PathList Module::getModulePaths ()
436{
437 // find plug-ins located in common/VST3
438 PathList list;
439 if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon))
440 {
441 filesystem::path p (*knownFolder);
442 p.append ("VST3");
443 findModules (p, list);
444 }
445
446 // find plug-ins located in VST3 (application folder)
447 WCHAR modulePath[MAX_PATH + 1];
448 GetModuleFileNameW (nullptr, modulePath, MAX_PATH);
449 auto appPath = ConvertToUTF8 (modulePath);
450 filesystem::path path (appPath);
451 path = path.parent_path ();
452 path = path.append ("VST3");
453 findModules (path, list);
454
455 return list;
456}
457
458//------------------------------------------------------------------------
459Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
460{
461 SnapshotList result;
462 auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
463 if (!path)
464 return result;
465
466 *path /= "Resources";
467 *path /= "Snapshots";
468
469 if (filesystem::exists (*path) == false)
470 return result;
471
472 PathList pngList;
473 findFilesWithExt (*path, ".png", pngList, false);
474 for (auto& png : pngList)
475 {
476 filesystem::path p (png);
477 auto filename = p.filename ().generic_string ();
478 auto uid = Snapshot::decodeUID (filename);
479 if (!uid)
480 continue;
481 auto scaleFactor = 1.;
482 if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
483 scaleFactor = *decodedScaleFactor;
484
485 Module::Snapshot::ImageDesc desc;
486 desc.scaleFactor = scaleFactor;
487 desc.path = std::move (png);
488 bool found = false;
489 for (auto& entry : result)
490 {
491 if (entry.uid != *uid)
492 continue;
493 found = true;
494 entry.images.emplace_back (std::move (desc));
495 break;
496 }
497 if (found)
498 continue;
499 Module::Snapshot snapshot;
500 snapshot.uid = *uid;
501 snapshot.images.emplace_back (std::move (desc));
502 result.emplace_back (std::move (snapshot));
503 }
504 return result;
505}
506
507//------------------------------------------------------------------------
508} // Hosting
509} // VST3
static RegisteredToolbarFactory factory
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
static ProjectFileIORegistry::AttributeWriterEntry entry
bool load(const std::string &inPath, std::string &errorDescription) override
bool(PLUGIN_API *)() InitModuleFunc
bool(PLUGIN_API *)() ExitModuleFunc
std::string ConvertToUTF8(const T *str)
VST3::Optional< filesystem::path > resolveShellLink(const filesystem::path &p)
Optional< std::string > getKnownFolder(REFKNOWNFOLDERID folderID)
void findFilesWithExt(const filesystem::path &path, const std::string &ext, Module::PathList &pathList, bool recursive=true)
void findModules(const filesystem::path &path, Module::PathList &pathList)
Optional< filesystem::path > getContentsDirectoryFromModuleExecutablePath(const std::string &modulePath)
bool isFolderSymbolicLink(const filesystem::path &p)
const TranslatableString desc
Definition: ExportPCM.cpp:51