Getting errors related to IL2CPP and MonoPInvokeCallback' with Steamworks

Hello, I am trying to add IAPs to my steam game. I have successfully launched my game from steam and all that however I have been struggling miserably with IAPs.

I build:
Windows x86_64 with IL2CPP
Unity Version: 2019.3.0f6

Here is my code:

public Callback<MicroTxnAuthorizationResponse_t> m_MicroTxnAuthorizationResponse;
   
    private async void BuyProductID(string productId)
    {
        if (steam.steamEnabled)
        {
            //gets steam info like country code, player id etc

            m_MicroTxnAuthorizationResponse = Callback<MicroTxnAuthorizationResponse_t>.Create(OnMicroTxnAuthorizationResponse);
            if (CanPurchase) BuyProductID(productId);
        }
    }

    private static bool CanPurchase;
   
    [AOT.MonoPInvokeCallback(typeof(MicroTxnAuthorizationResponse_t))]
    private static async void OnMicroTxnAuthorizationResponse(MicroTxnAuthorizationResponse_t pCallback)
    {
        CanPurchase = pCallback.m_bAuthorized == 1;
        if (pCallback.m_bAuthorized != 1) return;
        Log("Authorized Payment");
        const string url = "https://partner.steam-api.com/ISteamMicroTxn/FinalizeTxn/v2/?key=<key>&input_json";
        var values = new FinalizeTxn(10000, <id>);
        var json = new StringContent(JsonUtility.ToJson(values));
        await client.PostAsync(url, json);
    }

And this is one of the errors I get in the “Player.txt” file when calling “BuyProductID()”

Uploading Crash Report
NotSupportedException: IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: Steamworks.Callback`1[[Steamworks.MicroTxnAuthorizationResponse_t, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::OnRunCallResult
at Steamworks.Callback`1[T].BuildCCallbackBase () [0x00000] in <00000000000000000000000000000000>:0 
at Steamworks.Callback`1[T]..ctor (Steamworks.Callback`1+DispatchDelegate[T] func, System.Boolean bGameServer) [0x00000] in <00000000000000000000000000000000>:0 
at Steamworks.Callback`1[T].Create (Steamworks.Callback`1+DispatchDelegate[T] func) [0x00000] in <00000000000000000000000000000000>:0 
at GoogleMobileAds.Api.RewardedAd.remove_OnUserEarnedReward (System.EventHandler`1[TEventArgs] value) [0x00000] in <00000000000000000000000000000000>:0 
at Discord.ActivityManager+ActivityJoinHandler.Invoke (System.String secret) [0x00000] in <00000000000000000000000000000000>:0 
at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0 
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0 
at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
at Discord.ActivityManager+ActivityJoinHandler.Invoke (System.String secret) [0x00000] in <00000000000000000000000000000000>:0 
at UnityEngine.WaitForSeconds..ctor (System.Single seconds) [0x00000] in <00000000000000000000000000000000>:0 
at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0 
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
at Discord.ActivityManager+ActivityJoinHandler.Invoke (System.String secret) [0x00000] in <00000000000000000000000000000000>:0 
at UnityEngine.WaitForSeconds..ctor (System.Single seconds) [0x00000] in <00000000000000000000000000000000>:0 
at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
UnityEngine.WaitForSeconds:.ctor(Single)
UnityEngine.UnitySynchronizationContext:Exec()

I also have been getting this when I launch the game:

Uploading Crash Report
NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: SteamManager::SteamAPIDebugTextHook
at Steamworks.SteamClient.SetWarningMessageHook (Steamworks.SteamAPIWarningMessageHook_t pFunction) [0x00000] in <00000000000000000000000000000000>:0 
at SteamManager.OnEnable () [0x00000] in <00000000000000000000000000000000>:0 

It appears that it started showing up after I added [AOT.MonoPInvokeCallback(typeof(MicroTxnAuthorizationResponse_t))], maybe not, I am unsure and was a little unaware till I noticed it.

Anyone got any ideas? Thanks!

Also, I might be doing this all completely wrong too. Steam’s instructions on implementation were unclear to me.

Marshaling of async functions like this does not work. The error message here is a bit misleading, as it does not mention async, but instead thinks this is an instance method (which is incorrect, it is clearly static).

Can you re-write OnMicroTxnAuthorizationResponse so that it is not async?

Hello, this still occurs whether it’s async or not. D:

Do you have source code for “Steamworks.Callback`1[T].BuildCCallbackBase ()”? Looks like it’s trying to marshal a non-static function to native code there.

should i make this static?

// Steamworks.NET Specific
        private void BuildCCallbackBase() {
            m_CallbackBaseVTable = new CCallbackBaseVTable() {
                m_RunCallback = OnRunCallback,
                m_RunCallResult = OnRunCallResult,
                m_GetCallbackSizeBytes = OnGetCallbackSizeBytes
            };
            m_pVTable = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CCallbackBaseVTable)));
            Marshal.StructureToPtr(m_CallbackBaseVTable, m_pVTable, false);

            m_CCallbackBase = new CCallbackBase() {
                m_vfptr = m_pVTable,
                m_nCallbackFlags = 0,
                m_iCallback = CallbackIdentities.GetCallbackIdentity(typeof(T))
            };
            m_pCCallbackBase = GCHandle.Alloc(m_CCallbackBase, GCHandleType.Pinned);
        }

And should I make OnMicroTxnAuthorizationResponse non static?

You should make “OnRunCallback”, “OnRunCallResult” and “OnGetCallbackSizeBytes” static.

6111749--665426--upload_2020-7-20_20-28-29.png

should i make this field static?

Ok I made them static and these are the errors I get:
(also, I moved m_MicroTxnAuthorizationResponse = Callback<MicroTxnAuthorizationResponse_t>.Create(OnMicroTxnAuthorizationResponse); to the start where it initialized steamworks.

Full Errors

Uploading Crash Report
NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: SteamManager::SteamAPIDebugTextHook
at Steamworks.SteamClient.SetWarningMessageHook (Steamworks.SteamAPIWarningMessageHook_t pFunction) [0x00000] in <00000000000000000000000000000000>:0
at SteamManager.OnEnable () [0x00000] in <00000000000000000000000000000000>:0

(Filename: currently not available on il2cpp Line: -1)

UnityIAP Version: 1.23.3
0x00007FFA0DADAD7C (UnityPlayer)
0x00007FFA0DADD823 (UnityPlayer)
0x00007FFA0DAD2D4D (UnityPlayer)
0x00007FFA0E373E3E (UnityPlayer) UnityMain
0x00007FFA0DEE0CC7 (UnityPlayer) UnityMain
0x00007FFA0CBD0992 (GameAssembly) [UnityEngine.CoreModule.cpp:26249] Logger_Log_mB522987DEE1BF780EAEC6864D34D7E285F5E8AB2
0x00007FFA0C954A1E (GameAssembly) [Stores.cpp:34336] StandardPurchasingModule_Instance_mBFD020AAC15056A1B2FC83F818751676576D4A98
0x00007FFA0CE916BA (GameAssembly) [Assembly-CSharp5.cpp:40202] IAPManager_InitializePurchasing_m2E0B90FC1911C935C1FB216EC8EB77014FC42B25
0x00007FFA0CE93724 (GameAssembly) [Assembly-CSharp5.cpp:39185] IAPManager_Start_m468873CB081DE60E03D10A263F9812973E9E04AC
0x00007FFA0C6FAB7C (GameAssembly) [Il2CppInvokerTable.cpp:25721] RuntimeInvoker_TrueVoid_t22962CB4C05B1D89B55A6E1139F0E87A90987017
0x00007FFA0C6A4510 (GameAssembly) [Runtime.cpp:506] il2cpp::vm::Runtime::Invoke
0x00007FFA0DE60520 (UnityPlayer) UnityMain
0x00007FFA0DE71673 (UnityPlayer) UnityMain
0x00007FFA0DE7B818 (UnityPlayer) UnityMain
0x00007FFA0DE7B8AE (UnityPlayer) UnityMain
0x00007FFA0DE7A909 (UnityPlayer) UnityMain
0x00007FFA0DC02FCD (UnityPlayer) UnityMain
0x00007FFA0DD47827 (UnityPlayer) UnityMain
0x00007FFA0DD478C3 (UnityPlayer) UnityMain
0x00007FFA0DD49CCB (UnityPlayer) UnityMain
0x00007FFA0DB06DAE (UnityPlayer)
0x00007FFA0DB05B0A (UnityPlayer)
0x00007FFA0DB09AF8 (UnityPlayer)
0x00007FFA0DB0D5AB (UnityPlayer) UnityMain
0x00007FF7CC7D11F2 (CryptoClickers)
0x00007FFAB20C7BD4 (KERNEL32) BaseThreadInitThunk
0x00007FFAB33CCE51 (ntdll) RtlUserThreadStart

(Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)

Uploading Crash Report
NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: Steamworks.Callback`1[[Steamworks.MicroTxnAuthorizationResponse_t, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::OnRunCallResult
at Steamworks.Callback`1[T].BuildCCallbackBase () [0x00000] in <00000000000000000000000000000000>:0
at Steamworks.Callback`1[T]..ctor (Steamworks.Callback`1+DispatchDelegate[T] func, System.Boolean bGameServer) [0x00000] in <00000000000000000000000000000000>:0
at Steamworks.Callback`1[T].Create (Steamworks.Callback`1+DispatchDelegate[T] func) [0x00000] in <00000000000000000000000000000000>:0
at IAPManager.Start () [0x00000] in <00000000000000000000000000000000>:0

(Filename: currently not available on il2cpp Line: -1)

