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
77//------------------------------------------------------------------------
78namespace VST3 {
79namespace Hosting {
80
81//------------------------------------------------------------------------
82namespace {
83
84#if SMTG_OS_WINDOWS_ARM
85#define USE_OLE 0
86#if SMTG_PLATFORM_64
87constexpr auto architectureString = "arm_64-win";
88#else
89constexpr auto architectureString = "arm-win";
90#endif
91#else
92#define USE_OLE !USE_FILESYSTEM
93#if SMTG_PLATFORM_64
94constexpr auto architectureString = "x86_64-win";
95#else
96constexpr auto architectureString = "x86-win";
97#endif
98#endif
99
100#if USE_OLE
101//------------------------------------------------------------------------
102struct Ole
103{
104 static Ole& instance ()
105 {
106 static Ole gInstance;
107 return gInstance;
108 }
109
110private:
111 Ole () { OleInitialize (nullptr); }
112 ~Ole () { OleUninitialize (); }
113};
114#endif // USE_OLE
115
116//------------------------------------------------------------------------
117class Win32Module : public Module
118{
119public:
120 template <typename T>
121 T getFunctionPointer (const char* name)
122 {
123 return reinterpret_cast<T> (GetProcAddress (mModule, name));
124 }
125
126 ~Win32Module () override
127 {
128 factory = PluginFactory (nullptr);
129
130 if (mModule)
131 {
132 // ExitDll is optional
133 if (auto dllExit = getFunctionPointer<ExitModuleFunc> ("ExitDll"))
134 dllExit ();
135
136 FreeLibrary ((HMODULE)mModule);
137 }
138 }
139
140 bool load (const std::string& inPath, std::string& errorDescription) override
141 {
142 auto wideStr = StringConvert::convert (inPath);
143 mModule = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
144 if (!mModule)
145 {
146 filesystem::path p (inPath);
147 auto filename = p.filename ();
148 p /= "Contents";
150 p /= filename;
151 wideStr = StringConvert::convert (p.string ());
152 mModule = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
153 if (!mModule)
154 {
155 auto lastError = GetLastError ();
156 LPVOID lpMessageBuffer;
157 FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
158 nullptr, lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
159 (LPSTR)&lpMessageBuffer, 0, nullptr);
160 errorDescription = "LoadLibray failed: " + std::string ((char*)lpMessageBuffer);
161 LocalFree (lpMessageBuffer);
162
163 return false;
164 }
165 }
166 auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
167 if (!factoryProc)
168 {
169 errorDescription = "The dll does not export the required 'GetPluginFactory' function";
170 return false;
171 }
172 // InitDll is optional
173 auto dllEntry = getFunctionPointer<InitModuleFunc> ("InitDll");
174 if (dllEntry && !dllEntry ())
175 {
176 errorDescription = "Calling 'InitDll' failed";
177 return false;
178 }
179 auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (owned (factoryProc ()));
180 if (!f)
181 {
182 errorDescription = "Calling 'GetPluginFactory' returned nullptr";
183 return false;
184 }
185 factory = PluginFactory (f);
186 return true;
187 }
188
189 HINSTANCE mModule {nullptr};
190};
191
192//------------------------------------------------------------------------
193bool checkVST3Package (filesystem::path& p)
194{
195 auto filename = p.filename ();
196 p /= "Contents";
198 p /= filename;
199 auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (p.c_str ()), GENERIC_READ, FILE_SHARE_READ,
200 nullptr, OPEN_EXISTING, 0, nullptr);
201 if (hFile != INVALID_HANDLE_VALUE)
202 {
203 CloseHandle (hFile);
204 return true;
205 }
206 return false;
207}
208
209//------------------------------------------------------------------------
210bool isFolderSymbolicLink (const filesystem::path& p)
211{
212#if USE_FILESYSTEM
213 if (/*filesystem::exists (p) &&*/ filesystem::is_symlink (p))
214 return true;
215#else
216 std::wstring wString = p.generic_wstring ();
217 auto attrib = GetFileAttributesW (reinterpret_cast<LPCWSTR> (wString.c_str ()));
218 if (attrib & FILE_ATTRIBUTE_REPARSE_POINT)
219 {
220 auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.c_str ()), GENERIC_READ,
221 FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
222 if (hFile == INVALID_HANDLE_VALUE)
223 return true;
224 else
225 CloseHandle (hFile);
226 }
227#endif
228 return false;
229}
230
231//------------------------------------------------------------------------
232Optional<std::string> getKnownFolder (REFKNOWNFOLDERID folderID)
233{
234 PWSTR wideStr {};
235 if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr)))
236 return {};
237 return StringConvert::convert (wideStr);
238}
239
240//------------------------------------------------------------------------
241VST3::Optional<filesystem::path> resolveShellLink (const filesystem::path& p)
242{
243#if USE_FILESYSTEM
244 return {filesystem::read_symlink (p)};
245#else
246#if USE_OLE
247 Ole::instance ();
248
249 IShellLink* shellLink = nullptr;
250 if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
251 IID_IShellLink, reinterpret_cast<LPVOID*> (&shellLink))))
252 return {};
253
254 IPersistFile* persistFile = nullptr;
255 if (!SUCCEEDED (
256 shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast<void**> (&persistFile))))
257 return {};
258
259 if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ)))
260 return {};
261
262 if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500))))
263 return {};
264
265 WCHAR resolvedPath[MAX_PATH];
266 if (!SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, nullptr, SLGP_SHORTPATH)))
267 return {};
268
269 std::wstring longPath;
270 longPath.resize (MAX_PATH);
271 auto numChars =
272 GetLongPathNameW (resolvedPath, const_cast<wchar_t*> (longPath.data ()), MAX_PATH);
273 if (!numChars)
274 return {};
275 longPath.resize (numChars);
276
277 persistFile->Release ();
278 shellLink->Release ();
279
280 return {filesystem::path (longPath)};
281#else
282 // TODO for ARM
283 return {};
284#endif
285#endif
286}
287
288//------------------------------------------------------------------------
289void findFilesWithExt (const filesystem::path& path, const std::string& ext,
290 Module::PathList& pathList, bool recursive = true)
291{
292 for (auto& p : filesystem::directory_iterator (path))
293 {
294#if USE_FILESYSTEM
295 filesystem::path finalPath (p);
296 if (isFolderSymbolicLink (p))
297 {
298 if (auto res = resolveShellLink (p))
299 {
300 finalPath = *res;
301 if (!filesystem::exists (finalPath))
302 continue;
303 }
304 else
305 continue;
306 }
307 const auto& cpExt = finalPath.extension ();
308 if (cpExt == ext)
309 {
310 filesystem::path vstPath (finalPath);
311 if (checkVST3Package (vstPath))
312 {
313 pathList.push_back (vstPath.generic_string ());
314 continue;
315 }
316 }
317
318 if (filesystem::is_directory (finalPath))
319 {
320 if (recursive)
321 findFilesWithExt (finalPath, ext, pathList, recursive);
322 }
323 else if (cpExt == ext)
324 pathList.push_back (finalPath.generic_string ());
325#else
326 const auto& cp = p.path ();
327 const auto& cpExt = cp.extension ();
328 if (cpExt == ext)
329 {
330 if ((p.status ().type () == filesystem::file_type::directory) ||
332 {
333 filesystem::path finalPath (p);
334 if (checkVST3Package (finalPath))
335 {
336 pathList.push_back (finalPath.generic_u8string ());
337 continue;
338 }
339 findFilesWithExt (cp, ext, pathList, recursive);
340 }
341 else
342 pathList.push_back (cp.generic_u8string ());
343 }
344 else if (recursive)
345 {
346 if (p.status ().type () == filesystem::file_type::directory)
347 {
348 findFilesWithExt (cp, ext, pathList, recursive);
349 }
350 else if (cpExt == ".lnk")
351 {
352 if (auto resolvedLink = resolveShellLink (cp))
353 {
354 if (resolvedLink->extension () == ext)
355 {
356 if (filesystem::is_directory (*resolvedLink) ||
357 isFolderSymbolicLink (*resolvedLink))
358 {
359 filesystem::path finalPath (*resolvedLink);
360 if (checkVST3Package (finalPath))
361 {
362 pathList.push_back (finalPath.generic_u8string ());
363 continue;
364 }
365 findFilesWithExt (*resolvedLink, ext, pathList, recursive);
366 }
367 else
368 pathList.push_back (resolvedLink->generic_u8string ());
369 }
370 else if (filesystem::is_directory (*resolvedLink))
371 {
372 const auto& str = resolvedLink->generic_u8string ();
373 if (cp.generic_u8string ().compare (0, str.size (), str.data (),
374 str.size ()) != 0)
375 findFilesWithExt (*resolvedLink, ext, pathList, recursive);
376 }
377 }
378 }
379 }
380#endif
381 }
382}
383
384//------------------------------------------------------------------------
385void findModules (const filesystem::path& path, Module::PathList& pathList)
386{
387 if (filesystem::exists (path))
388 findFilesWithExt (path, ".vst3", pathList);
389}
390
391//------------------------------------------------------------------------
393 const std::string& modulePath)
394{
395 filesystem::path path (modulePath);
396
397 path = path.parent_path ();
398 if (path.filename () != architectureString)
399 return {};
400 path = path.parent_path ();
401 if (path.filename () != "Contents")
402 return {};
403
404 return Optional<filesystem::path> {std::move (path)};
405}
406
407//------------------------------------------------------------------------
408} // anonymous
409
410//------------------------------------------------------------------------
411Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
412{
413 auto _module = std::make_shared<Win32Module> ();
414 if (_module->load (path, errorDescription))
415 {
416 _module->path = path;
417 auto it = std::find_if (path.rbegin (), path.rend (),
418 [] (const std::string::value_type& c) { return c == '/'; });
419 if (it != path.rend ())
420 _module->name = {it.base (), path.end ()};
421 return _module;
422 }
423 return nullptr;
424}
425
426//------------------------------------------------------------------------
427Module::PathList Module::getModulePaths ()
428{
429 // find plug-ins located in common/VST3
430 PathList list;
431 if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon))
432 {
433 filesystem::path p (*knownFolder);
434 p.append ("VST3");
435 findModules (p, list);
436 }
437
438 // find plug-ins located in VST3 (application folder)
439 WCHAR modulePath[MAX_PATH + 1];
440 GetModuleFileNameW (nullptr, modulePath, MAX_PATH);
441 auto appPath = StringConvert::convert (modulePath);
442 filesystem::path path (appPath);
443 path = path.parent_path ();
444 path = path.append ("VST3");
445 findModules (path, list);
446
447 return list;
448}
449
450//------------------------------------------------------------------------
451Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
452{
453 SnapshotList result;
454 auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
455 if (!path)
456 return result;
457
458 *path /= "Resources";
459 *path /= "Snapshots";
460
461 if (filesystem::exists (*path) == false)
462 return result;
463
464 PathList pngList;
465 findFilesWithExt (*path, ".png", pngList, false);
466 for (auto& png : pngList)
467 {
468 filesystem::path p (png);
469 auto filename = p.filename ().generic_string ();
470 auto uid = Snapshot::decodeUID (filename);
471 if (!uid)
472 continue;
473 auto scaleFactor = 1.;
474 if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
475 scaleFactor = *decodedScaleFactor;
476
477 Module::Snapshot::ImageDesc desc;
478 desc.scaleFactor = scaleFactor;
479 desc.path = std::move (png);
480 bool found = false;
481 for (auto& entry : result)
482 {
483 if (entry.uid != *uid)
484 continue;
485 found = true;
486 entry.images.emplace_back (std::move (desc));
487 break;
488 }
489 if (found)
490 continue;
491 Module::Snapshot snapshot;
492 snapshot.uid = *uid;
493 snapshot.images.emplace_back (std::move (desc));
494 result.emplace_back (std::move (snapshot));
495 }
496 return result;
497}
498
499//------------------------------------------------------------------------
500} // Hosting
501} // VST3
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:74
const TranslatableString desc
Definition: ExportPCM.cpp:55
static ProjectFileIORegistry::AttributeWriterEntry entry
bool load(const std::string &inPath, std::string &errorDescription) override
bool(PLUGIN_API *)() InitModuleFunc
bool(PLUGIN_API *)() ExitModuleFunc
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)
static RegisteredToolbarFactory factory