Имя: Пароль:
IT
 
COM и C#
, ,
0 oleg_km
 
24.04.13
13:43
Написал на C# два COM-объекта, оба в одной dll в одном проекте. Хочу в метод одного передавать ссылку на объект второго, чтобы метод первого дернул метод второго. Пробовал разные варианты:
1) В параметрах нужного метода первого объекта просто указал интерфейс второго: при вызове выскочило Неизвестная ошибка
2) В параметрах нужного метода первого объекта указал object, в теле метода попытался следать приведение и через () и через as, ругается: Нет такого интерфейса
3) В параметрах нужного метода первого объекта указал dynamic, все заработало, но мне так не нравится: нет типизации, нужен .Net 4, нужно на компьютер пользователя устанавливать доп. сборку


Примерный код:
namespace TestCOM
{
   [Guid("E5B23F96-411D-4A67-AFBA-B3DF6B3B0ACB")]
   public interface ITestCOM
   {
       [DispId(4)]
       void TestOCX(ITestActiveX oTest); // 1-й вариант
       void TestOCX(object oTest); // 2-й вариант
       void TestOCX(dynamic oTest); // 3-й вариант
   }

   [ProgId("TestCOM.Test"),
   ClassInterface(ClassInterfaceType.None),
   Guid("1BD846DC-63F2-4070-AF23-8AECD6C158CC")]
   public class CTestCOM : ITestCOM
   {
       public void TestOCX(ITestActiveX oTest) // 1-й
       {
           oTest.ShowEvent("Test parameter");
       }

       public void TestOCX(object oTest) // 2-й
       {
           ITestActiveX ot = (ITestActiveX) oTest;
           ot.ShowEvent("Test parameter");
       }

       public void TestOCX(dynamic oTest) // 3-й
       {
           oTest.ShowEvent("Test parameter");
       }
   }

  [Guid("D783DC48-8FB0-4fe9-BDC2-0CEE3F5E8921")]
   [InterfaceType(ComInterfaceType.InterfaceIsDual)]
   public interface ITestActiveX
   {
       [DispId(6)]
       void ShowEvent(string msg);
   }

   [Guid("A94CEA0F-1CD3-4829-A75E-B8A81A1EE56D")]
   [ProgId("TestCOM.TestActiveX")]
   [ComDefaultInterface(typeof(ITestActiveX))]
   [ClassInterface(ClassInterfaceType.None)]
   public class TestActiveX : UserControl, ITestActiveX
   {
       public void ShowEvent(string msg)
       {
           MessageBox.Show(msg);
       }
   }
}

Очень хочу первый вариант, если что готов и на второй
1 Rie
 
24.04.13
13:49
(0) Так а где сам вызов-то?
2 Serginio1
 
24.04.13
13:49
[ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true), Guid("A94CEA0F-1CD3-4829-A75E-B8A81A1EE56D"), ProgId("TestCOM.TestActiveX")]
3 ptrtss
 
24.04.13
13:58
4 oleg_km
 
24.04.13
13:58
(1) TestOCX в интерфейсе ITestCOM, ну в 1С примерно так:

об1 = Новый COMОбъект("TestCOM.Test");
об2 = Новый COMОбъект("TestCOM.TestActiveX");

об1.TestOCX(об2);

(2) Имеется ввиду AutoDual вместо None? Только что попробовал, тоже самое.
5 oleg_km
 
24.04.13
14:01
(3) Так это вроде соответствует второму варианту. вызов метода идет, но не проходит приведение типа. Ругается что интерфейс D783DC48-8FB0-4fe9-BDC2-0CEE3F5E8921 (ITestActiveX) не отдается QueryInterface
6 Serginio1
 
24.04.13
14:11
(4) У тебя не получится второй вызвать.
Можно в  interface ITestCOM
Создать метод который создает экземпляр класса TestActiveX.
7 Serginio1
 
24.04.13
14:12
Например
ITestActiveX ПолучитьОббъектTestActiveX();
8 Serginio1
 
24.04.13
14:14
Об2=Об1.ПолучитьОббъектTestActiveX();
9 oleg_km
 
24.04.13
14:26
(6) Подожди, в 3-м варианте через dynamic получается же. Код из (4) в третьем варианте нормально работает, просто мне само использование dynamic не нравится.
10 Serginio1
 
24.04.13
14:56
Замени void TestOCX(ITestActiveX oTest);
На void TestOCX(IDispoatch oTest); это будет вариант с оbject и dynamic.
Просто получается разные соклассы.
11 oleg_km
 
24.04.13
15:02
(10) Вот так:

void TestOCX([MarshalAs(UnmanagedType.IUnknown)] object oTest);

Пробовал, тоже не получилось
12 Serginio1
 
24.04.13
15:15
IUnknown<>IDispatch
13 Serginio1
 
24.04.13
15:16
Ты лучше скажи
Об2=Об1.ПолучитьОббъектTestActiveX();
об1.TestOCX(об2);
работает?
14 oleg_km
 
24.04.13
15:20
(12) И с IDispatch тоже пробовал.
15 oleg_km
 
24.04.13
15:29
(13) Дело в том что объекты создаются независимо. но я сейчас попробовал наоборот во второй объект передать первый и все прошло нормально. Надо аккуратно все пересобрать
16 Serginio1
 
24.04.13
16:42
(15) Первый это основной coClass. Поэтому я и предложил попробовать 13
17 Ковычки
 
24.04.13
16:43
Вы написали не ком
18 oleg_km
 
24.04.13
16:55
(16) Первый это основной coClass

Как ты это понял, вообще-то это просто два разных КОМа, просто в одном проекте. Если быть точным один просто ком, второй активикс:

CTestCOM : ITestCOM  - просто ком
TestActiveX : UserControl, ITestActiveX - активикс, я просто выкинул всякие события прочее

(17) А как тогда его дергает 1С? 1С напрямую научилось подключаться к .NET?
19 oleg_km
 
24.04.13
16:56
(16) Первый это основной coClass

Вот смотрю в библиотеке типов: у каждого свой кокласс
20 Serginio1
 
24.04.13
18:29
Есть такая функция dllgetclassobject которая находит нужный
DLL
v8: Можно ли использовать библиотеки без COM-сервера?
И обычно вторым параметром передаетя IDispatch.
Но проблема скорее всего в
[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch)]
public class ContainerControl : ScrollableControl, IContainerControl


ComVisible(true), Designer("System.Windows.Forms.Design.ControlDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), DefaultEvent("Load"), DesignerCategory("UserControl"), ClassInterface(ClassInterfaceType.AutoDispatch), Designer("System.Windows.Forms.Design.UserControlDocumentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(IRootDesigner))]
public class UserControl : ContainerControl


То есть UserControl сам реализует IDispatch. Возможно в этом проблема
21 Serginio1
 
24.04.13
18:31
Кстати ты 13 не пробовал?
22 Serginio1
 
24.04.13
18:46
Посмотри http://proasutp.com/articles/scadasystems/vijeocitect/create_an_activex_control_for_scada_vijeo_citect_on_microsoft_visual_c_shapr_2012.html?page=2


Но там [ComVisible(true)]
   [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]

Да и для    [ProgId("TestCOM.Test"),
   ClassInterface(ClassInterfaceType.None),
   Guid("1BD846DC-63F2-4070-AF23-8AECD6C158CC")]
   public class CTestCOM : ITestCOM


 [InterfaceType(ComInterfaceType.InterfaceIsDual)]
23 oleg_km
 
25.04.13
11:17
Решил проблему. Дело в том что когда я написал пример вызывающего кода:

об1 = Новый COMОбъект("TestCOM.Test");
об2 = Новый COMОбъект("TestCOM.TestActiveX");
об1.TestOCX(об2);

я немножко слукавил - только один объект создается конструкцией CreateObject, второй то сажается на форму как ActiveX. Среда 1С дает мне доступ к этому объекту как ссылку на элемент формы и судя по всему это какой-то контейнер. Т.е. когда я вызываю методы своего объекта, на самом деле я дергаю контейнер, а контейнер уже дергает мой объект, а когда я передаю ссылку в другой объект, он пытается привести тип в соответствие с указанным типом моего параметра и обнаруживает, что тип (тип контейнера) у него отсутствует. Я просто в ActiveX добавил метод, который возвращает указатель на себя, приведенный к типу своего интерфейса:


 
         public ITestOCX1 GetThis()  
         {  
             return (ITestOCX1)this;  
         }

И вызов теперь выглядит:

обТест = Новый COMОбъект("TestCOM.TestCOM1");
обТест.CallTest(ЭлементыФормы.TestOCX1.GetThis());

Если это коряво, то как делать правильно?
24 Serginio1
 
25.04.13
11:30
Это нормально, об этом я тебе и писал в 22 (просто идет расширение  GetsIDOfNames и Invoke ), но мало чем отличается от того, что я предлагал тебе в 13.
25 oleg_km
 
25.04.13
11:37
(24) Я теперь понял в чем заключался совет: ссылку для дальнейшей работы в .NET нужно получать не от 1С, а от .NET
26 Serginio1
 
25.04.13
12:12
(25) Я тебе в 20 писал как получает 1С IDispatch через dllgetclassobject . TestActiveX  по сути реализует два IDispatch,но расширяет он методы UserControl котрый ничего не знает об ITestCOM. Но в Net могли бы и переопределить QueryInterface, но почему то они этого не сделали.