2009-12-30 3 views
2

У меня есть, что, вероятно, простая проблема. Я использую interop для вызова асинхронной функции в CompactFramework. После того, как я получу результат выполнения, я хотел бы поднять событие, которое будет улавливаться формой, и, основываясь на результатах, я бы отобразил некоторые данные на экране. Проблема, однако, в том, что, когда функция interop возвращает результат, она возвращает ее в рабочий поток, и если я подниму событие, оно останется на рабочем потоке, и я не могу отобразить какие-либо данные в форме, если только я не используйте Invoke.Throw event в основной теме из рабочей нити C# CF

Может ли кто-нибудь предложить способ объединить рабочий поток на основной поток? И поднять событие из основного потока? Я нашел несколько примеров, которые проходят через делегатов, подписавшихся на это событие, и используют BeginInvoke для создания события в основном потоке, однако все они используют ISynchronizeInvoke, который недоступен в Compact Framework.

Мой код ниже:

public delegate void CellTowerFoundEventHandler(CellTower towerInfo); 

public class CellTowerProvider 
{ 
    public delegate void RILRESULTCALLBACK(uint dwCode, IntPtr hrCmdID, IntPtr lpData, uint cbData, uint dwParam); 
    public delegate void RILNOTIFYCALLBACK(uint dwCode, IntPtr lpData, uint cbData, uint dwParam); 
    private RILCELLTOWERINFO towerDetails; 
    private CellTower cellTowerInfo; 
    private IntPtr radioInterfaceLayerHandle; 

    public CellTowerProvider() 
    { 
     radioInterfaceLayerHandle = IntPtr.Zero; 
     IntPtr radioResponseHandle = IntPtr.Zero; 

     // Initialize the radio layer with a result callback parameter. 
     radioResponseHandle = RIL_Initialize(1, new RILRESULTCALLBACK(CellDataCallback), null, 0, 0, out radioInterfaceLayerHandle); 

     // The initialize API call will always return 0 if initialization is successful. 
     if (radioResponseHandle != IntPtr.Zero) 
     { 
      return; 
     } 

     // Query for the current tower data. 
     radioResponseHandle = RIL_GetCellTowerInfo(radioInterfaceLayerHandle); 
    } 

    public void CellDataCallback(uint dwCode, IntPtr hrCmdID, IntPtr lpData, uint cbData, uint dwParam) 
    { 
     // Refresh the current tower details 
     towerDetails = new RILCELLTOWERINFO(); 

     // Copy result returned from RIL into structure 
     Marshal.PtrToStructure(lpData, towerDetails); 

     cellTowerInfo = new CellTower() 
     { 
      TowerId = Convert.ToInt32(towerDetails.dwCellID), 
      LocationAreaCode = Convert.ToInt32(towerDetails.dwLocationAreaCode), 
      MobileCountryCode = Convert.ToInt32(towerDetails.dwMobileCountryCode), 
      MobileNetworkCode = Convert.ToInt32(towerDetails.dwMobileNetworkCode), 
     }; 

     if (CellTowerFound != null) 
      CellTowerFound(cellTowerInfo); 

     // Release the RIL handle 
     RIL_Deinitialize(radioInterfaceLayerHandle); 
    } 

} 
+1

CF - обертка вокруг System.NotImplementedException. Это было бы проблемой без проблем в настольной версии, возможно, вам нужно снизить свои ожидания от того, что практично в приложении на основе CF. –

+0

Ahh! Хороший старый «CF - обертка вокруг System.NotImplementedException». - придумал Рори Блайт на сессии DotNetRocks, правильно? Я мог бы согласиться на v1.0, но не сегодня на v3.5! –

ответ

2

Почему бы не создать элемент управления в ctor вашего объекта библиотеки? Если вы предположите, что экземпляр библиотеки создан в потоке пользовательского интерфейса, вы можете затем вызвать Control.Invoke на , что управляет внутренней частью вашей библиотеки, а вызов вызова будет в потоке пользовательского интерфейса у потребителя.

Конечно, это не безошибочно. Потребитель может создать экземпляр у рабочего, но он по крайней мере обеспечивает некоторый уровень того, что вам нужно, и если они используют рабочий поток, в действительности они ожидают, что событиям потребуется вызов Invoke.

+0

Это немного взломать, но да, я думаю, это должно будет сделать. Создал новый ярлык в конструкторе, создал метод и делегат, когда interop возвращает значение, я вызываю Invoke на метке (label.Invoke (новый CellTowerFoundEventHandler (this.getLocationInfo), новый объект [] {cellTowerInfo});) и оттуда увольняй мое событие. Событие теперь находится в основном потоке. Единственное, что у меня есть, это то, что отладчик по-прежнему показывает рабочий поток. Надеюсь, GC позаботится об этом, но я не уверен на 100%. –

+0

Почему этикетка? Просто используйте базовый элемент управления. Кроме того, всегда проверяйте InvokeRequired, и я бы рекомендовал использовать BeginInvoke вместо Invoke, чтобы не допустить, чтобы потребитель блокировал вашу библиотеку. – ctacke

2

Ну Invoke это (собственно) способ «слияния» (синхронизации) нить с с основным потоком. Проще всего использовать Control (например, MainForm) и вызвать ctl.Invoke(myDelegate).

И второе, мы рейз события и throw исключений. Использование правильных слов позволяет избежать путаницы.

+0

Правильно, я понимаю, что Invoke был бы самым простым способом сделать это, однако я бы хотел, чтобы мой код был более или менее многоразовым, и было бы больно использовать Invoke каждый раз, когда вы получаете обратный вызов в пользовательском интерфейсе. Я предпочел бы изменить свою библиотеку, чтобы она сама по себе увеличивала события в основном потоке. –

+0

Насколько я знаю, SynchronizationContext не поддерживается на CF. Поэтому вам придется импровизировать. Если в вашей lib есть функция Init, вам требуется указать форму. Что-то вроде того. –

2

Это зависит от того, какой инструментарий вы используете для отображения данных в своем «основном потоке». Если вы используете Windows Forms, вы должны иметь возможность использовать Control.Invoke или Control.BeginInvoke для маршализации события обратно в поток пользовательского интерфейса. Это поддерживается в Compact Framework.

Большинство образцов используют ISynchronizeInvoke вместо элемента управления, но код должен быть идентичным. Вам понадобится ссылка на элемент пользовательского интерфейса вашего кода, однако, чтобы использовать его при вызове BeginInvoke, когда вы поднимаете свое событие.

+0

Я попытался это вместо того, чтобы просто стрелять событие: если (! CellTowerFound = NULL) { Еогеасп (делегат d в CellTowerFound.GetInvocationList()) { Форма F = d.Target как форма; если (f!= null) { f.BeginInvoke (d); } } } Однако это вызывает исключение InvalidArguments в приложении. Я бы предпочел исправить это в библиотеке, а не в пользовательском интерфейсе. –

+0

Если d.Target на самом деле является формой, вы должны иметь возможность использовать begin invoke - но «d» недостаточно информации: вам нужно: f.BeginInvoke (() => {d (this, EventArgs.Empty);}); - или что-то подобное - не тот тип делегата для begininvoke .. –

Смежные вопросы