I’m still on my journey of developing a GP extension using a Web based UI. I’ve shelved the idea of using Ultralight as I think it had some very complex requirement for preinstalled items and copied DDLs to make it workable which makes it go against the single DLL extension ethos.
I am now attempting to use WebView2 with an abstraction wrapper (and so it is Windows specific at the moment but can become cross platform via the aforementioned abstraction layer). I am having a compile issue relating to the linker. I’ll post my code below. Can anyone spot why I am getting “methods already declared” errors when compiling:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.3.2)
# Let's choose a name and version for the extension. Change the name to your
# liking. The version should be a string like "1.0".
set(PROJECT_NAME "BOULDEN.digital-SongSelector") # Change this to your liking
project(${PROJECT_NAME} VERSION 1.0)
# Import the SDK
include(FetchContent)
FetchContent_Declare(
gp-sdk
GIT_REPOSITORY https://github.com/gigperformer/gp-sdk.git
GIT_TAG 6c5432518ef42ea0870fb44597e9d1d3780e2f98 # v44
)
FetchContent_MakeAvailable(gp-sdk)
# Define our library including sources, include directories and dependencies
add_library(${PROJECT_NAME} SHARED)
find_program(NUGET_EXE NAMES nuget)
exec_program(${NUGET_EXE} ARGS install "Microsoft.Web.WebView2" -Version 1.0.1150.38 -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages)
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_PACKAGE_REFERENCES "Microsoft.Web.WebView2")
if(${CMAKE_SIZEOF_VOID_P} STREQUAL 8)
set(architecture x64)
else()
set(architecture x86)
endif()
target_sources(
${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src/LibMain.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/LibMain.h"
"${CMAKE_CURRENT_LIST_DIR}/src/SongSelector.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/SongSelector.h"
)
target_link_libraries(
${PROJECT_NAME} PRIVATE gigperformer::sdk::cpp
PUBLIC ${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2/build/native/${architecture}/WebView2LoaderStatic.lib
)
target_include_directories(
${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
PUBLIC ${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2/build/native/include
)
# Language options: this will be a pure C++20 project
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS NO)
# Install the extension on the development machine
install(
TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION "${GIG_PERFORMER_EXTENSIONS_DIRECTORY}"
RUNTIME DESTINATION "${GIG_PERFORMER_EXTENSIONS_DIRECTORY}")
LibMain.h:
#pragma once
#include "gigperformer/sdk/GPMidiMessages.h"
#include "gigperformer/sdk/GPUtils.h"
#include "gigperformer/sdk/GigPerformerAPI.h"
#include "gigperformer/sdk/types.h"
#include "SongSelector.h"
class LibMain : public gigperformer::sdk::GigPerformerAPI
{
protected:
// These are for creating menu items in Gig Performer that can be used to
// trigger external functions provided by the extension developer
int GetMenuCount() override;
std::string GetMenuName(int index) override;
void InvokeMenu(int itemIndex) override;
public:
// These must be here but no need to do anything unless you want extra behavior
explicit LibMain(LibraryHandle handle) : GigPerformerAPI(handle) {}
~LibMain() override {}
void OnStatusChanged(GPStatusType status) override
{
//consoleLog("Gig status changed to " + std::to_string(status));
}
void OnRackspaceActivated() override
{
//consoleLog("Rackspace activated");
}
void OnSetlistChanged(const std::string &newSetlistName) override
{
//consoleLog("Setlist switched to: " + newSetlistName);
}
void OnModeChanged(int mode) override;
// Now, simply override the callback methods in which you are interested
// and, in the Initialization method at the end of this class,
// call RegisterCallback for each of these methods
// A midi device was added or removed
void OnSongChanged(int oldIndex, int newIndex) override;
void Initialization() override;
// This MUST be defined in your class
std::string GetProductDescription() override;
void showSongSelector();
};
LibMain.cpp
#include "LibMain.h"
#include <cstdint>
#include <fstream>
#include <iostream>
using GPUtils = gigperformer::sdk::GPUtils;
/// Ignore a given value
/// \details this is a dummy function to suppress compiler warnings about unused parameters
template <typename T> void Ignore(T const &) noexcept {};
// define an XML string describing your product
const std::string XMLProductDescription =
// Replace with your information
"<Library>"
" <Product Name=\"BOULDEN.digital - SongSelector\" Version=\"1.0.0 - WebUI\" BuildDate=\"09/14/2022\"></Product>"
" <Description>Just testing a build pipeline</Description>"
" <ImagePath></ImagePath>"
"</Library>";
std::string pathToMe; // This needs to be initialized from the initialization
// section of the LibMain class so it can be used in the
// standalone functions directly below
// List of menu items
std::vector<std::string> menuNames = {
"Show Front Panels",
"Show Back Wiring",
"Show Setlists",
"Show Web View"
};
int LibMain::GetMenuCount()
{
return static_cast<int>(menuNames.size());
}
std::string LibMain::GetMenuName(int index)
{
std::string text;
if (index >= 0 && static_cast<std::size_t>(index) < menuNames.size())
{
text = menuNames[index];
}
return text;
}
void LibMain::InvokeMenu(int index)
{
if (index >= 0 && static_cast<std::size_t>(index) < menuNames.size())
{
switch (index)
{
case 0:
switchToPanelView();
break;
case 1:
switchToWiringView();
break;
case 2:
switchToSetlistView();
break;
case 3:
showSongSelector();
break;
default:
break;
}
}
}
void LibMain::OnModeChanged(int mode)
{
//consoleLog(std::string("Switching to mode: ") + ((mode == GP_SetlistMode) ? "Setlist" : "FrontBack"));
}
void LibMain::OnSongChanged(int oldIndex, int newIndex)
{
Ignore(oldIndex);
//consoleLog("Song changed from C++ example");
std::string name = getSongName(newIndex);
//consoleLog("New song is called " + name);
}
void LibMain::Initialization()
{
// Do any initialization that you need
// .... your code here
std::ofstream outfile;
outfile.open("C:/Users/Public/Documents/Gig Performer/Extensions/log.txt", std::ios::app);
outfile << "Running SongSelector Init" << std::endl;
outfile.close();
//consoleLog("Running SongSelector Init");
// Finally, register all the methods that you are going to actually use,
// i.e, the ones you declared above as override
registerCallback("OnSongChanged");
registerCallback("OnStatusChanged");
registerCallback("OnModeChanged");
registerCallback("OnSetlistChanged");
registerCallback("OnRackspaceActivated");
//consoleLog("path to library " + getPathToMe());
}
std::string LibMain::GetProductDescription()
{
// Generally don't touch this - simply define the constant
// 'XMLProductDescription' at the top of this file with an XML description of
// your product
return XMLProductDescription;
}
void LibMain::showSongSelector() {
//consoleLog("Switching to web view");
}
namespace gigperformer
{
namespace sdk
{
GigPerformerAPI *CreateGPExtension(LibraryHandle handle)
{
return new LibMain(handle);
}
} // namespace sdk
} // namespace gigperformer
SongSelector.h
#pragma once
#include "../lib/webview.h"
class SongSelector
{
private:
public:
SongSelector();
void showSelector();
};
SongSelector.cpp
#include "SongSelector.h"
#include <fstream>
#include <iostream>
SongSelector::SongSelector()
{
std::ofstream outfile;
outfile.open("C:/Users/Public/Documents/Gig Performer/Extensions/log.txt", std::ios::app);
outfile << "Init SongSelector Class" << std::endl;
outfile.close();
showSelector();
}
void SongSelector::showSelector()
{
webview::webview w(false, nullptr);
w.set_title("Basic Example");
w.set_size(480, 320, WEBVIEW_HINT_NONE);
w.set_html("Thanks for using webview!");
w.run();
}
and webview.h
is from this repo which I am using as an abstraction layer to WebView2: https://github.com/webview/webview:
The compiler issues errors claiming the methods in webview.h
are being redefined in SongSelector.cpp.obj
at the linking stage with this type of error message:
LNK2005 webview_create already defined in LibMain.cpp.obj [in file] C:\Users\DaveBoulden\source\repos\gp-extension-cpp\SongSelector.cpp.obj [at line] 1