【问题标题】:How does one make function calls or trigger events from a Native component into a C#/XAML component?如何将 Native 组件中的函数调用或触发事件转换为 C#/XAML 组件?
【发布时间】:2013-06-22 15:59:36
【问题描述】:

我正在开发一个带有 Native (DirectX/D3D) 组件和 C#/XAML 组件的 WP8 应用程序。

Native 组件绘制到 UI 元素,而 C#/XAML 组件具有围绕它的应用程序(和其他事物)的控件。通常,我将信息从 C#/XAML 组件发送到 Native 组件。但是在某些时候,我想根据在 Native 组件中完成处理的时间来触发 C#/XAML 组件中的事件。在一项计划中的功能中,我设想了一个 C#/XAML 进度条,该进度条通过 Native 组件中触发的事件保持最新。

不幸的是,尽管在将信息从 Native 组件传递到 C#/XAML 组件方面进行了一些平庸的尝试,但我并没有取得太大进展,我感觉自己已经死了。

我将不胜感激任何有关这方面的指导。感谢阅读。

【问题讨论】:

    标签: c# c++ windows-phone-8 directx c++-cx


    【解决方案1】:

    经过大量探索(以及在此线程中回答的一些人的大力帮助!)我能够找到这个解决方案:Calling C# method from C++ code in WP8

    为简单起见,我将在此处复制它:

    在费了很多力气试图找出所需的代码之后,我认为值得在这里发布最终版本

    C++/CX

    //.h
    [Windows::Foundation::Metadata::WebHostHidden]
    public interface class ICallback
    {
    public:
        virtual void Exec( Platform::String ^Command, Platform::String ^Param);
    };
    //.cpp
    ICallback ^CSCallback = nullptr;
    void Direct3DInterop::SetCallback( ICallback ^Callback)
    {
        CSCallback = Callback;
    }
    //...
    
    if (CSCallback != nullptr)
        CSCallback->Exec( "Command", "Param" );
    
    C#
    public class CallbackImpl : ICallback
    {
        public void Exec(String Command, String Param)
        {
            //Execute some C# code, if you call UI stuff you will need to call this too
            //Deployment.Current.Dispatcher.BeginInvoke(() => { 
            // //Lambda code
            //}
        }
    }
    //...
    CallbackImpl CI = new CallbackImpl();
    D3DComponent.SetCallback( CI);
    

    【讨论】:

      【解决方案2】:

      这是因为您无法将 C++ 代码调用到 C#。 您只能将 C# 调用(并返回)到 C++。

      http://social.msdn.microsoft.com/Forums/wpapps/en-us/a850b680-6052-41c5-825a-d764d3369fe9/calling-c-code-from-c-in-windows-phone-8

      【讨论】:

      • 哎呀,这对我的应用程序的功能集来说是一个严重的打击。有没有其他办法?
      • 仔细想想就明白了。应用程序首先允许使用本机代码的主要原因是可移植性。他们不希望人们不得不更新旧的(甚至是遗留的)代码。那不会发生。那么他们为什么要更新语言以允许对 C# 的新调用呢?因此,对于您的示例,您必须将 C# 放在计时器上以调用 C++,返回结果,然后更新进度条。
      • 嗯,那是我的备用计划。使用轮询技术来更新进度条而不是回调。它会工作的。感谢您的帮助。
      【解决方案3】:

      您可以做的就是在 C# 中保留一个指向函数的函数指针。您可以从 C# 调用本机 C++ 来存储函数指针,然后您可以随时调用它。函数 as 是静态的。

      例如在 C# 中

      [DllImport( @"SomeDLL.dll" )]
      static extern void SetAchievementUnlocked( AchievementUnlockCallBack callback );
      
      public delegate int AchievementUnlockCallBack( AchievementsType id, IntPtr message );
      static AchievementUnlockCallBack mAchievementUnlockCallBack = AchievementUnlocked;
      
      static int AchievementUnlocked( AchievementsType id, IntPtr pMessage )
      {
          string achievementName = Marshal.PtrToStringAnsi( pMessage );
          DisplayAchievement( achievementName );
          MainWindowHandler.Context.Focus();
          return 1;
      }
      

      并且在构造函数中可能有

      SetAchievementUnlocked( mAchievementUnlockCallBack );
      

      然后在 C++ 中

      typedef int (*AchievementUnlockCallBack)(AchievementsType pType, char* pMessage);
      AchievementUnlockCallBack globalCallback = NULL;
      __declspec(dllexport) void SetAchievementUnlocked( AchievementUnlockCallBack callback )
      {
          globalCallback = callback
      }
      

      然后,您可以在任何可以访问该变量的地方进行操作

      void Achievement::Unlock()
      {
          if(globalCallback)
          {
               globalCallback(this->getType(), this->getName().c_str());
          }
      }
      

      希望这可以帮助你。如果您不使用 dll 和互操作服务,它可能会起作用,但不幸的是我没有测试过并且没有示例。

      【讨论】:

      • 嗯,有趣。当我第一次处理这个问题时,我正在考虑做类似的事情。我肯定会试一试(也许不是今天,先做一些其他任务)然后报告。
      • 试一试,看看它是否有效(并告诉我们)。听起来很直接。我的猜测是您会遇到问题,因为系统可能正在对该内存地址进行一些反射,而您的 C# 函数确实不在该地址。
      【解决方案4】:

      反之亦然。 您可以通过 COM 互操作从 C#(托管代码)调用本机代码:http://msdn.microsoft.com/en-us/library/aa645736%28v=VS.71%29.aspx

      我认为不可能从本机调用托管代码。你想做什么?

      【讨论】:

        【解决方案5】:

        您可以将委托从您的 C# 传递给 C++ 组件。

        在您的 C++ 组件中,您将定义一个委托和一个方法调用来设置该委托,如下所示:

        public delegate void SomeDelegate(Platform::String^ args);
        
        public ref class Component sealed
        {
          public:
          void SetDelegate(SomeDelegate^);
        
          private:
          Platform::Agile<SomeDelegate> m_delegate;
        }
        

        然后在您的 C# 中,您可以创建一个与委托签名匹配的函数,并将其传递给您的组件:

        public void DelegateFunction(string args)
        {
           // run C# code that was invoked from C++
        }
        
        // elsewhere in your C# code
        component.SetDelegate(DelegateFunction);
        

        然后在您的 C++ 中,您可以在需要时调用委托方法。

        编辑:我写了一篇关于这个主题的博客文章 - http://robwirving.com/2014/07/21/calling-c-methods-c-winrt-components/

        【讨论】:

          猜你喜欢
          • 2019-06-14
          • 1970-01-01
          • 2020-02-01
          • 2020-04-06
          • 2021-12-16
          • 2020-12-12
          • 1970-01-01
          相关资源
          最近更新 更多