Позднее связывание, на примере взаимодействия с терминалом сбора данных Cipher 8001L

В данной публикации я продемонстрирую взаимодействие с COM объектом на языке C#. В примере будет использована OLE-компонента для взаимодействия с ТСД Cipher 8001L компании CitySoft. Думаю, всё то что описано можно использовать и для других COM-Объектов.

Для начала поясню в чём суть позднего связывания, и зачем это надо. Позднее связывание необходимо нам тогда, когда тип объекта заранее не известен и мы не можем напрямую обращаться к параметрам, методам и свойствам используемого объекта — вот для этого и используется позднее связывание.

Для вызова метода надо знать его название и список формальных параметров, которые он принимает. Для вызова используем метод InvokeMember().

Вообще метод InvokeMember() и имеет три модификации:
//Первая
public object InvokeMember(
  string name, BindingFlags flags, Binder binder, 
  object target, object[] args);
//Вторая
public object InvokeMember(
  string name, BindingFlags flags, Binder binder, 
  object target, object[] args, CultureInfo info); 
//Третья
public abstract object InvokeMember(
  string name, BindingFlags flags, Binder binder,
  object target, object[] args, ParameterModifier[] modifiers,
  CultureInfo info, string[] namedParameters);

Для наших целей понадобится только первая модификация.
public object InvokeMember(
  string name, BindingFlags flags, Binder binder, 
  object target, object[] args);

Первый параметр это — строковое название метода (поля, свойства) того объекта, с которым устанавливается связь. В названии не должно быть пробелов или лишних символов, ещё, этот параметр чувствителен к регистру.

Второй параметр принимает на вход флаги, характеризующие связывание. В нашем случае понадобятся только следующие флаги:

BindingFlags.InvokeMethod – найти метод, определить его точку входа, и выполнить, передав ему массив фактических параметров.
BindingFlags.GetProperty – получить значение свойства
BindingFlags.SetProperty – установить свойство.

Третий параметр – binder – устанавливается в null.

Четвертым параметром – target – передается ссылка на объект, к методу которого нужно обратиться.

Пятый параметр – args – это массив с параметрами, который принимает на вход вызываемый поздним связыванием метод или массив, который содержит один элемент – устанавливаемое значение свойства.

Метод InvokeMember() возвращает результат выполнения метода или значение свойства.

На этом краткая часть с теорией завершена, перехожу к практическому программированию Cipher 80XX через библиотеку от CitySoft.

Пример загрузки файла в терминал сбора данных:
string appProgID = "CitySoftWarehouseDos.Cpt";
// Получаем ссылку на интерфейс
Type CptType = Type.GetTypeFromProgID(appProgID);
// Запускаем Объект CPT
object CPT = Activator.CreateInstance(CptType);

object[] args = new object[1];

object[] args1 = new object[1];
args1[0] = 2; //Номер COM порта.
//Устанавливаем свойство номера порта.
CPT.GetType().InvokeMember("Port", BindingFlags.SetProperty, null, CPT, args1);

object[] args2= new object[1];
args2[0] = 1; // Скорость 115200
CPT.GetType().InvokeMember("BaudRate", BindingFlags.SetProperty, null, CPT, args2);

object[] args3 = new object[1];
args3[0] = 0; //Тип соединения 0 - ИК-Подставка.
CPT.GetType().InvokeMember("ConnectionType", BindingFlags.SetProperty, null, CPT, args3);


CPT.GetType().InvokeMember("InitComm", BindingFlags.InvokeMethod, null, CPT, null);


object[] args4 = new object[1];
args4[0] = 0; //Кодировка 0 - Win-1251.
CPT.GetType().InvokeMember("CodePage", BindingFlags.SetProperty, null, CPT, args4);
object[] args5 = new object[1];
args5[0] = 44; //Разделитель 44 - это запятая.
CPT.GetType().InvokeMember("LookupDelimiter", BindingFlags.SetProperty, null, CPT, args5);

            
object[] args10 = new object[3];
args10[0] = "DB.txt";//Имя файла, можно указать путь целиком.
args10[1] = 0;
args10[2] = 1;

object err = CPT.GetType().InvokeMember("Write", BindingFlags.InvokeMethod, null, CPT, args10);

while (err.ToString() == "80")
            {
                err = CPT.GetType().InvokeMember("State", BindingFlags.GetProperty, null, CPT, null);
            }

            if (err.ToString() != "0")
            {
                MessageBox.Show("Ошибка №" + err.ToString());
            }
            else
            {
                MessageBox.Show("Загрузка успешно завершена!");
            }

//Закрываем порт            
CPT.GetType().InvokeMember("CloseComm", BindingFlags.InvokeMethod, null, CPT, null);

            
// Уничтожение объекта.
Marshal.ReleaseComObject(CPT);
// Вызываем сборщик мусора для очистки памяти
GC.GetTotalMemory(true); 


Получение файла с устройства:
string appProgID = "CitySoftWarehouseDos.Cpt";
// Получаем ссылку на интерфейс 
Type CptType = Type.GetTypeFromProgID(appProgID);
// Запускаем CPT
object CPT = Activator.CreateInstance(CptType);

object[] args = new object[1];

object[] args1 = new object[1];
args1[0] = 2;
CPT.GetType().InvokeMember("Port", BindingFlags.SetProperty, null, CPT, args1);

object[] args2 = new object[1];
args2[0] = 1;
CPT.GetType().InvokeMember("BaudRate", BindingFlags.SetProperty, null, CPT, args2);

object[] args3 = new object[1];
args3[0] = 0;
CPT.GetType().InvokeMember("ConnectionType", BindingFlags.SetProperty, null, CPT, args3);


CPT.GetType().InvokeMember("InitComm", BindingFlags.InvokeMethod, null, CPT, null);

object[] args4 = new object[1];
args4[0] = 0;
CPT.GetType().InvokeMember("CodePage", BindingFlags.SetProperty, null, CPT, args4);
object[] args5 = new object[1];
args5[0] = 44;
CPT.GetType().InvokeMember("LookupDelimiter", BindingFlags.SetProperty, null, CPT, args5);

object[] args10 = new object[5];
args10[0] = "file.txt"; //Имя файла.
args10[1] = 0; //Формат файла 0 - txt / 1 - DBF
args10[2] = 0; //Тип файла  0 - собранный файл / 1 - база
args10[3] = 1; //Номер файла 1..10 для собранных / 1..3 для данных
args10[4] = 1; //Флаг перезаписи - если 0 - существующий файл не будет перезаписан, 1 - наоборот.

//Читаем файл с терминала сбора данных
object err = CPT.GetType().InvokeMember("Read", BindingFlags.InvokeMethod, null, CPT, args10);

while (err.ToString() == "81")
            {
                err = CPT.GetType().InvokeMember("State", BindingFlags.GetProperty, null, CPT, null);
            }

            if (err.ToString() != "0")
            {
                MessageBox.Show("Ошибка №" + err.ToString());
            }
            else
            {
                //Если всё удалось удаляем данные из файла ТСД.
                object[] args11 = new object[2];
                args11[0] = 0; //Тип файла 0 - файл / 1 - база.
                args11[1] = 1; //Номер файла

                object err1 = CPT.GetType().InvokeMember("DeleteData", BindingFlags.InvokeMethod, null, CPT, args11);

                if (err1.ToString() == "-1")
                {
                    MessageBox.Show("Выгрузка произведена успешно, но файл собранных данных удалить не удалось!");
                }
                {
                    MessageBox.Show("Выгрузка успешно завершена!");
                }
            }

//Закрываем порт
CPT.GetType().InvokeMember("CloseComm", BindingFlags.InvokeMethod, null, CPT, null);

// Уничтожение объекта Excel.
Marshal.ReleaseComObject(CPT);
// Вызываем сборщик мусора для немедленной очистки памяти
GC.GetTotalMemory(true); 


P.S. Для того чтобы всё заработало, необходимо иметь установленную OLE-компоненту от CitySoft которая поставляется на диске с ТСД.

P.P.S. Рабочий исходник примера: CipherOLE_Test.rar

Если есть интерес, пишите в комментариях, могу разместить пример прямого взаимодействия с ТСД через компоненту, без файла. Такой метод работает медленнее но, как выяснилось на практике, несколько надёжнее.
  • 22 августа 2011, 01:32
  •      
  • Roman


Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.