在这里,我们将给大家分享关于Windows多进程通信API总结的知识,让您更了解windows多进程编程的本质,同时也会涉及到如何更有效地.NET5中的WindowsAPI-Microsoft.Win
在这里,我们将给大家分享关于Windows 多进程通信 API 总结的知识,让您更了解windows多进程编程的本质,同时也会涉及到如何更有效地.NET 5 中的 Windows API - Microsoft.Windows.SDK.NET.dll 是 25MB?、Apifox:API 文档、API 调试、API Mock、API 自动化测试一体化协作平台、AudioFocusRequest.Builder() 需要 API 级别 26,但我想在 API 级别 22 上使用它,有什么方法可以在低于 API 26 的 API 级别上使用它吗?、C# windows 窗体应用程序 Word Interop 适用于 Windows 7 但不适用于 Windows 10的内容。
本文目录一览:- Windows 多进程通信 API 总结(windows多进程编程)
- .NET 5 中的 Windows API - Microsoft.Windows.SDK.NET.dll 是 25MB?
- Apifox:API 文档、API 调试、API Mock、API 自动化测试一体化协作平台
- AudioFocusRequest.Builder() 需要 API 级别 26,但我想在 API 级别 22 上使用它,有什么方法可以在低于 API 26 的 API 级别上使用它吗?
- C# windows 窗体应用程序 Word Interop 适用于 Windows 7 但不适用于 Windows 10
Windows 多进程通信 API 总结(windows多进程编程)
在一个大型的应用系统中,往往需要多个进程相互协作,进程间通信 (IPC,Inter Process Communication) 就显得比较重要了。在 Linux 系统中,有很多种 IPC 机制,比如说,信号 (signal)、管道 (pipe)、消息队列 (message queue)、信号量 (semaphore) 和共享内存 (shared memory)、套接字 (socket) 等,其实 Windows 操作系统也支持这些东西。在 IBM 的 Developerworks 发现了一篇关于 Windows 与 Linux 之间 IPC 机制 API 比较的文章,写得很不错,链接
http://www.ibm.com/developerworks/cn/linux/l-ipc2lin1.html
下面大部分内容是关于这些机制的 API 的实现。
创建进程
进程的创建可以调用 CreateProcess 函数,CreateProcess 有三个重要的参数,运行进程的名称、指向 STARTUPINFO 结构的指针、指向 PROCESS_INFORMATION 结构的指针。其原型如下:
[cpp] view plaincopyprint?
BOOL CreateProcess
(
LPCTSTRlpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFOlpStartupInfo,
LPPROCESS_INFORMATIONlpProcessInformation
);
给个例子,如果启动时应用程序带有命令行参数,进程将输出命令行参数,并创建一个不带任何参数的子线程;如果不带有任何参数,则会输出一条提示消息。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[]){
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
if (argc>1)
{
cout<<"Argument"<<argv[1]<<endl;
cout<<"开启子线程"<<endl;
ZeroMemory(&process_info,sizeof(process_info));
ZeroMemory(&startup_info,sizeof(startup_info));
startup_info.cb=sizeof(startup_info);
if (CreateProcess(argv[0],0,0,0,0,0,0,0,&startup_info,&process_info)==0)
{
cout<<"Error"<<endl;
}
WaitForSingleObject(process_info.hProcess,INFINITE);
}
else{
cout<<"No arguments"<<endl;
}
getchar();
}
再给个例子,利用 CreateProcess 开启一个新线程,启动 IE 浏览器,打开百度的主页,5s 后再将其关闭。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
#define IE L"C:\\Program Files\\Internet Explorer\\iexplore.exe"
#define CMD L"open http://www.baidu.com/"
int _tmain(int argc, _TCHAR* argv[]){
STARTUPINFO startup_info;
GetStartupInfo(&startup_info);
PROCESS_INFORMATION process_info;
startup_info.dwFlags=STARTF_USESHOWWINDOW;
startup_info.wShowWindow=SW_HIDE;
if (!CreateProcess(IE,CMD,NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL,&startup_info,&process_info))
{
cout<<"Create Process Error:"<<GetLastError()<<endl;
return 0;
}
Sleep(5000);
TerminateProcess(process_info.hProcess,0);
return 0;
}
被创建的句柄通过 process_info.hProcess 返回。如果传递参数给新的进程,第一个命令行参数必须重复应用程序名称,整个命令行会被传递给子进程。
传递参数给新进程。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[]){
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
if (argc==1)
{
cout<<"No arguments given starting child process"<<endl;
wchar_t argument[256];
wsprintf(argument,L"\"%s\" Hello",argv[0]);
ZeroMemory(&process_info,sizeof(process_info));
ZeroMemory(&startup_info,sizeof(startup_info));
startup_info.cb=sizeof(startup_info);
if (CreateProcess(argv[0],argument,0,0,0,0,0,0,&startup_info,&process_info)==0)
{
cout<<"Error "<<GetLastError()<<endl;
}
WaitForSingleObject(process_info.hProcess,INFINITE);
}
else{
cout<<"Argument "<<argv[1]<<endl;
}
getchar();
}
IPC
进程间可以共享内存,进程建立具有共享属性的内存区域后,另一个进程可以打开此内存区域,并将其映射到自己的地址空间。共享内存可以使用文件映射函数 CreateFileMapping,创建共享内存区域的句柄,通过 MapViewOfFile () 把这个区域映射到进程,然后再连接到现有的共享内存区域,可以通过 OpenFileMapping 获得句柄。在进程使用完共享内存后,需要调用 UnmapViewOfFile () 取消映射,再调用 CloseHandle () 关闭相应的句柄,避免内存泄露。
给个例子,如果启动时应用程序不带任何参数,应用程序会创建一个子进程。父进程也将建立一个共享内存区域,并将一个字符串保存到共享内存。共享内存取名为 sharedmemory,在 Local\ 命名空间中创建,即该共享内存对该用户所有的全部进程可见。(进程的命名空间分为两种,全局命名空间以 Global\ 标识符开头,本地命名空间以 Local\ 开头)
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[]){
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
HANDLE filehandle;
TCHAR ID[]=TEXT("Local\\sharedmemory");
char* memory;
if (argc==1)
{
filehandle=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1024,ID);
memory=(char*)MapViewOfFile(filehandle,FILE_MAP_ALL_ACCESS,0,0,0);
sprintf_s(memory,1024,"%s","Data from first process");
cout<<"First process:"<<memory<<endl;
ZeroMemory(&process_info,sizeof(process_info));
ZeroMemory(&startup_info,sizeof(startup_info));
startup_info.cb=sizeof(startup_info);
wchar_t cmdline[256];
wsprintf(cmdline,L"\"%s\" Child\n",argv[0]);
CreateProcessW(argv[0],cmdline,0,0,0,0,0,0,&startup_info,&process_info);
WaitForSingleObject(process_info.hProcess,INFINITE);
UnmapViewOfFile(memory);
CloseHandle(filehandle);
}
else{
filehandle=OpenFileMapping(FILE_MAP_ALL_ACCESS,0,ID);
memory=(char*)MapViewOfFile(filehandle,FILE_MAP_ALL_ACCESS,0,0,0);
cout<<"Second process: "<<memory;
UnmapViewOfFile(memory);
CloseHandle(filehandle);
}
getchar();
return 0;
}
从结果可以看出,子进程连接到共享内存,并能输出父进程存储在那里的字符串。子进程输出字符串以后,就取消内存映射,关闭文件句柄,然后退出。子进程退出后,父进程就可以取消内存映射、关闭文件句柄并退出。
子进程可以继承父进程所有资源的句柄,最简单的方法是通过命令行传递值。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[]){
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
SECURITY_ATTRIBUTES sa;
HANDLE filehandle;
TCHAR ID[]=TEXT("Local\\sharedmemory");
wchar_t* memory;
if (argc==1)
{
// 父进程
sa.nLength=sizeof(sa);// 设置安全属性
sa.bInheritHandle=TRUE;// 使句柄可以被继承
sa.lpSecurityDescriptor=NULL;
filehandle=CreateFileMapping(INVALID_HANDLE_VALUE,&sa,PAGE_READWRITE,0,1024,ID);
memory=(wchar_t*)MapViewOfFile(filehandle,FILE_MAP_ALL_ACCESS,0,0,0);
// 用共享内存设置命令行
swprintf(memory,1024,L"\"%s\" %i",argv[0],filehandle);
cout<<"First process memory:"<<memory<<" handle: "<<filehandle<<endl;
ZeroMemory(&process_info,sizeof(process_info));
ZeroMemory(&startup_info,sizeof(startup_info));
startup_info.cb=sizeof(startup_info);
// 启动子进程
CreateProcess(NULL,memory,0,0,true,0,0,0,&startup_info,&process_info);
WaitForSingleObject(process_info.hProcess,INFINITE);
UnmapViewOfFile(memory);
CloseHandle(filehandle);
}
else{
filehandle=(HANDLE)_wtoi(argv[1]);// 从 argv [1] 获得句柄
memory=(wchar_t*)MapViewOfFile(filehandle,FILE_MAP_ALL_ACCESS,0,0,0);
cout<<"Second process memory : "<<memory<<" handle: "<<filehandle<<endl;
UnmapViewOfFile(memory);
CloseHandle(filehandle);
}
getchar();
return 0;
}
进程间共享互斥量,可以通过调用 CreateMutex 或者 OpenMutex 函数来获取互斥量的句柄。但是,只有一个进程可以创建互斥量,其他的进程只能打开现有的互斥量;互斥量的名称必须唯一;互斥量的名称必须传递给其他进程。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[]){
HANDLE sharedmutex;
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info,sizeof(process_info));
ZeroMemory(&startup_info,sizeof(startup_info));
startup_info.cb=sizeof(startup_info);
sharedmutex=CreateMutex(0,0,L"mymutex");
if (GetLastError()!=ERROR_ALIAS_EXISTS)
{
if (CreateProcess(argv[0],0,0,0,0,0,0,0,&startup_info,&process_info)==0)
{
cout<<"Error : "<<GetLastError()<<endl;
}
WaitForSingleObject(process_info.hProcess,INFINITE);
}
WaitForSingleObject(sharedmutex,INFINITE);
for (int i=0;i<100;i++)
{
cout<<"Process "<<GetCurrentProcessId()<<" count"<<i<<endl;
}
ReleaseMutex(sharedmutex);
CloseHandle(sharedmutex);
getchar();
return 0;
}
使用共享互斥量来确保两个进程中一次只有一个能计数从 0 数到 19,如果没有互斥量的话,那么两个进程可能同时在跑,则控制台的输出将是混合的输出,使用互斥量以后,一次只有一个进程在输出。
也可以用管道进行通信,管道是流式通信的一种方式,管道有两种命名管道和匿名管道。匿名管道的创建可以调用 CreatePipe (),创建命名管道可以调用 CreateNamedPipe (),调用 WriteFile 通过管道发送数据,ReadFile 从管道读取数据。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <process.h>
#include <iostream>
#include <stdio.h>
using namespace std;
HANDLE readpipe,writepipe;
unsigned int __stdcall stage1(void * param)
{
char buf[200];
DWORD len;
for (int i=0;i<10;i++)
{
sprintf(buf,"Text %i",i);
WriteFile(writepipe,buf,strlen(buf)+1,&len,0);
}
CloseHandle(writepipe);
return 0;
}
unsigned int __stdcall stage2(void * param)
{
char buf[200];
DWORD len;
while(ReadFile(readpipe,buf,200,&len,0))
{
DWORD offset=0;
while(offset<len)
{
cout<<&buf[offset]<<endl;
offset+=strlen(&buf[offset])+1;
}
}
CloseHandle(readpipe);
return 0;
}
int _tmain(int argc, _TCHAR* argv[]){
HANDLE thread1,thread2;
CreatePipe(&readpipe,&writepipe,0,0);
thread1=(HANDLE)_beginthreadex(0,0,&stage1,0,0,0);
thread2=(HANDLE)_beginthreadex(0,0,&stage2,0,0,0);
WaitForSingleObject(thread1,INFINITE);
WaitForSingleObject(thread2,INFINITE);
getchar();
return 0;
}
第一个线程将文本信息放入管道,第二个线程接收并输出这些信息。
还可以用套接字进行通信。WindowsSockets API 以 BSD Sockets API 为基础,与类 UNIX 操作系统的代码很相似。
[cpp] view plaincopyprint?
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <tchar.h>
#include <process.h>
#include <WinSock2.h>
#include <iostream>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
HANDLE hevent;
// 响应线程
void handleecho(void *data)
{
char buf[1024];
int count;
ZeroMemory(buf,sizeof(buf));
int socket=(int)data;
while((count=recv(socket,buf,1023,0))>0)
{
cout<<"received "<<buf<<"from client"<<endl;
int ret=send(socket,buf,count,0);
}
cout<<"close echo thread"<<endl;
shutdown(socket,SD_BOTH);
closesocket(socket);
}
// 客户端线程
unsigned int __stdcall client(void *data)
{
SOCKET ConnectSockket=socket(AF_INET,SOCK_STREAM,0);
WaitForSingleObject(hevent,INFINITE);
struct sockaddr_in server;
ZeroMemory(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr("192.168.1.107");
server.sin_port=7780;
connect(ConnectSockket,(struct sockaddr*)&server,sizeof(server));
cout<<"send ''abcd'' to server"<<endl;
char buf[1024];
ZeroMemory(buf,sizeof(buf));
strncpy_s(buf,1024,"abcd",5);
send(ConnectSockket,buf,strlen(buf)+1,0);
ZeroMemory(buf,sizeof(buf));
recv(ConnectSockket,buf,1024,0);
//cout<<"get "<<buf<<"from server"<<endl;
printf("get ''%s'' from server\n",buf);
cout<<"close client"<<endl;
shutdown(ConnectSockket,SD_BOTH);
closesocket(ConnectSockket);
return 0;
}
// 服务器线程
unsigned int __stdcall server(void *data)
{
SOCKET newsocket;
SOCKET ServerSocket=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in server;
ZeroMemory(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
server.sin_port=7780;
bind(ServerSocket,(struct sockaddr*)&server,sizeof(server));
listen(ServerSocket,SOMAXCONN);
SetEvent(hevent);
while((newsocket=accept(ServerSocket,0,0))!=INVALID_SOCKET)
{
HANDLE newthread;
newthread=(HANDLE)_beginthread(&handleecho,0,(void *)newsocket);
}
cout<<"close server"<<endl;
shutdown(ServerSocket,SD_BOTH);
closesocket(ServerSocket);
return 0;
}
// 主线程启动客户端线程和服务端线程
int _tmain(int argc, _TCHAR* argv[]){
HANDLE serverthread,clienthread;
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
hevent=CreateEvent(0,true,0,0);
serverthread=(HANDLE)_beginthreadex(0,0,&server,0,0,0);
clienthread=(HANDLE)_beginthreadex(0,0,&client,0,0,0);
WaitForSingleObject(clienthread,INFINITE);
CloseHandle(clienthread);
CloseHandle(hevent);
getchar();
WSACleanup();
return 0;
}
服务器线程的第一个操作是打开一个套接字,接着绑定连接。套接字置于监听状态,值 SOMAXCONN 包含排队等待接受的连接的最大值。然后服务器发信号给事件,事件继而使客户端线程尝试连接。接着,主线程循环等待接受连接,直到收到 INVALID_SOCKET 的连接。Windows 套接字关闭时会发生这种情况。服务器线程在其他线程退出后清理退出。服务器每次接受一个连接时都会创建一个新线程,且新连接的标识会传递给新创建的线程。当循环收到 INVALID_SOCKET 时,服务器线程关闭,然后关闭套接字。
Windows API 也提供了很多原子操作,互锁函数。InterlockedIncrement 就是一个互锁函数。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <process.h>
#include <iostream>
using namespace std;
int isPrime(int num)
{
int i;
for (i=2;i<(int)(sqrt((float)num)+1.0);i++)
{
if (num%i==0)
return 0;
}
return 1;
}
volatile long counter=2;
unsigned int __stdcall test(void *)
{
while (counter<20)
{
int num=InterlockedIncrement(&counter);
//int num=counter++;
printf("Thread ID : %i; value = %i, is prime = %i\n",GetCurrentThreadId(),num,isPrime(num));
}
return 0;
}
int _tmain(int argc,_TCHAR* argv[])
{
HANDLE h1,h2;
h1=(HANDLE)_beginthreadex(0,0,&test,(void *)0,0,0);
h2=(HANDLE)_beginthreadex(0,0,&test,(void *)0,0,0);
WaitForSingleObject(h1,INFINITE);
WaitForSingleObject(h2,INFINITE);
CloseHandle(h1);
CloseHandle(h2);
getchar();
return 0;
}
还有一个问题就是线程本地存储 (TLS, ThreadLocal Storage),TLS 是一个机制,利用该机制,程序可以拥有全局变量,但处于 “每一线程各不相同” 的状态。也就是说,进程中的所有线程都可以拥有全局变量,但这些变量其实是特定对某个线程才有意义,各个线程拥有全局变量的一个副本,各自之间不相影响。每个线程访问数据的方式相同,但看不到其他线程持有的值。比如说,定义一个全局变量 int a=10,那么在线程 1 中对 a 进行操作 a=a-1,如果没用 TLS,那么线程 2 开始获得的 a 就是 9。但是,如果采取了 TLS,不管线程 1 中对 a 的值进行了如何的修改操作,其他的线程一开始获得的 a 还是 10,不会被修改。这个全局的变量 a 是没有存储在线程堆栈中的,是在全局的堆栈中,但是却被各个线程 “共享” 且互不影响。可以认为线程本地存储的本质是 “全局” 数据的作用域受到了执行线程的限制。
线程本地分配可以调用__declspec、TlsAlloc () 等函数。TlsAlloc 可以分配全局索引,该索引由所有线程共享,但是每个线程存储在索引中的数据为调用的线程私有,也就是说其他线程看不到持有的值。当不再需要全局索引提供线程本地存储时,可以调用 TlsFree 来释放全局索引。
给个例子。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <process.h>
#include <iostream>
using namespace std;
DWORD TLSIndex;
void setdata(int value)
{
cout<<"Thread "<<GetCurrentThreadId()<<": set value = "<<value<<endl;
TlsSetValue(TLSIndex,(void*)value);
}
void getdata()
{
int value;
value=(int)TlsGetValue(TLSIndex);
cout<<"Thread "<<GetCurrentThreadId()<<": has value = "<<value<<endl;
}
unsigned int __stdcall workthread(void *data)
{
int value=(int)data;
cout<<"Thread "<<GetCurrentThreadId()<<": got value = "<<value<<endl;
setdata(value);
Sleep(1000);
getdata();
return 0;
}
int _tmain(int argc,_TCHAR* argv[])
{
HANDLE h[8];
TLSIndex=TlsAlloc();
for (int i=0;i<8;i++)
{
h[i]=(HANDLE)_beginthreadex(0,0,&workthread,(void*)i,0,0);
}
for (int i=0;i<8;i++)
{
WaitForSingleObject(h[i],INFINITE);
}
TlsFree(TLSIndex);
getchar();
return 0;
}
线程本地存储用于保存传给各个线程的值,每个线程在被创建的时候就被传递一个唯一的值,并通过 setdata 存储在线程本地存储中。getdata 可以读取线程本地值,每个线程调用 setdata 方法,接着休眠 1s 让其他线程运行,然后调用 getdata 读取数据。
还有个问题,就是优先级的问题。线程的优先级越高,获得的 CPU 资源 (时间) 就越多。在有些情况下,调整一个应用程序中不同线程的优先级会非常有用。比如说,当某个应用执行一个长时间的后台任务时,为了保证机器的高响应性,这个后台任务最好以低优先级运行。
Windows 操作系统中提供了相关的 API。
[cpp] view plaincopyprint?
#include <Windows.h>
#include <tchar.h>
#include <process.h>
#include <iostream>
#include <time.h>
using namespace std;
unsigned int __stdcall fastthread(void *data)
{
double d=1.0;
cout<<"fast thread started"<<endl;
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
clock_t start=clock();
for (int i=0;i<1000000000;i++)
{
d+=i;
}
clock_t end=clock();
cout<<"fast thread finished, it takes "<<(double)(end-start)/CLOCKS_PER_SEC<<"s to finish the task"<<endl;
return 0;
}
unsigned int __stdcall slowthread(void *data)
{
double d=0.0;
cout<<"slow thread started"<<endl;
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_BELOW_NORMAL);
clock_t start=clock();
for (int i=0;i<1000000000;i++)
{
d+=i;
}
clock_t end=clock();
cout<<"slow thread finished, it takes "<<(double)(end-start)/CLOCKS_PER_SEC<<"s to finnish the task"<<endl;
return 0;
}
int _tmain(int argc,_TCHAR* argv[])
{
HANDLE fast,slow;
slow=(HANDLE)_beginthreadex(0,0,&slowthread,0,0,0);
fast=(HANDLE)_beginthreadex(0,0,&fastthread,0,0,0);
WaitForSingleObject(fast,INFINITE);
WaitForSingleObject(slow,INFINITE);
getchar();
return 0;
}
有时候调整线程的优先级会带来优先级反转的问题。
小结
主要实现了 windows 操作系统中 IPC 的 API,主要有进程之间共享内存、子进程中继承句柄、互斥量、管道、套接字等。此外,还有 Windows 中的互锁函数。线程本地化存储 (TLS)、线程的优先级等。
.NET 5 中的 Windows API - Microsoft.Windows.SDK.NET.dll 是 25MB?
如何解决.NET 5 中的 Windows API - Microsoft.Windows.SDK.NET.dll 是 25MB?
我按照 Calling Windows APIs in .NET5 指南在我的 WPF 应用程序中调用通知 API。但是现在它在我的输出目录中放置了 25MB Microsoft.Windows.SDK.NET.dll
。仅对于一个小功能,这会将我的应用程序的下载大小增加 50%。有没有办法将其缩减为我需要的 API?
解决方法
也许你可以设计一个更新包或一组更新包来制定一个优化的补丁策略,通过将它安排在一个单独的依赖包中来推迟这个胖程序集下载。更重要的是,如果在安装过程中没有选择您的新功能,用户甚至不会下载它。 MSIX 应有资格建立这种包生态系统。
Apifox:API 文档、API 调试、API Mock、API 自动化测试一体化协作平台
我是 ABin-阿斌:写一生代码,创一世佳话,筑一览芳华。 如果小伙伴们觉得文章有点 feel ,那就点个赞再走哦。
声明:原位地址:https://blog.csdn.net/web15286201346/article/details/126098695
文章目录
- 一、apifox简介及下载:
- 二、apifox页面布局简介:
- 1、apifox几个简单概念:
- 2、以项目单位分组
- 3、点击项目后进入项目,在该项目下管理接口。
- 1、新建接口
- 2、修改接口:
- 3、运行接口:
- 4、断言:
- 5、批量运行:
- 三、 总结
一、apifox简介及下载:
1、apifox:是 API 文档、API 调试、API Mock、API 自动化测试一体化协作平台
2、定位 :Postman + Swagger + Mock + JMeter
。
3、下载与安装:
官网下载地址:https://www.apifox.cn/
按照需要下载对应版本,下载完毕后解压安装即可。
二、apifox页面布局简介:
1、apifox几个简单概念:
(1)团队:该工具支持团队协同办公,可以根据需要 创建不同的团队,在工具页面左侧,显示自己的团队,也可新建团队
新建团队,需要一个团队 名称:
创建成功团队后,可以邀请成员、设置权限等,或删除团队
有了团队,就可以开始我们接口的管理及测试工作了。
2、以项目单位分组
- apifox是以团队下项目来管理接口的,将所需接口维护在项目中,在不同的项目中对 接口进行维护及操作。
3、点击项目后进入项目,在该项目下管理接口。
1、新建接口
- 维护接口信息,包括接口URL,接口基础信息,请求参数等,需要注意的是,此处只维护接口信息,类似于接口文档,不运行接口
接口URL,http协议及域名部分,建议设置在环境中,页面右上角选择环境处,可维护环境信息,因为我们在测试工作中,往往会有多个环境,将协议及域名维护在环境中,测试不同环境的同一个接口时,只需要切换环境即可,不用不同环境维护不同的接口。
对于需要cookie的接口,在维护接口时,请求参数中,别忘了维护cookie信息。
2、修改接口:
在接口管理-修改文档下,可修改已维护的接口信息
3、运行接口:
- 接口运行,往往是依据测试用例,在接口测试中,可以简单的认为不同的传值即为不同的测试用例,apifox中,运行接口的入口在项目中,接口管理-运行下,在此处修改参数值,点击发送后,可已看到返回信息
- 此外,可将运行数据保存为用例,保存成功后,此次运行的数据会保存,下次打开该用例,其中参数值可复用(注:运行接口时,需要选择环境)
若设置了断言,可根据断言判断此条用例是否通过:
修改了参数值信息,需要点击保存才能更新成功,若不保参数值依然为修改前值。
测试用例显示在接口的下一级,可通过复制用例的方式,维护多个用例。
4、断言:
对测试用例,可以设置其断言,即期望结果,apifox在后置操作中进行断言
apifox断言核心为提取表达式,该提取表达式很简单,即将目标返回字段提取出来,$及为根节点,一级一级定位到目标字段即可
举个例子:若返回信息如下图所示,我想通过sort_finish字段值断言,则提取该字段的表达式为:$.data.sort_data.Box_no
5、批量运行:
- apifox的批量运行,在自动化测试页面,可在该页面添加一个分组,在分组下添加测试用例,创建完测试用例后进入所创用例,即可添加步骤,此时可导入接口用例
导入用例后,可根据需要设置循环次数及线程数等信息,点击运行,即可批量执行,执行完成后,显示此次执行结果:
三、 总结
- 以上:为 apifox 基本使用功能,变量提取、套件使用等,待续~
AudioFocusRequest.Builder() 需要 API 级别 26,但我想在 API 级别 22 上使用它,有什么方法可以在低于 API 26 的 API 级别上使用它吗?
如何解决AudioFocusRequest.Builder() 需要 API 级别 26,但我想在 API 级别 22 上使用它,有什么方法可以在低于 API 26 的 API 级别上使用它吗?
我正在开发一个媒体播放器应用程序,我希望它支持的最小 API 级别是 API 22。
我想使用 Audio Focus 进行媒体播放,但是 AudioFocusRequest.Builder() 在低于 API 26 的 API 级别上不起作用,并且曾经在低于 API 26 的 API 级别上工作的方法 requestAudioFocus() 是现在已弃用,那么我如何在应用中使用 Audio Focus 以使其适用于低于 API 26 的 API 级别以及更高的 API 级别?
audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUdioFOCUS_GAIN)
.setonAudioFocuschangelistener(audioFocuschangelistener)
.setFocusGain(AudioManager.AUdioFOCUS_GAIN)
.build();
这段代码不适用于低于 26 的 API 级别并给出此错误消息,
“调用需要 API 级别 26(当前最小值为 22):新的 android.media.AudioFocusRequest.Builder”
解决方法
Check the API level at runtime 选择使用 requestAudioFocus()
还是 AudioFocusRequest.Builder
。
if (Build.VERSION.SDK_INT >= 26) {
// Use AudioFocusRequest.Builder
} else {
// Use requestAudioFocus
}
这应该会使您收到的错误和 IDE 中的弃用消息都清除。
C# windows 窗体应用程序 Word Interop 适用于 Windows 7 但不适用于 Windows 10
如何解决C# windows 窗体应用程序 Word Interop 适用于 Windows 7 但不适用于 Windows 10
我编写了一个应用程序来帮助用户使用 C# 生成字母。我创建了以富文本格式保存在 sql Server 数据库中的模板。我的开发机器是 Windows 7,我使用 Visual Studio 2019 来编写应用程序。我使用 NuGet 添加 Word 的互操作引用。该应用程序配置为面向 x86 的 Release、平台 Active(Any cpu)。它是一个 ClickOnce 应用程序,从共享驱动器安装在一个单独的目录中,但与保存字母的驱动器相同。
该应用程序在我的机器上正常运行,但在 Windows 10 用户机器上运行不正常。当它尝试保存文件时,她收到一条错误消息,提示“抱歉,我们找不到该文件”。我们都有 Word 2016。两台机器都是 64 位。我将这封信保存为 Word 中的备份,然后导出为 PDF。在导出为 PDF 之前,代码无法保存 Word 文档。请参阅下面的代码片段:
public static void SavetoWord2(string CoverLetter,string LetterText,string FileSave,string BackUpSave,ref string ErrorString)
{
try
{
Microsoft.Office.Interop.Word.Application oWord = new Microsoft.Office.Interop.Word.Application();
string headerfooterFile = Properties.FileResources.headerfooterTemplate;
Document odoc = new Document();
odoc = oWord.Documents.Add(@headerfooterFile);
odoc.Activate();
try
{
Clipboard.Clear();
Clipboard.SetText(CoverLetter,TextDataFormat.Rtf);
odoc.ActiveWindow.Selection.Paste();
Clipboard.Clear();
Clipboard.SetText(LetterText,TextDataFormat.Rtf);
odoc.ActiveWindow.Selection.Paste();
//01/26/2021 JS having trouble with the save on Pam''s machine so going to try to capture the correct error.
try
{
odoc.SaveAs(@BackUpSave);
}
catch (Exception exBU)
{
ErrorString = "Error trying to save " + @BackUpSave + ": " + exBU.Message;
}
try
{
odoc.ExportAsFixedFormat(@FileSave,WdExportFormat.wdExportFormatPDF);
}
catch (Exception exPDF)
{
if (string.IsNullOrEmpty(ErrorString))
{
ErrorString = "Error trying to save " + @FileSave + " PDF: " + exPDF.Message;
}
else
{
ErrorString += " and Error trying to save " + @FileSave + " PDF: " + exPDF.Message;
}
}
}
catch (Exception exInner)
{
ErrorString = exInner.Message;
MessageBox.Show(exInner.Message,Properties.LetterResources.SavetoWord,MessageBoxButtons.OK,MessageBoxIcon.Error);
}
Clipboard.Clear();
oWord.Quit(SaveChanges: 0);
Marshal.FinalReleaseComObject(oWord);
//oWord.Visible = true;
}
catch (Exception ex)
{
ErrorString = ex.Message;
MessageBox.Show(ex.Message,MessageBoxIcon.Error);
}
}
错误发生在 odoc.SaveAs 行上,但仅在从 Windows 10 计算机运行时发生。最初,我使用了我机器上的互操作,但后来将其更改为 NuGet 互操作,但这并没有解决问题。我尝试将 Embed Interop Types 更改为 False 但这并没有解决任何问题,因此我将其改回。互操作引用的别名属性是全局的,特定版本属性是 True。由于富文本,我担心更改文档编写器的类型。应用程序的其余部分对于 Windows 10 用户来说运行良好。有什么想法吗?
解决方法
OpenXML 没有成功,因为它不允许我正确添加标题或导出为 PDF。尽管我非常希望使用 OpenXML,但我最终还是回到了 Word Interop。我能够通过如下设置参考属性来解决这个问题:
嵌入互操作类型 = False 复制本地 = True
现在无论哪个操作系统运行程序,保存都有效。我有上面的部分答案,但忽略了 Copy Local = true 部分。
我们今天的关于Windows 多进程通信 API 总结和windows多进程编程的分享就到这里,谢谢您的阅读,如果想了解更多关于.NET 5 中的 Windows API - Microsoft.Windows.SDK.NET.dll 是 25MB?、Apifox:API 文档、API 调试、API Mock、API 自动化测试一体化协作平台、AudioFocusRequest.Builder() 需要 API 级别 26,但我想在 API 级别 22 上使用它,有什么方法可以在低于 API 26 的 API 级别上使用它吗?、C# windows 窗体应用程序 Word Interop 适用于 Windows 7 但不适用于 Windows 10的相关信息,可以在本站进行搜索。
本文标签: