项目中需要将C++代码计算出的参数传递给C#代码做后续处理,就需要跨语言通信。
由于命名管道可以跨语言,跨平台,跨线程通信,实际上命名管道不仅可在同一台计算机的不同进程之间传输数据,甚至能在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。
C#端:命名管道的API都封装在类PipeNative中,在使用时,直接调用类中的函数即可。
类PipeNative:
/****************************** Module Header ******************************\
* Module Name: PipeNative.cs
* Project: CSNamedPipeClient
* Copyright (c) Microsoft Corporation.
*
* The P/Invoke signatures of some native Named Pipe APIs.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* History:
* * 1/27/2009 9:21 PM Jialiang Ge Created
\***************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
/// <summary>
/// Desired Access of File/Device
/// </summary>
[Flags]
public enum FileDesiredAccess : uint
{
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000,
GENERIC_EXECUTE = 0x20000000,
GENERIC_ALL = 0x10000000
}
/// <summary>
/// File share mode
/// </summary>
[Flags]
public enum FileShareMode : uint
{
Zero = 0x00000000, // No sharing.
FILE_SHARE_DELETE = 0x00000004,
FILE_SHARE_READ = 0x00000001,
FILE_SHARE_WRITE = 0x00000002
}
/// <summary>
/// File Creation Disposition
/// </summary>
[Flags]
public enum FileCreationDisposition : uint
{
CREATE_NEW = 1,
CREATE_ALWAYS = 2,
OPEN_EXISTING = 3,
OPEN_ALWAYS = 4,
TRUNCATE_EXISTING = 5
}
/// <summary>
/// Named Pipe Open Modes
/// http://msdn.microsoft.com/en-us/library/aa365596.aspx
/// </summary>
[Flags]
public enum PipeOpenMode : uint
{
PIPE_ACCESS_INBOUND = 0x00000001, // Inbound pipe access.
PIPE_ACCESS_OUTBOUND = 0x00000002, // Outbound pipe access.
PIPE_ACCESS_DUPLEX = 0x00000003 // Duplex pipe access.
}
/// <summary>
/// Named Pipe Type, Read, and Wait Modes
/// http://msdn.microsoft.com/en-us/library/aa365605.aspx
/// </summary>
public enum PipeMode : uint
{
// Type Mode
PIPE_TYPE_BYTE = 0x00000000, // Byte pipe type.
PIPE_TYPE_MESSAGE = 0x00000004, // Message pipe type.
// Read Mode
PIPE_READMODE_BYTE = 0x00000000, // Read mode of type Byte.
PIPE_READMODE_MESSAGE = 0x00000002, // Read mode of type Message.
// Wait Mode
PIPE_WAIT = 0x00000000, // Pipe blocking mode.
PIPE_NOWAIT = 0x00000001 // Pipe non-blocking mode.
}
/// <summary>
/// The class exposes kernel32.dll methods for named pipes communication.
/// </summary>
[SuppressUnmanagedCodeSecurity]
public class PipeNative
{
/// <summary>
/// Unlimited server pipe instances.
/// </summary>
public const uint PIPE_UNLIMITED_INSTANCES = 255;
/// <summary>
/// The operation completed successfully.
/// </summary>
public const ulong ERROR_SUCCESS = 0;
/// <summary>
/// The system cannot find the file specified.
/// </summary>
public const ulong ERROR_CANNOT_CONNECT_TO_PIPE = 2;
/// <summary>
/// All pipe instances are busy.
/// </summary>
public const ulong ERROR_PIPE_BUSY = 231;
/// <summary>
/// The pipe is being closed.
/// </summary>
public const ulong ERROR_NO_DATA = 232;
/// <summary>
/// No process is on the other end of the pipe.
/// </summary>
public const ulong ERROR_PIPE_NOT_CONNECTED = 233;
/// <summary>
/// More data is available.
/// </summary>
public const ulong ERROR_MORE_DATA = 234;
/// <summary>
/// There is a process on other end of the pipe.
/// </summary>
public const ulong ERROR_PIPE_CONNECTED = 535;
/// <summary>
/// Waiting for a process to open the other end of the pipe.
/// </summary>
public const ulong ERROR_PIPE_LISTENING = 536;
/// <summary>
/// Waits indefinitely when connecting to a pipe.
/// </summary>
public const uint NMPWAIT_WAIT_FOREVER = 0xffffffff;
/// <summary>
/// Does not wait for the named pipe.
/// </summary>
public const uint NMPWAIT_NOWAIT = 0x00000001;
/// <summary>
/// Uses the default time-out specified in a call to the
/// CreateNamedPipe method.
/// </summary>
public const uint NMPWAIT_USE_DEFAULT_WAIT = 0x00000000;
/// <summary>
/// Invalid operating system handle.
/// </summary>
public const int INVALID_HANDLE_VALUE = -1;
/// <summary>
/// Retrieves the calling thread's last-error code value.
/// </summary>
/// <returns>The return value is the calling thread's last-error
/// code value.</returns>
[DllImport("kernel32.dll", SetLastError = true)] //设置SetLastError = true,表示可利用GetLastError获取上一次函数运行时反返回的错误
public static extern uint GetLastError(); //〖2〗-系统找不到指定的文件。
/// <summary>
/// Creates an instance of a named pipe and returns a handle for
/// subsequent pipe operations.
/// </summary>
/// <param name="lpName">Pipe name</param>
/// <param name="dwOpenMode">Pipe open mode</param>
/// <param name="dwPipeMode">Pipe-specific modes</param>
/// <param name="nMaxInstances">Maximum number of instances</param>
/// <param name="nOutBufferSize">Output buffer size</param>
/// <param name="nInBufferSize">Input buffer size</param>
/// <param name="nDefaultTimeOut">Time-out interval</param>
/// <param name="pipeSecurityAttributes">Security attributes</param>
/// <returns>If the function succeeds, the return value is a handle
/// to the server end of a named pipe instance.</returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateNamedPipe(
String lpName, // Pipe name
PipeOpenMode dwOpenMode, // Pipe open mode
PipeMode dwPipeMode, // Pipe-specific modes
uint nMaxInstances, // Maximum number of instances
uint nOutBufferSize, // Output buffer size
uint nInBufferSize, // Input buffer size
uint nDefaultTimeOut, // Time-out interval
IntPtr pipeSecurityAttributes // Security Attributes
);
/// <summary>
/// Enables a named pipe server process to wait for a client
/// process to connect to an instance of a named pipe.
/// </summary>
/// <param name="hHandle">Handle to the server end of a named pipe
/// instance.</param>
/// <param name="lpOverlapped">Pointer to an Overlapped object.
/// </param>
/// <returns>If the function succeeds, the return value is nonzero.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ConnectNamedPipe(
IntPtr hHandle, // Handle to named pipe
IntPtr lpOverlapped // Overlapped structure
);
/// <summary>
/// Waits until either a time-out interval elapses or an instance
/// of the specified named pipe is available for connection.
/// </summary>
/// <param name="name">Pointer to a null-terminated string that
/// specifies the name of the named pipe.</param>
/// <param name="timeout">Number of milliseconds that the function
/// willwait for an instance of the named pipe to be available.
/// </param>
/// <returns>If an instance of the pipe is available before the
/// time-out interval elapses, the return value is nonzero.</param>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WaitNamedPipe(
String name,
int timeout);
/// <summary>
/// Sets the read mode and the blocking mode of the specified named pipe.
/// </summary>
/// <remarks>
/// If the specified handle is to the client end of a named pipe and if
/// the named pipe server process is on a remote computer, the function
/// can also be used to control local buffering.
/// </remarks>
/// <param name="hHandle">Handle to the named pipe instance.</param>
/// <param name="lpMode">Pointer to a variable that supplies the new mode.
/// </param>
/// <param name="lpMaxCollectionCount">Pointer to a variable that
/// specifies the maximum number of bytes collected on the client computer
/// before transmission to the server.</param>
/// <param name="lpCollectDataTimeout">Pointer to a variable that
/// specifies the maximum time, in milliseconds, that can pass before
/// a remote named pipe transfers information over the network.</param>
/// <returns>If the function succeeds, the return value is nonzero.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetNamedPipeHandleState(
IntPtr hHandle,
ref PipeMode lpMode,
IntPtr lpMaxCollectionCount,
IntPtr lpCollectDataTimeout);
/// <summary>
/// Creates or opens a file, directory, physical disk, volume, console
/// buffer, tape drive, communications resource, mailslot, or named
/// pipe.
/// </summary>
/// <param name="lpFileName">File name.</param>
/// <param name="dwDesiredAccess">Access to the object (reading,
/// writing, or both).</param>
/// <param name="dwShareMode">Sharing mode of the object (reading,
/// writing, both, or neither).</param>
/// <param name="fileSecurityAttributes">Pointer to a
/// SecurityAttributes object that determines whether the returned
/// handle can be inherited by child processes.</param>
/// <param name="dwCreationDisposition">Action to take on files that
/// exist, and which action to take when files do not exist</param>
/// <param name="dwFlagsAndAttributes">File attributes and flags.
/// </param>
/// <param name="hTemplateFile">Handle to a template file.</param>
/// <returns>If the function succeeds, the return value is an open
/// handle to the specified file.</returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFile(
String lpFileName, // File name 文件名
FileDesiredAccess dwDesiredAccess, // Access mode 通道模式
FileShareMode dwShareMode, // Share mode 共享模式
IntPtr fileSecurityAttributes, // Security Attributes 安全属性
FileCreationDisposition dwCreationDisposition,// How to create
uint dwFlagsAndAttributes, // File attributes 文件属性
uint hTemplateFile); // Handle to template file
/// <summary>
/// Reads data from a file, starting at the position indicated by the
/// file pointer.
/// </summary>
/// <param name="hHandle">Handle to the file to be read.</param>
/// <param name="lpBuffer">Pointer to the buffer that receives the
/// data read from the file.</param>
/// <param name="nNumberOfBytesToRead">Number of bytes to be read from
/// the file.</param>
/// <param name="lpNumberOfBytesRead">Pointer to the variable that
/// receives the number of bytes read.</param>
/// <param name="lpOverlapped">Pointer to an Overlapped object.</param>
/// <returns>The ReadFile function returns when one of the following
/// conditions is met: a write operation completes on the write end
/// of the pipe, the number of bytes requested has been read, or an
/// error occurs.</returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(
IntPtr hHandle, // Handle to file
byte[] lpBuffer, // Data buffer
uint nNumberOfBytesToRead, // Number of bytes to read
out uint lpNumberOfBytesRead, // Number of bytes read
IntPtr lpOverlapped // Overlapped buffer
);
/// <summary>
/// Writes data to a file at the position specified by the file pointer.
/// </summary>
/// <param name="hHandle">Handle to the file.</param>
/// <param name="lpBuffer">Pointer to the buffer containing the data to
/// be written to the file.</param>
/// <param name="nNumberOfBytesToWrite">Number of bytes to be write to
/// the file.</param>
/// <param name="lpNumberOfBytesWritten">Pointer to the variable that
/// receives the number of bytes written.</param>
/// <param name="lpOverlapped">Pointer to an Overlapped object.
/// </param>
/// <returns>If the function succeeds, the return value is nonzero.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteFile(
IntPtr hHandle, // Handle to file
byte[] lpBuffer, // Data buffer
uint nNumberOfBytesToWrite, // Number of bytes to write
out uint lpNumberOfBytesWritten, // number of bytes written
IntPtr lpOverlapped // Overlapped buffer
);
/// <summary>
/// Closes an open object handle.
/// </summary>
/// <param name="hHandle">Handle to an open object.</param>
/// <returns>If the function succeeds, the return value is nonzero.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hHandle);
/// <summary>
/// Flushes the buffers of the specified file and causes all buffered
/// data to be written to the file.
/// </summary>
/// <param name="hHandle">Handle to an open file.</param>
/// <returns>If the function succeeds, the return value is nonzero.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FlushFileBuffers(IntPtr hHandle);
/// <summary>
/// Disconnects the server end of a named pipe instance from a client
/// process.
/// </summary>
/// <param name="hHandle">Handle to an instance of a named pipe.
/// </param>
/// <returns>If the function succeeds, the return value is nonzero.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool DisconnectNamedPipe(IntPtr hHandle);
} // class PipeNative
C#端创建管道:
private void ConnectPipe(string strPipeName)
{
while (true)
{
hPipe = PipeNative.CreateFile(
strPipeName, //管道名称
FileDesiredAccess.GENERIC_READ | FileDesiredAccess.GENERIC_WRITE, //访问模式,读模式或写模式
FileShareMode.Zero, //0表示不共享,共享模式
IntPtr.Zero, //一个只读字段,代表已初始化为零的指针或句柄。指向安全属性的指针
FileCreationDisposition.OPEN_EXISTING, //如何创建。文件必须已经存在。由设备提出要求
0, //文件属性
0); //用于复制文件句柄,不使用模板
if (hPipe.ToInt32() != PipeNative.INVALID_HANDLE_VALUE) break;//PipeNative.INVALID_HANDLE_VALUE = -1.管道创建失败
if (PipeNative.GetLastError() != PipeNative.ERROR_PIPE_BUSY //PipeNative.ERROR_PIPE_BUSY = 231
|| PipeNative.WaitNamedPipe(strPipeName, 5 * 1000)) //在超时时间前管道的一个实例有效则返回非0,在超时时间内没有一个有效的实例,则返回0
{
Console.WriteLine("无法连接管道:{0} ERROR:{1}", strPipeName, PipeNative.GetLastError());
return;
}
}
Console.WriteLine("管道{0}连接成功。", strPipeName);
}
C++端创建管道
借用先人总结的创建过程:
1):服务器进程调用CreateNamedPipe函数来创建一个有名称的命名管道在创建命名管道的时候必须指定一个本地的命名管道名称。windows允许同一个本地的命名管道名称右多个命名管道实例。所以,服务器进程在调用CreateNamedPipe函数时必须指定最大允许的实例数(0-255).如果CreateNamedPipe函数成功返回后,服务器进程得到一个指向一个命名管道实例的句柄。
2):服务器进程就可以调用ConnectNamedPipe来等待客户的连接请求,这个ConnectNamedPipe既支持同步形式,又支持异步形式,若服务器进程以同步形式调用 ConnectNamedPipe函数,如果没有得到客户端的连接请求,则会一直等到客户端的连接请求。当该函数返回时,客户端和服务器之间的命名管道连接已经建立起来了。
3):这个时候服务器端就可以向客户端读(ReadFile)/写(WriteFile)数据了。
4):在已经建立连接的命名管道实例中,服务器进程就会得到一个指向该管道实例的句柄,这个句柄称之为服务器端句柄,同时服务端进程可以调用DisconnectNamedPipe函数,将一个管道实例与当前建立连接的客户端进程断开,从而可以重新连接到新的客户端进程。当然,服务器也可以调用CloseHandle来关闭一个已经建立连接的命名管道实例。
调用CreateNamedPipe函数:
HANDLE WINAPI CreateNamedPipe(
_In_ LPCTSTR lpName,
_In_ DWORD dwOpenMode,
_In_ DWORD dwPipeMode,
_In_ DWORD nMaxInstances,
_In_ DWORD nOutBufferSize,
_In_ DWORD nInBufferSize,
_In_ DWORD nDefaultTimeOut,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
该函数用来创建一个命名管道的实例,并返回这个命名管道的句柄。如果需要创建一个命名管道的多个实例,就需要多次调用CreateNamedPipe函数。
参数 lpName 为一个字符串,其格式必须为 \\.\pipe\pipeName,其中圆点 ”.” 表示的是本地机器,如果想要与远程的服务器建立连接,那么这个圆点位置处应指定这个远程服务器的名称,而其中的 “pipe” 这个是个固定的字符串,也就是说不能进行改变的,最后的 “pipename” 则代表的是我将要创建的命名管道的名称了,参数 dwOpenMode 用来指定管道的访问方式,重叠方式,写直通方式,还有管道句柄的安全访问方式。
具体代码如下
HANDLE creatpipe()
{
setlocale(LC_ALL, "chs"); //解决显示中文问题
CString strPipeName;
strPipeName.Format(_T("\\\\%s\\pipe\\%s"), _T("."), _T("myPipe2"));
SECURITY_ATTRIBUTES sa;
sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);//为安全描述符分配内存空间
InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);//InitializeSecurityDescriptor函数的第二个参数必需是1
SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, NULL, FALSE);//此函数用来设置DACL中的信息。如果一个DACL已经在security descriptor中存在,那么此DACL将被替换。
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
HANDLE hPipe = CreateNamedPipe(
strPipeName, //管道名称
PIPE_ACCESS_DUPLEX, //管道的访问模式,双向模式
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, //安全访问模式,以消息流的形式写入管道,以消息流的方式从管道读取数据,等待方式为同步方式,即一直等待到一个连接建立
PIPE_UNLIMITED_INSTANCES, //最大实例数 0-255
usize, //输出缓冲区大小
usize, //输入缓冲区大小
NMPWAIT_USE_DEFAULT_WAIT, //超时数
&sa //命名管道的安全性
);
if (hPipe == INVALID_HANDLE_VALUE) //指向一个命名管道的实力句柄 无效
{
_tprintf(L"error\n");//宽字符串
exit(1);//程序结束时,返回1给系统
}
_tprintf(_T("创建管道:%s成功\n"), strPipeName);
_tprintf(_T("等待连接.....\n"));
BOOL bc = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); //若连接成功,则bc为true,否则为GetLastError的返回值
if (!bc)
{
_tprintf(_T("连接错误.\n"));
CloseHandle(hPipe);
exit(1);
}
printf("连接成功.\n");
return hPipe;
}
两端管道创建完毕之后就可以进行读写操作了。
C#端:
如果创建一个管道进行连续写入和读出的话,有可能会出现写入快,读出慢。读出的数据会发生混乱。称为管道阻塞。因此,有连续写入读出得需求时,就要在读出一次数据后回发一个标志位告诉写入端读出成功可以写入,写入端继续写入,否则等待标志位的到来。
不同类型数据同时写入:
由于写入管道的是byte[],所以需要写入的数据必须先由各自类型转为byte[]。下面是C#端的转换函数:
Int转byte:
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public byte[] intToBytes(int value)
{
byte[] src = new byte[4];
src[3] = (byte)((value >> 24) & 0xFF);
src[2] = (byte)((value >> 16) & 0xFF);
src[1] = (byte)((value >> 8) & 0xFF);
src[0] = (byte)(value & 0xFF);
return src;
}
Int转byte方法2:
public byte[] ConvertIntToByteArray(int m)
{
byte[] arry = new byte[4];
arry[0] = (byte)(m & 0xFF);
arry[1] = (byte)((m & 0xFF00) >> 8);
arry[2] = (byte)((m & 0xFF0000) >> 16);
arry[3] = (byte)((m >> 24) & 0xFF);
return arry;
}
Byte转int:
/// <summary>
///
/// </summary>
/// <param name="src"></param>
/// <param name="offset"></param>
/// <returns></returns>
public int bytesToInt(byte[] src, int offset)
{
int value;
value = (int)((src[offset] & 0xFF)
| ((src[offset + 1] & 0xFF) << 8)
| ((src[offset + 2] & 0xFF) << 16)
| ((src[offset + 3] & 0xFF) << 24));
return value;
}
String转byte:
byte[] byte_XX = System.Text.Encoding.Default.GetBytes(int_XX);
float转byte:
byte[] byte_XX = BitConverter.GetBytes(float_XX);
乱入一下:
当需要一次发送不同类型的数据时,就需要将这些数据按每个数据类型的长度依次插入到byte数组当中。
编写测试代码可算出各类型所占字节数:
#include "iostream"
using namespace std;
int main()
{
cout<<sizeof(char)<<endl;
cout<<sizeof(short)<<endl;
cout<<sizeof(int)<<endl;
cout<<sizeof(float)<<endl;
cout<<sizeof(long)<<endl;
cout<<sizeof(double)<<endl;
return 0;
}
结果为:
1
2
4
4
4
8
可见32位系统,vc编译器中,short占 2 字节,int 、float、long 都占 4 字节,
只有double 占8 字。指针长度和地址总线有关。因为指针记录的就是一个地址,那么32位的就是4字节,64位的就是8字节。
64位编译器和32为区别不大:
char :1个字节
char*(即指针变量): 8个字节
short int: 2个字节
int: 4个字节
unsignedint : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
longlong: 8个字节
unsignedlong: 8个字节
言归正传:
依次插入byte数组参考Buffer.BlockCopy方法:
将指定数目的字节从起始于特定偏移量的源数组复制到起始于特定偏移量的目标数组。
publicstatic void BlockCopy (
Array src,
int srcOffset,
Array dst,
int dstOffset,
int count
)
参数
src
源缓冲区。
srcOffset
src 的字节偏移量。
dst
目标缓冲区。
dstOffset
dst 的字节偏移量。
count
要复制的字节数。
当要一次发送int型1,string型和float型5这三个数据,那么就要考虑string的长度。
具体代码如下:
int int_ S1_length = typecn.getStringLength(S1);//计算长度
byte[] byte_ S1_length = typecn.intToBytes(int_ S1_length);//转换为byte
System.Buffer.BlockCopy(byte_ S1_length, 0, writebuffer, 0, byte_CamID_length.Length);
Int N1 = 1;
byte[] byte_N1 = typecn.intToBytes(N1);//转换为byte
System.Buffer.BlockCopy(byte_N1, 0, writebuffer, 4, byte_N1.Length);
string S1 = "s";
byte[] byte_S1 = System.Text.Encoding.Default.GetBytes(S1);
System.Buffer.BlockCopy(byte_S1, 0, writebuffer, 8, byte_S1.Length);
float F1 = 5;
byte[] byte_F1 = BitConverter.GetBytes(F1);
System.Buffer.BlockCopy(byte_F1, 0, writebuffer, 4+ int_ S1_length, byte_F1.Length);
uint real_write = 0;
bool writeresult;
uint len_write = (uint)writebuffer.Length;
writeresult = PipeNative.WriteFile( //将从数据库读出的信息发给C++
hPipe,
writebuffer,
len_write,
out real_write,
IntPtr.Zero);
if (writeresult)
{
Console.WriteLine("成功写入管道");
}
C++端读数据:
和C#端写数据的思路是一样的,一次按顺序读出来。
当管道读出byte数组时,可以直接赋值给任意类型的数组,系统会自动进行类型转换。为方便恢复到原来的类型,我定义为char[].用memcpy就可按位读取并且进行类型转换。
举个memcpy的例子:
#include<string.h>
int main(
{
char* s="GoldenGlobalView";
char d[20];
memcpy(d,s+12,4);//从第13个字符(V)开始复制,连续复制4个字符(View)
d[4]='\0';//memcpy(d,s+12*sizeof(char),4*sizeof(char));也可
printf("%s",d);
getchar();
return 0;
}
输出结果: View
从管道中读数据具体代码如下:
int int_ S1_length;
int N1;
string S1;
float F1;
DWORD rlen = 0;
BOOL rRead;
char c[500] = {};
rRead = ReadFile(
hPipe,
c,
500,
&rlen,
0);
if (rRead)
{
printf("读出成功\n");
}
memcpy(&int_ S1_length, c,4);
memcpy(&N1, c + 4, 4);
memcpy(&S1, c + 8, int_ S1_length);
memcpy(&F1, c + 8+ int_ S1_length, 4);