Type ObjCRuntime.Selector which is passed to unmanaged code must have a StructLayout attribute.

CarLoOSXCarLoOSX USMember ✭✭
edited February 16 in Xamarin.iOS

Hello Xamarin.iOS developers !!!

Apple is not allowing a setfocus () operation on an html element in their WKWebview.

We have ported a solution based on objective-c to c #, attached code

Objective-C Code
- (void) keyboardDisplayDoesNotRequireUserAction { 
     SEL sel = sel_getUid "_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");

     Class WKContentView = NSClassFromString(@"WKContentView"); 
     Method method = class_getInstanceMethod(WKContentView, sel); 
     IMP originalImp = method_getImplementation(method); 

     IMP imp = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) {      
     ((void (*)(id, SEL, void*, BOOL, BOOL, id))originalImp)(me, sel, arg0, TRUE, arg2, arg3);
}); 

     method_setImplementation(method, imp); 
}  

c# code

using System;
using System.Runtime.InteropServices;

using CoreGraphics;
using Foundation;
using ObjCRuntime;
using WebKit;

namespace JBCWebManager.iOS.ViewControllers
{
    public class WKWebViewEx : WKWebView
    {
        [DllImport("/usr/lib/libobjc.dylib")] static extern IntPtr class_getInstanceMethod(IntPtr classHandle, IntPtr Selector);

        [DllImport("/usr/lib/libobjc.dylib")] static extern IntPtr method_getImplementation(IntPtr method);

        [DllImport("/usr/lib/libobjc.dylib")] static extern IntPtr imp_implementationWithBlock(ref BlockLiteral block);

        [DllImport("/usr/lib/libobjc.dylib")] static extern void method_setImplementation(IntPtr method, IntPtr imp);


        private static IntPtr _original;


        public WKWebViewEx(NSCoder coder) : base(coder)
        {
            AllowDisplayingKeyboardWithoutUserAction();
        }

        protected WKWebViewEx(NSObjectFlag t) : base(t)
        {
            AllowDisplayingKeyboardWithoutUserAction();
        }

        protected internal WKWebViewEx(IntPtr handle) : base(handle)
        {
            AllowDisplayingKeyboardWithoutUserAction();
        }

        public WKWebViewEx(CGRect frame, WKWebViewConfiguration configuration) : base(frame, configuration)
        {
            AllowDisplayingKeyboardWithoutUserAction();
        }

        static Selector selector1;

        private void AllowDisplayingKeyboardWithoutUserAction()
        {
            IntPtr @class = Class.GetHandle("WKContentView");
            NSOperatingSystemVersion iOS_11_3_0 = new NSOperatingSystemVersion(11, 3, 0);

            NSProcessInfo processInfo = NSProcessInfo.ProcessInfo;
            bool isIOS1130 = processInfo.IsOperatingSystemAtLeastVersion(iOS_11_3_0);
            if (isIOS1130)
            {
                Selector selector = new Selector("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
                IntPtr method = class_getInstanceMethod(@class, selector.Handle);
                _original = method_getImplementation(method);

                BlockLiteral block = new BlockLiteral();
                CaptureDelegate d = MyCapture;
                block.SetupBlock(d, null);
                IntPtr @override = imp_implementationWithBlock(ref block);
                method_setImplementation(method, @override);

                //IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) 
                //{
                //    ((void(*)(id, SEL, void *, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
                //});
            }
            else
            {
                selector1 = new Selector("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");
                IntPtr method = class_getInstanceMethod(@class, selector1.Handle);
                _original = method_getImplementation(method);

                BlockLiteral block = new BlockLiteral();
                CaptureDelegate d = MyCapture;
                block.SetupBlock(d, null);
                IntPtr @override = imp_implementationWithBlock(ref block);
                method_setImplementation(method, @override);

                //IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) 
                //{
                //    ((void(*)(id, SEL, void *, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3);
                //});
            }
        }

        [MonoNativeFunctionWrapper]
        public delegate void OriginalDelegate(NSObject me, Selector sel, IntPtr arg0, bool arg1, bool arg2, NSObject arg3);

        delegate void CaptureDelegate(NSObject me, IntPtr arg0, bool arg1, bool arg2, NSObject arg3);

        [MonoPInvokeCallback(typeof(CaptureDelegate))]
        static void MyCapture(NSObject me, IntPtr arg0, bool arg1, bool arg2, NSObject arg3)
        {
            OriginalDelegate del = (OriginalDelegate)Marshal.GetDelegateForFunctionPointer(_original, typeof(OriginalDelegate));
            del(me, selector1, arg0, true, arg2, arg3);
        }
    }
}

The code compiles correctly. The problem is very localized in the call of (me, selector1, arg0, true, arg2, arg3), and specifically in the selector1 parameter. This call executes the delegate OriginalDelegate.

The error that occurs at runtime is:

Type ObjCRuntime.Selector which is passed to unmanaged code must have a StructLayout attribute.

and it tells me that the Selector object must have the StructAttribute attribute defined. ????

Thank you so much and sorry for my bad English !!

Answers

  • nando1200nando1200 ESMember ✭✭

    I have a same problem!!!

  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    You are passing a Selector to del, did you mean to pass it's Handle?

    The selector is our c# type, the Handle is the pointer to the wrapped native type.

  • CarLoOSXCarLoOSX USMember ✭✭
    I’ve also tried this,

    If I try to Change type Selector to IntPtr then I've the following error:


    Selector selector1;
    [MonoNativeFunctionWrapper]
    public delegate void OriginalDelegate(NSObject me, IntPtr sel, IntPtr arg0, bool arg1, bool arg2, NSObject arg3);

    In MyCapture del(me, selector1.Handle, arg0, true, arg2, arg3);

    Objective-C exception thrown.Name: NSInvalidArgumentException Reason: -_NSMallocBlock__ _stopAssistingNode]: unrecognized selector sent to instance 0x12ff844d0
    Native stack trace:
    0 CoreFoundation 0x000000011465e12b __exceptionPreprocess + 171
    1 libobjc.A.dylib 0x00000001153fcf41 objc_exception_throw + 48
    2 CoreFoundation 0x00000001146df024 -[NSObject(NSObject) doesNotRecognizeSelector:]*- + 132
    3 CoreFoundation 0x00000001145e0f78 ___forwarding___ + 1432
    4 CoreFoundation 0x00000001145e0958 _CF_forwarding_prep_0 + 120
    5 WebKit 0x00000001072de47e -[WKContentView(WKInteraction) _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:] + 291
    6 ??? 0x000000013562b176 0x0 + 5190627702

    I cant type this code as “code” , I’m typing from my smartphone rigth now

    Thank you!
  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    Well it sounds like you are calling some selector on something that isn't expecting it. I would start by stepping through your code, figure out what line is causing an issue, then verify what object / selector pair you are invoking upon.

    Then compare this to your obj-c example.

    Manual binding, specially such low level work you are doing here is non-trivial to get right and might take some time / iteration.

  • mbmleonembmleone NLMember ✭✭

    @CarLoOSX, were you able to solve this?
    I want to port the same code, so if you managed to do this then I'm really interested.

  • CarLoOSXCarLoOSX USMember ✭✭

    @mbmleone said:
    @CarLoOSX, were you able to solve this?
    I want to port the same code, so if you managed to do this then I'm really interested.

    If you give me an email I could send you the packages I made with Sharpie. I made the solution in native and then ported to C# with this tool, but you will need to use the new Class XXXWebView with inherits from the modified web view in native. It's almost the same.

  • mbmleonembmleone NLMember ✭✭

    @CarLoOSX said:

    @mbmleone said:
    @CarLoOSX, were you able to solve this?
    I want to port the same code, so if you managed to do this then I'm really interested.

    If you give me an email I could send you the packages I made with Sharpie. I made the solution in native and then ported to C# with this tool, but you will need to use the new Class XXXWebView with inherits from the modified web view in native. It's almost the same.

    @CarLoOSX, I appreciate the offer and have sent you multiple messages, but haven't received it yet.
    I really hope you can still send it to me.

    If someone else has a solution also then I would love to hear.
    @nando1200, did you maybe find a solution?

  • nando1200nando1200 ESMember ✭✭

    Hey @CarLoOSX,

    How long !!, I attached a zip file with the project and control.

    I hope it serves you

    a greeting!!

  • mbmleonembmleone NLMember ✭✭

    Many thanks, it works perfectly!

  • nando1200nando1200 ESMember ✭✭
    Enjoy!!
Sign In or Register to comment.