Мне нужно получить доступ к устаревшему приложению через DDE. У меня нет отладочного доступа к этому унаследованному приложению, поэтому для меня это почти черный ящик.
Однако я знаю, что это работает, потому что у меня есть некоторый код VBA, который без проблем связывается с приложением через DDE. Первые несколько строк в коде VBA в основном выглядят так.
lngDDEChannel = DDEInitiate(App:="LEGAPP", Topic:="DDECONV")
DDEExceute channel:=lngDDEChannel, Command:="START_SESSION_12345"
strInfo$ = DDERequest(channel:=lngDDEChannel, Item:="INFO_FIELD")
NB. Фактические имена и команды отличаются от показанных здесь. Количество символов в команде равно числу символов в исходной команде. Только символы, которые могут быть представлены в ASCII.
Как уже упоминалось, этот код работает отлично. Как примечание, DDERequest будет предоставлять данные только после выполнения команды DDExecute. Если нет, он вернет пустые буферы.
Я пытаюсь перенести этот код на C#. Поскольку Microsoft не реализовала DDE в Do tNet, я решил go с библиотекой с открытым исходным кодом NDDE .
Вот как я переписал фрагмент кода выше:
using (var client = new DdeClient("LEGAPP", "DDECONV")
{
client.Connect();
client.Execute("START_SESSION_12345");
strVar = client.Request("INFO_FIELD");
}
Соединение между клиентом и сервером установлено, я это проверил. К сожалению, сервер не понимает команду execute. Вместо команды "START_SESSION_12345", похоже, получено немного гиббери sh.
Первоначально я думал, что должна быть проблема с кодировкой строки. Строки в VBA - это 7-битные строки ANSI, а строки в Do tNet - 16-битные. Однако NDDE кажется умнее, чем при использовании контекста соединения, можно задать специфицированную кодировку c для всех данных, которыми обмениваются с сервером DDE. По умолчанию используется ASCII, который на самом деле должен быть правильным выбором, поскольку все символы в командной строке могут быть представлены в ASCII. Тем не менее, я попытался поиграть с контекстной кодировкой со всеми возможными значениями, такими как UTF-7, UTF-8, ANSI, ASCII, UTF-16 (младший и старший порядковый номер). Независимо от того, что я пытаюсь, я продолжаю получать гиббери sh, это просто немного другой гиббери sh, который доказывает, что кодировка действительно учитывается.
Я посмотрел на источник NDDE. Соответствующие строки находятся в исходном файле DdemlClient.cs.
var data = _Context.Encoding.GetBytes(command + "\0");
//Send the command to the server.
int returnFlags = 0;
var result = Ddeml.DdeClientTransaction(
data,
data.Length,
_ConversationHandle,
IntPtr.Zero,
Ddeml.CF_TEXT,
Ddeml.XTYP_EXECUTE,
timeout,
ref returnFlags);
Функция DDEClientTransaction подробно описана здесь . NDDE вызывает функцию двумя способами:
[DllImport("user32.dll", EntryPoint = "DdeClientTransaction", CharSet = CharSet.Ansi)]
public static extern IntPtr DdeClientTransaction(IntPtr pData, int cbData, IntPtr hConv, IntPtr hszItem, int wFmt, int wType, int dwTimeout, ref int pdwResult);
[DllImport("user32.dll", EntryPoint = "DdeClientTransaction", CharSet = CharSet.Ansi)]
public static extern IntPtr DdeClientTransaction(byte[] pData, int cbData, IntPtr hConv, IntPtr hszItem, int wFmt, int wType, int dwTimeout, ref int pdwResult);
Код в моем примере использует второе объявление pinvoke, но я также пробовал использовать первое, без разницы.
Я застрял. Поскольку диалог по DDE через VBA работает просто отлично, я должен предположить, что с сервером DDE нет общей проблемы. Я понятия не имею, почему это не работает.
РЕДАКТИРОВАТЬ
Я провел несколько тестов, чтобы выяснить для себя, какие данные передаются. Для целей этого теста я изменил команду execute просто на «ABCD».
Encoding.Default (= кодовая страница ANSI) создает следующий байтовый массив:
Приложение получает это как команду:
Encoding.ASCII создает следующий байтовый массив:
Приложение получает это как команду:
Encoding.Unicode (т. е. UTF-16) создает следующий байтовый массив:
Приложение получает это как команду:
Может быть, это даст кому-то умнее меня правильная идея, что не так.