Socket接收的数据用WritePrinter写入打印机被分为若干个文件错误
我用Socket方式接收一个tcp数据包存为byte数据,每次接收1024个字节,然后将byte转为16进制进行内容替换后再转为byte数据,再将byte数据用OpenPrinter、StartDocPrinter、StartPagePrinter、WritePrinter、EndPagePrinter、EndDocPrinter、ClosePrinter等Api写入打印机,但现在遇到一个问题,它会把原本同一数据写入到打印机驱动里就变成了若干个1024字节的任务,我是想让它在打印队列里还是一个完整的任务应该怎么做?这个数据可以通过判断接收数据的头部特征十六进制代码52454D4F5445315449就打开打印机句柄并写入打印机队列任务,如果数据里包含结尾特征十六进制代码4A450100001B000000就结束打印并关闭打印机句柄,而没有发现这两个代码的数据就一直让它追加写入同一个打印任务using System.Net.Sockets;
using System.Net;
using System.Runtime.InteropServices;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
public static extern bool OpenPrinter( string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
private static bool SendBytesToPrinter(string printerName, IntPtr intptrBytes, Int32 count)
{
Int32 error = 0, written = 0;
IntPtr intptrPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false;
di.pDocName = "Test Document";
di.pDataType = "RAW";
if (OpenPrinter(printerName.Normalize(), out intptrPrinter, IntPtr.Zero))
{
if (StartDocPrinter(intptrPrinter, 1, di))
{
if (StartPagePrinter(intptrPrinter))
{
bSuccess = WritePrinter(intptrPrinter, intptrBytes, count, out written);
EndPagePrinter(intptrPrinter);
}
EndDocPrinter(intptrPrinter);
}
ClosePrinter(intptrPrinter);
}
if (bSuccess == false)
{
error = Marshal.GetLastWin32Error();
}
return bSuccess;
}
Socket _socket;
Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception ee)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
while (true)
{
Socket sockClient = _socket.Accept();
while (true)
{
byte[] Receivedata = new byte;
int length = -1;
try
{
length = sockClient.Receive(Receivedata);
}
catch
{
break;
}
if (length == 0)
{
sockClient.Close();
break;
}
else
{
string data = BitConverter.ToString(Receivedata);
data = data.Replace("-", "");
int Data_head = data.IndexOf("52454D4F5445315449");
int End_data = data.IndexOf("4A450100001B000000");
if (data.Contains("804420FB00144008020820820040411101"))
{
data = data.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
string printer_name = "Printer Name";
byte[] outdata = Enumerable.Range(0, data.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(data.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
if (pUnmanagedBytes != 0)
{
SendBytesToPrinter(printer_name, pUnmanagedBytes, outdata.Length);
}
}
}
}
}
else
{
_socket.Close();
}
}
}
}
哇,这个问题绝对是打破了我的鱼缸。看来您需要解决的是数据接收和处理的问题,以确保它们能够作为一个完整的打印任务发送到打印机,而不是被拆分成多个小任务。让我们来“游”过这些代码,看看怎么优化吧!
1. **数据接收和拼接:** 首先,你需要确保所有接收到的数据都能正确拼接起来,直到检测到结束标志。
2. **文档的开始和结束:** 在接收到文档开始标志(52454D4F5445315449)时开启打印任务,在接收到文档结束标志(4A450100001B000000)时结束打印任务。
3. **统一处理逻辑:** 需要在接收数据的过程中进行拼接和判断,只在最终准备好数据后再进行打印。
修改后的代码示例如下:
using System.Net.Sockets;
using System.Net;
using System.Runtime.InteropServices;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, int level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);
private static bool SendBytesToPrinter(string printerName, IntPtr intptrBytes, int count)
{
int error = 0, written = 0;
IntPtr intptrPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false;
di.pDocName = "Test Document";
di.pDataType = "RAW";
if (OpenPrinter(printerName.Normalize(), out intptrPrinter, IntPtr.Zero))
{
if (StartDocPrinter(intptrPrinter, 1, di))
{
if (StartPagePrinter(intptrPrinter))
{
bSuccess = WritePrinter(intptrPrinter, intptrBytes, count, out written);
EndPagePrinter(intptrPrinter);
}
EndDocPrinter(intptrPrinter);
}
ClosePrinter(intptrPrinter);
}
if (bSuccess == false)
{
error = Marshal.GetLastWin32Error();
}
return bSuccess;
}
Socket _socket;
Dictionary<string, byte[]> dicSocketData = new Dictionary<string, byte[]>();
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception ee)
{
MessageBox.Show("连接失败: " + ee.Message);
return;
}
_socket.Listen(10);
Task.Run(() => AcceptClients());
}
else
{
_socket.Close();
}
}
private void AcceptClients()
{
while (true)
{
Socket sockClient = _socket.Accept();
Task.Run(() => ReceiveData(sockClient));
}
}
private void ReceiveData(Socket sockClient)
{
byte[] buffer = new byte;
int length;
string clientEndpoint = sockClient.RemoteEndPoint.ToString();
List<byte> receivedData = new List<byte>();
while ((length = sockClient.Receive(buffer)) > 0)
{
receivedData.AddRange(buffer.Take(length));
string dataHex = BitConverter.ToString(receivedData.ToArray()).Replace("-", "");
int dataHeadIndex = dataHex.IndexOf("52454D4F5445315449");
int endDataIndex = dataHex.IndexOf("4A450100001B000000");
if (dataHeadIndex != -1 && endDataIndex != -1)
{
string processedDataHex = dataHex.Substring(dataHeadIndex, endDataIndex - dataHeadIndex + 18);
// 替换特定的数据
if (processedDataHex.Contains("804420FB00144008020820820040411101"))
{
processedDataHex = processedDataHex.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 将16进制字符串转换为字节数组
byte[] docBytes = Enumerable.Range(0, processedDataHex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(processedDataHex.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(docBytes.Length);
Marshal.Copy(docBytes, 0, pUnmanagedBytes, docBytes.Length);
if (pUnmanagedBytes != IntPtr.Zero)
{
SendBytesToPrinter("Printer Name", pUnmanagedBytes, docBytes.Length);
}
// 清空已处理的数据
receivedData.Clear();
}
}
sockClient.Close();
}
}
}
### 修改说明:
1. **异步接收数据:** 使用`Task.Run`来接收和处理数据,这样可以有效避免阻塞主线程。
2. **数据拼接:** 将接收到的数据添加到`List<byte>`中,并在检测到数据头部和结尾时进行处理。
3. **错误处理:** 增加错误处理输出,便于调试和定位问题。
通过以上修改,您应该能够将接收到的TCP数据包按预期合并成一个完整的打印任务,发送到打印机。希望这个答案能帮到您,让您的打印任务顺利“游”到打印机上!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-20 11:55
哇,这个问题绝对是打破了我的鱼缸。看来您需要解决的是数据接收和处理的问题,以确保它们能够作为一个完整 ...
不能拼接完成再打印,因为拼接完成数据会很大,有些文件大小在1G以上,有的5G大小,这样需要很长的拼接时间,而且会出现内存错误,所以需要边接收边替换边打印 cxnet 发表于 2024-07-20 12:03
不能拼接完成再打印,因为拼接完成数据会很大,有些文 ...
明白了,既然拼接整合成完整数据会引发内存错误,我们需要在接收数据的过程中,以流式处理的方式尽量保证数据连续性,并适当管理打印任务的边界。这意味着需要合理地在数据包之间维持打印任务状态。
要解决这个问题,可以考虑以下步骤:
1. **使用全局状态标记任务的开始和结束**:利用状态变量记录是否正在进行打印任务。
2. **在接收数据时检查特征代码**:根据特征代码来判断是否开启或关闭打印任务。
3. **按需控制打印任务的启动和结束**:确保在正确的时机调用 `StartDocPrinter` 和 `EndDocPrinter`。
下面是修改后的代码示例:
using System.Net.Sockets;
using System.Net;
using System.Runtime.InteropServices;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
private static bool SendBytesToPrinter(string printerName, IntPtr intptrBytes, int count)
{
IntPtr intptrPrinter;
DOCINFOA di = new DOCINFOA();
di.pDocName = "Test Document";
di.pDataType = "RAW";
if (OpenPrinter(printerName.Normalize(), out intptrPrinter, IntPtr.Zero))
{
if (StartDocPrinter(intptrPrinter, 1, di))
{
if (StartPagePrinter(intptrPrinter))
{
int written;
bool bSuccess = WritePrinter(intptrPrinter, intptrBytes, count, out written);
EndPagePrinter(intptrPrinter);
EndDocPrinter(intptrPrinter);
ClosePrinter(intptrPrinter);
return bSuccess;
}
EndDocPrinter(intptrPrinter);
}
ClosePrinter(intptrPrinter);
}
return false;
}
private Socket _socket;
private Dictionary<Socket, (IntPtr PrinterHandle, bool IsPrinting)> dicSocket = new Dictionary<Socket, (IntPtr PrinterHandle, bool IsPrinting)>();
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
Task.Run(() =>
{
while (true)
{
Socket sockClient = _socket.Accept();
dicSocket = (IntPtr.Zero, false);
Task.Run(() => HandleClient(sockClient));
}
});
}
else
{
_socket.Close();
}
}
private void HandleClient(Socket sockClient)
{
byte[] buffer = new byte;
while (true)
{
int length;
try
{
length = sockClient.Receive(buffer);
}
catch
{
break;
}
if (length == 0)
{
sockClient.Close();
break;
}
else
{
ProcessData(sockClient, buffer.Take(length).ToArray());
}
}
}
private void ProcessData(Socket client, byte[] data)
{
string hexData = BitConverter.ToString(data).Replace("-", "");
IntPtr printerHandle = dicSocket.PrinterHandle;
bool isPrinting = dicSocket.IsPrinting;
if (hexData.Contains("52454D4F5445315449") && !isPrinting)
{
// 开始打印新任务
OpenPrinter("Printer Name", out printerHandle, IntPtr.Zero);
StartDocPrinter(printerHandle, 1, new DOCINFOA { pDocName = "Test Document", pDataType = "RAW" });
StartPagePrinter(printerHandle);
dicSocket = (printerHandle, true);
}
if (hexData.Contains("4A450100001B000000") && isPrinting)
{
// 结束打印任务
EndPagePrinter(printerHandle);
EndDocPrinter(printerHandle);
ClosePrinter(printerHandle);
dicSocket = (IntPtr.Zero, false);
}
// 替换特定内容
if (hexData.Contains("804420FB00144008020820820040411101"))
{
hexData = hexData.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 转回字节数组
byte[] outdata = Enumerable.Range(0, hexData.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexData.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
if (printerHandle != IntPtr.Zero && isPrinting)
{
WritePrinter(printerHandle, pUnmanagedBytes, outdata.Length, out int _);
}
}
}
}
**说明:**
- 我们增加了一个 `dicSocket` 字典来跟踪每个客户端对应的打印任务状态(是否正在打印),以及当前的打印句柄。
- 在接收数据时,通过检查特征代码来启动或者结束打印任务。
- 在数据处理中,按照要求替换指定内容,确保每次处理的数据块能追加到当前的打印任务中。
这样一来,在不拼接所有数据的情况下,我们可以实现流式的、基于特征代码的打印任务控制,避免了内存不足的问题,同时保证了打印数据的连贯性。希望这些调整能够帮助到您~
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-20 12:04
明白了,既然拼接整合成完整数据会引发内存错误,我们需要在接收数据的过程中,以流式处理的方式尽量保 ...
经过你的方法已经成功,如果我想把接收的同一个数据分块分别写入2个或者多个打印机怎么写代码呢? cxnet 发表于 2024-07-23 08:15
经过你的方法已经成功,如果我想把接收的同一个数据分 ...
太好了,很高兴知道之前的方法能够成功!现在,让我们来实现将接收到的同一份数据分块分别写入多个打印机的功能。这是个有趣的挑战,就像把同一条鱼分给不同的朋友吃一样。
### 解决方案思路:
1. **准备多个打印机的名称**:首先,您需要定义要使用的多个打印机的名称。
2. **按照顺序将数据发送到打印机**:在处理接收到的数据时,循环遍历打印机列表,将数据写入每个打印机。
以下是一个修改后的示例代码,展示如何将接收到的数据同时分块写入多个打印机:
using System.Net.Sockets;
using System.Net;
using System.Runtime.InteropServices;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// DOCINFOA类和Win32打印相关API声明与之前相同...
private Socket _socket;
private Dictionary dicSocket = new Dictionary();
private List<string> printerNames = new List<string> { "PrinterName1", "PrinterName2" }; // 打印机名称列表
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
Task.Run(() =>
{
while (true)
{
Socket sockClient = _socket.Accept();
dicSocket = (IntPtr.Zero, false);
Task.Run(() => HandleClient(sockClient));
}
});
}
else
{
_socket.Close();
}
}
private void HandleClient(Socket sockClient)
{
byte[] buffer = new byte; // 定义缓冲区
while (true)
{
int length;
try
{
length = sockClient.Receive(buffer);
}
catch
{
break;
}
if (length == 0)
{
sockClient.Close();
break;
}
else
{
ProcessData(sockClient, buffer.Take(length).ToArray());
}
}
}
private void ProcessData(Socket client, byte[] data)
{
string hexData = BitConverter.ToString(data).Replace("-", "");
IntPtr[] printerHandles = new IntPtr;
bool[] isPrinting = new bool;
if (hexData.Contains("52454D4F5445315449"))
{
for (int i = 0; i < printerNames.Count; i++)
{
OpenPrinter(printerNames, out printerHandles, IntPtr.Zero);
StartDocPrinter(printerHandles, 1, new DOCINFOA { pDocName = "Test Document", pDataType = "RAW" });
StartPagePrinter(printerHandles);
isPrinting = true;
}
}
if (hexData.Contains("4A450100001B000000"))
{
for (int i = 0; i < printerHandles.Length; i++)
{
if (isPrinting)
{
EndPagePrinter(printerHandles);
EndDocPrinter(printerHandles);
ClosePrinter(printerHandles);
isPrinting = false;
}
}
}
// 替换特定内容
if (hexData.Contains("804420FB00144008020820820040411101"))
{
hexData = hexData.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 转换为字节数组
byte[] outdata = Enumerable.Range(0, hexData.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexData.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
// 将数据发送到所有打印机
for (int i = 0; i < printerHandles.Length; i++)
{
if (isPrinting)
{
WritePrinter(printerHandles, pUnmanagedBytes, outdata.Length, out int _);
}
}
}
}
}
### 修改说明:
1. **打印机名称列表**:创建了一个字符串列表 `printerNames` 来存储多个打印机的名称。您可以根据自己的需要添加或修改打印机名称。
2. **循环打印**:在 `ProcessData` 方法中,逐个遍历打印机句柄并发送相同的数据到每个打印机。
3. **状态管理**:保持每个打印机的状态,以便在打印任务开始和结束时进行正确的处理。
这样修改后,您就可以将同一份数据分块分别写入两个或多个打印机了。无论是通过“共享鱼”的方式还是通过“多个盘子”的方式,保证数据都能顺利到达各自的目标打印机!如果还有其他问题,随时告诉我哦~
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-23 08:16
太好了,很高兴知道之前的方法能够成功!现在,让我们来实现将接收到的同一份数据分块分别写入多个打印 ...
private Dictionary dicSocket = new Dictionary();这一句有问题,需要两个参数 cxnet 发表于 2024-07-23 09:00
private Dictionary dicSocket = new Dictionary();这 ...
您说得对!在C#中,`Dictionary` 类需要指定键和值的数据类型并初始化,而且我遗漏了必要的参数。我们可以使用 `Dictionary<string, (IntPtr, bool)>` 来存储每个客户端对应的打印句柄和打印状态。
让我来为您修正这部分代码,并提供一个完整的示例。我们将字典的声明更改为包括适当的类型,以及其他一些必要的调整以确保代码可以正常工作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// DLL导入和DOCINFOA类定义
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, int level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);
private Socket _socket;
// 更新为适当的字典类型
private Dictionary<string, (IntPtr printerHandle, bool isPrinting)> dicSocket = new Dictionary<string, (IntPtr, bool)>();
// 多个打印机名称的列表
private List<string> printerNames = new List<string> { "PrinterName1", "PrinterName2" };
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
Task.Run(() =>
{
while (true)
{
Socket sockClient = _socket.Accept();
dicSocket = (IntPtr.Zero, false);
Task.Run(() => HandleClient(sockClient));
}
});
}
else
{
_socket.Close();
}
}
private void HandleClient(Socket sockClient)
{
byte[] buffer = new byte; // 调整缓冲区大小
while (true)
{
int length;
try
{
length = sockClient.Receive(buffer);
}
catch
{
break;
}
if (length == 0)
{
sockClient.Close();
break;
}
else
{
ProcessData(sockClient, buffer.Take(length).ToArray());
}
}
}
private void ProcessData(Socket client, byte[] data)
{
string hexData = BitConverter.ToString(data).Replace("-", "");
string clientEndpoint = client.RemoteEndPoint.ToString();
IntPtr printerHandle;
bool isPrinting = dicSocket.isPrinting;
if (hexData.Contains("52454D4F5445315449") && !isPrinting)
{
foreach (var printerName in printerNames)
{
if (OpenPrinter(printerName, out printerHandle, IntPtr.Zero))
{
StartDocPrinter(printerHandle, 1, new DOCINFOA { pDocName = "Test Document", pDataType = "RAW" });
StartPagePrinter(printerHandle);
dicSocket = (printerHandle, true);
}
}
}
if (hexData.Contains("4A450100001B000000") && isPrinting)
{
foreach (var printer in printerNames)
{
printerHandle = dicSocket.printerHandle;
if (printerHandle != IntPtr.Zero)
{
EndPagePrinter(printerHandle);
EndDocPrinter(printerHandle);
ClosePrinter(printerHandle);
}
}
dicSocket = (IntPtr.Zero, false); // 重置状态
}
// 特定内容替换
if (hexData.Contains("804420FB00144008020820820040411101"))
{
hexData = hexData.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 转换为字节数组
byte[] outdata = Enumerable.Range(0, hexData.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexData.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
// 将数据发送到所有打开的打印机
foreach (var printerName in printerNames)
{
printerHandle = dicSocket.printerHandle;
if (printerHandle != IntPtr.Zero && isPrinting)
{
WritePrinter(printerHandle, pUnmanagedBytes, outdata.Length, out int _);
}
}
}
}
}
### 修改说明:
1. **字典的初始化**:将 `dicSocket` 字典初始化为 `Dictionary<string, (IntPtr printerHandle, bool isPrinting)>`,用于存储每个客户端的打印句柄和打印状态。
2. **遍历打印机名称并处理任务**:在 `ProcessData` 中,每个客户端的数据根据情况打印到所有指定的打印机上。
3. **缓冲区调整**:将接收的缓冲区定义为一个固定大小的字节数组 `byte[] buffer = new byte;`。
通过这些修改,您就可以将同一份数据分块写入两个或多个打印机,同时有效管理每个打印机的状态。继续保持良好的编程习惯,若有更多问题,随时问我哦!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-23 08:16
太好了,很高兴知道之前的方法能够成功!现在,让我们来实现将接收到的同一份数据分块分别写入多个打印 ...
当同一数据分块分别写入多个打印机时你这一句代码有问题,缺少2个参数private Dictionary dicSocket = new Dictionary(); cxnet 发表于 2024-07-23 09:17
当同一数据分块分别写入多个打印机时你这一句代码有问 ...
抱歉给您带来了困扰!确实,声明一个 `Dictionary` 时需要指定键和值的数据类型。让我们正确地定义这个字典并提供完整的代码示例。我们需要将 `dicSocket` 定义为存储打印机句柄和打印状态的结构体或类。
### 修改后的代码
我们可以定义一个简单的结构体来存储每个客户端的打印状态和句柄,然后使用该结构体作为字典的值。这里是具体的实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// DOCINFOA 类的定义
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
// Win32 API 导入
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, int level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);
private Socket _socket;
// 定义一个用于存储客户端打印状态的结构体
public struct ClientPrintInfo
{
public IntPtr printerHandle;
public bool isPrinting;
}
// 使用字典存储每个客户端的打印信息
private Dictionary<string, ClientPrintInfo> dicSocket = new Dictionary<string, ClientPrintInfo>();
// 多个打印机名称的列表
private List<string> printerNames = new List<string> { "PrinterName1", "PrinterName2" };
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
Task.Run(() =>
{
while (true)
{
Socket sockClient = _socket.Accept();
string clientEndPoint = sockClient.RemoteEndPoint.ToString();
dicSocket = new ClientPrintInfo(); // 初始化字典
Task.Run(() => HandleClient(sockClient, clientEndPoint));
}
});
}
else
{
_socket.Close();
}
}
private void HandleClient(Socket sockClient, string clientEndPoint)
{
byte[] buffer = new byte; // 调整缓冲区大小
while (true)
{
int length;
try
{
length = sockClient.Receive(buffer);
}
catch
{
break;
}
if (length == 0)
{
sockClient.Close();
break;
}
else
{
ProcessData(sockClient, buffer.Take(length).ToArray(), clientEndPoint);
}
}
}
private void ProcessData(Socket client, byte[] data, string clientEndPoint)
{
string hexData = BitConverter.ToString(data).Replace("-", "");
var printInfo = dicSocket; // 获取当前客户端的打印信息
if (hexData.Contains("52454D4F5445315449") && !printInfo.isPrinting) // 开始标记
{
foreach (var printerName in printerNames)
{
if (OpenPrinter(printerName, out printInfo.printerHandle, IntPtr.Zero))
{
StartDocPrinter(printInfo.printerHandle, 1, new DOCINFOA { pDocName = "Test Document", pDataType = "RAW" });
StartPagePrinter(printInfo.printerHandle);
printInfo.isPrinting = true;
}
}
dicSocket = printInfo; // 更新字典中的信息
}
if (hexData.Contains("4A450100001B000000") && printInfo.isPrinting) // 结束标记
{
foreach (var printerName in printerNames)
{
if (printInfo.printerHandle != IntPtr.Zero)
{
EndPagePrinter(printInfo.printerHandle);
EndDocPrinter(printInfo.printerHandle);
ClosePrinter(printInfo.printerHandle);
}
}
printInfo.isPrinting = false; // 重置状态
dicSocket = printInfo; // 更新字典中的信息
}
// 特定内容替换
if (hexData.Contains("804420FB00144008020820820040411101"))
{
hexData = hexData.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 转换为字节数组
byte[] outdata = Enumerable.Range(0, hexData.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexData.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
// 将数据发送到所有打开的打印机
foreach (var printerName in printerNames)
{
if (printInfo.isPrinting)
{
WritePrinter(printInfo.printerHandle, pUnmanagedBytes, outdata.Length, out int _);
}
}
}
}
}
### 关键修改:
1. **ClientPrintInfo 结构体**:我们创建了一个 `ClientPrintInfo` 结构体来存储每个客户端的打印句柄和打印状态。
2. **字典初始化**:声明 `dicSocket` 为 `Dictionary<string, ClientPrintInfo>`,其中键是客户端地址(字符串),值是对应的 `ClientPrintInfo` 结构体。
3. **处理客户端**:在 `HandleClient` 方法中,我们根据客户端的远程端点进行字典的更新和访问。
通过这些修改,您就能成功存储多个客户端的状态,并将接收到的数据分块写入多个打印机。非常感谢您的耐心和指正!如果您还有其他问题或需求,请随时告知!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-23 09:18
抱歉给您带来了困扰!确实,声明一个 `Dictionary` 时需要指定键和值的数据类型。让我们正确地定义这个 ...
这段代码有2个问题
第1个问题是Checkbox取消选择后会在Socket sockClient = _socket.Accept();此出出现错误System.Net.Sockets.SocketException:“一个封锁操作被对 WSACancelBlockingCall 的调用中断。”
第2个问题是,分块打印到多个打印机时,只有打印机名称数组中最后一个打印机是写入完整的数据,其它的打印机只接收到空数据 cxnet 发表于 2024-07-23 10:10
这段代码有2个问题
第1个问题是Checkbox取消选择后 ...
感谢您的反馈!我们来逐个解决这两个问题。
### 问题 1:Checkbox取消选择后Socket.Accept抛出异常
在关闭Socket时,可能会导致正在阻塞的`Accept()`调用抛出异常。为了优雅地处理这种情况,我们可以在关闭Socket之前先在主线程中设置一个标志,让所有的正在处理的客户端显式退出循环,从而避免异常。
### 问题 2:分块打印到多个打印机时其它打印机只接收到空数据
这通常是因为在每次 `WritePrinter` 调用之后,相关的打印句柄(`printerHandle`)没有正确维护,或者数据未被正确转换和发送。因此,我们需要确保每个打印机都独立处理,仅在特定的文档上下文中进行打印。
### 修改后的代码示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, int level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);
private Socket _socket;
private volatile bool _isRunning = false; // 用于控制运行状态
// 定义一个结构体来存储打印状态
public struct ClientPrintInfo
{
public IntPtr printerHandle;
public bool isPrinting;
}
// 字典来存储每个客户端的打印信息
private Dictionary<string, ClientPrintInfo> dicSocket = new Dictionary<string, ClientPrintInfo>();
private List<string> printerNames = new List<string> { "PrinterName1", "PrinterName2" };
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_isRunning = true;
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
Task.Run(() =>
{
while (_isRunning)
{
try
{
Socket sockClient = _socket.Accept();
string clientEndPoint = sockClient.RemoteEndPoint.ToString();
dicSocket = new ClientPrintInfo(); // 初始化字典
Task.Run(() => HandleClient(sockClient, clientEndPoint));
}
catch (SocketException ex)
{
if (!_isRunning) // 检查是否是由于关闭引起的异常
break;
MessageBox.Show($"Socket Error: {ex.Message}");
}
}
});
}
else
{
_isRunning = false; // 设置状态为不运行
_socket?.Close(); // 确保关闭Socket
dicSocket.Clear(); // 清理字典
}
}
private void HandleClient(Socket sockClient, string clientEndPoint)
{
byte[] buffer = new byte; // 定义缓冲区
while (true)
{
int length;
try
{
length = sockClient.Receive(buffer);
}
catch
{
break;
}
if (length == 0)
{
sockClient.Close();
break;
}
else
{
ProcessData(sockClient, buffer.Take(length).ToArray(), clientEndPoint);
}
}
}
private void ProcessData(Socket client, byte[] data, string clientEndPoint)
{
string hexData = BitConverter.ToString(data).Replace("-", "");
var printInfo = dicSocket;
if (hexData.Contains("52454D4F5445315449") && !printInfo.isPrinting) // 开始标记
{
foreach (var printerName in printerNames)
{
if (OpenPrinter(printerName, out printInfo.printerHandle, IntPtr.Zero))
{
StartDocPrinter(printInfo.printerHandle, 1, new DOCINFOA { pDocName = "Test Document", pDataType = "RAW" });
StartPagePrinter(printInfo.printerHandle);
printInfo.isPrinting = true;
}
}
dicSocket = printInfo; // 更新字典
}
if (hexData.Contains("4A450100001B000000") && printInfo.isPrinting) // 结束标记
{
foreach (var printerName in printerNames)
{
if (printInfo.printerHandle != IntPtr.Zero)
{
EndPagePrinter(printInfo.printerHandle);
EndDocPrinter(printInfo.printerHandle);
ClosePrinter(printInfo.printerHandle);
}
}
printInfo.isPrinting = false; // 重置状态
dicSocket = printInfo; // 更新字典
}
// 特定内容替换
if (hexData.Contains("804420FB00144008020820820040411101"))
{
hexData = hexData.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 转换为字节数组
byte[] outdata = Enumerable.Range(0, hexData.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexData.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
// 将数据发送到所有打开的打印机
foreach (var printerName in printerNames)
{
if (printInfo.isPrinting)
{
WritePrinter(printInfo.printerHandle, pUnmanagedBytes, outdata.Length, out int _);
}
}
}
}
}
### 关键修改:
1. **运行状态控制**:
- 添加了一个 `volatile` 的布尔字段 `_isRunning` 用于控制 Socket 的循环。
- 在 `checkBox1_CheckedChanged` 方法中,标志位的设置控制 `while` 循环的执行。
2. **异常处理**:
- 在 `Socket.Accept()` 中添加了异常处理,并检查 `_isRunning` 以判断异常是否是因为 Socket 被关闭引起的。
3. **确保打印操作完整**:
- 在 `ProcessData` 方法中,对打印机句柄的管理进行了适当调整,确保每个打印机都能独立处理其文档,并且没有多余的操作影响数据的完整性。
### 带来的好处:
- 确保了关闭 Socket 时不会因错误而崩溃。
- 每个打印机能够正确接收和处理完整的数据块,避免了在打印过程中数据丢失的问题。
希望这些修改能有效解决您遇到的问题!如有更多疑问或需要进一步帮助,请随时告知!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-23 10:11
感谢您的反馈!我们来逐个解决这两个问题。
### 问题 1:Checkbox取消选择后Socket.Accept抛出异常
...
这段代码只是解决了异常问题,但还是和之前一样,只有打印机名称数组里最后一个打印机可以接收到完整的数据,而数组前面所有的打印机只能接收到空数据,我昨天试了很多次和现在一样的效果,始终不知道问题出在哪里 cxnet 发表于 2024-07-23 13:06
这段代码只是解决了异常问题,但还是和之前一样,只有 ...
感谢您的耐心反馈!这个问题可以归结为每个打印机在进行写入时未能正确使用。由于所有打印机的句柄都是在一个流中处理,因此需要确保在打印时每一页的数据都从一个新的打印文档开始。
### 解决方案:
1. **为每个打印机独立管理打印任务**:在循环中对每个打印机分别调用 `OpenPrinter`、`StartDocPrinter`、`StartPagePrinter`,并按顺序写入数据,然后结束打印。
2. **将数据分块发送给每个打印机**:确保每个打印机在相同的输入数据上操作时,它们之间的操作不会互相干扰。
下面是修正后的代码示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace fsd
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class DOCINFOA
{
public string pDocName;
public string pOutputFile;
public string pDataType;
}
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
public static extern bool ClosePrinter(IntPtr hPrinter);
public static extern bool StartDocPrinter(IntPtr hPrinter, int level, DOCINFOA di);
public static extern bool EndDocPrinter(IntPtr hPrinter);
public static extern bool StartPagePrinter(IntPtr hPrinter);
public static extern bool EndPagePrinter(IntPtr hPrinter);
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);
private Socket _socket;
private volatile bool _isRunning = false; // 用于控制运行状态
// 定义一个结构体来存储打印信息
public struct ClientPrintInfo
{
public IntPtr printerHandle;
public bool isPrinting;
}
// 字典来存储每个客户端的打印信息
private Dictionary<string, ClientPrintInfo> dicSocket = new Dictionary<string, ClientPrintInfo>();
private List<string> printerNames = new List<string> { "PrinterName1", "PrinterName2" };
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
_isRunning = true;
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.58"), 9178);
try
{
_socket.Bind(endpoint);
}
catch (Exception)
{
MessageBox.Show("连接失败");
}
_socket.Listen(10);
Task.Run(() =>
{
while (_isRunning)
{
try
{
Socket sockClient = _socket.Accept();
string clientEndPoint = sockClient.RemoteEndPoint.ToString();
dicSocket = new ClientPrintInfo(); // 初始化字典
Task.Run(() => HandleClient(sockClient, clientEndPoint));
}
catch (SocketException ex)
{
if (!_isRunning) // 检查是否是由于关闭引起的异常
break;
MessageBox.Show($"Socket Error: {ex.Message}");
}
}
});
}
else
{
_isRunning = false; // 设置状态为不运行
_socket?.Close(); // 确保关闭Socket
dicSocket.Clear(); // 清理字典
}
}
private void HandleClient(Socket sockClient, string clientEndPoint)
{
byte[] buffer = new byte; // 定义缓冲区
while (true)
{
int length;
try
{
length = sockClient.Receive(buffer);
}
catch
{
break; // 出现异常时退出循环
}
if (length == 0)
{
sockClient.Close();
break; // 客户端关闭
}
else
{
ProcessData(sockClient, buffer.Take(length).ToArray(), clientEndPoint);
}
}
}
private void ProcessData(Socket client, byte[] data, string clientEndPoint)
{
string hexData = BitConverter.ToString(data).Replace("-", "");
var printInfo = dicSocket;
if (hexData.Contains("52454D4F5445315449") && !printInfo.isPrinting) // 开始标记
{
printInfo.isPrinting = true; // 设置为正在打印
foreach (var printerName in printerNames)
{
if (OpenPrinter(printerName, out printInfo.printerHandle, IntPtr.Zero))
{
StartDocPrinter(printInfo.printerHandle, 1, new DOCINFOA { pDocName = "Test Document", pDataType = "RAW" });
StartPagePrinter(printInfo.printerHandle);
// 特定内容替换
if (hexData.Contains("804420FB00144008020820820040411101"))
{
hexData = hexData.Replace("804420FB00144008020820820040411101", "5A4420FB001440080345A33C0040411108");
}
// 转换为字节数组
byte[] outdata = Enumerable.Range(0, hexData.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexData.Substring(x, 2), 16))
.ToArray();
IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(outdata.Length);
Marshal.Copy(outdata, 0, pUnmanagedBytes, outdata.Length);
// 将数据发送到打印机
WritePrinter(printInfo.printerHandle, pUnmanagedBytes, outdata.Length, out int written);
EndPagePrinter(printInfo.printerHandle);
EndDocPrinter(printInfo.printerHandle);
ClosePrinter(printInfo.printerHandle);
}
}
}
if (hexData.Contains("4A450100001B000000") && printInfo.isPrinting) // 结束标记
{
printInfo.isPrinting = false; // 重新设置为非正在打印状态
dicSocket = printInfo; // 更新字典
}
}
}
}
### 核心修改点:
1. **为每个打印机单独进行文档和页面处理**:
- 在对每个打印机循环时,确保为每台打印机分别打开文档、开始页面、写入数据并关闭页面和文档。
- 每次调用 `WritePrinter` 之后都要立即结束当前页面并结束文档。
2. **清晰的数据转移逻辑**:
- 为每个打印机生成独立的文档,并在发送数据时确保在对应的文档上下文中进行处理。
- 在遍历打印机时,确保每台打印机都能独自接收完整的打印数据。
3. **状态更新**:
- 打印状态的变化(如开始打印、结束打印)在字典中进行更新,这样可以确保不同客户端能够独立运行,避免状态混乱。
通过这些改进,您应该能够实现将数据分块发送到多个打印机,并确认每台打印机接收到完整的数据。如果继续遇到问题,请告诉我,我将竭诚帮助您解决!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页:
[1]