diff --git a/SCADA/Program/.vs/DataExchange/v15/.suo b/SCADA/Program/.vs/DataExchange/v15/.suo
index 9efd29a..bd5ed9f 100644
Binary files a/SCADA/Program/.vs/DataExchange/v15/.suo and b/SCADA/Program/.vs/DataExchange/v15/.suo differ
diff --git a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide
index 4c45f43..22e344f 100644
Binary files a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide and b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide differ
diff --git a/SCADA/Program/.vs/DataExchange/v15/sqlite3/storage.ide b/SCADA/Program/.vs/DataExchange/v15/sqlite3/storage.ide
index 76b1dcd..dab93b0 100644
Binary files a/SCADA/Program/.vs/DataExchange/v15/sqlite3/storage.ide and b/SCADA/Program/.vs/DataExchange/v15/sqlite3/storage.ide differ
diff --git a/SCADA/Program/DDEDriver/DDEDriver.cs b/SCADA/Program/DDEDriver/DDEDriver.cs
new file mode 100644
index 0000000..e208123
--- /dev/null
+++ b/SCADA/Program/DDEDriver/DDEDriver.cs
@@ -0,0 +1,743 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using DataService;
+
+namespace DDEDriver
+{
+ [Description("DDE 通讯协议")]
+ public class DDEReader : IDriver
+ {
+ //DDE用常量定义#region DDE用常量定义
+ /**/
+ ///
+ /// ///////////////////////////////////////////////////////////////////////////
+ /// /***** conversation states (usState) *****/
+ /**/
+ ///
+ private int dwDDEInst; // DDE Instance value
+ private IntPtr hconvCurrent;
+ //private bool bAutoConnect;
+
+ //public string LinkDesc;
+ private DdeCallback _Callback;
+
+ short _id;
+ public string LinkName;
+
+ public short ID
+ {
+ get
+ {
+ return _id;
+ }
+ }
+
+ string _name;
+ public string Name
+ {
+ get
+ {
+ return _name;
+ }
+ }
+
+ string _server;
+ public string ServerName
+ {
+ get { return _server; }
+ set { _server = value; }
+ }
+
+ string _topic;
+ public string Topic
+ {
+ get
+ {
+ return _topic;
+ }
+ set
+ {
+ _topic = value;
+ }
+ }
+
+ internal bool _connected;
+ public bool IsClosed
+ {
+ get { return !_connected; }
+ }
+
+ int _timeOut;
+ public int TimeOut
+ {
+ get { return _timeOut; }
+ set { _timeOut = value; }
+ }
+
+ List _groups = new List(3);
+ public IEnumerable Groups
+ {
+ get { return _groups; }
+ }
+
+ IDataServer _parent;
+ public IDataServer Parent
+ {
+ get { return _parent; }
+ }
+
+ public DDEValueChangeEventHandler DDEValueChange;
+
+ public DDEReader(IDataServer parent, short id, string name, string server, int timeOut = 2000, string topic = null, string spare2 = null)
+ {
+ _id = id;
+ _parent = parent;
+ _name = name;
+ _server = server;
+ _timeOut = timeOut;
+ _topic = topic;
+ LinkName = _server + "|" + _topic;
+ _Callback = DdeClientCallback;
+ }
+
+ public unsafe bool Connect()
+ {
+ IntPtr hConv, hszService, hszTopic;
+
+ if (dwDDEInst != 0)
+ {
+ Ddeml.DdeUninitialize(dwDDEInst);
+ dwDDEInst = 0;
+ }
+ if (hconvCurrent != IntPtr.Zero)
+ {
+ Ddeml.DdeDisconnect(hconvCurrent);
+ hconvCurrent = IntPtr.Zero;
+ };
+ //如果是第一次,则什么也不做
+ Ddeml.DdeInitialize(ref dwDDEInst, _Callback, 0x3f000, 0);
+ // Connect to the server
+ hszTopic = Ddeml.DdeCreateStringHandle(dwDDEInst, _topic, Ddeml.CP_WINANSI);
+ hszService = Ddeml.DdeCreateStringHandle(dwDDEInst, _server, Ddeml.CP_WINANSI);
+ CONVCONTEXT cc = new CONVCONTEXT();
+ cc.cb = sizeof(CONVCONTEXT);
+ hConv = Ddeml.DdeConnect(dwDDEInst, hszService, hszTopic, ref cc);
+ //int DdeErrcode = Win32.DdeGetLastError(dwDDEInst);
+
+ Ddeml.DdeFreeStringHandle(dwDDEInst, hszTopic);
+ Ddeml.DdeFreeStringHandle(dwDDEInst, hszService);
+
+ if (hConv != IntPtr.Zero)
+ {
+ if (hconvCurrent != IntPtr.Zero)
+ {
+ Ddeml.DdeDisconnect(hconvCurrent);
+ }
+ hconvCurrent = hConv;
+ _connected = true;
+ return true;
+ }
+ else
+ {
+ _connected = false;
+ return false;
+ }
+ }
+
+ private IntPtr DdeClientCallback(int uType, ConversionFormat uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, uint dwData1, uint dwData2)
+ {
+ int dwLength = 0;
+ //DateTime time = DateTime.Now;
+ unsafe
+ {
+ switch (uType)
+ {
+ case Ddeml.XTYP_ADVDATA:
+ try
+ {
+ sbyte* pData = (sbyte*)Ddeml.DdeAccessData(hData, ref dwLength);
+ if (pData != null)
+ {
+ sbyte* pSZ = stackalloc sbyte[0xFF];
+ Ddeml.DdeQueryString(dwDDEInst, hsz2, pSZ, 0xFF, Ddeml.CP_WINANSI);
+ if (DDEValueChange != null)
+ DDEValueChange(this, new DDEValueChangeEventArgs(new string(pSZ).ToUpper(), new string(pData, 0, dwLength)));
+ }
+ }
+ catch { }
+ finally
+ {
+ if (hData != IntPtr.Zero) Ddeml.DdeUnaccessData(hData);
+ }
+ break;
+ case Ddeml.XTYP_DISCONNECT:
+ if (OnError != null)
+ OnError(this, new IOErrorEventArgs("XTYP_DISCONNECT"));
+ _connected = false;
+ break;
+ case Ddeml.XTYP_XACT_COMPLETE:
+ break;
+ default:
+ break;
+ }
+ }
+ return new IntPtr(Ddeml.DDE_FACK);
+ }
+
+ //与Server建立Hoot连接
+ public unsafe bool TransactItem(string name)
+ {
+ IntPtr hszItem, hDDEData;
+ int dwResult = 0;
+ if (string.IsNullOrEmpty(name)) return false;
+ if (hconvCurrent != IntPtr.Zero)
+ {
+ hszItem = Ddeml.DdeCreateStringHandle(dwDDEInst, name, Ddeml.CP_WINANSI);
+
+ hDDEData = Ddeml.DdeClientTransaction(null,
+ 0,
+ hconvCurrent,
+ hszItem,
+ ConversionFormat.TEXT,//CF_TEXT,
+ Ddeml.XTYP_ADVSTART,
+ Ddeml.TIMEOUT_ASYNC, // ms timeout
+ ref dwResult);
+
+ Ddeml.DdeFreeStringHandle(dwDDEInst, hszItem);
+
+ if (hDDEData != IntPtr.Zero)
+ {
+ try
+ {
+ int dwLength = 0;
+ sbyte* pData = (sbyte*)Ddeml.DdeAccessData(hDDEData, ref dwLength);
+ if ((pData != null) && (dwLength != 0))
+ {
+ if (DDEValueChange != null)
+ DDEValueChange(this, new DDEValueChangeEventArgs(name, new string(pData, 0, dwLength)));
+ }
+ }
+ catch { }
+ finally
+ {
+ Ddeml.DdeUnaccessData(hDDEData);
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+
+ public int Execute(string command)
+ {
+ int res = 0;
+ byte[] data = Encoding.ASCII.GetBytes(command);
+ Ddeml.DdeClientTransaction(data, data.Length, hconvCurrent, IntPtr.Zero, ConversionFormat.NONE, Ddeml.XTYP_EXECUTE, _timeOut, ref res);
+ return res;
+ }
+
+ //必须设置item.ItemName和item.ItemValue后调用
+ public int Poke(string name, string value)
+ {
+ IntPtr hszItem, hDDEData;
+ int dwResult = 0;
+ if (string.IsNullOrEmpty(name)) return -2;
+ if (hconvCurrent != IntPtr.Zero)
+ {
+ hszItem = Ddeml.DdeCreateStringHandle(dwDDEInst, name, Ddeml.CP_WINANSI);
+ if (!string.IsNullOrEmpty(name))
+ {
+ //int errcode=Win32.DdeGetLastError(dwDDEInst);
+ byte[] ByteArray = Encoding.ASCII.GetBytes(value);
+ IntPtr hd = Ddeml.DdeCreateDataHandle(dwDDEInst, ByteArray, ByteArray.Length, 0, hszItem, ConversionFormat.TEXT, 0);
+ hDDEData = Ddeml.DdeClientTransaction(
+ ByteArray, ByteArray.Length,
+ hconvCurrent, hszItem,
+ ConversionFormat.TEXT,//CF_TEXT,
+ Ddeml.XTYP_POKE,
+ Ddeml.TIMEOUT_ASYNC, // ms timeout
+ ref dwResult);
+ Ddeml.DdeFreeStringHandle(dwDDEInst, hszItem);
+
+ if (hDDEData != IntPtr.Zero)
+ {
+ return 1;
+ }
+ }
+ return -1;
+ }
+ else
+ return -1;
+ }
+
+ // [10/24/2003]
+ //主动请求Item的值
+ public unsafe string Request(string name)
+ {
+ IntPtr hszItem, hDDEData;
+ int dwResult = 0;
+ int dwLength = 0;
+ if (name != null && hconvCurrent != IntPtr.Zero)
+ {
+ hszItem = Ddeml.DdeCreateStringHandle(dwDDEInst, name, Ddeml.CP_WINANSI);
+
+ hDDEData = Ddeml.DdeClientTransaction(null,
+ 0,
+ hconvCurrent,
+ hszItem,
+ ConversionFormat.TEXT,//CF_TEXT,
+ (int)Ddeml.XTYP_REQUEST,
+ 5000, // ms timeout
+ ref dwResult);
+ Ddeml.DdeFreeStringHandle(dwDDEInst, hszItem);
+
+ if (hDDEData != IntPtr.Zero)
+ {
+ sbyte* pData = (sbyte*)Ddeml.DdeAccessData(hDDEData, ref dwLength);
+ {
+ if ((pData != null) && (dwLength != 0))
+ {
+ return new string(pData, 0, dwLength);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+
+ public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false)
+ {
+ if (_groups.Count > 0)
+ return null;
+ DDEGroup grp = new DDEGroup(id, name, updateRate, active, this);
+ _groups.Add(grp);
+ return grp;
+ }
+
+ public bool RemoveGroup(IGroup grp)
+ {
+ grp.IsActive = false;
+ return _groups.Remove(grp);
+ }
+
+ public void Dispose()
+ {
+ foreach (IGroup grp in _groups)
+ {
+ grp.Dispose();
+ }
+ _groups.Clear();
+ Ddeml.DdeDisconnect(hconvCurrent);
+ }
+
+ public event IOErrorEventHandler OnError;
+ }
+
+ public class DDEGroup : IGroup
+ {
+ bool _isActive;
+ public bool IsActive
+ {
+ get
+ {
+ return _isActive;
+ }
+ set
+ {
+ _isActive = value;
+ }
+ }
+
+ short _id;
+ public short ID
+ {
+ get
+ {
+ return _id;
+ }
+ }
+
+ int _updateRate;
+ public int UpdateRate
+ {
+ get
+ {
+ return _updateRate;
+ }
+ set
+ {
+ _updateRate = value;
+ }
+ }
+
+ string _name;
+ public string Name
+ {
+ get
+ {
+ return _name;
+ }
+ set
+ {
+ _name = value;
+ }
+ }
+
+ float deadband;
+ public float DeadBand
+ {
+ get
+ {
+ return deadband;
+ }
+ set
+ {
+ deadband = value;
+ }
+ }
+
+ DDEReader _plcReader;
+ public IDriver Parent
+ {
+ get
+ {
+ return _plcReader;
+ }
+ }
+
+
+ List _changedList;
+ public List ChangedList
+ {
+ get
+ {
+ return _changedList;
+ }
+ }
+
+
+ List _items;
+ public IEnumerable Items
+ {
+ get { return _items; }
+ }
+
+ IDataServer _server;
+ public IDataServer Server
+ {
+ get
+ {
+ return _server;
+ }
+ }
+
+ Dictionary _mapping;
+
+ public DDEGroup(short id, string address, int updateRate, bool active, DDEReader plcReader)
+ {
+ this._id = id;
+ this._updateRate = updateRate;
+ this._isActive = active;
+ this._plcReader = plcReader;
+ this._name = address;
+ this._server = _plcReader.Parent;
+ this._changedList = new List(100);
+ plcReader.DDEValueChange += new DDEValueChangeEventHandler(plcReader_DDEValueChange);
+ }
+
+ void plcReader_DDEValueChange(object sender, DDEValueChangeEventArgs e)
+ {
+ string name = e.Name;
+ string data = e.Data;
+ ITag tag = _server[name];
+ if (tag != null)
+ {
+ Storage value = Storage.Empty;
+ switch (tag.Address.VarType)
+ {
+ case DataType.BOOL:
+ value.Boolean = bool.Parse(data);
+ break;
+ case DataType.BYTE:
+ value.Byte = byte.Parse(data);
+ break;
+ case DataType.WORD:
+ case DataType.SHORT:
+ value.Int16 = short.Parse(data);
+ break;
+ case DataType.INT:
+ value.Int32 = int.Parse(data);
+ break;
+ case DataType.STR:
+ var stag = tag as StringTag;
+ if (stag != null)
+ {
+ stag.String = data;
+ }
+ break;
+ }
+ tag.Update(value, DateTime.Now, QUALITIES.QUALITY_GOOD);
+ if (DataChange != null)
+ DataChange(this, new DataChangeEventArgs(1, new HistoryData[]{
+ new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,tag.Value,tag.TimeStamp)}));
+ }
+ }
+
+
+ public bool AddItems(IList items)
+ {
+ int count = items.Count;
+ if (_items == null)
+ {
+ _items = new List(count);
+ _mapping = new Dictionary(count);
+ }
+ lock (_server.SyncRoot)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ ITag dataItem = null;
+ TagMetaData meta = items[i];
+ if (meta.GroupID == this._id)
+ {
+ DeviceAddress addr = new DeviceAddress { Start = meta.ID, DataSize = meta.Size, VarType = meta.DataType };
+ switch (meta.DataType)
+ {
+ case DataType.BOOL:
+ dataItem = new BoolTag(meta.ID, addr, this);
+ break;
+ case DataType.BYTE:
+ dataItem = new ByteTag(meta.ID, addr, this);
+ break;
+ case DataType.WORD:
+ case DataType.SHORT:
+ dataItem = new ShortTag(meta.ID, addr, this);
+ break;
+ case DataType.DWORD:
+ case DataType.INT:
+ dataItem = new IntTag(meta.ID, addr, this);
+ break;
+ case DataType.FLOAT:
+ dataItem = new FloatTag(meta.ID, addr, this);
+ break;
+ case DataType.STR:
+ dataItem = new StringTag(meta.ID, addr, this);
+ break;
+ }
+ if (dataItem != null)
+ {
+ //dataItem.Active = meta.Active;
+ _items.Add(dataItem);
+ _plcReader.TransactItem(meta.Name);
+ _mapping.Add(meta.ID, meta.Address);
+ _server.AddItemIndex(meta.Name, dataItem);
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ public bool AddTags(IEnumerable tags)
+ {
+ if (_items == null)
+ {
+ _items = new List();
+ }
+ foreach (ITag tag in tags)
+ {
+ if (tag != null)
+ {
+ _items.Add(tag);
+ }
+ }
+ return true;
+ }
+
+ public bool RemoveItems(params ITag[] items)
+ {
+ foreach (var item in items)
+ {
+ _server.RemoveItemIndex(item.GetTagName());
+ _mapping.Remove(item.ID);
+ _items.Remove(item);
+ }
+ return true;
+ }
+
+ public bool SetActiveState(bool active, params short[] items)
+ {
+ return true;
+ }
+
+ public ITag FindItemByAddress(DeviceAddress addr)
+ {
+ return null;
+ }
+
+ public ITag GetItemByID(short id)
+ {
+ return _server[id];
+ }
+
+ public HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray)
+ {
+ return ExtMethods.BatchRead(source, itemArray);
+ }
+
+ public int BatchWrite(SortedDictionary items, bool isSync = true)
+ {
+ int rev = ExtMethods.BatchWrite(items);
+ if (DataChange != null && rev >= 0)
+ {
+ int len = items.Count;
+ HistoryData[] data = new HistoryData[len];
+ int i = 0;
+ foreach (var item in items)
+ {
+ ITag tag = item.Key;
+ data[i].ID = tag.ID;
+ data[i].TimeStamp = tag.TimeStamp;
+ data[i].Quality = tag.Quality;
+ //data[i].Value = item.Value.ToStorge();
+ i++;
+ }
+ foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList())
+ {
+ deleg.BeginInvoke(this, new DataChangeEventArgs(1, data), null, null);
+ }
+ }
+ return rev;
+ }
+
+ public ItemData ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(int.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(short.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadUInt32(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(uint.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadUInt16(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(ushort.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadByte(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(byte.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(float.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadBool(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(false, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(bool.Parse(data), 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public ItemData ReadString(DeviceAddress address, DataSource source = DataSource.Cache)
+ {
+ string data = _plcReader.Request(_mapping[(short)address.Start]);
+ return string.IsNullOrEmpty(data) ? new ItemData(null, 0, QUALITIES.QUALITY_BAD) :
+ new ItemData(data, 0, QUALITIES.QUALITY_GOOD);
+ }
+
+ public int WriteInt32(DeviceAddress address, int value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public int WriteInt16(DeviceAddress address, short value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public int WriteUInt32(DeviceAddress address, uint value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public int WriteUInt16(DeviceAddress address, ushort value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public int WriteFloat(DeviceAddress address, float value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public int WriteString(DeviceAddress address, string value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value);
+ }
+
+ public int WriteBit(DeviceAddress address, bool value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public int WriteBits(DeviceAddress address, byte value)
+ {
+ return _plcReader.Poke(_mapping[(short)address.Start], value.ToString());
+ }
+
+ public event DataChangeEventHandler DataChange;
+
+ public void Dispose()
+ {
+ _plcReader.DDEValueChange -= new DDEValueChangeEventHandler(plcReader_DDEValueChange);
+ _items.Clear();
+ _mapping.Clear();
+ _items = null;
+ _mapping = null;
+ }
+ }
+
+ public delegate void DDEValueChangeEventHandler(object sender, DDEValueChangeEventArgs e);
+
+ public class DDEValueChangeEventArgs : EventArgs
+ {
+ public DDEValueChangeEventArgs(string name, string data)
+ {
+ this.Name = name;
+ this.Data = data;
+ }
+
+ public string Name;
+ public string Data;
+ }
+}
diff --git a/SCADA/Program/DDEDriver/DDEDriver.csproj b/SCADA/Program/DDEDriver/DDEDriver.csproj
new file mode 100644
index 0000000..d813441
--- /dev/null
+++ b/SCADA/Program/DDEDriver/DDEDriver.csproj
@@ -0,0 +1,64 @@
+
+
+
+ Debug
+ x86
+ 8.0.30703
+ 2.0
+ {14938033-7870-477D-925C-A447933A44E7}
+ Library
+ Properties
+ DDEDriver
+ DDEDriver
+ v4.0
+ Client
+ 512
+
+
+ x86
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ x86
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {8965e389-6466-4b30-bd43-83c909044637}
+ DataService
+
+
+
+
+
\ No newline at end of file
diff --git a/SCADA/Program/DDEDriver/DDELib.cs b/SCADA/Program/DDEDriver/DDELib.cs
new file mode 100644
index 0000000..39c9c3b
--- /dev/null
+++ b/SCADA/Program/DDEDriver/DDELib.cs
@@ -0,0 +1,481 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace DDEDriver
+{
+ public delegate IntPtr DdeCallback(
+ int uType, ConversionFormat uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, uint dwData1, uint dwData2);
+
+ public static class Ddeml
+ {
+ public const int MAX_STRING_SIZE = 255;
+
+ public const int APPCMD_CLIENTONLY = unchecked((int)0x00000010);
+ public const int APPCMD_FILTERINITS = unchecked((int)0x00000020);
+ public const int APPCMD_MASK = unchecked((int)0x00000FF0);
+ public const int APPCLASS_STANDARD = unchecked((int)0x00000000);
+ public const int APPCLASS_MONITOR = unchecked((int)0x00000001);
+ public const int APPCLASS_MASK = unchecked((int)0x0000000F);
+
+ public const int CBR_BLOCK = unchecked((int)0xFFFFFFFF);
+
+ public const int CBF_FAIL_SELFCONNECTIONS = unchecked((int)0x00001000);
+ public const int CBF_FAIL_CONNECTIONS = unchecked((int)0x00002000);
+ public const int CBF_FAIL_ADVISES = unchecked((int)0x00004000);
+ public const int CBF_FAIL_EXECUTES = unchecked((int)0x00008000);
+ public const int CBF_FAIL_POKES = unchecked((int)0x00010000);
+ public const int CBF_FAIL_REQUESTS = unchecked((int)0x00020000);
+ public const int CBF_FAIL_ALLSVRXACTIONS = unchecked((int)0x0003f000);
+ public const int CBF_SKIP_CONNECT_CONFIRMS = unchecked((int)0x00040000);
+ public const int CBF_SKIP_REGISTRATIONS = unchecked((int)0x00080000);
+ public const int CBF_SKIP_UNREGISTRATIONS = unchecked((int)0x00100000);
+ public const int CBF_SKIP_DISCONNECTS = unchecked((int)0x00200000);
+ public const int CBF_SKIP_ALLNOTIFICATIONS = unchecked((int)0x003c0000);
+
+ public const int CF_TEXT = 1;
+
+ public const int CP_WINANSI = 1004;
+ public const int CP_WINUNICODE = 1200;
+
+ public const int DDE_FACK = unchecked((int)0x8000);
+ public const int DDE_FBUSY = unchecked((int)0x4000);
+ public const int DDE_FDEFERUPD = unchecked((int)0x4000);
+ public const int DDE_FACKREQ = unchecked((int)0x8000);
+ public const int DDE_FRELEASE = unchecked((int)0x2000);
+ public const int DDE_FREQUESTED = unchecked((int)0x1000);
+ public const int DDE_FAPPSTATUS = unchecked((int)0x00ff);
+ public const int DDE_FNOTPROCESSED = unchecked((int)0x0000);
+
+ public const int DMLERR_NO_ERROR = unchecked((int)0x0000);
+ public const int DMLERR_FIRST = unchecked((int)0x4000);
+ public const int DMLERR_ADVACKTIMEOUT = unchecked((int)0x4000);
+ public const int DMLERR_BUSY = unchecked((int)0x4001);
+ public const int DMLERR_DATAACKTIMEOUT = unchecked((int)0x4002);
+ public const int DMLERR_DLL_NOT_INITIALIZED = unchecked((int)0x4003);
+ public const int DMLERR_DLL_USAGE = unchecked((int)0x4004);
+ public const int DMLERR_EXECACKTIMEOUT = unchecked((int)0x4005);
+ public const int DMLERR_INVALIDPARAMETER = unchecked((int)0x4006);
+ public const int DMLERR_LOW_MEMORY = unchecked((int)0x4007);
+ public const int DMLERR_MEMORY_ERROR = unchecked((int)0x4008);
+ public const int DMLERR_NOTPROCESSED = unchecked((int)0x4009);
+ public const int DMLERR_NO_CONV_ESTABLISHED = unchecked((int)0x400A);
+ public const int DMLERR_POKEACKTIMEOUT = unchecked((int)0x400B);
+ public const int DMLERR_POSTMSG_FAILED = unchecked((int)0x400C);
+ public const int DMLERR_REENTRANCY = unchecked((int)0x400D);
+ public const int DMLERR_SERVER_DIED = unchecked((int)0x400E);
+ public const int DMLERR_SYS_ERROR = unchecked((int)0x400F);
+ public const int DMLERR_UNADVACKTIMEOUT = unchecked((int)0x4010);
+ public const int DMLERR_UNFOUND_QUEUE_ID = unchecked((int)0x4011);
+ public const int DMLERR_LAST = unchecked((int)0x4011);
+
+ public const int DNS_REGISTER = unchecked((int)0x0001);
+ public const int DNS_UNREGISTER = unchecked((int)0x0002);
+ public const int DNS_FILTERON = unchecked((int)0x0004);
+ public const int DNS_FILTEROFF = unchecked((int)0x0008);
+
+ public const int EC_ENABLEALL = unchecked((int)0x0000);
+ public const int EC_ENABLEONE = unchecked((int)0x0080);
+ public const int EC_DISABLE = unchecked((int)0x0008);
+ public const int EC_QUERYWAITING = unchecked((int)0x0002);
+
+ public const int HDATA_APPOWNED = unchecked((int)0x0001);
+
+ public const int MF_HSZ_INFO = unchecked((int)0x01000000);
+ public const int MF_SENDMSGS = unchecked((int)0x02000000);
+ public const int MF_POSTMSGS = unchecked((int)0x04000000);
+ public const int MF_CALLBACKS = unchecked((int)0x08000000);
+ public const int MF_ERRORS = unchecked((int)0x10000000);
+ public const int MF_LINKS = unchecked((int)0x20000000);
+ public const int MF_CONV = unchecked((int)0x40000000);
+
+ public const int MH_CREATE = 1;
+ public const int MH_KEEP = 2;
+ public const int MH_DELETE = 3;
+ public const int MH_CLEANUP = 4;
+
+ public const int QID_SYNC = unchecked((int)0xFFFFFFFF);
+ public const int TIMEOUT_ASYNC = unchecked((int)0xFFFFFFFF);
+
+ public const int XTYPF_NOBLOCK = unchecked((int)0x0002);
+ public const int XTYPF_NODATA = unchecked((int)0x0004);
+ public const int XTYPF_ACKREQ = unchecked((int)0x0008);
+ public const int XCLASS_MASK = unchecked((int)0xFC00);
+ 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 XCLASS_NOTIFICATION = unchecked((int)0x8000);
+ public const int XTYP_ERROR = unchecked((int)(0x0000 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
+ public const int XTYP_ADVDATA = unchecked((int)(0x0010 | XCLASS_FLAGS));
+ public const int XTYP_ADVREQ = unchecked((int)(0x0020 | XCLASS_DATA | XTYPF_NOBLOCK));
+ public const int XTYP_ADVSTART = unchecked((int)(0x0030 | XCLASS_BOOL));
+ public const int XTYP_ADVSTOP = unchecked((int)(0x0040 | XCLASS_NOTIFICATION));
+ public const int XTYP_EXECUTE = unchecked((int)(0x0050 | XCLASS_FLAGS));
+ public const int XTYP_CONNECT = unchecked((int)(0x0060 | XCLASS_BOOL | XTYPF_NOBLOCK));
+ public const int XTYP_CONNECT_CONFIRM = unchecked((int)(0x0070 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
+ public const int XTYP_XACT_COMPLETE = unchecked((int)(0x0080 | XCLASS_NOTIFICATION));
+ public const int XTYP_POKE = unchecked((int)(0x0090 | XCLASS_FLAGS));
+ public const int XTYP_REGISTER = unchecked((int)(0x00A0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
+ public const int XTYP_REQUEST = unchecked((int)(0x00B0 | XCLASS_DATA));
+ public const int XTYP_DISCONNECT = unchecked((int)(0x00C0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
+ public const int XTYP_UNREGISTER = unchecked((int)(0x00D0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
+ public const int XTYP_WILDCONNECT = unchecked((int)(0x00E0 | XCLASS_DATA | XTYPF_NOBLOCK));
+ public const int XTYP_MONITOR = unchecked((int)(0x00F0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK));
+ public const int XTYP_ADVSTARTNODATA = unchecked(XTYP_ADVSTART | XTYPF_NODATA);
+ public const int XTYP_ADVSTARTACKREQ = unchecked(XTYP_ADVSTART | XTYPF_ACKREQ);
+ public const int XTYP_MASK = unchecked((int)0x00F0);
+ public const int XTYP_SHIFT = unchecked((int)0x0004);
+
+ [DllImport("user32.dll", EntryPoint = "DdeAbandonTransaction", CharSet = CharSet.Ansi)]
+ public static extern bool DdeAbandonTransaction(int idInst, IntPtr hConv, int idTransaction);
+
+ [DllImport("user32.dll", EntryPoint = "DdeAccessData", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeAccessData(IntPtr hData, ref int pcbDataSize);
+
+ [DllImport("user32.dll", EntryPoint = "DdeAddData", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeAddData(IntPtr hData, byte[] pSrc, int cb, int cbOff);
+
+ [DllImport("user32.dll", EntryPoint = "DdeClientTransaction", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeClientTransaction(
+ byte[] pData, int cbData, IntPtr hConv, IntPtr hszItem, ConversionFormat wFmt, int wType, int dwTimeout, ref int pdwResult);
+
+ [DllImport("user32.dll", EntryPoint = "DdeCmpStringHandles", CharSet = CharSet.Ansi)]
+ public static extern int DdeCmpStringHandles(IntPtr hsz1, IntPtr hsz2);
+
+ [DllImport("user32.dll", EntryPoint = "DdeConnect", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeConnect(int idInst, IntPtr hszService, IntPtr hszTopic, ref CONVCONTEXT pCC);
+
+ [DllImport("user32.dll", EntryPoint = "DdeConnectList", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeConnectList(int idInst, IntPtr hszService, IntPtr hszTopic, IntPtr hConvList, ref CONVCONTEXT pCC);
+
+ [DllImport("user32.dll", EntryPoint = "DdeCreateDataHandle", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeCreateDataHandle(int idInst, byte[] pSrc, int cb, int cbOff, IntPtr hszItem, ConversionFormat wFmt, int afCmd);
+
+ [DllImport("user32.dll", EntryPoint = "DdeCreateStringHandle", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeCreateStringHandle(int idInst, string psz, int iCodePage);
+
+ [DllImport("user32.dll", EntryPoint = "DdeDisconnect", CharSet = CharSet.Ansi)]
+ public static extern bool DdeDisconnect(IntPtr hConv);
+
+ [DllImport("user32.dll", EntryPoint = "DdeDisconnectList", CharSet = CharSet.Ansi)]
+ public static extern bool DdeDisconnectList(IntPtr hConvList);
+
+ [DllImport("user32.dll", EntryPoint = "DdeEnableCallback", CharSet = CharSet.Ansi)]
+ public static extern bool DdeEnableCallback(int idInst, IntPtr hConv, int wCmd);
+
+ [DllImport("user32.dll", EntryPoint = "DdeFreeDataHandle", CharSet = CharSet.Ansi)]
+ public static extern bool DdeFreeDataHandle(IntPtr hData);
+
+ [DllImport("user32.dll", EntryPoint = "DdeFreeStringHandle", CharSet = CharSet.Ansi)]
+ public static extern bool DdeFreeStringHandle(int idInst, IntPtr hsz);
+
+ [DllImport("user32.dll", EntryPoint = "DdeGetData", CharSet = CharSet.Ansi)]
+ public unsafe static extern int DdeGetData(IntPtr hData, [Out] byte* pDst, int cbMax, int cbOff);
+
+ [DllImport("user32.dll", EntryPoint = "DdeGetLastError", CharSet = CharSet.Ansi)]
+ public static extern int DdeGetLastError(int idInst);
+
+ [DllImport("user32.dll", EntryPoint = "DdeImpersonateClient", CharSet = CharSet.Ansi)]
+ public static extern bool DdeImpersonateClient(IntPtr hConv);
+
+ [DllImport("user32.dll", EntryPoint = "DdeInitialize", CharSet = CharSet.Ansi)]
+ public static extern int DdeInitialize(ref int pidInst, DdeCallback pfnCallback, int afCmd, int ulRes);
+
+ [DllImport("user32.dll", EntryPoint = "DdeKeepStringHandle", CharSet = CharSet.Ansi)]
+ public static extern bool DdeKeepStringHandle(int idInst, IntPtr hsz);
+
+ [DllImport("user32.dll", EntryPoint = "DdeNameService", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeNameService(int idInst, IntPtr hsz1, IntPtr hsz2, int afCmd);
+
+ [DllImport("user32.dll", EntryPoint = "DdePostAdvise", CharSet = CharSet.Ansi)]
+ public static extern bool DdePostAdvise(int idInst, IntPtr hszTopic, IntPtr hszItem);
+
+ [DllImport("user32.dll", EntryPoint = "DdeQueryConvInfo", CharSet = CharSet.Ansi)]
+ public static extern int DdeQueryConvInfo(IntPtr hConv, int idTransaction, IntPtr pConvInfo);
+
+ [DllImport("user32.dll", EntryPoint = "DdeQueryNextServer", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeQueryNextServer(IntPtr hConvList, IntPtr hConvPrev);
+
+ [DllImport("user32.dll", EntryPoint = "DdeQueryString", CharSet = CharSet.Ansi)]
+ public unsafe static extern int DdeQueryString(int idInst, IntPtr hsz, sbyte* psz, int cchMax, int iCodePage);
+
+ [DllImport("user32.dll", EntryPoint = "DdeReconnect", CharSet = CharSet.Ansi)]
+ public static extern IntPtr DdeReconnect(IntPtr hConv);
+
+ [DllImport("user32.dll", EntryPoint = "DdeSetUserHandle", CharSet = CharSet.Ansi)]
+ public static extern bool DdeSetUserHandle(IntPtr hConv, int id, IntPtr hUser);
+
+ [DllImport("user32.dll", EntryPoint = "DdeUnaccessData", CharSet = CharSet.Ansi)]
+ public static extern bool DdeUnaccessData(IntPtr hData);
+
+ [DllImport("user32.dll", EntryPoint = "DdeUninitialize", CharSet = CharSet.Ansi)]
+ public static extern bool DdeUninitialize(int idInst);
+
+ public static String DDEGetErrorMsg(int error)
+ {
+ switch (error)
+ {
+ case DMLERR_NO_ERROR:
+ return "no DDE error.";
+ case DMLERR_ADVACKTIMEOUT:
+ return "a request for a synchronous advise transaction has timed out.";
+
+ case DMLERR_BUSY:
+ return "the response to the transaction caused the DDE_FBUSY bit to be set.";
+
+ case DMLERR_DATAACKTIMEOUT:
+ return "a request for a synchronous data transaction has timed out.";
+
+ case DMLERR_DLL_NOT_INITIALIZED:
+ return "a DDEML function was called without first calling the DdeInitialize function,\nor an invalid instance identifier\nwas passed to a DDEML function.";
+
+ case DMLERR_DLL_USAGE:
+ return "an application initialized as APPCLASS_MONITOR has\nattempted to perform a DDE transaction,\nor an application initialized as APPCMD_CLIENTONLY has \nattempted to perform server transactions.";
+
+ case DMLERR_EXECACKTIMEOUT:
+ return "a request for a synchronous execute transaction has timed out.";
+
+ case DMLERR_INVALIDPARAMETER:
+ return "a parameter failed to be validated by the DDEML.";
+
+ case DMLERR_LOW_MEMORY:
+ return "a DDEML application has created a prolonged race condition.";
+
+ case DMLERR_MEMORY_ERROR:
+ return "a memory allocation failed.";
+
+ case DMLERR_NO_CONV_ESTABLISHED:
+ return "a client's attempt to establish a conversation has failed.";
+
+ case DMLERR_NOTPROCESSED:
+ return "a transaction failed.";
+
+ case DMLERR_POKEACKTIMEOUT:
+ return "a request for a synchronous poke transaction has timed out.";
+
+ case DMLERR_POSTMSG_FAILED:
+ return "an internal call to the PostMessage function has failed. ";
+
+ case DMLERR_REENTRANCY:
+ return "reentrancy problem.";
+
+ case DMLERR_SERVER_DIED:
+ return "a server-side transaction was attempted on a conversation\nthat was terminated by the client, or the server\nterminated before completing a transaction.";
+
+ case DMLERR_SYS_ERROR:
+ return "an internal error has occurred in the DDEML.";
+
+ case DMLERR_UNADVACKTIMEOUT:
+ return "a request to end an advise transaction has timed out.";
+
+ case DMLERR_UNFOUND_QUEUE_ID:
+ return "an invalid transaction identifier was passed to a DDEML function.\nOnce the application has returned from an XTYP_XACT_COMPLETE callback,\nthe transaction identifier for that callback is no longer valid.";
+
+ default:
+ return "Unknown DDE error %08x";
+ }
+ }
+
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct HSZPAIR
+ {
+ public IntPtr hszSvc;
+ public IntPtr hszTopic;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SECURITY_QUALITY_OF_SERVICE
+ { // sqos
+ public ushort Length;
+ public int ImpersonationLevel; //SECURITY_IMPERSONATION_LEVEL
+ public int ContextTrackingMode; //SECURITY_CONTEXT_TRACKING_MODE
+ public bool EffectiveOnly;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CONVINFO
+ {
+ public int cb;
+ public IntPtr hUser;
+ public IntPtr hConvPartner;
+ public IntPtr hszSvcPartner;
+ public IntPtr hszServiceReq;
+ public IntPtr hszTopic;
+ public IntPtr hszItem;
+ public int wFmt;
+ public int wType;
+ public int wStatus;
+ public int wConvst;
+ public int wLastError;
+ public IntPtr hConvList;
+ public CONVCONTEXT ConvCtxt;
+ public IntPtr hwnd;
+ public IntPtr hwndPartner;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CONVCONTEXT
+ {
+ public int cb;
+ public int wFlags;
+ public int wCountryID;
+ public int iCodePage;
+ public int dwLangID;
+ public int dwSecurity;
+ public SECURITY_QUALITY_OF_SERVICE qos;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MONCBSTRUCT
+ {
+ public int cb;
+ public int dwTime;
+ public IntPtr hTask;
+ public IntPtr dwRet;
+ public int wType;
+ public int wFmt;
+ public IntPtr hConv;
+ public IntPtr hsz1;
+ public IntPtr hsz2;
+ public IntPtr hData;
+ public uint dwData1;
+ public uint dwData2;
+ public CONVCONTEXT cc;
+ public int cbData;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+ public byte[] Data;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MONCONVSTRUCT
+ {
+ public int cb;
+ public bool fConnect;
+ public int dwTime;
+ public IntPtr hTask;
+ public IntPtr hszSvc;
+ public IntPtr hszTopic;
+ public IntPtr hConvClient;
+ public IntPtr hConvServer;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MONERRSTRUCT
+ {
+ public int cb;
+ public int wLastError;
+ public int dwTime;
+ public IntPtr hTask;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MONHSZSTRUCT
+ {
+ public int cb;
+ public int fsAction;
+ public int dwTime;
+ public IntPtr hsz;
+ public IntPtr hTask;
+ public IntPtr str;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MONLINKSTRUCT
+ {
+ public int cb;
+ public int dwTime;
+ public IntPtr hTask;
+ public bool fEstablished;
+ public bool fNoData;
+ public IntPtr hszSvc;
+ public IntPtr hszTopic;
+ public IntPtr hszItem;
+ public int wFmt;
+ public bool fServer;
+ public IntPtr hConvClient;
+ public IntPtr hConvServer;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MONMSGSTRUCT
+ {
+ public int cb;
+ public IntPtr hwndTo;
+ public int dwTime;
+ public IntPtr hTask;
+ public int wMsg;
+ public IntPtr wParam;
+ public IntPtr lParam;
+ public DDEML_MSG_HOOK_DATA dmhd;
+
+ } // struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DDEML_MSG_HOOK_DATA
+ {
+ public IntPtr uiLo;
+ public IntPtr uiHi;
+ public int cbData;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+ public byte[] Data;
+
+ } // struct
+
+ public enum ConversionFormat : uint
+ {
+ NONE = 0,
+ TEXT = 1,
+ BITMAP = 2,
+ METAFILEPICT = 3,
+ SYLK = 4,
+ DIF = 5,
+ TIFF = 6,
+ OEMTEXT = 7,
+ DIB = 8,
+ PALETTE = 9,
+ PENDATA = 10,
+ RIFF = 11,
+ WAVE = 12,
+ UNICODETEXT = 13,
+ ENHMETAFILE = 14,
+ HDROP = 15,
+ LOCALE = 16,
+ DIBV5 = 17,
+ OWNERDISPLAY = 0x0080,
+ DSPTEXT = 0x0081,
+ DSPBITMAP = 0x0082,
+ DSPMETAFILEPICT = 0x0083,
+ DSPENHMETAFILE = 0x008E,
+ // "Private" formats don't get GlobalFree()'d
+ PRIVATEFIRST = 0x0200,
+ PRIVATELAST = 0x02FF,
+ // "GDIOBJ" formats do get DeleteObject()'d
+ GDIOBJFIRST = 0x0300,
+ GDIOBJLAST = 0x03FF
+ }
+
+ [Flags]
+ public enum DDEResult : uint
+ {
+ FACK = 0x8000U,
+ FBUSY = 0x4000U,
+ FDEFERUPD = 0x4000,
+ FACKREQ = 0x8000,
+ FRELEASE = 0x2000,
+ FREQUESTED = 0x1000,
+ FAPPSTATUS = 0x00ff,
+ FNOTPROCESSED = 0x0,
+ FACKRESERVED = (~(FACK | FBUSY | FAPPSTATUS)),
+ FADVRESERVED = (~(FACKREQ | FDEFERUPD)),
+ FDATRESERVED = (~(FACKREQ | FRELEASE | FREQUESTED)),
+ FPOKRESERVED = (~(FRELEASE))
+ }
+}
diff --git a/SCADA/Program/DDEDriver/DDEServer.cs b/SCADA/Program/DDEDriver/DDEServer.cs
new file mode 100644
index 0000000..aef8e01
--- /dev/null
+++ b/SCADA/Program/DDEDriver/DDEServer.cs
@@ -0,0 +1,620 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace DDEDriver
+{
+ public abstract class DdemlServer : IDisposable
+ {
+ private int _InstanceId; // DDEML instance identifier
+ private string _Service; // DDEML service name
+ private IntPtr _ServiceHandle = IntPtr.Zero; // DDEML service handle
+
+ private bool _Disposed = false;
+ private HashSet _ConversationTable = new HashSet();
+ private DdeCallback _Callback;
+
+ public DdemlServer()
+ {
+ _Callback = OnDdeCallback;
+ }
+
+ ~DdemlServer()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected void Dispose(bool disposing)
+ {
+ if (!_Disposed)
+ {
+ _Disposed = true;
+ if (IsRegistered)
+ {
+ // Unregister the service name.
+ Unregister();
+ if (disposing)
+ {
+ _ServiceHandle = IntPtr.Zero;
+ _InstanceId = 0;
+ }
+ }
+ }
+ }
+
+ public string Service
+ {
+ get { return _Service; }
+ }
+
+ public bool IsRegistered
+ {
+ get { return _ServiceHandle != IntPtr.Zero; }
+ }
+
+ internal bool IsDisposed
+ {
+ get { return _Disposed; }
+ }
+
+ public int Initialize(int afCmd)
+ {
+ int instanceId = 0;
+ Ddeml.DdeInitialize(ref instanceId, _Callback, afCmd, 0);
+ return instanceId;
+ }
+
+ public void Uninitialize()
+ {
+ Ddeml.DdeUninitialize(_InstanceId);
+ }
+
+ public string Register(string service)
+ {
+ if (IsRegistered || _InstanceId != 0)
+ {
+ throw new InvalidOperationException("AlreadyRegisteredMessage");
+ }
+ if (service == null || service.Length > Ddeml.MAX_STRING_SIZE)
+ {
+ throw new ArgumentNullException("service");
+ }
+ _Service = service;
+ _ConversationTable.Clear();
+ _InstanceId = Initialize(Ddeml.APPCLASS_STANDARD);
+ _ServiceHandle = Ddeml.DdeCreateStringHandle(_InstanceId, _Service, Ddeml.CP_WINANSI);
+ // Register the service name.
+ if (Ddeml.DdeNameService(_InstanceId, _ServiceHandle, IntPtr.Zero, Ddeml.DNS_REGISTER) == IntPtr.Zero)
+ {
+ Ddeml.DdeFreeStringHandle(_InstanceId, _ServiceHandle);
+ _ServiceHandle = IntPtr.Zero;
+ }
+ // If the service handle is null then the service name could not be registered.
+ if (_ServiceHandle == IntPtr.Zero)
+ {
+ int error = Ddeml.DdeGetLastError(_InstanceId);
+ return Ddeml.DDEGetErrorMsg(error);
+ }
+ return null;
+ }
+
+ public void Unregister()
+ {
+ Ddeml.DdeNameService(_InstanceId, _ServiceHandle, IntPtr.Zero, Ddeml.DNS_UNREGISTER);
+ // Free the service string handle.
+ Ddeml.DdeFreeStringHandle(_InstanceId, _ServiceHandle);
+ // Indicate that the service name is no longer registered.
+ _ServiceHandle = IntPtr.Zero;
+ _InstanceId = 0;
+ }
+
+ private IntPtr OnDdeCallback(int uType, ConversionFormat uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, uint dwData1, uint dwData2)
+ {
+ // Create a new transaction object that will be dispatched to a DdemlClient, DdemlServer, or ITransactionFilter.
+ // Dispatch the transaction.
+ switch (uType)
+ {
+ case Ddeml.XTYP_MONITOR:
+ switch (dwData2)
+ {
+ case Ddeml.MF_CALLBACKS:
+ {
+ // Get the MONCBSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONCBSTRUCT mon = (MONCBSTRUCT)Marshal.PtrToStructure(phData, typeof(MONCBSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnCallback(mon);
+ return IntPtr.Zero;
+ }
+ case Ddeml.MF_CONV:
+ {
+ // Get the MONCONVSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONCONVSTRUCT mon = (MONCONVSTRUCT)Marshal.PtrToStructure(phData, typeof(MONCONVSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnConversation(mon);
+ return IntPtr.Zero;
+ }
+ case Ddeml.MF_ERRORS:
+ {
+ // Get the MONERRSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONERRSTRUCT mon = (MONERRSTRUCT)Marshal.PtrToStructure(phData, typeof(MONERRSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnError(mon);
+ return IntPtr.Zero;
+ }
+ case Ddeml.MF_HSZ_INFO:
+ {
+ // Get the MONHSZSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONHSZSTRUCT mon = (MONHSZSTRUCT)Marshal.PtrToStructure(phData, typeof(MONHSZSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnString(mon);
+ return IntPtr.Zero;
+ }
+ case Ddeml.MF_LINKS:
+ {
+ // Get the MONLINKSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONLINKSTRUCT mon = (MONLINKSTRUCT)Marshal.PtrToStructure(phData, typeof(MONLINKSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnLink(mon);
+ return IntPtr.Zero;
+ }
+ case Ddeml.MF_POSTMSGS:
+ {
+ // Get the MONMSGSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONMSGSTRUCT mon = (MONMSGSTRUCT)Marshal.PtrToStructure(phData, typeof(MONMSGSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnPost(mon);
+ return IntPtr.Zero;
+ }
+ case Ddeml.MF_SENDMSGS:
+ {
+ // Get the MONMSGSTRUCT object.
+ int length = 0;
+ IntPtr phData = Ddeml.DdeAccessData(hData, ref length);
+ MONMSGSTRUCT mon = (MONMSGSTRUCT)Marshal.PtrToStructure(phData, typeof(MONMSGSTRUCT));
+ Ddeml.DdeUnaccessData(hData);
+ OnSend(mon);
+ return IntPtr.Zero;
+ }
+ }
+ break;
+ case Ddeml.XTYP_ADVDATA:
+ unsafe
+ {
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ byte* bt = stackalloc byte[Ddeml.MAX_STRING_SIZE];
+ len = Ddeml.DdeGetData(hData, bt, Ddeml.MAX_STRING_SIZE, 0);
+ byte[] bytes = new byte[len]; for (int i = 0; i < len; i++) { bytes[i] = *bt++; };
+ if (hData != IntPtr.Zero) Ddeml.DdeUnaccessData(hData);
+ return new IntPtr((int)OnAdvData(uFmt, topic, item, bytes));
+ }
+ case Ddeml.XTYP_ADVREQ:
+ unsafe
+ {
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ byte[] data = OnAdvReq(uFmt, topic, item);
+ // Create and return the data handle representing the data being advised.
+ if (data != null && data.Length > 0)
+ {
+ return Ddeml.DdeCreateDataHandle(_InstanceId, data, data.Length, 0, hsz2, uFmt, 0); ;
+ }
+ // This transaction could not be Ddeml.DDE_FACK here.
+ return IntPtr.Zero;
+ }
+ case Ddeml.XTYP_ADVSTART:
+ unsafe
+ {
+ // Get the item name from the hsz2 string handle.
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ // Get a value indicating whether an advise loop should be initiated from the subclass.
+ //AdvStart(hConv, item, uFmt, Ddeml.XTYPF_ACKREQ);
+ return OnAdvStart(uFmt, topic, item) ? new IntPtr(1) : IntPtr.Zero;
+ }
+ case Ddeml.XTYP_ADVSTOP:
+ unsafe
+ {
+ // Get the item name from the hsz2 string handle.
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ // Inform the subclass that the advise loop has been terminated.
+ //AdvStop(hConv, item, uFmt);
+ OnAdvStop(uFmt, topic, item);
+ break;
+ }
+ case Ddeml.XTYP_CONNECT:
+ unsafe
+ {
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ // Get a value from the subclass indicating whether the connection should be allowed.
+ return OnConnect(topic, new CONVCONTEXT(), true) ? new IntPtr(1) : IntPtr.Zero;
+ }
+ case Ddeml.XTYP_CONNECT_CONFIRM:
+ unsafe
+ {
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ // Create a Conversation object and add it to the hConv table.
+ _ConversationTable.Add(hConv);
+ // Inform the subclass that a hConv has been established.
+ OnConnectConfirm(topic, true);
+ break;
+ }
+ case Ddeml.XTYP_DISCONNECT:
+ {
+ // Remove the Conversation from the hConv table.
+ _ConversationTable.Remove(hConv);
+ // Inform the subclass that the hConv has been disconnected.
+ OnDisconnect(true);
+ // Return zero to indicate that there are no problems.
+ return IntPtr.Zero;
+ }
+ case Ddeml.XTYP_EXECUTE:
+ unsafe
+ {
+ // Get the command from the data handle.
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ byte* bt = stackalloc byte[Ddeml.MAX_STRING_SIZE];
+ len = Ddeml.DdeGetData(hData, bt, Ddeml.MAX_STRING_SIZE, 0);
+ string command = new string((sbyte*)bt);
+ // Send the command to the subclass and get the resul
+ return new IntPtr((int)OnExecute(topic, command.TrimEnd('\0')));
+ }
+ case Ddeml.XTYP_POKE:
+ unsafe
+ {
+ // Get the item name from the hsz2 string handle.
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ byte* bt = stackalloc byte[Ddeml.MAX_STRING_SIZE];
+ len = Ddeml.DdeGetData(hData, bt, Ddeml.MAX_STRING_SIZE, 0);
+ byte[] data = new byte[len]; for (int i = 0; i < len; i++) { data[i] = *bt++; };
+ // Send the data to the subclass and get the resul
+ return new IntPtr((int)OnPoke(uFmt, topic, item, data));
+ }
+ case Ddeml.XTYP_REQUEST:
+ unsafe
+ {
+ // Get the item name from the hsz2 string handle.
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ // Send the request to the subclass and get the resul
+ var result = OnRequest(uFmt, topic, item);
+ // Return a data handle if the subclass Ddeml.DDE_FACK the request successfully.
+ if (result != null)
+ {
+ return Ddeml.DdeCreateDataHandle(_InstanceId, result, result.Length, 0, hsz2, uFmt, 0);
+ }
+ // Return DDE_FDdeml.DDE_FNOTDdeml.DDE_FACK if the subclass did not process the command.
+ return new IntPtr(Ddeml.DDE_FNOTPROCESSED);
+ }
+ case Ddeml.XTYP_XACT_COMPLETE:
+ unsafe
+ {
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string topic = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string item = new string(pSZ);
+ OnXactComplete(uFmt, topic, item, hData, dwData1);
+ break;
+ }
+ case Ddeml.XTYP_WILDCONNECT:
+ {
+ // This library does not support wild connects.
+ return IntPtr.Zero;
+ }
+
+ case Ddeml.XTYP_ERROR:
+ {
+ // Get the error code, but do nothing with it at this time.
+ return IntPtr.Zero;
+ }
+ case Ddeml.XTYP_REGISTER:
+ unsafe
+ {
+ // Get the service name from the hsz1 string handle.
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string bas = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string inst = new string(pSZ);
+ OnRegister(bas, inst);
+ return IntPtr.Zero;
+ }
+
+ case Ddeml.XTYP_UNREGISTER:
+ unsafe
+ {
+ sbyte* pSZ = stackalloc sbyte[Ddeml.MAX_STRING_SIZE];
+ int len = Ddeml.DdeQueryString(_InstanceId, hsz1, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string bas = new string(pSZ);
+ len = Ddeml.DdeQueryString(_InstanceId, hsz2, pSZ, Ddeml.MAX_STRING_SIZE, Ddeml.CP_WINANSI);
+ string inst = new string(pSZ);
+ OnUnRegister(bas, inst);
+ return IntPtr.Zero;
+ }
+ }
+ return IntPtr.Zero;
+ }
+
+ public void Advise(string topic, string item)
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().ToString());
+ }
+ if (!IsRegistered)
+ {
+ throw new InvalidOperationException("NotRegisteredMessage");
+ }
+ if (topic == null || topic.Length > Ddeml.MAX_STRING_SIZE)
+ {
+ throw new ArgumentNullException("topic");
+ }
+ if (item == null || item.Length > Ddeml.MAX_STRING_SIZE)
+ {
+ throw new ArgumentNullException("item");
+ }
+ // Assume the topic name and item name are wild.
+ IntPtr topicHandle = topic != "*" ? Ddeml.DdeCreateStringHandle(_InstanceId, topic, Ddeml.CP_WINANSI) : IntPtr.Zero;
+ IntPtr itemHandle = item != "*" ? Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI) : IntPtr.Zero;
+ // Check the result to see if the post failed.
+ if (!Ddeml.DdePostAdvise(_InstanceId, topicHandle, itemHandle))
+ {
+ int error = Ddeml.DdeGetLastError(_InstanceId);
+ var msg = Ddeml.DDEGetErrorMsg(error); if (msg != null) { }
+ }
+ Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle);
+ Ddeml.DdeFreeStringHandle(_InstanceId, topicHandle);
+ }
+
+ public void Pause(IntPtr hConv)
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().ToString());
+ }
+ if (!IsRegistered)
+ {
+ throw new InvalidOperationException("NotRegisteredMessage");
+ }
+ // Check the result to see if the DDEML callback was disabled.
+ if (!Ddeml.DdeEnableCallback(_InstanceId, hConv, Ddeml.EC_DISABLE))
+ {
+ int error = Ddeml.DdeGetLastError(_InstanceId);
+ Ddeml.DDEGetErrorMsg(error);
+ }
+ }
+
+ public void Resume(IntPtr hConv)
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().ToString());
+ }
+ if (!IsRegistered)
+ {
+ throw new InvalidOperationException("NotRegisteredMessage");
+ }
+ // Check the result to see if the DDEML callback was enabled.
+ if (!Ddeml.DdeEnableCallback(_InstanceId, hConv, Ddeml.EC_ENABLEALL))
+ {
+ int error = Ddeml.DdeGetLastError(_InstanceId);
+ Ddeml.DDEGetErrorMsg(error);
+ }
+ }
+
+ public void Disconnect(IntPtr hConv)
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().ToString());
+ }
+ if (!IsRegistered)
+ {
+ throw new InvalidOperationException("NotRegisteredMessage");
+ }
+ if (_ConversationTable.Contains(hConv))
+ {
+ // Terminate the hConv.
+ Ddeml.DdeDisconnect(hConv);
+ // Remove the Conversation from the hConv table.
+ _ConversationTable.Remove(hConv);
+ }
+ }
+
+ public void Disconnect()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().ToString());
+ }
+ if (!IsRegistered)
+ {
+ throw new InvalidOperationException("NotRegisteredMessage");
+ }
+ // Terminate all conversations.
+ foreach (IntPtr hConv in _ConversationTable)
+ {
+ Ddeml.DdeDisconnect(hConv);
+ }
+ // clear the hConv table.
+ _ConversationTable.Clear();
+ }
+
+ // Démarre une transaction Advise.
+ public bool AdvStart(IntPtr hConv, string item, ConversionFormat wFormat, int wFlag)
+ {
+ if (!IsRegistered)
+ return false;
+ int res = 0;
+ // Création de la chaîne DDE de l'élément.
+ IntPtr hszItem = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI);
+ if ((hszItem == IntPtr.Zero) && (item.Length != 0))
+ return false;
+ // Exécution de la transaction.
+ Ddeml.DdeClientTransaction(null, 0, hConv, hszItem, wFormat, (wFlag == Ddeml.XTYPF_ACKREQ) ?
+ (Ddeml.XTYP_ADVSTARTACKREQ) : ((wFlag == Ddeml.XTYPF_NODATA) ? (Ddeml.XTYP_ADVSTARTNODATA) : (Ddeml.XTYP_ADVSTART)), Ddeml.TIMEOUT_ASYNC, ref res);
+ // Libération de la chaîne DDE.
+ if (hszItem != IntPtr.Zero)
+ Ddeml.DdeFreeStringHandle(_InstanceId, hszItem);
+ return res != 0;
+ }
+
+ // Arrête une transaction Advise.
+ public void AdvStop(IntPtr hConv, string item, ConversionFormat wFormat)
+ {
+ if (!IsRegistered)
+ return;
+ // Création de la chaîne DDE de l'élément.
+ IntPtr hszItem = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI);
+ if ((hszItem == IntPtr.Zero) && (item.Length != 0))
+ return;
+ // Exécution de la transaction.
+ int res = 0;
+ Ddeml.DdeClientTransaction(null, 0, hConv, hszItem, wFormat, Ddeml.XTYP_ADVSTOP, Ddeml.TIMEOUT_ASYNC, ref res);
+ // Libération de la chaîne DDE.
+ if (hszItem != IntPtr.Zero)
+ Ddeml.DdeFreeStringHandle(_InstanceId, hszItem);
+ }
+
+
+ protected virtual DDEResult OnAdvData(ConversionFormat uFormat, string topic, string item, byte[] data)
+ {
+ return DDEResult.FNOTPROCESSED;
+ }
+
+ protected virtual byte[] OnAdvReq(ConversionFormat uFormat, string topic, string item)
+ {
+ return null;
+ }
+
+ protected virtual bool OnAdvStart(ConversionFormat uFormat, string topic, string item)
+ {
+ return true;
+ }
+
+ protected virtual void OnAdvStop(ConversionFormat uFormat, string topic, string item)
+ {
+ }
+
+ protected virtual bool OnConnect(string topic, CONVCONTEXT context, bool sameInstance)
+ {
+ return true;
+ }
+
+ protected virtual void OnConnectConfirm(string topic, bool sameInstance)
+ {
+ }
+
+ protected virtual void OnDisconnect(bool sameInstance)
+ {
+ }
+
+ protected virtual void OnError(ushort errorCode)
+ {
+ }
+
+ protected virtual DDEResult OnExecute(string topic, string command)
+ {
+ return DDEResult.FNOTPROCESSED;
+ }
+
+ protected virtual byte[] OnRequest(ConversionFormat ufmt, string topic, string item)
+ {
+ return null;
+ }
+
+ protected virtual DDEResult OnPoke(ConversionFormat uFormat, string topic, string item, byte[] data)
+ {
+ return DDEResult.FNOTPROCESSED;
+ }
+
+ protected virtual void OnRegister(string baseServiceName, string instanceServiceName)
+ {
+ }
+
+ protected virtual void OnUnRegister(string baseServiceName, string instanceServiceName)
+ {
+ }
+
+ protected virtual void OnXactComplete(ConversionFormat uFormat, string topic, string item, IntPtr data, uint transactionID)
+ {
+ }
+
+ protected virtual void OnCallback(MONCBSTRUCT mon)
+ {
+ }
+
+ protected virtual void OnConversation(MONCONVSTRUCT mon)
+ {
+ }
+
+ protected virtual void OnError(MONERRSTRUCT mon)
+ {
+ }
+
+ protected virtual void OnString(MONHSZSTRUCT mon)
+ {
+ }
+
+ protected virtual void OnLink(MONLINKSTRUCT mon)
+ {
+ }
+
+ protected virtual void OnPost(MONMSGSTRUCT mon)
+ {
+ }
+
+ protected virtual void OnSend(MONMSGSTRUCT mon)
+ {
+ }
+ ///
+ /// This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific.
+
+ } // class
+
+}
diff --git a/SCADA/Program/DDEDriver/Properties/AssemblyInfo.cs b/SCADA/Program/DDEDriver/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..0743eeb
--- /dev/null
+++ b/SCADA/Program/DDEDriver/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DDEDriver")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DDEDriver")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6d9e7bae-896d-4ac9-a3c3-2b8e01c96fe0")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SCADA/Program/DataExchange.sln b/SCADA/Program/DataExchange.sln
index 8caf0ec..48ca860 100644
--- a/SCADA/Program/DataExchange.sln
+++ b/SCADA/Program/DataExchange.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2020
+VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OPCDriver", "OPCDriver\OPCDriver.csproj", "{054DB7AC-EB2C-439F-80D4-63E823B279C8}"
EndProject
@@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmronPlcDriver", "OmronPlcD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ABPLCDriver", "ABPLCReader\ABPLCDriver.csproj", "{02EA8F3A-29F0-4E11-B4F6-3BF87242D99F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DDEDriver", "DDEDriver\DDEDriver.csproj", "{14938033-7870-477D-925C-A447933A44E7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -343,6 +345,20 @@ Global
{02EA8F3A-29F0-4E11-B4F6-3BF87242D99F}.Release|x64.Build.0 = Release|x64
{02EA8F3A-29F0-4E11-B4F6-3BF87242D99F}.Release|x86.ActiveCfg = Release|Any CPU
{02EA8F3A-29F0-4E11-B4F6-3BF87242D99F}.Release|x86.Build.0 = Release|Any CPU
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|Itanium.ActiveCfg = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|x64.ActiveCfg = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|x86.ActiveCfg = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Debug|x86.Build.0 = Debug|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|Any CPU.ActiveCfg = Release|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|Itanium.ActiveCfg = Release|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|Mixed Platforms.Build.0 = Release|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|x64.ActiveCfg = Release|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|x86.ActiveCfg = Release|x86
+ {14938033-7870-477D-925C-A447933A44E7}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SCADA/dll/DDEDriver.dll b/SCADA/dll/DDEDriver.dll
new file mode 100644
index 0000000..f6494f2
Binary files /dev/null and b/SCADA/dll/DDEDriver.dll differ