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