Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.89/76: Рейтинг темы: голосов - 76, средняя оценка - 4.89
Xero201
65 / 63 / 19
Регистрация: 27.12.2008
Сообщений: 212
1

Универсальный обмен данными между приложениями

28.05.2011, 17:54. Просмотров 14838. Ответов 26
Метки нет (Все метки)

Посоветуйте, пожалуйста, подход для решения следующей штуки:
Передача данных из приложенияА (C#) в приложениеB (C#, C++, Java, остальные будет хорошо, но не обязательно), где они изменяются и передаются обратно.
Данные могут быть любого объема и состоять из нескольких аргументов разных типов (например, {текст, кодировка, число}).
Достаточно, чтобы передача была между приложениями на локальном компьютере, где приложениеА запускает приложениеВ.
Скорость передача желательна, но не является критичной.
Спасибо.
0
Лучшие ответы (1)
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
28.05.2011, 17:54
Ответы с готовыми решениями:

Как организовать обмен данными между приложениями по интернету
Здравствуйте! Подскажите пожалуйста, каким образом можно организовать обмен данными между...

Обмен данных между приложениями
Добрый день! Такая задача, клиент базы данных Oracle стоить 32 битный, а приложение которое должно...

Обмен данными между С++ и С#
Имеются две независимые программы, одна из них написана на С++, другая на С#. Как от одной передать...

Обмен данными между устройствами
Всем привет. Требуется совет по поводу реализации обмена данными, между несколькими устройствами....

Обмен данными между потоками
Здравствуйте. Есть класс, в нем создается поток, который создается TCP сокет и слушает...

26
Shymep
13 / 13 / 2
Регистрация: 10.01.2010
Сообщений: 34
28.05.2011, 18:13 2
Файлы не подходят?
0
Xero201
65 / 63 / 19
Регистрация: 27.12.2008
Сообщений: 212
28.05.2011, 18:21  [ТС] 3
))) подходят, но пишу диплом, поэтому хочется чтобы было прям ах)))
К тому же, вдруг все-таки захочется через сеть работать)
Вот прочел такие названия как "сокеты, Remoting, пайп-каналы, WM_COPYDATA...", не владею ни одним из этих слов, а изучить их всех - не успею, нужно выбрать что-то одно.
0
Петррр
6182 / 3483 / 898
Регистрация: 28.10.2010
Сообщений: 5,928
28.05.2011, 18:48 4
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Клиент
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
 
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpClient client = new TcpClient();
            client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12000));
 
            StreamWriter sw = new StreamWriter(client.GetStream());
            sw.AutoFlush = true;
            Console.WriteLine("Client : Привет");
            sw.WriteLine("Привет");
 
            StreamReader sr = new StreamReader(client.GetStream());
            Console.WriteLine("Server : " + sr.ReadLine());
 
            Console.WriteLine("Client : Пока");
            sw.WriteLine("Пока");
            Console.WriteLine("Server : " + sr.ReadLine());
            client.Close();
 
            Console.ReadKey();
        }
    }
}
 
Сервер
/*********************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
 
namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpListener listner = new TcpListener(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12000));
            listner.Start();
            while (true)
            {
                TcpClient client = listner.AcceptTcpClient();
 
                StreamReader sr = new StreamReader(client.GetStream());
                Console.WriteLine("Client : " + sr.ReadLine());
 
                StreamWriter sw = new StreamWriter(client.GetStream());
                sw.AutoFlush = true;
                Console.WriteLine("Server : Привет");
                sw.WriteLine("Привет");
 
                Console.WriteLine("Client : " + sr.ReadLine());
                Console.WriteLine("Server : Пока");
                sw.WriteLine("Пока");
 
                client.Close();
            }
        }
    }
}
5
28.05.2011, 18:48
Xero201
65 / 63 / 19
Регистрация: 27.12.2008
Сообщений: 212
28.05.2011, 19:32  [ТС] 5
Спасибо, очень забавно)))
но у меня не получилось передать таким способом многострочные данные(
C#
1
2
3
4
5
//сервер
 sw.Write("Привет\nКак дела?");
 
//клиент
Console.Write("Server : " + sr.ReadToEnd());
или это был намек, что нужно брать сокеты?)
0
finflaex
-19 / 1 / 2
Регистрация: 05.11.2012
Сообщений: 48
04.01.2014, 21:06 6
http://metanit.com/sharp/articles/4.php
0
Anklav
446 / 304 / 47
Регистрация: 23.01.2013
Сообщений: 661
Завершенные тесты: 2
05.01.2014, 01:36 7
Пример с использованием WM_COPYDATA.
С помощью SendDataMessage отправлять можно только структуру не содержащую ссылочные типы.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
namespace CyberWinFormTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            Helper.SendStringMessage(this.Handle, IntPtr.Zero, "ПРИУЕТ");//оправляем строку
            Helper.SendDataMessage(this.Handle, IntPtr.Zero, new Data() { Value = 10 });//отправляем структуру
        }
 
        //здесь ловим данные
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == Helper.WM_COPYDATA)
            {
                string message;
                switch(Helper.GetDataType(m.Msg, m.WParam, m.LParam))
                {
                    case Helper.DataType.String:
                        message = Helper.ReceiveStringMessage(m.Msg, m.WParam, m.LParam);
                        break;
 
                    case Helper.DataType.Struct:
                        Data data = Helper.ReceiveDataMessage<Data>(m.Msg, m.WParam, m.LParam);
                        message = data.Value.ToString();
                        break;
 
                    default:
                        message = string.Empty;
                        break;
                }
                
                MessageBox.Show(message);
            }
 
            base.WndProc(ref m);
        }
    }
 
    struct Data
    {
        public int Value { get; set; }
    }
 
    public static class Helper
    {
        [DllImport("User32.dll")]
        private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
 
        public enum DataType
        {
            String,
            Struct
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct DataStorage
        {
            public DataType Type;
            public IntPtr Data;
        }
 
        private static int SendData(IntPtr ReceivingWindowHandle, IntPtr SendingWindowHanlde, DataStorage data)
        {
            int result = 0;
 
            if (ReceivingWindowHandle != IntPtr.Zero)
            {
                IntPtr DataPointer = Marshal.AllocHGlobal(Marshal.SizeOf(data));
                Marshal.StructureToPtr(data, DataPointer, false);
 
                COPYDATASTRUCT dataStruct = new COPYDATASTRUCT();
                dataStruct.cbData = Marshal.SizeOf(data);
                dataStruct.dwData = IntPtr.Zero;
                dataStruct.lpData = DataPointer;
 
                IntPtr dataStructPointer = Marshal.AllocHGlobal(Marshal.SizeOf(dataStruct));
                Marshal.StructureToPtr(dataStruct, dataStructPointer, false);
 
                result = SendMessage(ReceivingWindowHandle, WM_COPYDATA, SendingWindowHanlde, dataStructPointer);
 
                Marshal.FreeHGlobal(dataStruct.lpData);
                Marshal.FreeHGlobal(dataStructPointer);
            }
 
            return result;
        }
 
        public const int WM_COPYDATA = 0x4A;
 
        public static DataType GetDataType(int msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg != WM_COPYDATA)
                throw new ArgumentException("Message is not WM_COPYDATA");
 
            COPYDATASTRUCT dataStruct = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
            DataStorage storage = (DataStorage)Marshal.PtrToStructure(dataStruct.lpData, typeof(DataStorage));
 
            return storage.Type;
        }
 
        public static int SendStringMessage(IntPtr ReceivingWindowHandle, IntPtr SendingWindowHanlde, string message)
        {
            DataStorage storage = new DataStorage();
            storage.Type = DataType.String;
            storage.Data = Marshal.StringToHGlobalUni(message);
 
            int result = SendData(ReceivingWindowHandle, SendingWindowHanlde, storage);
 
            Marshal.FreeHGlobal(storage.Data);
 
            return result;
        }
 
        public static string ReceiveStringMessage(int msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg != WM_COPYDATA)
                throw new ArgumentException("Message is not WM_COPYDATA");
 
            COPYDATASTRUCT dataStruct = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
            DataStorage storage = (DataStorage)Marshal.PtrToStructure(dataStruct.lpData, typeof(DataStorage));
 
            if (storage.Type != DataType.String)
                throw new ArgumentException("Message not contain string");
 
            return Marshal.PtrToStringUni(storage.Data);
        }
 
        public static int SendDataMessage<T>(IntPtr ReceivingWindowHandle, IntPtr SendingWindowHanlde, T data) where T : struct
        {
            IntPtr dataPointer = Marshal.AllocHGlobal(Marshal.SizeOf(data));
            Marshal.StructureToPtr(data, dataPointer, false);
 
            DataStorage storage = new DataStorage();
            storage.Type = DataType.Struct;
            storage.Data = dataPointer;
 
            int result = SendData(ReceivingWindowHandle, SendingWindowHanlde, storage);
 
            Marshal.FreeHGlobal(storage.Data);
 
            return result;
        }
 
        public static T ReceiveDataMessage<T>(int msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg != WM_COPYDATA)
                throw new ArgumentException("Message is not WM_COPYDATA");
 
            COPYDATASTRUCT dataStruct = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
            DataStorage storage = (DataStorage)Marshal.PtrToStructure(dataStruct.lpData, typeof(DataStorage));
 
            if (storage.Type != DataType.Struct)
                throw new ArgumentException("Message not contain struct");
 
            return (T)Marshal.PtrToStructure(storage.Data, typeof(T));
        }
    }
}
Добавлено через 1 час 39 минут
Упс, не глянул на дату первого сообщения. Ну может кому-то другому поможет.
1
Убежденный
Ушел с форума
Эксперт С++
16245 / 7311 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
05.01.2014, 14:14 8
Лучший ответ Сообщение было отмечено как решение

Решение

Ну раз уж подняли старую тему...

В Windows самый эффективный механизм обмена данными между приложениями - это
разделяемая память (или отображаемые в память файлы, это почти одно и то же), а
также все механизмы, построенные на этой основе (pipes, например). Разделяемая
память поддерживается аппаратно (отображение одной физической страницы на разные
виртуальные адреса), для обмена данными не требуется ни доступ к диску/сети,
ни смена задач, ни переключение в ядро, поэтому скорость максимальная.

У Memory-Mapped Files есть лишь один недостаток - для обмена данными между
сессиями нужно создавать объект MMF в глобальном пространстве имен, а для этого
нужна привилегия SE_CREATE_GLOBAL_NAME, которая, начиная с Windows Vista,
только у администраторов и служб.

У именованных каналов (named pipes) такой проблемы нет, к тому же они поддерживают
обмен данными по сети, работу с безопасностью (например, пайп-сервер может выполнять
запрос в контексте безопасности пайп-клиента), message mode, таймауты и кое-что еще.
Правда, написать хорошо работающий пайп-сервер не так просто, как кажется, но такова
цена за его эффективность и функциональность. IpcChannel и NetNamedPipeBinding из WCF
тоже построены на pipes, так что в .NET, вероятно, стоит использовать их.

Еше один вариант - COM/RPC.
Скорость здесь будет ниже на порядок, да и для тех, кто не знаком с технологией COM,
написание даже простейшего клиент-сервера может быстро завести в тупик. Но это один из
немногих способов работать с данными в объектном ключе (классы, свойства, методы,
интерфейсы) и вызывать удаленные (в том числе на удаленных машинах) методы так, как
будто они находятся в одном и том же процессе. К тому же COM поддерживается очень
многими средами, это большой плюс. Про компоненты, которые пишутся на C++, а затем
дергаются из JScript или .NET, или используются в Office/1C, рассказывать не буду.

Если ничто из вышеперечисленного использовать нельзя (или не позволяет "религия"),
тогда остаются сокеты. Но это не самое лучшее решение. В двух словах - long path.
Даже простейший вызов send, recv или connect на локалхост проходит длинную цепочку
вызовов, от уровня winsock вниз, через провайдеров сети, затем в ядро (AFD), потом в
стек TCP, где для него создаются I/O-запросы, выполняются ожидания, доставка APC и
прочее-прочее. И только пройдя этот "лабиринт" вызовов и часовых механизмов, данные
попадают в ожидающее приложение. Если на компьютере установлен какой-нибудь фаервол и
он настроен на фильтрацию локальных соединений (а такая опция есть у многих из них),
то картина будет еще более запутанной, а путь - еще более длинным.

Что касается WM_COPYDATA, то это вообще последний вариант, когда ничего другого не
остается. Здесь нет гарантии доставки, нет возможности отправить данные в ответ.
Для обработки нужна оконная процедура. Сами сообщения летают только в пределах одного
десктопа, а на Windows Vista и выше "режутся" UAC-ом, например если их пытаются слать
от обычного процесса к процессу, запущенному в контексте администратора. А если
разрешить доставку (ChangeWindowMessageFilter), то всякое зловредное приложение
сможет делать то же самое. В общем, это и ненадежно, и неудобно, и несекьюрно.
11
EVG-1980
190 / 197 / 82
Регистрация: 11.04.2013
Сообщений: 1,086
05.01.2014, 18:37 9
про DDE забыли
0
Anklav
446 / 304 / 47
Регистрация: 23.01.2013
Сообщений: 661
Завершенные тесты: 2
05.01.2014, 22:53 10
Убежденный, можно узнать почему при посылке сообщение WM_COPYDATA нет гарантии доставки?
0
Убежденный
Ушел с форума
Эксперт С++
16245 / 7311 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
06.01.2014, 00:01 11
1) SendMessage не вернет управления, пока второй процесс не обработает WM_COPYDATA.
Если процесс, которому предназначено сообщение, завис, отправитель тоже будет висеть.

2) Оконное сообщение режется UAC-ом (точнее говоря, UIPI).
Например, если процесс А, запущенный от обычного пользователя (standard user),
посылает сообщение в процесс Б, который запущен от имени администратора. У них
тогда будут разные integrity level (А - medium, Б - high), и сообщение не пройдет.
Единственная "радость" - SendMessage в этом случае вернет 0, а не 1.

3) Для отправки сообщения нужен оконный хэндл. А оконные хэндлы - штука специфическая,
на них не удерживается никаких референсов, не нужно делать CloseHandle, например.
Может случиться, что между тем, когда хэндл целевого окна получен (FindWindow), и
отправкой ему сообщения WM_COPYDATA, целевой процесс будет завершен, а его хэндл с
тем же числовым значением присвоен окну какого-нибудь другого процесса.
3
Yura Ost
0 / 0 / 0
Регистрация: 21.04.2015
Сообщений: 8
Завершенные тесты: 1
05.01.2017, 14:29 12
Всем привет,

очень заинтересовало

Цитата Сообщение от Убежденный Посмотреть сообщение
Еше один вариант - COM/RPC.
Скорость здесь будет ниже на порядок, да и для тех, кто не знаком с технологией COM,
написание даже простейшего клиент-сервера может быстро завести в тупик. Но это один из
немногих способов работать с данными в объектном ключе (классы, свойства, методы,
интерфейсы) и вызывать удаленные (в том числе на удаленных машинах) методы так, как
будто они находятся в одном и том же процессе. К тому же COM поддерживается очень
многими средами, это большой плюс. Про компоненты, которые пишутся на C++, а затем
дергаются из JScript или .NET, или используются в Office/1C, рассказывать не буду.
особенно:

Цитата Сообщение от Убежденный Посмотреть сообщение
Но это один из
немногих способов работать с данными в объектном ключе (классы, свойства, методы,
интерфейсы)
не подскажите по поводу других способов реализации обмена объектами, классами и пр? преимущества/недостатки? интересуют именно те варианты которые могут работать на одной машине, не обязательно по сети.
0
Убежденный
Ушел с форума
Эксперт С++
16245 / 7311 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
05.01.2017, 14:38 13
Цитата Сообщение от Yura Ost Посмотреть сообщение
не подскажите по поводу других способов реализации обмена объектами, классами и пр?
Если мы говорим о каких-то удобных стандартных средствах, то их просто нет.
В C++ отсутствует двоичный интерфейс (ABI), поэтому обмениваться классами и объектами
не получится не то, что между разными языками, но даже между компиляторами или
разными версиями одного и того же компилятора. COM/RPC решает эту проблему.
0
Yura Ost
0 / 0 / 0
Регистрация: 21.04.2015
Сообщений: 8
Завершенные тесты: 1
05.01.2017, 14:51 14
Цитата Сообщение от Убежденный Посмотреть сообщение
Если мы говорим о каких-то удобных стандартных средствах, то их просто нет.
В C++ отсутствует двоичный интерфейс (ABI), поэтому обмениваться классами и объектами
не получится не то, что между разными языками, но даже между компиляторами или
разными версиями одного и того же компилятора. COM/RPC решает эту проблему.
другими словами без использования COM/RPC не получится реализовать такой обмен между разными процессами даже если оба приложения на .NET и работают на одной машине в рамках одной ОС?
0
Убежденный
Ушел с форума
Эксперт С++
16245 / 7311 / 1183
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
05.01.2017, 15:08 15
В .NET есть WCF, нужно смотреть в эту сторону, как мне кажется.
Я в .NET не специалист, так что подождем ответов кого-нибудь еще

Добавлено через 15 секунд
В .NET есть WCF, нужно смотреть в эту сторону, как мне кажется.
Я в .NET не специалист, так что подождем ответов кого-нибудь еще
0
insite2012
Модератор
Эксперт .NET
4890 / 3842 / 1097
Регистрация: 12.10.2013
Сообщений: 11,104
Записей в блоге: 2
05.01.2017, 15:26 16
Цитата Сообщение от Убежденный Посмотреть сообщение
В .NET есть WCF, нужно смотреть в эту сторону
Да, я тоже поддерживаю. WCF - универсальная технология, так что ее можно использовать во множестве сценариев взаимодействия.

Добавлено через 16 секунд
Цитата Сообщение от Убежденный Посмотреть сообщение
В .NET есть WCF, нужно смотреть в эту сторону
Да, я тоже поддерживаю. WCF - универсальная технология, так что ее можно использовать во множестве сценариев взаимодействия.
0
Yura Ost
0 / 0 / 0
Регистрация: 21.04.2015
Сообщений: 8
Завершенные тесты: 1
06.01.2017, 08:43 17
поковырял (поверхностно) инфу по WCF в мсдн. Попробовал тестовый пример со страницы. Вещь полезная, но для обмена объектами классы должны поддерживать сериализацию, а это не совсем то что мне подходит
0
insite2012
Модератор
Эксперт .NET
4890 / 3842 / 1097
Регистрация: 12.10.2013
Сообщений: 11,104
Записей в блоге: 2
06.01.2017, 14:20 18
Цитата Сообщение от Yura Ost Посмотреть сообщение
для обмена объектами классы должны поддерживать сериализацию
А так практически везде. И обмен идет не объектами по сути, а их состоянием. На мой взгляд, WCF на данный момент самая простая и эффективная технология, и для вашей задачи подойдет как нельзя лучше.
Кстати, сериализация - не обязательное требование. Лучше использовать контракты данных.
0
Yura Ost
0 / 0 / 0
Регистрация: 21.04.2015
Сообщений: 8
Завершенные тесты: 1
06.01.2017, 15:44 19
Цитата Сообщение от insite2012 Посмотреть сообщение
А так практически везде. И обмен идет не объектами по сути, а их состоянием. На мой взгляд, WCF на данный момент самая простая и эффективная технология, и для вашей задачи подойдет как нельзя лучше.
Кстати, сериализация - не обязательное требование. Лучше использовать контракты данных.
о контрактах данных в мсдн

сервис не запустился когда я пометил класс атрибутом DataContract и создал в нем функции которые принимают/возвращают объекты классов не поддерживающих сериализацию.

я склоняюсь к использованию COM. Если я не ошибаюсь, то тут сериализация для обмена вшита в саму технологию. Возни с атрибутами в коде конечно не убавиться, но зато многие из классов в .NET уже сделаны COM-видимыми. Огорчает что COM считается устаревшей технологией.

К тому-же на данный момент архитектура клиент/сервис не вписывается в планируемое приложение.
0
insite2012
Модератор
Эксперт .NET
4890 / 3842 / 1097
Регистрация: 12.10.2013
Сообщений: 11,104
Записей в блоге: 2
06.01.2017, 16:07 20
Цитата Сообщение от Yura Ost Посмотреть сообщение
о контрактах данных в мсдн
К счастью, мне это известно и без вашей ссылки.
Цитата Сообщение от Yura Ost Посмотреть сообщение
сервис не запустился когда я пометил класс атрибутом DataContract и создал в нем функции которые принимают/возвращают объекты классов не поддерживающих сериализацию.
Вы просто не умеете их готовить. Много раз использовал их в своих проектах, и все работает именно так, как и задумывалось.
Цитата Сообщение от Yura Ost Посмотреть сообщение
на данный момент архитектура клиент/сервис не вписывается в планируемое приложение.
Ну самое простое - используйте NamedPipes. Максимальная скорость передачи, как вам и хочется.
0
06.01.2017, 16:07
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.01.2017, 16:07

Обмен данными между формами
Как в C# сделать обмен данными между двумя формами? на С++ делал следующим образом: ...

Обмен данными между процессами
Доброго времени суток. Проблема такая: есть два процесса, один как бы главный, а второй дочерний,...

Обмен данными между формами
Знаю, что тема ворошилась не один раз, но проблемма такая: (если уже где-то обсуждалась, пжл...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru