I have written some C++ code to automatically rename the Network Connection in ncpa.cpl. It works fine on Win7 and Win8 but fails in Win10. The function I used is INetConnection::Rename, its return value is 0x80071a90, which means:
HRESULT_FROM_WIN32(ERROR_TRANSACTIONAL_CONFLICT) : The function attempted to use a name that is reserved for use by another transaction.
But the new connection name I used is something like "Npcap Loopback Adapter", which doesn't seem to be a "reserved" name for Windows.
Someone told me that the built-in netsh.exe tool also uses this way to rename an interface, I have tried the command as "netsh.exe interface set interface name="Ethernet 5" newname="Npcap Loopback Adapter"" and the interface "Ethernet 5" is successfully renamed to "Npcap Loopback Adapter". So I think this way should work out, there must be something wrong in my code.
I want to implement this in C/C++, so don't tell me to wrap the netsh.exe command, I wonder what's wrong with my function call code? Thanks.
My code is:
/*++
Copyright (c) Nmap.org. All rights reserved.
Module Name:
LoopbackRename.cpp
Abstract:
This is used for enumerating our "Npcap Loopback Adapter" using NetCfg API, if found, we changed its name from "Ethernet X" to "Npcap Loopback Adapter".
Also, we need to make a flag in registry to let the Npcap driver know that "this adapter is ours", so send the loopback traffic to it.
This code is modified based on example: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364686.aspx
--*/
#pragma warning(disable: 4311 4312)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objbase.h>
#include <netcon.h>
#include <stdio.h>
#include "LoopbackRename.h"
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")
#define NPCAP_LOOPBACK_INTERFACE_NAME NPF_DRIVER_NAME_NORMAL_WIDECHAR L" Loopback Adapter11"
#define BUF_SIZE 255
BOOL DoTheWork(INetSharingManager *pNSM, wchar_t strDeviceName[])
{ // add a port mapping to every firewalled or shared connection
BOOL bFound = FALSE;
INetSharingEveryConnectionCollection * pNSECC = NULL;
HRESULT hr = pNSM->get_EnumEveryConnection (&pNSECC);
if (!pNSECC)
wprintf (L"failed to get EveryConnectionCollection!\r\n");
else {
// enumerate connections
IEnumVARIANT * pEV = NULL;
IUnknown * pUnk = NULL;
hr = pNSECC->get__NewEnum (&pUnk);
if (pUnk) {
hr = pUnk->QueryInterface (__uuidof(IEnumVARIANT),
(void**)&pEV);
pUnk->Release();
}
if (pEV) {
VARIANT v;
VariantInit (&v);
while ((S_OK == pEV->Next (1, &v, NULL)) && (bFound == FALSE)) {
if (V_VT (&v) == VT_UNKNOWN) {
INetConnection * pNC = NULL;
V_UNKNOWN (&v)->QueryInterface (__uuidof(INetConnection),
(void**)&pNC);
if (pNC) {
NETCON_PROPERTIES *pNETCON_PROPERTIES;
pNC->GetProperties(&pNETCON_PROPERTIES);
wchar_t currentGUID[BUF_SIZE];
GUID guid = pNETCON_PROPERTIES->guidId;
wsprintf(currentGUID, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
if (wcscmp(currentGUID, strDeviceName) == 0)
{
hr = pNC->Rename(NPCAP_LOOPBACK_INTERFACE_NAME);
bFound = TRUE;
if (hr != S_OK)
{
wprintf(L"failed to create rename NPCAP_LOOPBACK_INTERFACE_NAME\r\n");
}
}
pNC->Release();
}
}
VariantClear(&v);
}
pEV->Release();
}
pNSECC->Release();
}
return bFound;
}
BOOL RenameLoopbackNetwork(wchar_t strDeviceName[])
{
BOOL bResult = FALSE;
/* CoInitialize (NULL);*/
// init security to enum RAS connections
CoInitializeSecurity (NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, NULL);
INetSharingManager * pNSM = NULL;
HRESULT hr = ::CoCreateInstance (__uuidof(NetSharingManager),
NULL,
CLSCTX_ALL,
__uuidof(INetSharingManager),
(void**)&pNSM);
if (!pNSM)
{
wprintf (L"failed to create NetSharingManager object\r\n");
return bResult;
}
else {
// add a port mapping to every shared or firewalled connection.
bResult = DoTheWork(pNSM, strDeviceName);
pNSM->Release();
}
/* CoUninitialize ();*/
return bResult;
}
Update:
I have tried this function call 100 times in a loop, but all failed.
if (wcscmp(currentGUID, strDeviceName) == 0)
{
int iTime = 0;
aaa:
hr = pNC->Rename(NPCAP_LOOPBACK_INTERFACE_NAME);
bFound = TRUE;
if (hr == HRESULT_FROM_WIN32(ERROR_TRANSACTIONAL_CONFLICT) && iTime < 100)
{
iTime ++;
goto aaa;
}
else if (hr != S_OK)
{
wprintf(L"failed to create rename NPCAP_LOOPBACK_INTERFACE_NAME\r\n");
}
}
pNC->Release();
The code is open-source in github:
https://github.com/nmap/npcap/tree/master/packetWin7/NPFInstall, it's a Visual Studio 2005 project, but actually you can build it in other Visual Studio versions. After compiling out NPFInstall.exe, use command "NPFInstall.exe -il" to install a new loopback adapter and rename it to "Npcap Loopback Adapter", "NPFInstall.exe -ul" to uninstall. the source to rename the adapter is: LoopbackRename.cpp. I hope this can help solve the problem.
Related
EDIT: I tested with a static IP on both the board and my computer with a python SSL server and it works as expected, leading me to believe that the DHCP is the problem. If anyone has a lead on what may be occuring it would be greatly appreciated.
I am using the mbedTLS library on a STM32F746-NUCLEO board and I want to use it as both a SSL client and server. The server works well, so i tried to use the client example code (as is, in a separate project).
The following mbedtls_net_connect call returns -68 (MBEDTLS_ERR_NET_CONNECT_FAILED). Digging deeper reveals that it is due to a routing error (line 900 in tcp.c from LwIP), because local_ip is set to 0. The board is in DHCP mode on a home router which is connected to the internet. The destination server is up and running and the SERVER_NAME is the IP address in plain text.
mbedtls_entropy_context client_entropy;
static mbedtls_net_context server_fd;
mbedtls_x509_crt cacert;
static uint32_t flags;
static uint8_t vrfy_buf[512];
static const uint8_t* client_pers = "ssl_client";
mbedtls_ssl_config client_config;
mbedtls_ctr_drbg_context client_ctr_drbg;
mbedtls_ssl_context client_ssl;
static uint8_t client_buf[1024];
void SSL_Server(void const *argument) {
int ret, len;
UNUSED(argument);
mbedtls_net_init(&server_fd);
mbedtls_ssl_init(&client_ssl);
mbedtls_ssl_config_init(&client_config);
mbedtls_x509_crt_init(&cacert);
mbedtls_ctr_drbg_init(&client_ctr_drbg);
// Seeding the random number generator
mbedtls_entropy_init( &client_entropy );
len = strlen((char *) client_pers);
if((ret = mbedtls_ctr_drbg_seed(&client_ctr_drbg, mbedtls_entropy_func,
&client_entropy, (const unsigned char *) client_pers, len)) != 0)
{
goto exit;
}
// 1. Initialize certificates
ret = mbedtls_x509_crt_parse( &cacert, (const unsigned char *) mbedtls_test_cas_pem,
mbedtls_test_cas_pem_len );
if( ret < 0 )
{
goto exit;
}
if((ret = mbedtls_net_connect(&server_fd, SERVER_NAME, SERVER_PORT,
MBEDTLS_NET_PROTO_TCP)) != 0)
{
mbedtls_printf( " failed\n ! mbedtls_net_connect returned %d\n\n", ret );
goto exit;
}
}
Here the SSL_Server function is a FreeRTOS thread called in the main(). I can also confirm that the network interface has been assigned an IP address when the error occurs.
I expect the connection call to return 0 and connect to the server to initiate the SSL handshake.
You need to set the default netif route for LWIP to be able to route the remote address.
Simply add netif_set_default(&netif); after dhcp_start() inside the function mbedtls_net_init().
void mbedtls_net_init( mbedtls_net_context *ctx ) {
...
/* add the network interface */
netif_add(&netif, &addr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
/* register the default network interface */
netif_set_up(&netif);
#ifdef USE_DHCP
netif.ip_addr.addr = 0;
dhcp_start(&netif);
#endif
netif_set_default(&netif); // <-- Here
osDelay(500);
start = HAL_GetTick();
while((netif.ip_addr.addr == 0) && (HAL_GetTick() - start < 10000))
{
}
if (netif.ip_addr.addr == 0) {
printf(" Failed to get ip address! Please check your network configuration.\n");
Error_Handler();
}
...
The documentation for MbedTLS can be kinda tricky, hope this helps.
I am working on a game written in C using SDL. Given that it already uses SDL, SDL_image, and SDL_ttf, I decided to add SDL_mixer and SDL_net to my engine. Getting SDL_mixer set up and working was very easy, but I am having a lot of trouble with SDL_net.
To test I created a very simple application with the following rules:
Run without arguments act as a TCP server on port 9999
Run with an argument try to connect to the server at the given IP address on port 9999
Here are some of the key lines of the program (I'm not going to post my whole event-driven SDL engine because its too long):
char *host = NULL;
if (argc > 1) host = argv[1];
and...
IPaddress ip;
TCPsocket server = NULL;
TCPsocket conn = NULL;
if (host) { /* client mode */
if (SDLNet_ResolveHost(&ip,host,port) < 0)
return NULL; //this is actually inside an engine method
if (!(conn = SDLNet_TCP_Open(&ip)))
return NULL;
} else { /* server mode */
if (SDLNet_ResolveHost(&ip,NULL,port) < 0)
return NULL;
if (!(server = SDLNet_TCP_Open(&ip)))
return NULL;
}
and... inside the event loop
if (server) {
if (!conn)
conn = SDLNet_TCP_Accept(server);
}
if (conn) {
void *buf = malloc(size); //server, conn, size are actually members of a weird struct
while (SDLNet_TCP_Recv(conn,buf,size))
onReceive(buf); //my engine uses a callback system to handle things
free(buf);
}
The program seems to start up just fine. However for some reason when I run it in client mode trying to connect to my home computer (which I have on a different IP) from my laptop I find that the call to SDLNet_TCP_Open blocks the program for awhile (5-10 seconds) then returns NULL. Can anybody see what I did wrong? Should I post more of the code? Let me know.
I have ported WinPcap to a NDIS 6 filter driver: https://github.com/nmap/npcap. But it still doesn't support capturing all 802.11 native packets (like control and management frames are not captured).
I noticed there is a way setting DOT11_OPERATION_MODE_NETWORK_MONITOR for the wireless adapter using WlanSetInterface function. But this call succeeds (the return value is OK, and my wi-fi network disconnects after this call). But the problem is I can't see any packets on the Wi-Fi interface using Wireshark, not even the 802.11 data in fake ethernet form. So there must be something wrong with it.
I know that from NDIS 6 and vista, enabing this feature is possible (at least Microsoft's own Network Monitor 3.4 supports this).
So I want to know how to enable monitor mode for the NDIS 6 version WinPcap? Thanks.
My code is shown as below:
// WlanTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <wlanapi.h>
#define WLAN_CLIENT_VERSION_VISTA 2
void SetInterface(WLAN_INTF_OPCODE opcode, PVOID* pData, GUID* InterfaceGuid)
{
DWORD dwResult = 0;
HANDLE hClient = NULL;
DWORD dwCurVersion = 0;
DWORD outsize = 0;
// Open Handle for the set operation
dwResult = WlanOpenHandle(WLAN_CLIENT_VERSION_VISTA, NULL, &dwCurVersion, &hClient);
dwResult = WlanSetInterface(hClient, InterfaceGuid, opcode, sizeof(ULONG), pData, NULL);
WlanCloseHandle(hClient, NULL);
}
// enumerate wireless interfaces
UINT EnumInterface(HANDLE hClient, WLAN_INTERFACE_INFO sInfo[64])
{
DWORD dwError = ERROR_SUCCESS;
PWLAN_INTERFACE_INFO_LIST pIntfList = NULL;
UINT i = 0;
__try
{
// enumerate wireless interfaces
if ((dwError = WlanEnumInterfaces(
hClient,
NULL, // reserved
&pIntfList
)) != ERROR_SUCCESS)
{
__leave;
}
// print out interface information
for (i = 0; i < pIntfList->dwNumberOfItems; i++)
{
memcpy(&sInfo[i], &pIntfList->InterfaceInfo[i], sizeof(WLAN_INTERFACE_INFO));
}
return pIntfList->dwNumberOfItems;
}
__finally
{
// clean up
if (pIntfList != NULL)
{
WlanFreeMemory(pIntfList);
}
}
return 0;
}
// open a WLAN client handle and check version
DWORD
OpenHandleAndCheckVersion(
PHANDLE phClient
)
{
DWORD dwError = ERROR_SUCCESS;
DWORD dwServiceVersion;
HANDLE hClient = NULL;
__try
{
*phClient = NULL;
// open a handle to the service
if ((dwError = WlanOpenHandle(
WLAN_API_VERSION,
NULL, // reserved
&dwServiceVersion,
&hClient
)) != ERROR_SUCCESS)
{
__leave;
}
// check service version
if (WLAN_API_VERSION_MAJOR(dwServiceVersion) < WLAN_API_VERSION_MAJOR(WLAN_API_VERSION_2_0))
{
// No-op, because the version check is for demonstration purpose only.
// You can add your own logic here.
}
*phClient = hClient;
// set hClient to NULL so it will not be closed
hClient = NULL;
}
__finally
{
if (hClient != NULL)
{
// clean up
WlanCloseHandle(
hClient,
NULL // reserved
);
}
}
return dwError;
}
// get interface state string
LPWSTR
GetInterfaceStateString(__in WLAN_INTERFACE_STATE wlanInterfaceState)
{
LPWSTR strRetCode;
switch (wlanInterfaceState)
{
case wlan_interface_state_not_ready:
strRetCode = L"\"not ready\"";
break;
case wlan_interface_state_connected:
strRetCode = L"\"connected\"";
break;
case wlan_interface_state_ad_hoc_network_formed:
strRetCode = L"\"ad hoc network formed\"";
break;
case wlan_interface_state_disconnecting:
strRetCode = L"\"disconnecting\"";
break;
case wlan_interface_state_disconnected:
strRetCode = L"\"disconnected\"";
break;
case wlan_interface_state_associating:
strRetCode = L"\"associating\"";
break;
case wlan_interface_state_discovering:
strRetCode = L"\"discovering\"";
break;
case wlan_interface_state_authenticating:
strRetCode = L"\"authenticating\"";
break;
default:
strRetCode = L"\"invalid interface state\"";
}
return strRetCode;
}
int main()
{
HANDLE hClient = NULL;
WLAN_INTERFACE_INFO sInfo[64];
RPC_CSTR strGuid = NULL;
TCHAR szBuffer[256];
DWORD dwRead;
if (OpenHandleAndCheckVersion(&hClient) != ERROR_SUCCESS)
return -1;
UINT nCount = EnumInterface(hClient, sInfo);
for (UINT i = 0; i < nCount; ++i)
{
if (UuidToStringA(&sInfo[i].InterfaceGuid, &strGuid) == RPC_S_OK)
{
printf(("%d. %s\n\tDescription: %S\n\tState: %S\n"),
i,
strGuid,
sInfo[i].strInterfaceDescription,
GetInterfaceStateString(sInfo[i].isState));
RpcStringFreeA(&strGuid);
}
}
UINT nChoice = 0;
// printf("for choice wireless card:");
//
// if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE), szBuffer, _countof(szBuffer), &dwRead, NULL) == FALSE)
// {
// puts("error input");
// return -1;
// }
// szBuffer[dwRead] = 0;
// nChoice = _ttoi(szBuffer);
//
// if (nChoice > nCount)
// {
// puts("error input.");
// return -1;
// }
//ULONG targetOperationMode = DOT11_OPERATION_MODE_EXTENSIBLE_STATION;
ULONG targetOperationMode = DOT11_OPERATION_MODE_NETWORK_MONITOR;
SetInterface(wlan_intf_opcode_current_operation_mode, (PVOID*)&targetOperationMode, &sInfo[nChoice].InterfaceGuid);
return 0;
}
Update:
Guy has made me clear about what should the high-level library side of WinPcap do about the monitor mode, in nature is setting/getting OID values. But what should the WinPcap driver do, do I need to change the driver? I think the WlanSetInterface call is actually doing the same thing as setting the DOT11_OPERATION_MODE_NETWORK_MONITOR using OID request? Does the fact that it doesn't work mean that the npf driver also needs some kind of changes?
(Answer updated for question update and followup comments.)
Use pcap_oid_set_request_win32(), which is in pcap-win32.c in the version of libpcap in the master branch, to do OID setting/getting operations. If p->opt.rfmon is set in pcap_activate_win32(), set the OID OID_DOT11_CURRENT_OPERATION_MODE with a DOT11_CURRENT_OPERATION_MODE structure with uCurrentOpMode set to DOT11_OPERATION_MODE_NETWORK_MONITOR.
For pcap_can_set_rfmon_win32(), try to get a handle for the device (note that this is done before the activate call) and, if that succeeds, use pcap_oid_get_request_win32() to attempt to get the value of that OID; if it succeeds, you can set it, otherwise, either you can't set it or you got an error.
The driver already supports a generic get/set OID operation - that's what PacketRequest() uses, and pcap_oid_get_request_win32()/pcap_oid_set_request_win32() are implemented atop PacketRequest(), so it's what they use.
As I indicated in messages in the thread you started on the wireshark-dev list, the code that handles receive indications from NDIS has to be able to handle "raw packet" receive indications, and you might have to add those to the NDIS packet filter as well. (And you'll have to hack dumpcap, if you're going to use Wireshark to test the changes; you won't be able to change NPcap so that people can just drop it in and existing versions of Wireshark will support monitor mode.)
I also indicated there how to query a device to find out whether it supports monitor mode.
As for turning monitor mode back off, that's going to require driver, packet.dll, and libpcap work. In the drivers:
in the NDIS 6 driver, for each interface, have a count of "monitor mode instances" and a saved operating mode and, for each opened NPF instance for an interface, have a "monitor mode" flag;
in the Windows 9x and NDIS 4/5 drivers, add a "turn on monitor mode" BIOC call, which always fails with ERROR_NOT_SUPPORTED;
in the NDIS 6 driver, add the same BIOC call, which, if the instance's "monitor mode" flag isn't set, attempts to set the operating mode to monitor mode and, if it succeeds, saves the old operating mode if the interface's monitor mode count is zero, increments the interface's monitor mode count and sets the instance's "monitor mode" flag (it could also add the appropriate values to the packet filter);
have the routine that closes an opened NPF instance check the "monitor mode" flag for the instance and, if it's set, decrement the "monitor mode instances" count and, if the count reaches zero, restores the old operating mode.
In packet.dll, add a PacketSetMonitorMode() routine, which is a wrapper around the BIOC ioctl in question.
In pcap-win32.c, call PacketSetMonitorMode() if monitor mode was requested, rather than setting the operation mode directly.
For setting OIDs in drivers, see the code path for BIOCQUERYOID and BIOCSETOID in NPF_IoControl() - the new BIOC ioctl would be handled in NPF_IoControl().
(And, of course, do the appropriate MP locking.)
The monitor mode count might not be necessary, if you can enumerate all the NPF instances for an interface - the count is just the number of instances that have the monitor mode flag set.
Doing it in the driver means that if a program doing monitor-mode capturing terminates abruptly, so that no user-mode code gets to do any cleanup, the mode can still get reset.
What's the easiest way to get the filename associated with an open HANDLE in Win32?
I tried the code posted by Mehrdad here. It works, but with limitations:
It should not be used for network shares because the MountPointManager may hang for a very long time.
It uses undocumented API (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) I don't like that very much
It does not support USB devices that create virtual COM ports (I need that in my project)
I also studied other approaches like GetFileInformationByHandleEx() and GetFinalPathNameByHandle(), but these are useless as they return only Path + Filename but without drive. Additionally GetFinalPathNameByHandle() also has the hanging bug.
The GetMappedFileName() approach in the MSDN (posted by Max here) is also very limited:
It works only with real files
The file size must not be zero bytes
Directories, Network and COM ports are not supported
The code is clumsy
So I wrote my own code. I tested it on Win XP and on Win 7, 8, and 10. It works perfectly.
NOTE: You do NOT need any additional LIB file to compile this code!
CPP FILE:
t_NtQueryObject NtQueryObject()
{
static t_NtQueryObject f_NtQueryObject = NULL;
if (!f_NtQueryObject)
{
HMODULE h_NtDll = GetModuleHandle(L"Ntdll.dll"); // Ntdll is loaded into EVERY process!
f_NtQueryObject = (t_NtQueryObject)GetProcAddress(h_NtDll, "NtQueryObject");
}
return f_NtQueryObject;
}
// returns
// "\Device\HarddiskVolume3" (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp" (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat" (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" (DVD drive)
// "\Device\Serial1" (real COM port)
// "\Device\USBSER000" (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini" (network drive share, Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" (network drive share, Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd" (internet socket)
// "\Device\Console000F" (unique name for any Console handle)
// "\Device\NamedPipe\Pipename" (named pipe)
// "\BaseNamedObjects\Objectname" (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt" (HKEY_CLASSES_ROOT\.txt)
DWORD GetNtPathFromHandle(HANDLE h_File, CString* ps_NTPath)
{
if (h_File == 0 || h_File == INVALID_HANDLE_VALUE)
return ERROR_INVALID_HANDLE;
// NtQueryObject() returns STATUS_INVALID_HANDLE for Console handles
if (IsConsoleHandle(h_File))
{
ps_NTPath->Format(L"\\Device\\Console%04X", (DWORD)(DWORD_PTR)h_File);
return 0;
}
BYTE u8_Buffer[2000];
DWORD u32_ReqLength = 0;
UNICODE_STRING* pk_Info = &((OBJECT_NAME_INFORMATION*)u8_Buffer)->Name;
pk_Info->Buffer = 0;
pk_Info->Length = 0;
// IMPORTANT: The return value from NtQueryObject is bullshit! (driver bug?)
// - The function may return STATUS_NOT_SUPPORTED although it has successfully written to the buffer.
// - The function returns STATUS_SUCCESS although h_File == 0xFFFFFFFF
NtQueryObject()(h_File, ObjectNameInformation, u8_Buffer, sizeof(u8_Buffer), &u32_ReqLength);
// On error pk_Info->Buffer is NULL
if (!pk_Info->Buffer || !pk_Info->Length)
return ERROR_FILE_NOT_FOUND;
pk_Info->Buffer[pk_Info->Length /2] = 0; // Length in Bytes!
*ps_NTPath = pk_Info->Buffer;
return 0;
}
// converts
// "\Device\HarddiskVolume3" -> "E:"
// "\Device\HarddiskVolume3\Temp" -> "E:\Temp"
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" -> "E:\Temp\transparent.jpeg"
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" -> "I:\foto.jpg"
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" -> "P:\Data\Passwords.txt"
// "\Device\Floppy0\Autoexec.bat" -> "A:\Autoexec.bat"
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" -> "H:\VIDEO_TS\VTS_01_0.VOB"
// "\Device\Serial1" -> "COM1"
// "\Device\USBSER000" -> "COM4"
// "\Device\Mup\ComputerName\C$\Boot.ini" -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" -> "\\ComputerName\Shares\Dance.m3u"
// returns an error for any other device type
DWORD GetDosPathFromNtPath(const WCHAR* u16_NTPath, CString* ps_DosPath)
{
DWORD u32_Error;
if (wcsnicmp(u16_NTPath, L"\\Device\\Serial", 14) == 0 || // e.g. "Serial1"
wcsnicmp(u16_NTPath, L"\\Device\\UsbSer", 14) == 0) // e.g. "USBSER000"
{
HKEY h_Key;
if (u32_Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\DeviceMap\\SerialComm", 0, KEY_QUERY_VALUE, &h_Key))
return u32_Error;
WCHAR u16_ComPort[50];
DWORD u32_Type;
DWORD u32_Size = sizeof(u16_ComPort);
if (u32_Error = RegQueryValueEx(h_Key, u16_NTPath, 0, &u32_Type, (BYTE*)u16_ComPort, &u32_Size))
{
RegCloseKey(h_Key);
return ERROR_UNKNOWN_PORT;
}
*ps_DosPath = u16_ComPort;
RegCloseKey(h_Key);
return 0;
}
if (wcsnicmp(u16_NTPath, L"\\Device\\LanmanRedirector\\", 25) == 0) // Win XP
{
*ps_DosPath = L"\\\\";
*ps_DosPath += (u16_NTPath + 25);
return 0;
}
if (wcsnicmp(u16_NTPath, L"\\Device\\Mup\\", 12) == 0) // Win 7
{
*ps_DosPath = L"\\\\";
*ps_DosPath += (u16_NTPath + 12);
return 0;
}
WCHAR u16_Drives[300];
if (!GetLogicalDriveStrings(300, u16_Drives))
return GetLastError();
WCHAR* u16_Drv = u16_Drives;
while (u16_Drv[0])
{
WCHAR* u16_Next = u16_Drv +wcslen(u16_Drv) +1;
u16_Drv[2] = 0; // the backslash is not allowed for QueryDosDevice()
WCHAR u16_NtVolume[1000];
u16_NtVolume[0] = 0;
// may return multiple strings!
// returns very weird strings for network shares
if (!QueryDosDevice(u16_Drv, u16_NtVolume, sizeof(u16_NtVolume) /2))
return GetLastError();
int s32_Len = (int)wcslen(u16_NtVolume);
if (s32_Len > 0 && wcsnicmp(u16_NTPath, u16_NtVolume, s32_Len) == 0)
{
*ps_DosPath = u16_Drv;
*ps_DosPath += (u16_NTPath + s32_Len);
return 0;
}
u16_Drv = u16_Next;
}
return ERROR_BAD_PATHNAME;
}
HEADER FILE:
#pragma warning(disable: 4996) // wcsnicmp deprecated
#include <winternl.h>
// This makro assures that INVALID_HANDLE_VALUE (0xFFFFFFFF) returns FALSE
#define IsConsoleHandle(h) (((((ULONG_PTR)h) & 0x10000003) == 0x3) ? TRUE : FALSE)
enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
};
struct OBJECT_NAME_INFORMATION
{
UNICODE_STRING Name; // defined in winternl.h
WCHAR NameBuffer;
};
typedef NTSTATUS (NTAPI* t_NtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength);
There is a correct (although undocumented) way to do this on Windows XP which also works with directories -- the same method GetFinalPathNameByHandle uses on Windows Vista and later.
Here are the eneded declarations. Some of these are already in WInternl.h and MountMgr.h but I just put them here anyway:
#include "stdafx.h"
#include <Windows.h>
#include <assert.h>
enum OBJECT_INFORMATION_CLASS { ObjectNameInformation = 1 };
enum FILE_INFORMATION_CLASS { FileNameInformation = 9 };
struct FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; };
struct IO_STATUS_BLOCK { PVOID Dummy; ULONG_PTR Information; };
struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; };
struct MOUNTMGR_TARGET_NAME { USHORT DeviceNameLength; WCHAR DeviceName[1]; };
struct MOUNTMGR_VOLUME_PATHS { ULONG MultiSzLength; WCHAR MultiSz[1]; };
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryObject(IN HANDLE Handle OPTIONAL,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation OPTIONAL, IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL);
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryInformationFile(IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation,
IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass);
#define MOUNTMGRCONTROLTYPE ((ULONG) 'm')
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH \
CTL_CODE(MOUNTMGRCONTROLTYPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
union ANY_BUFFER {
MOUNTMGR_TARGET_NAME TargetName;
MOUNTMGR_VOLUME_PATHS TargetPaths;
FILE_NAME_INFORMATION NameInfo;
UNICODE_STRING UnicodeString;
WCHAR Buffer[USHRT_MAX];
};
Here's the core function:
LPWSTR GetFilePath(HANDLE hFile)
{
static ANY_BUFFER nameFull, nameRel, nameMnt;
ULONG returnedLength; IO_STATUS_BLOCK iosb; NTSTATUS status;
status = NtQueryObject(hFile, ObjectNameInformation,
nameFull.Buffer, sizeof(nameFull.Buffer), &returnedLength);
assert(status == 0);
status = NtQueryInformationFile(hFile, &iosb, nameRel.Buffer,
sizeof(nameRel.Buffer), FileNameInformation);
assert(status == 0);
//I'm not sure how this works with network paths...
assert(nameFull.UnicodeString.Length >= nameRel.NameInfo.FileNameLength);
nameMnt.TargetName.DeviceNameLength = (USHORT)(
nameFull.UnicodeString.Length - nameRel.NameInfo.FileNameLength);
wcsncpy(nameMnt.TargetName.DeviceName, nameFull.UnicodeString.Buffer,
nameMnt.TargetName.DeviceNameLength / sizeof(WCHAR));
HANDLE hMountPointMgr = CreateFile(_T("\\\\.\\MountPointManager"),
0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, NULL);
__try
{
DWORD bytesReturned;
BOOL success = DeviceIoControl(hMountPointMgr,
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &nameMnt,
sizeof(nameMnt), &nameMnt, sizeof(nameMnt),
&bytesReturned, NULL);
assert(success && nameMnt.TargetPaths.MultiSzLength > 0);
wcsncat(nameMnt.TargetPaths.MultiSz, nameRel.NameInfo.FileName,
nameRel.NameInfo.FileNameLength / sizeof(WCHAR));
return nameMnt.TargetPaths.MultiSz;
}
__finally { CloseHandle(hMountPointMgr); }
}
and here's an example usage:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFile(_T("\\\\.\\C:\\Windows\\Notepad.exe"),
0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
assert(hFile != NULL && hFile != INVALID_HANDLE_VALUE);
__try
{
wprintf(L"%s\n", GetFilePath(hFile));
// Prints:
// C:\Windows\notepad.exe
}
__finally { CloseHandle(hFile); }
return 0;
}
edit Thanks for the comments about this being Vista or Server 2008 only. I missed that in the page. Guess I should have read the whole article ;)
It looks like you can use GetFileInformationByHandleEx() to get this information.
You'll likely want to do something like:
GetFileInformationByHandleEx( fileHandle, FILE_NAME_INFO, lpFileInformation, sizeof(FILE_NAME_INFO));
Double check the MSDN page to make sure I haven't misled you too badly :)
Cheers,
Taylor
FWIW, here's the same solution from the MSDN article suggested by Prakash in Python using the wonderful ctypes:
from ctypes import *
# get handle to c:\boot.ini to test
handle = windll.kernel32.CreateFileA("c:\\boot.ini", 0x80000000, 3, 0, 3, 0x80, 0)
hfilemap = windll.kernel32.CreateFileMappingA(handle, 0, 2, 0, 1, 0)
pmem = windll.kernel32.MapViewOfFile(hfilemap, 4, 0, 0, 1)
name = create_string_buffer(1024)
windll.psapi.GetMappedFileNameA(windll.kernel32.GetCurrentProcess(), pmem, name, 1024)
print "The name for the handle 0x%08x is %s" % (handle, name.value)
# convert device name to drive letter
buf = create_string_buffer(512)
size = windll.kernel32.GetLogicalDriveStringsA(511, buf)
names = buf.raw[0:size-1].split("\0")
for drive in names:
windll.kernel32.QueryDosDeviceA(drive[0:2], buf, 512)
if name.value.startswith(buf.value):
print "%s%s" % (drive[0:2], name.value[len(buf.value):])
break
For Windows Vista and later I prefer to use
GetFinalPathNameByHandle()
char buf[MAX_PATH];
GetFinalPathNameByHandleA(fileHandle, buf, sizeof(buf), VOLUME_NAME_DOS)
For Windows XP I prefer the solution by Mehrdad.
So I load GetFinalPathNameByHandle() dynamically via GetProcAddress() and if this fails (because it's Windows XP) I go for Mehrdad's solution with NtQueryObject()
If you need to do this on Win32 pre-Vista or Server 2008, look at the GetMappedFileName(...) function, which is one of the best kept secrets in Win32. WIth a little C/C++-fu, you can memory map a small portion of the file in question, and then pass that handle to this function.
Also, on Win32, you cannot really delete a file that is open (the open/unlink issue mentioned on another answer) - you can mark it for deletion on close, but it will still hang around until its last open handle is closed. Dunno if mapping (via mmap(...)) the file in this case would help, because it has to point back to a physical file...
-=- James.
On unixes there is no real way of reliably doing this. In unix with the traditional unix filesystem, you can open a file and then unlink it (remove its entry from the directory) and use it, at which point the name isn't stored anywhere. In addition, because a file may have multiple hardlinks into the filesystem, each of the names are equivalent, so once you've got just the open handle it wouldn't be clear which filename you should map back towards.
So, you may be able to do this on Win32 using the other answers, but should you ever need to port the application to a unix enviornment, you'll be out of luck. My advice to you is to refactor your program, if possible, so that you don't need the OS to be able to maintain an open resource to filename connection.
How do I remove a USB drive using the Win32 API? I do a lot of work on embedded systems and on one of these I have to copy my programs on a USB stick and insert it into the target hardware.
Since I mostly work on the console I don't like to use the mouse and click on the small task-bar icon hundred times a day.
I'd love to write a little program to do exactly that so I can put it into my makefiles, but I haven't found any API call that does the same thing.
Any ideas?
You can use the CM_Request_Device_Eject() function as well as some other possibilities.
Consult the following projects and articles:
DevEject: Straightforward.
http://www.withopf.com/tools/deveject/
A useful CodeProject article:
http://www.codeproject.com/KB/system/RemoveDriveByLetter.aspx
It looks like Sync lets you specify -e to eject removable drives. While not a win32 API, you could probably just call sync -e [drive_letter] from your makefile.
Here is a technet article about removable storage media. Look for DismountNtmsMedia.
Here's a solution in Delphi, that I've modified and put into a service for use in a very large enterprise. Go to: link text
Look for "scapi (Setup & Config Manager API)", and download it. There will be a demo program called USBView that will get you on your way. If you have Delphi, this also includes a TUSBDeviceTree component that you can use to gather information about a USB device when.
Regards
#include<SetupAPI.h>
#include <windows.h>
#include<initguid.h>
#include <newdev.h>
#include <Cfgmgr32.h>
#pragma comment(lib, "Cfgmgr32.lib")
#pragma comment(lib, "Setupapi.lib")
#pragma comment(lib, "Newdev.lib")
int RemoveDevice(const GUID *guid, const wchar_t *hwID) {
HDEVINFO m_hDevInfo;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd =
(PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
printf("try to remove device::%ws\n", hwID);
m_hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT| DIGCF_DEVICEINTERFACE);
if (m_hDevInfo == INVALID_HANDLE_VALUE)
{
printf("GetClassDevs Failed!\n");
return 0;
}
spdid.cbSize = sizeof(spdid);
for (int i = 0; SetupDiEnumDeviceInterfaces(m_hDevInfo, NULL, guid, i, &spdid); i++) {
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(m_hDevInfo,
&spdid, NULL, 0, &dwSize, NULL);
if (dwSize != 0 && dwSize <= sizeof(Buf)) {
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory((PVOID)&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res =
SetupDiGetDeviceInterfaceDetail(m_hDevInfo, &
spdid, pspdidd,
dwSize, &dwSize,
&spdd);
if (res) {
OLECHAR* guidString;
OLECHAR* guidString2;
StringFromCLSID(&spdd.ClassGuid, &guidString);
StringFromCLSID(&spdid.InterfaceClassGuid, &guidString2);
printf("%d, %ws, %ws, %ws\n", spdd.DevInst, pspdidd->DevicePath, guidString, guidString2);
CoTaskMemFree(guidString);
CoTaskMemFree(guidString2);
if (!memcmp(pspdidd->DevicePath, hwID, 2 * lstrlenW(hwID))) {
DEVINST DevInstParent = 0;
res = CM_Get_Parent(&DevInstParent, spdd.DevInst, 0);
for (long tries = 0; tries < 10; tries++) {
// sometimes we need some tries...
WCHAR VetoNameW[MAX_PATH];
PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
VetoNameW[0] = 0;
res = CM_Request_Device_EjectW(DevInstParent,
&VetoType, VetoNameW, MAX_PATH, 0);
if ((res == CR_SUCCESS &&
VetoType == PNP_VetoTypeUnknown)) {
printf("remove %ws success!\n", pspdidd->DevicePath);
SetupDiDestroyDeviceInfoList(m_hDevInfo);
return 1;
}
Sleep(500); // required to give the next tries a chance!
}
break;
}
}
}
}
printf("Remove Device Failed!\n");
SetupDiDestroyDeviceInfoList(m_hDevInfo);
return 0;
}
int main(){
GUID GUID_DEVINTERFACE_USB_HUB;
CLSIDFromString(L"F18A0E88-C30C-11D0-8815-00A0C906BED8", &GUID_DEVINTERFACE_USB_HUB);
RemoveDevice(&GUID_DEVINTERFACE_USB_HUB, L"\\\\?\\usb#root_hub30");
return 0;
}
refrences:
How to Prepare a USB Drive for Safe Removal
GUID_DEVINTERFACE