I have a program that currently works as a chat server. It's a turn based card game with a chat box to communicate with the opponent. The chat stuff all works over the connection, but I'm to the point where I need to start sending certain cards to the opponent, so that when I play a card, my opponent will see it on his screen. I want the client computer to receive the object or collection of objects, figure out what the card type is based on its properties, and then put the card in the correct location. The sending and receiving part is what I don't understand how to accomplish. From what I've read, this requires serialization and I just don't know where to begin with this. Please assist! I'm using visual studio.
Answering my own question again... I ended up creating a new class called card container which contains the cardType as a string and the card id as a int. There are also other properties for the card container so I know where the card goes. Then I serialized the card container (which is 'cc' in the following code) and sent it as follows:
Dim cc as new CardContainer
cc.id = card.id
cc.cardType = card.GetType.ToString
cc.discard = true
Dim bf As New BinaryFormatter
Dim ms As New MemoryStream
Dim b() As Byte
'serializes to the created memory stream
bf.Serialize(ms, cc)
'converts the memory stream to the byte array
b = ms.ToArray()
ms.Close()
'sends the byte array to client or host
SyncLock mobjClient.GetStream
mobjClient.GetStream.Write(b, 0, b.Length)
End SyncLock
The client listens on its TCPClient for anything at all and picks up the card with the following code:
Dim intCount As Integer
Try
SyncLock mobjClient.GetStream
'returns the number of bytes to know how many to read
intCount = mobjClient.GetStream.EndRead(ar)
End SyncLock
'if no bytes then we are disconnected
If intCount < 1 Then
MarkAsDisconnected()
Exit Sub
End If
Dim bf As New BinaryFormatter
Dim cc As New CardContainer
'moves the byte array found in arData to the memory stream
Dim ms As New MemoryStream(arData)
'cuts off the byte array in the memory stream after all the received data
ms.SetLength(intCount)
'the new cardContainer will now be just like the sent one
cc = bf.Deserialize(ms)
ms.Close()
'starts the listener again
mobjClient.GetStream.BeginRead(arData, 0, 3145728, AddressOf DoRead, Nothing)
Depending on what data the cardcontainer has determines which method the client now calls. For example, when this is received, the created cc is passed to my CardReceived method which then has a bunch of if, elseif statements. One of those would be
ElseIf cc.discard = true then
'makes a new card from the id and cardType properties
dim temp as new object = MakeCard
'discard method will find the correct card in the Host's known hand and discard it to the correct pile
Discard(temp)
Related
i use readfile to read data from serial port by api instead of WinComm. it works okay but hang on data receive when other side does not sent data, here are some codes:
'----------on port connect section---------------
With cTimeOuts
.ReadIntervalTimeout = 20
.ReadTotalTimeoutMultiplier = 20
.ReadTotalTimeoutConstant = 20
.WriteTotalTimeoutMultiplier = 20
.WriteTotalTimeoutConstant = 20
End With
rtLng = SetCommTimeouts(hPort, cTimeOuts)
'----------on data receive section---------------
With dOverlapped
.Internal = 0
.InternalHigh = 0
.offset = 0
.OffsetHigh = 0
.hEvent = 0
End With
call ReadFile(hPort, BytesBuffer(0), UBound(BytesBuffer) + 1, dwBytesRead, dOverlapped)
i tried:
a. set ReadIntervalTimeout to MAXDWORD and ReadTotalTimeoutMultiplier & ReadTotalTimeoutConstant both to 0, it can avoid hang on receive data, but the receive data will incomplete for random occurred, so this is not my option.
b. some example code on internet using
Call ReadFile(hPort, BytesBuffer(0), UBound(BytesBuffer) + 1, dwBytesRead, 0) using 0 instead of Overlapped setting, but the program crashed.
so i don't know using what to fix this, another thread? or Overlapped? some actual vb6 code is better cause i'm not familiar with this section, thank you!
Since upgrading MQ to "IBM MQ Explorer V9.1", the XMS libraries that always worked in previous versions have started behaving differently.
Essentially, the code still recognises the messages as IBytesMessage type, and successfully writes them to file via a Byte array, but the file itself, which is a split zip file, fails to reconstitute itself.
Here's the lions share of that code:
Dim FactoryFactory As XMSFactoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ)
'Create a ConnectionFactory Object
cfConnectionFactory = FactoryFactory.CreateConnectionFactory()
'this variable will contain the full path of any file downloaded from MQ
Dim strMQMessageOutputFileDestinationFilePath As String = ""
'This variable will be used to evaluate whether the MQ Message Output file exists
Dim fiMQMessageOutputFile As FileInfo = Nothing
'Set various Connection Factory properties
cfConnectionFactory.SetStringProperty(XMSC.WMQ_HOST_NAME, Me.HostName)
cfConnectionFactory.SetIntProperty(XMSC.WMQ_PORT, 1414)
cfConnectionFactory.SetStringProperty(XMSC.WMQ_CHANNEL, "SYSTEM.DEF.SVRCONN")
cfConnectionFactory.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, 1)
cfConnectionFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, Me.QueueManager)
cfConnectionFactory.SetIntProperty(XMSC.WMQ_BROKER_VERSION, 0)
'Create a new Iconnection object via the Connection Factory
connection = cfConnectionFactory.CreateConnection()
'Create a sesion via the Connection Object, using ClientAcknowledge mode
'ClientAcknowledge is being used because it allows us to control precisely
'when a message should be removed from the queue
session = connection.CreateSession(False, AcknowledgeMode.ClientAcknowledge)
'Create a destination using the Session Object
destination = session.CreateQueue(Me.mstrDestinationURI)
destination.SetIntProperty(XMSC.DELIVERY_MODE, 1)
'Create a consumer using the Session & Destination Objects
Consumer = session.CreateConsumer(destination)
connection.Start()
'IMessage is the base class that is returned from the Consumer's Receive method
Dim recvMsg As IMessage = Nothing
' Retrieve message from Queue
recvMsg = Consumer.ReceiveNoWait
strFileNameFromMsg = If(Not recvMsg.PropertyExists("fileName"), "",
recvMsg.GetStringProperty("fileName"))
If TypeOf (recvMsg) Is IBytesMessage Then
'Binary Message
Dim msg As IBytesMessage = CType(recvMsg, IBytesMessage)
Dim buffer(msg.BodyLength) As Byte
msg.ReadBytes(buffer)
Dim content As String = Text.Encoding.UTF8.GetString(buffer)
'The PrepareDestinationFile Function will generate a unique file name for the new file
'and ensure that the file does not already exist on the drive
strMQMessageOutputFileDestinationFilePath = PrepareDestinationFile(strFileNameFromMsg)
'A FileStream object is needed to write a binary array to a file
Dim fsZipFile As FileStream = New FileStream(strMQMessageOutputFileDestinationFilePath, FileMode.Create)
'Write the contents of the Byte Array to the File via the FileStream object
fsZipFile.Write(buffer, 0, buffer.Length)
fsZipFile.Close()
End If
So, the code doesn't throw any kind of exception - the code still recognises the messages as IBytesMessage, but the files won't unzip correctly.
Oddly, If we use rfhutilc.exe, we can manually pull files provided we set the Write options as No Headers and not Include MQMD - but the code above always worked in the previous version of MQ / XMS
Any assistance you can provide would be very much appreciated.
I am trying to create a JPOS web service using JAVA. Here is the code I am using.
GenericPackager packager;
packager = new GenericPackager("ISO_8583_1987.xml");
org.jpos.util.Logger logger = new org.jpos.util.Logger();
logger.addListener(new SimpleLogListener(System.out));
((LogSource)packager).setLogger(logger, "debug");
ISOMsg isoMsg = new ISOMsg();
isoMsg.setPackager(packager);
isoMsg.unpack(b);
printISOMessage(isoMsg);
The problem is all the way in the beginning on hex2byte method.
Length of everything up until Datafield 19 is in multiples of 2. Datafield 19 in my packager is IFB_NUMERIC with length 3. When I send message 02000000200000000000177 (Only datafield 19 set), hex2byte appends 0 to the left to make the message length even. Because of that, MTI comes as 0020 instead of 0200. This also messes up the bitmap since the bit set for field 17-18-19-20 is not bitmap for 21-22-23-24. As expected, when I run this, unpack method shows bit 23 as 017. This of course works fine if I send message 020000002000000000001770 (additional 0 at the end).
Should I be using something other than ISOUtil.hex2byte?
I am able to add call attrs using 'Genesyslab.Platform.Voice.Protocols.TServer.Requests.Userdata.RequestAttachUserData' when the call is online but how to do when the call is dropped?
I found this in WDE
void SelectDispositionCodeSetAttachedData(string dispositionCodeValueName);
//
// Summary:
// Update or add the keys of the specificed KeyValueCollection in the attached data
// of the interaction . The current list of attached data can then be retrieved
// using GetAttachedData. If the interaction media type is 'voice' or 'instant message'
// and the interaction is not released the added/updated values are immediately
// committed to T/SIP Server. If the interaction media type is 'voice' or 'instant
// message' and the interaction is released the added/updated values are sent to
// T/SIP Server as a UserEvent when the interaction is marked done (programmatic?aly
// or by Agent). If it is an eServices interaction (e-mail, chat, etc.) and the
// interaction is still handled by the agent the added/updated values are immediately
// committed to Interaction Server. After e-Services interaction is released, no
// further programmatical update is committed to Interaction Server. For all interaction
// types any attached data programmatical update applied after interaction release
// is not reflected in UI controls such as 'Case information'.
This is my code:
Genesyslab.Platform.Commons.Collections.KeyValueCollection keyValueCollectionUpDate = new Genesyslab.Platform.Commons.Collections.KeyValueCollection();
keyValueCollectionUpDate.Add("Business Result", "Platform: Business Result");
keyValueCollectionUpDate.Add("StrAttribute1", "AttachedData.Business Result"); RequestAttachUserData requestAttachUserData= RequestAttachUserData.Create("7012", GetConnectionID(ExtractedArtributes[1][0].Value.ToString()), keyValueCollectionUpDate); IMessage respondingEvent2=tserverProtocol.Request(requestAttachUserData);
Need to add call attts after the call is dropped
You can't update attached data when the call is dropped.
I do a workaround by using WDE itself to attach data when agent clicks 'mark done' in WDE custom command.
The way to attach data to voice calls after disconnect, is to send a UserEvent. This does require the AfterCallWork state to be enabled in your environment. You have already mentioned understanding how to insert Commands into the Command Chain. This example Execute function of a command can be inserted into the "BundleClose" Command Chain, prior to the "Close" Command.
This sample is in VB, apologies, but I guess you can easily convert to c#.
Public Function Execute(ByVal parameters As IDictionary(Of String, Object), ByVal progress As IProgressUpdater) As Boolean Implements IElementOfCommand.Execute
' To go to the main thread
If Application.Current.Dispatcher IsNot Nothing AndAlso Not Application.Current.Dispatcher.CheckAccess() Then
Dim result As Object = Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, New ExecuteDelegate(AddressOf Execute), parameters, progress)
Return CBool(result)
Else
Dim interactionsBundle As IInteractionsBundle = Nothing
Dim interaction As IInteraction = Nothing
interactionsBundle = parameters("CommandParameter")
interaction = interactionsBundle.MainInteraction
Dim channel As Enterprise.Model.Channel.IClientChannel = interaction.EntrepriseInteractionCurrent.ActiveChannel 'agent.Channel
Dim protocol As IProtocol = channel.Protocol
Dim kvp As Platform.Commons.Collections.KeyValueCollection = New Platform.Commons.Collections.KeyValueCollection()
kvp.Add("keyname", "keyvalue")
Dim userevent As Platform.Voice.Protocols.TServer.CommonProperties = Platform.Voice.Protocols.TServer.CommonProperties.Create()
userevent.UserData = kvp
Dim connID As Platform.Voice.Protocols.ConnectionId = Nothing
Dim interactionVoice as IInteractionVoice = TryCast(interaction, IInteractionVoice)
If interactionVoice IsNot Nothing Then
userevent.UserEvent = Platform.Voice.Protocols.TServer.Events.EventUserEvent.MessageId
connID = New Platform.Voice.Protocols.ConnectionId(interactionVoice.TConnectionId)
Dim strDN As String = Nothing
'ensure the correct DN is passed when attaching reason codes, in case agent is logged into multiple DNs
Dim devices() As Enterprise.Model.Device.IDevice = interactionVoice.Device
Dim device As Enterprise.Core.DN = devices(0)
strDN = device.Name
userevent.ThisDN = strDN
userevent.ConnID = connID
Dim req = Platform.Voice.Protocols.TServer.Requests.Special.RequestSendEvent.Create()
req.UserEvent = userevent
'send request
protocol.Send(req)
End If
End If
End Function
Solution (kinda):
Turns out this impersonation with .NET's security only allows application-level access. Since the COM object is at the system level, the impersonated user still cannot instantiate it. I figured this out by right-clicking the executable and selecting "Run As...", the program functioned fine. I found out that launches the program with system access (assuming the user you are running it with has those credentials). Now I am in the process of creating an external program that will launch this application using this method.
Thanks for the tips :D
I have a windows XP installation on a virtual machine. It is part of my domain, but the logged in user is a local user only. Obviously, if I try to access a network share it will prompt for a user/password:
The program I am testing out on the virtual machine uses a COM object to interface with data from another program. If I do not impersonate, I get errors because I do not have the proper credentials.
I did some research into the matter and found a number of websites that had a decent amount of VB.NET information. The problem I am having with the code I wrote is I can access the network resources, but I cannot instantiate the COM object.
If I fill and submit the credential prompt (above) before attempting to instantiate it, it works fine. That leads me to believe there must be something that the WinXP credential prompt is doing that I am not. Below is the code I am using for Impersonation:
Public Sub BeginImpersonation()
Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Const SecurityImpersonation As Integer = 2
Dim win32ErrorNumber As Integer
_tokenHandle = IntPtr.Zero
_dupeTokenHandle = IntPtr.Zero
If Not LogonUser(_username, _domainname, _password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, _tokenHandle) Then
win32ErrorNumber = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Throw New ImpersonationException(win32ErrorNumber, GetErrorMessage(win32ErrorNumber), _username, _domainname)
End If
If Not DuplicateToken(_tokenHandle, SecurityImpersonation, _dupeTokenHandle) Then
win32ErrorNumber = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
CloseHandle(_tokenHandle)
Throw New ImpersonationException(win32ErrorNumber, "Unable to duplicate token!", _username, _domainname)
End If
Dim newId As New System.Security.Principal.WindowsIdentity(_dupeTokenHandle)
_impersonatedUser = newId.Impersonate()
_impersonating = True
End Sub
I have also tried sending different flags to the impersonator method, but nothing seems to be working. Here are the different flags I found:
Enum LOGON32_LOGON
INTERACTIVE = 2
NETWORK = 3
BATCH = 4
SERVICE = 5
UNLOCK = 7
NETWORK_CLEARTEXT = 8
NEW_CREDENTIALS = 9
End Enum
Enum LOGON32_PROVIDER
[DEFAULT] = 0
WINNT35 = 1
WINNT40 = 2
WINNT50 = 3
End Enum
Enum SECURITY_LEVEL
Anonymous = 0
Identification = 1
Impersonation = 2
Delegation = 3
End Enum
I have run into this before, and used two different soloution - the easiest was using a third party app: TqcRunas: http://www.quimeras.com/Products/products.asp which allows you to package the required creentials in an encrypted file. However is a pain if the password is forced to expire.
The other solution that I have used is to call a new process with alternative credentials:
Dim myProcessStartInfo As ProcessStartInfo = New ProcessStartInfo
With myProcessStartInfo
.FileName = "file path and name"
.Domain = "domainname"
.UserName = "username"
'password needs to be a SerureString
Using NewPassword As New Security.SecureString
With NewPassword
For Each c As Char In "password".ToCharArray
.AppendChar(c)
Next c
.MakeReadOnly()
End With
.Password = NewPassword.Copy
End Using
'UseShellExecute must be false for impersonated process
.UseShellExecute = False
End With
Using Process As New System.Diagnostics.Process
With Process
.StartInfo = myProcessStartInfo
.Start()
End With
End Using
With your definitions, I use
LogonUser(_username, _domainname, _password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, _tokenHandle)
in my code that is authenticating across the network. I am impersonating a local user on the remote box, as you are. It was so long ago that I don't remember the rationale for using these values, however.
I do a similar thing to map network drives for copying files between machines. I didn't write the code but it's pretty much the same as yours, except for two things:
After the Impersonate method returns I close both tokens using the CloseHandle routine, before I exit my impersonator method.
At the top of the impersonator the first thing that happens is a call to RevertToSelf, presumably to cancel any previous impersonation.
I don't know if they would make a difference but it's worth a try. Here are the relevant declarations:
Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long