forked from tsai/SharpSCADA
10 changed files with 1961 additions and 1 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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用常量定义
|
||||
|
/**/ |
||||
|
/// <summary>
|
||||
|
/// ///////////////////////////////////////////////////////////////////////////
|
||||
|
/// /***** conversation states (usState) *****/
|
||||
|
/**/ |
||||
|
/// </summary>
|
||||
|
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<IGroup> _groups = new List<IGroup>(3); |
||||
|
public IEnumerable<IGroup> 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<short> _changedList; |
||||
|
public List<short> ChangedList |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _changedList; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
List<ITag> _items; |
||||
|
public IEnumerable<ITag> Items |
||||
|
{ |
||||
|
get { return _items; } |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
public IDataServer Server |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _server; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Dictionary<short, string> _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<short>(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<TagMetaData> items) |
||||
|
{ |
||||
|
int count = items.Count; |
||||
|
if (_items == null) |
||||
|
{ |
||||
|
_items = new List<ITag>(count); |
||||
|
_mapping = new Dictionary<short, string>(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<ITag> tags) |
||||
|
{ |
||||
|
if (_items == null) |
||||
|
{ |
||||
|
_items = new List<ITag>(); |
||||
|
} |
||||
|
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<ITag, object> 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<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<int>(int.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<short>(short.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<uint> ReadUInt32(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<uint>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<uint>(uint.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<ushort> ReadUInt16(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<ushort>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<ushort>(ushort.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<byte>(byte.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<float>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<float>(float.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<bool>(bool.Parse(data), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
string data = _plcReader.Request(_mapping[(short)address.Start]); |
||||
|
return string.IsNullOrEmpty(data) ? new ItemData<string>(null, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<string>(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; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform> |
||||
|
<ProductVersion>8.0.30703</ProductVersion> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
<ProjectGuid>{14938033-7870-477D-925C-A447933A44E7}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>DDEDriver</RootNamespace> |
||||
|
<AssemblyName>DDEDriver</AssemblyName> |
||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> |
||||
|
<TargetFrameworkProfile>Client</TargetFrameworkProfile> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> |
||||
|
<PlatformTarget>x86</PlatformTarget> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> |
||||
|
<PlatformTarget>x86</PlatformTarget> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup> |
||||
|
<StartupObject /> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="System" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="DDEDriver.cs" /> |
||||
|
<Compile Include="DDELib.cs" /> |
||||
|
<Compile Include="DDEServer.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="C:\Users\ADOLF\Documents\GitHub\SharpSCADA\SCADA\SourceCode\DataService\DataService.csproj"> |
||||
|
<Project>{8965e389-6466-4b30-bd43-83c909044637}</Project> |
||||
|
<Name>DataService</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
||||
|
Other similar extension points exist, see Microsoft.Common.targets. |
||||
|
<Target Name="BeforeBuild"> |
||||
|
</Target> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -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)) |
||||
|
} |
||||
|
} |
||||
@ -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<IntPtr> _ConversationTable = new HashSet<IntPtr>(); |
||||
|
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) |
||||
|
{ |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific.
|
||||
|
|
||||
|
} // class
|
||||
|
|
||||
|
} |
||||
@ -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")] |
||||
Binary file not shown.
Loading…
Reference in new issue