Uploading Crash Report
InvalidOperationException: Handle is not pinned.
at System.Runtime.CompilerServices.FixedBufferAttribute..ctor (System.Type elementType, System.Int32 length) [0x00000] in <00000000000000000000000000000000>:0
at Steamworks.Callback`1[T].Unregister () [0x00000] in <00000000000000000000000000000000>:0
at Steamworks.Callback`1[T].Dispose () [0x00000] in <00000000000000000000000000000000>:0
at Steamworks.Callback`1[T].Finalize () [0x00000] in <00000000000000000000000000000000>:0
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
System.Reflection.AddEventAdapter:Invoke(Object, Delegate)

(Filename: currently not available on il2cpp Line: -1)``` 

Right, so how you added [MonoPInvokeCallback] attribute on OnMicroTxnAuthorizationResponse on your first post, you need to do the same for these three methods.

Well doing that now causes: https://hatebin.com/psreopmqol on build. Maybe its unrelated? I am not sure.
In all honesty, I am not really sure why this is a struggle to implement. There is absolutely 0 forums online where people are in the same position as me.

Ok so I have done some more research and my system is still messed up. So I fixed it and now I am getting the Steam Purchase confirmation. However, my callback is still generating errors:

private void Start()
    {
        if (steam.steamEnabled)
        {
            //m_MicroTxnAuthorizationResponse = Callback<MicroTxnAuthorizationResponse_t>.Create(OnMicroTxnAuthorizationResponse);
            m_steamInventoryResultReady_t = Callback<SteamInventoryResultReady_t>.Create(OnSteamInventoryResultReady);
        }
    }

[MonoPInvokeCallback(typeof(SteamInventoryResultReady_t))]
    private void OnSteamInventoryResultReady(SteamInventoryResultReady_t result)
    {
        print(result.m_result);
        CanPurchase = result.m_result == EResult.k_EResultOK;
        if (CanPurchase) BuyIAPChoice(productID);
        productID = "";
    }
Uploading Crash Report
NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: Steamworks.Callback`1[[Steamworks.SteamInventoryResultReady_t, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::OnRunCallResult
at Steamworks.Callback`1[T].BuildCCallbackBase () [0x00000] in <00000000000000000000000000000000>:0 
at Steamworks.Callback`1[T]..ctor (Steamworks.Callback`1+DispatchDelegate[T] func, System.Boolean bGameServer) [0x00000] in <00000000000000000000000000000000>:0 
at Steamworks.Callback`1[T].Create (Steamworks.Callback`1+DispatchDelegate[T] func) [0x00000] in <00000000000000000000000000000000>:0 
at IAPManager.Start () [0x00000] in <00000000000000000000000000000000>:0 

Any idea?

Looks like OnRunCallResult doesn’t have [MonoPInvokeCallback] attribute on it anymore? Did you remove it?

Are you reffering to this:

[MonoPInvokeCallbackAttribute(typeof(SteamInventoryResultReady_t))]
    private static void OnSteamInventoryResultReady(SteamInventoryResultReady_t result)
    {
        print(result.m_result);
        CanPurchase = result.m_result == EResult.k_EResultOK;
        //if (CanPurchase) BuyIAPChoice(productID);
        //productID = "";
    }

...

[MonoPInvokeCallbackAttribute(typeof(SteamAPICall_t))]
      private static void OnRunCallResult(
#if !STDCALL
         IntPtr thisptr,
#endif
         IntPtr pvParam, bool bFailed, ulong hSteamAPICall_) {
         SteamAPICall_t hSteamAPICall = (SteamAPICall_t)hSteamAPICall_;
         if (hSteamAPICall == m_hAPICall) {
            m_hAPICall = SteamAPICall_t.Invalid; // Caller unregisters for us

            try {
               m_Func((T)Marshal.PtrToStructure(pvParam, typeof(T)), bFailed);
            }
            catch (Exception e) {
               CallbackDispatcher.ExceptionHandler(e);
            }
         }
      }

Is that this method? “Steamworks.Callback`1[[Steamworks.SteamInventoryResultReady_t, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::OnRunCallResult”.

It’s bizarre it’s giving you “To marshal a managed method, please add an attribute named ‘MonoPInvokeCallback’ to the method definition.” error if it’s the right method.

Well I am not sure if it’s referring to “OnRunCallResult” or SteamInventoryResultReady because it has both.

But the error state that it originates in IAPManager.Start()

private void Start()
    {
        iap = SaveSystemIAP.SaveExists("IAP") ? SaveSystemIAP.LoadPlayer<IAPData>("IAP") : new IAPData();

        if (steam.steamEnabled)
        {
            m_steamInventoryResultReady_t = Callback<SteamInventoryResultReady_t>.Create(OnSteamInventoryResultReady);
        }
    }

Could it be something with this?

OIt is referring to Steamworks.Callback<SteamInventoryResultReady_t>.OnRunCallResult. If that’s the only OnRunCallResult method, it has that attribute and you’re still getting that error it might be a bug in Unity. Could you report a bug on ti?

Sure, where can I do this? Also I am on version 2019.3.0f6, should I try updating?

How to report a bug: Unity QA: Building quality with passion

2019.3.0f6 is pretty old at this point. You should try updating to latest 2019.4.x version.

Doing that now, and thank you.