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