Частенько меня спрашивают на эту тему. User32.dll и чуть-чуть соображалки для представления логики работы.
Как оказалось, в моём датасервере много "лишнего" для разъяснения вопрошающим принципа работы, пришлось изрядно покромсать и переписать. На реальном ДДЕ-обмене не проверял, но если что - спрашивайте.
Давненько не заглядывал в код, некоторые вещи сейчас сделал бы по другому
Как оказалось, в моём датасервере много "лишнего" для разъяснения вопрошающим принципа работы, пришлось изрядно покромсать и переписать. На реальном ДДЕ-обмене не проверял, но если что - спрашивайте.
Давненько не заглядывал в код, некоторые вещи сейчас сделал бы по другому
Код:
public partial class Form1 : Form
{
public struct Cell
{
public short t;
public string s;
public double d;
}
[DllImport("user32.dll", EntryPoint = "DdeInitialize", CharSet = CharSet.Ansi)]
static extern int DdeInitialize(ref int pidInst, DDECallBackDelegateQK pfnCallback, int afCmd, int ulRes);
[DllImport("user32.dll", EntryPoint = "DdeCreateStringHandle", CharSet = CharSet.Ansi)]
static extern IntPtr DdeCreateStringHandle(int idInst, string psz, int iCodePage);
[DllImport("user32.dll", EntryPoint = "DdeNameService", CharSet = CharSet.Ansi)]
static extern IntPtr DdeNameService(int idInst, IntPtr hsz1, IntPtr hsz2, int afCmd);
[DllImport("user32.dll", EntryPoint = "DdeUninitialize", CharSet = CharSet.Ansi)]
static extern bool DdeUninitialize(int idInst);
[DllImport("user32.dll", EntryPoint = "DdeQueryString", CharSet = CharSet.Ansi)]
static extern int DdeQueryString(int idInst, IntPtr hsz, byte[] psz, int cchMax, int iCodePage);
[DllImport("user32.dll", EntryPoint = "DdeGetData", CharSet = CharSet.Ansi)]
static extern int DdeGetData(IntPtr hData, [Out] byte[] pDst, int cbMax, int cbOff);
[DllImport("user32.dll", EntryPoint = "DdeConnect", CharSet = CharSet.Ansi)]
static extern IntPtr DdeConnect(int idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC);
[DllImport("user32.dll", EntryPoint = "DdeFreeStringHandle", CharSet = CharSet.Ansi)]
static extern bool DdeFreeStringHandle(int idInst, IntPtr hsz);
[DllImport("user32.dll", EntryPoint = "DdeGetLastError", CharSet = CharSet.Ansi)]
static extern int DdeGetLastError(int idInst);
internal delegate IntPtr DDECallBackDelegateQK(int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2);
public const int DDE_FACK = unchecked((int)0x8000);
public const int CP_WINANSI = 1004;
public const int DNS_REGISTER = unchecked((int)0x0001);
public const int DNS_UNREGISTER = unchecked((int)0x0002);
public const int XCLASS_BOOL = unchecked((int)0x1000);
public const int XCLASS_DATA = unchecked((int)0x2000);
public const int XCLASS_FLAGS = unchecked((int)0x4000);
public const int XTYPF_NOBLOCK = unchecked((int)0x0002);
public const int XTYP_CONNECT = unchecked((int)(0x0060 | XCLASS_BOOL | XTYPF_NOBLOCK));
public const int XTYP_POKE = unchecked((int)(0x0090 | XCLASS_FLAGS));
public const int XTYP_DISCONNECT = unchecked((int)(0x00C0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
public const int XCLASS_NOTIFICATION = unchecked((int)0x8000);
public const int XTYP_CONNECT_CONFIRM = unchecked((int)(0x0070 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
public const int XTYP_REGISTER = unchecked((int)(0x00A0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
public const int XTYP_UNREGISTER = unchecked((int)(0x00D0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
IntPtr hzService, hszTopicTTP;
public IntPtr hConvTTP;
DDECallBackDelegateQK _DDECallbackQK;
int idInstr;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_DDECallbackQK = new DDECallBackDelegateQK(DDECallBackQK);
int res = DdeInitialize(ref idInstr, _DDECallbackQK, 0, 0); //MF_POSTMSGS | MF_SENDMSGS, 0);
hzService = DdeCreateStringHandle(idInstr, "MyDDEServer", CP_WINANSI); // В Квике: DDE сервер - "MyDDEServer"
hszTopicTTP = DdeCreateStringHandle(idInstr, ("[ExChange]TTP"), CP_WINANSI); // Рабочая книга - "ExChange", лист - "TTP"
/// hszTopicTicks = DdeCreateStringHandle(idInstr, ("[ExChange]Ticks"), CP_WINANSI); //
if (DdeNameService(idInstr, hzService, IntPtr.Zero, DNS_REGISTER).ToInt32() != 1) throw new Exception("DDENameService fail");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
DdeFreeStringHandle(idInstr, hszTopicTTP);
IntPtr DDENS = DdeNameService(idInstr, hzService, IntPtr.Zero, DNS_UNREGISTER);
DdeUninitialize(idInstr);
}
IntPtr DDECallBackQK(int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2)
{
if (uType == XTYP_REGISTER)
{
return (IntPtr)DDE_FACK;
}
if (uType == XTYP_UNREGISTER)
{
return IntPtr.Zero;
}
if (uType == XTYP_CONNECT)
{
return (IntPtr)DDE_FACK;
}
if (uType == XTYP_CONNECT_CONFIRM)
{
if (hsz1 == hszTopicTTP)
{
hConvTTP = hConv;
return (IntPtr)DDE_FACK;
}
throw new Exception("UnkTable");
//return IntPtr.Zero;
}
if (uType == XTYP_DISCONNECT)
{
if (hConv == hConvTTP)
{
hConvTTP = IntPtr.Zero;
return (IntPtr)DDE_FACK;
}
throw new Exception("UnkTable");
//return IntPtr.Zero;
}
if (uType == XTYP_POKE)
{
if (hsz1 == hszTopicTTP)
{
// Это пример работы с ДДЕ. На самом деле добавляем в очередь, а отдельный поток изымает из очереди и обрабатывает, как следствие - масимально быстро освобождаем коллбэк
if (hConvTTP != hConv) throw new Exception("hConvTTP != hConv"); // на всякий случай
int iCount = DdeQueryString(idInstr, hsz2, null, 0, CP_WINANSI) + 1;
byte[] bf = new byte[iCount];
DdeQueryString(idInstr, hsz2, bf, iCount, CP_WINANSI);
//string s = ""; for (int i = 0; i < iCount - 1; i++) s = s + (char)bf[i];
string s = System.Text.Encoding.Default.GetString(bf, 0, iCount - 1);
int firstRow = int.Parse(s.Substring(1, s.IndexOf('C') - 1)) - 1;
iCount = DdeGetData(hData, null, 0, 0);
bf = new byte[iCount];
DdeGetData(hData, bf, iCount, 0);
short Cols, Rows;
Cell[,] Tbl = ParseXTbl(bf, out Cols, out Rows); // не самое красивое решение, предложения принимаются, приведение типов и парсить прямо тут - не предлагать
// Ну вот, имеем таблицу
for (int r = 0; r < Rows; r++)
{
// имеем как хотим :)
string Name = Tbl[r, 0].s;
double Cs = Tbl[r, 0].d;
}
return (IntPtr)DDE_FACK;
}
throw new Exception("ХЗ что за Tbl"); //return IntPtr.Zero;
//return IntPtr.Zero;
}
//DF.TTP.DDELog.WriteLine(DF.NTP.Now.ToString() + " " + uType.ToString());
return IntPtr.Zero;
}
public Cell[,] ParseXTbl(byte[] bf, out short Cols, out short Rows)
{
//byte cb = (byte)(bf[a] + bf[a + 1] * 256); a += 2;
short cb = BitConverter.ToInt16(bf, 0);
if (cb != 16) throw new Exception("<>16");
int a = 2;
//cb = (byte)(bf[a] + bf[a + 1] * 256); a += 2;
//Rows = (Int16)(bf[a] + bf[a + 1] * 256); a += 2;
//Cols = (Int16)(bf[a] + bf[a + 1] * 256); a += 2;
cb = BitConverter.ToInt16(bf, a); a += 2;
Rows = BitConverter.ToInt16(bf, a); a += 2;
Cols = BitConverter.ToInt16(bf, a); a += 2;
Cell[,] Tbl = new Cell[Rows, Cols];
//fixed (byte* ptr = bf)
for (int R = 0; R < Rows; R++) for (int C = 0; C < Cols; )
{
//byte Tp = (byte)(bf[a] + bf[a + 1] * 256); a += 2;
//cb = (byte)(bf[a] + bf[a + 1] * 256); a += 2;
short Tp = BitConverter.ToInt16(bf, a); a += 2;
cb = BitConverter.ToInt16(bf, a); a += 2;
if (Tp == 1)
{
for (; cb > 0; cb -= 8, a += 8, C++)
{
Tbl[R, C].d = BitConverter.ToDouble(bf, a);
Tbl[R, C].t = Tp;
//Tbl[R, C].d = *((double*)(ptr + a)); Tbl[R, C].t = Tp;
}
}
else if (Tp == 2)
{
for (; cb > 0; cb--, C++)
{
byte cb1 = bf[a]; a++;
cb -= cb1;
Tbl[R, C].s = System.Text.Encoding.Default.GetString(bf, a, cb1); a += cb1;
//Tbl[R, C].s = "";
//for (; 0 < cb1; cb1--, a++) Tbl[R, C].s = Tbl[R, C].s + (char)bf[a];
Tbl[R, C].t = Tp;
}
}
else throw new Exception("UnkType");
}
return Tbl;
}
}
Последнее редактирование: