@Tautvydas-Zilys , I’m still struggling to get the the activation factory gets loaded correctly by the winrt runtime.
Two problems:
- I tried using IL2CPP_EXPORT/__declspec(dllexport) but because of the __stdcall decoration, the function name is getting decorated (compared to the other IL2CPP exports):
Dump of file GameAssembly.dll
File Type: DLL
...
1 0 02C20DA0 CloseZStream = _CloseZStream
2 1 02C20EB0 CreateZStream = _CreateZStream
4 2 02C21020 Flush = _Flush
5 3 02C21050 ReadZStream = _ReadZStream
6 4 02AC93F0 UnityPalGetLocalTimeZoneData = _UnityPalGetLocalTimeZoneData
7 5 02AC99B0 UnityPalGetTimeZoneDataForID = _UnityPalGetTimeZoneDataForID
8 6 02ACB480 UnityPalTimeZoneInfoGetTimeZoneIDs = _UnityPalTimeZoneInfoGetTimeZoneIDs
9 7 02ACB6E0 UseUnityPalForTimeZoneInformation = _UseUnityPalForTimeZoneInformation
10 8 02C211B0 WriteZStream = _WriteZStream
3 9 02C331F0 _DllGetActivationFactory@8 = _DllGetActivationFactory@8
11 A 02A72580 il2cpp_add_internal_call = _il2cpp_add_internal_call
...
Not using STDAPI/__stdcall is easy enough for DllGetActivationFactory but doing this for DllCanUnloadNow cause combaseapi.h to complain about DllCanUnloadNow redefintion with different linkage:
1>...\src\Unity\WindowsStoreApp\Il2CppOutputProject\Source\CppPlugins\Il2CppActivationFactoryWorkaround.cpp(92): error C2375: 'DllCanUnloadNow': redefinition; different linkage
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.19512.0\um\combaseapi.h(1397): note: see declaration of 'DllCanUnloadNow'
It looks like the correct way is exposing these via a DEF file but I don’t see how that’s done in the Il2CppOutputProject.
It’s also not clear to me if DllCanUnloadNow is actually required for this to function here considering this is WinRT (I’m not familiar enough with the nuance here).
- Unfortunately, the WinRT runtime, for some reasons, didn’t seem to like the MyActivationFactory implementation returned. It simply barfed with:
Exception thrown: read access violation.
ppActivationFactory was nullptr.
Callstack just showed this is deep in the runtime code:
> combase.dll!CClassCache::GetOrLoadWinRTInprocClass(IWinRTRuntimeClassInfo * pRuntimeClassInfo, IActivationFactory * * ppActivationFactory) Line 4942 C++
[Inline Frame] combase.dll!CCGetOrLoadWinRTInprocClass(IWinRTRuntimeClassInfo *) Line 8123 C++
[Inline Frame] combase.dll!WinRTGetActivationFactoryOfInprocClass(IWinRTRuntimeClassInfo * pRuntimeClassInfo, const _GUID &) Line 2339 C++
combase.dll!_RoGetActivationFactory(HSTRING__ * activatableClassId, unsigned __int64 userContext, const _GUID & iid, void * * factory) Line 931 C++
combase.dll!RoGetActivationFactory(HSTRING__ * activatableClassId, const _GUID & iid, void * * factory) Line 1030 C++
[Inline Frame] vccorlib140_app.dll!Windows::Foundation::GetActivationFactory(HSTRING__ *) Line 236 C++
vccorlib140_app.dll!__getActivationFactoryByHSTRING(HSTRING__ * str, Platform::Guid & riid, void * * ppActivationFactory) Line 70 C++
vccorlib140_app.dll!FactoryCache::GetFactory(const wchar_t * acid, Platform::Guid & iid, void * * pFactory) Line 343 C++
vccorlib140_app.dll!GetActivationFactoryByPCWSTR(void * str, Platform::Guid & riid, void * * ppActivationFactory) Line 419 C++
Stepping through the DllGetActivationFactory method shows that we’re correctly instantiating MyActivationFactory and returning it, so its not clear what its unhappy about.
Here’s the full code for reference (I tried out a couple of things but its mostly some as what you originally posted):
#include <activation.h>
#include <hstring.h>
#include <wrl.h>
#include "il2cpp-api.h"
#include "vm\Assembly.h"
#include "vm\Class.h"
#include "vm\Image.h"
#include "vm\Object.h"
#include "vm\Runtime.h"
#include "vm\CCW.h"
#include "utils\StringUtils.h"
#include "codegen\il2cpp-codegen.h"
INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; // Static initialization
static BOOL CALLBACK InitializeIL2CPP(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* lpContext)
{
// Assumption:
// Getting the ActivationFactory to instantiate a managed WinRT object is only expected to be invoked
// by the non-Unity process. In those cases, we'll need to make sure that IL2CPP is initialized.
// This needs to match the game name. It can probably be computed but is hardcoded for now.
const wchar_t* kExecutablePath[] = { L"mygame.exe" };
il2cpp_set_commandline_arguments_utf16(1, kExecutablePath, nullptr);
il2cpp_init("Background Process Domain");
il2cpp_set_config_utf16(kExecutablePath[0]);
return TRUE;
}
class MyActivationFactory : public Microsoft::WRL::RuntimeClass<
::Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>,
::IActivationFactory,
::Microsoft::WRL::FtmBase>
{
public:
HRESULT RuntimeClassInitialize(HSTRING className)
{
HRESULT hr = m_ClassName.Set(className);
return hr;
}
//
// Start Boilerplate IUnknown/Inspectable code
// This is usually implemented by the InspectableClass macro; but it does not expect ActivationFactory implementations so we're duplicating that here
//
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
{
return RuntimeClassT::QueryInterface(riid, ppvObject);
}
STDMETHOD_(ULONG, Release)()
{
return RuntimeClassT::Release();
}
STDMETHOD_(ULONG, AddRef)()
{
return RuntimeClassT::AddRef();
}
// Inspectable
STDMETHOD(GetIids)(ULONG *iidCount, IID **iids)
{
return RuntimeClassT::GetIids(iidCount, iids);
}
STDMETHOD(GetRuntimeClassName)(HSTRING* runtimeName)
{
*runtimeName = nullptr;
return ::WindowsCreateString(L"MyActivationFactory", static_cast<UINT32>(::wcslen(L"MyActivationFactory")), runtimeName);
}
STDMETHOD(GetTrustLevel)(TrustLevel* trustLvl)
{
*trustLvl = TrustLevel::BaseTrust;
return S_OK;
}
//
// End Boilerplate IUnknown/Inspectable code
//
// IActivationFactory
STDMETHOD(ActivateInstance)(IInspectable** instance)
{
InitOnceExecuteOnce(&g_InitOnce, InitializeIL2CPP, nullptr, nullptr); // Receives pointer to event object stored in g_InitOnce
unsigned length;
auto buffer = m_ClassName.GetRawBuffer(&length);
auto classNameUTF8 = il2cpp::utils::StringUtils::Utf16ToUtf8(buffer, length);
auto lastDot = classNameUTF8.find_last_of('.');
if (lastDot == std::string::npos)
return REGDB_E_CLASSNOTREG;
auto namespaze = classNameUTF8.substr(0, lastDot);
auto name = classNameUTF8.substr(lastDot + 1);
for (auto assembly : *il2cpp::vm::Assembly::GetAllAssemblies())
{
auto image = il2cpp::vm::Assembly::GetImage(assembly);
auto klass = il2cpp::vm::Image::ClassFromName(image, namespaze.c_str(), name.c_str());
if (klass != nullptr)
{
auto constructor = il2cpp::vm::Class::GetMethodFromName(klass, ".ctor", 0);
if (constructor == nullptr)
return E_FAIL; // class has no constructor
auto instantiatedObject = il2cpp::vm::Object::New(klass);
Il2CppException* exception;
il2cpp::vm::Runtime::Invoke(constructor, instantiatedObject, nullptr, &exception);
if (exception != nullptr)
return E_FAIL; // constructor threw an exception
*instance = reinterpret_cast<IInspectable*>(il2cpp::vm::CCW::GetOrCreate(instantiatedObject, Il2CppIInspectable::IID));
return S_OK;
}
}
return REGDB_E_CLASSNOTREG;
}
private:
Microsoft::WRL::Wrappers::HString m_ClassName;
};
extern "C"
{
STDAPI DllCanUnloadNow()
{
// Always return S_FALSE to prevent the library from getting unloaded
return S_FALSE;
}
__declspec(dllexport) HRESULT DllGetActivationFactory(HSTRING activatableClassId, IActivationFactory** ppActivationFactory)
{
Microsoft::WRL::ComPtr<IActivationFactory> activationFactory;
HRESULT hr = Microsoft::WRL::MakeAndInitialize<MyActivationFactory>(&activationFactory, activatableClassId);
if (FAILED(hr)) return hr;
*ppActivationFactory = activationFactory.Detach();
return S_OK;
}
}
Any insights here?