使用Win32 API获得端口占用列表并关闭对应进程
获得端口占用可以用命令netstat -ab
实现,结束进程可以用taskkill
,但是用C++怎么实现呢?
答案是Windows已经提供了API实现功能。GetTcpTable可以得到当前占用的端口数量以及具体的ip和端口,但是得不到对应的进程。GetTcpTable2可以额外获得占用端口对应进程的pid,拿到pid,后面问题就好办了。
下面是效果图:
源码:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <ws2tcpip.h>
#include <Windows.h>
#include <psapi.h>//GetModuleFileNameEx
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
//查找占用网络端口的进程,返回进程的pid,没有则返回0
//throw: 内存不足抛出runtime_error
DWORD GetIdOfOccupiedPortProcess(unsigned short port)
{
ULONG ulSize = sizeof(MIB_TCPTABLE2);
PMIB_TCPTABLE2 pTcpTable = (PMIB_TCPTABLE2)malloc(ulSize);
if (pTcpTable == nullptr)
throw runtime_error("memory is not enough.");
//调用GetTcpTable2,若内存不够,则释放,然后再申请
if (GetTcpTable2(pTcpTable, &ulSize, TRUE) == ERROR_INSUFFICIENT_BUFFER)
{
free(pTcpTable);
pTcpTable = (PMIB_TCPTABLE2)malloc(ulSize);
if (pTcpTable == nullptr)
throw runtime_error("memory is not enough.");
}
if (GetTcpTable2(pTcpTable, &ulSize, TRUE) == NO_ERROR)
{
//遍历端口
cout << "start finding TCP port:" << endl;
for (int i = 0; i < pTcpTable->dwNumEntries; ++i)
{
//得到端口号
unsigned short localPort = ntohs((u_short)pTcpTable->table[i].dwLocalPort);
cout << localPort << endl;
//得到占用端口的进程pid
auto pid = pTcpTable->table[i].dwOwningPid;
//发现目标端口,返回pid
if (port == localPort)
{
cout << "found the destination port." << endl;
free(pTcpTable);
return pid;
}
}
}
free(pTcpTable);
return 0;
}
//提升权限为DEBUG,用于OpenProcess失败,GetLastError返回5“无权限操作错误”的情况
//应在调用OpenProcess之前调用
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
BOOL fOk = FALSE;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOk;
}
int main(int argc, char* argv[])
{
//提升权限
if (EnableDebugPrivilege() == FALSE)
{
cout << "Failure in EnableDebugPrivilege." << endl;
return -1;
}
while (1)
{
//提示输入一个端口号
unsigned short port;
cout << "Please input a port number:";
cin >> port;
//找到占用端口的进程的pid
auto pid = GetIdOfOccupiedPortProcess(port);
if (pid == 0)
{
cout << "No program occupied port" << port << "." << endl;
continue;
}
//打开进程
HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (hProcess == NULL)
{
//出错时报错
DWORD errCode = GetLastError();
cout << "Failed in OpenProcess. Error code: " << errCode << endl;
system("pause");
return -1;
}
//得到进程对应exe文件名
TCHAR buf[MAX_PATH];
if (GetModuleFileNameEx(hProcess, 0, buf, MAX_PATH))
{
cout << "Do you want to kill the process " << buf << "?(y/n)" << endl;
}
else
{
cout << "Failed in GetModuleFileNameEx. Maybe no authorization." << endl;
system("pause");
return -1;
}
char key;
cin >> key;
if (key == 'y')
{
if (::TerminateProcess(hProcess, 0))
{
cout << "Success." << endl;
}
else
{
cout << "Failed in TerminateProcess." << endl;
system("pause");
return -1;
}
}
CloseHandle(hProcess);
}
return 0;
}
参考
GetTcpTable2 function
Build your own netstat.exe with c# | Tim Van Wassenhove’s blog
h
如何在C++中获取进程名?