Author Topic: Program code to simulate removing and inserting USB stick  (Read 5142 times)

0 Members and 1 Guest are viewing this topic.

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Program code to simulate removing and inserting USB stick
« on: January 13, 2021, 08:21:44 am »
Is it possible to code in QB64 the

"Eject USB stick"  AND also (later on)  "Simulating that same USB stick as having been just inserted"

in that order, actions - without having to physically remove and insert the USB stick?


Offline euklides

  • Forum Regular
  • Posts: 128
    • View Profile
Re: Program code to simulate removing and inserting USB stick
« Reply #1 on: January 13, 2021, 08:32:44 am »
Well, it is not exactly your problem, but perhaps look here: https://www.qb64.org/forum/index.php?topic=1507.msg107057#msg107057 (Working with files located on USB devices)
Why not yes ?

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Program code to simulate removing and inserting USB stick
« Reply #2 on: January 13, 2021, 12:03:20 pm »
Ah, I miss the good ol' days when you could eject the CD tray with a simple PEEK/POKE command.

To "safely" eject a usb device would probably require a Windows API call.

Now to check to see if a drive is active, hmmm, the good ol' days again... I used to check to see if a drive was active on my QuickBASIC computer programs by polling drives a-z using an error trap to resume if no drive was found. In QB64, you could use the same method. CHDIR, for instance. Use it to map all your current drives a-z. Let's say the routine errors out all but 3 times, meaning it found 3 drives. Now insert a USB stick, and run the routine again. Now it will error out all but 4 times, meaning a new drive was detected.

Something like this, for a portion of what would be required. Note: I did not run this code, so there may be some errors. It's just to give you an idea to consider...

Code: QB64: [Select]
  1. ON ERROR GOTO handler
  2. FOR i% = 1 TO 26
  3.     CHDIR LTRIM$(STR$(i% + 64)) + ":\" ' A - Z
  4.     IF er1% THEN
  5.         er1% = 0
  6.     ELSE
  7.         numberofdrives = numberofdrives + 1
  8.         PRINT "Drive Found: "; LTRIM$(STR$(i% + 95))
  9.     END IF
  10. CHDIR mydir$
  11.  
  12. handler:
  13. er1% = -1

Pete
« Last Edit: January 13, 2021, 02:44:26 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #3 on: January 13, 2021, 01:33:51 pm »
I'm pretty busy nowadays but I can look at this. Like Pete said, you'd need a Windows API call. I have found a function that is designed to do that. It doesn't seem simple to set up. I'll have to do some testing at home and then write a wrapper for it.

https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_request_device_ejectw

or this, which looks much simpler:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/22790e90-d923-4069-a32c-caaf96f7d0f6/whats-eject-usb-drive-api-?forum=csharpgeneral
« Last Edit: January 13, 2021, 01:35:23 pm by SpriggsySpriggs »
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #4 on: January 14, 2021, 10:26:08 am »
@Richard

Believe it or not, I don't have a USB handy to test this with. I based this code off the second link in my last reply. All this does is cause an ejection of a drive when you call the sub EjectDrive with the drive letter that you are wanting to eject. I don't have code ready for trying to mount the drive again if it is still in the port. Feel free to test this with some USB that you have laying around.

Code: QB64: [Select]
  1.  
  2. CONST OPEN_EXISTING = 3
  3. CONST GENERIC_READ = &H80000000
  4. CONST GENERIC_WRITE = &H40000000
  5. CONST IOCTL_STORAGE_EJECT_MEDIA = &H2D4808
  6.  
  7.     FUNCTION CloseHandle% (BYVAL hObject AS LONG)
  8.     FUNCTION DeviceIoControl% (BYVAL hDevice AS LONG, BYVAL dwIoControlCode AS LONG, BYVAL lpInBuffer AS _OFFSET, BYVAL nInBufferSize AS LONG, BYVAL lpOutBuffer AS _OFFSET, BYVAL nOutBufferSize AS LONG, BYVAL lpBytesReturned AS _OFFSET, BYVAL lpOverlapped AS _OFFSET)
  9.     FUNCTION CreateFile& ALIAS CreateFileA (BYVAL lpFileName AS _OFFSET, BYVAL dwDesiredAccess AS LONG, BYVAL dwShareMode AS LONG, BYVAL lpSecurityAttributes AS _OFFSET, BYVAL dwCreationDisposition AS LONG, BYVAL dwFlagsAndAttributes AS LONG, BYVAL hTemplateFile AS LONG)
  10.  
  11. SUB EjectDrive (driveLetter AS STRING)
  12.     DIM path AS STRING
  13.     path = "\\.\" + driveLetter + ":"
  14.     DIM handle AS LONG
  15.     handle = CreateFile(_OFFSET(path), GENERIC_READ OR GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
  16.     IF handle = -1 THEN
  17.         PRINT "Unable to open drive " + driveLetter
  18.         EXIT SUB
  19.     END IF
  20.     DIM dummy AS INTEGER
  21.     DIM dev AS INTEGER
  22.     dev = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, 0, 0, 0, 0, _OFFSET(dummy), 0)
  23.     dev = CloseHandle(handle)
  24.     PRINT "OK to remove drive."
« Last Edit: January 14, 2021, 10:30:04 am by SpriggsySpriggs »
Shuwatch!

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Program code to simulate removing and inserting USB stick
« Reply #5 on: January 14, 2021, 12:09:52 pm »
@SpriggsySpriggs

Thanks - it worked with a usb stick in drive "J" - look forward to the mounting of same.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #6 on: January 14, 2021, 12:27:29 pm »
Here is the reference I will use for making code to mount the unmounted drive:

https://stackoverflow.com/questions/16295774/winapi-calls-to-access-usb-storage-which-has-no-drive-letter
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #7 on: January 14, 2021, 03:30:45 pm »
@Richard

I have some code made up that I based on that Stack Overflow answer. I haven't verified if it remounts an ejected drive (I don't have anything to eject right now). It is supposed to do that, though. This might require some fine finishing later as I haven't broken it down into a function. Run that first code I gave you to eject that USB drive you had and then run this code (leaving the drive plugged in) and see if it remounts it to a free letter. Again, it should but I can't test it for real just yet.

Code: QB64: [Select]
  1.  
  2.     FUNCTION FindFirstVolume& ALIAS FindFirstVolumeA (BYVAL lpszVolumeName AS _OFFSET, BYVAL cchBufferLength AS LONG)
  3.     FUNCTION FindNextVolume% ALIAS FindNextVolumeA (BYVAL hFindVolume AS LONG, BYVAL lpszVolumeName AS _OFFSET, BYVAL cchBufferLength AS LONG)
  4.     FUNCTION GetVolumePathNamesForVolumeName% ALIAS GetVolumePathNamesForVolumeNameA (BYVAL lpszVolumeName AS _OFFSET, BYVAL lpszVolumePathNames AS _OFFSET, BYVAL cchBufferLength AS LONG, BYVAL lpcchReturnLength AS _OFFSET)
  5.     FUNCTION GetLogicalDrives& ()
  6.     FUNCTION SetVolumeMountPoint% ALIAS SetVolumeMountPointA (BYVAL lpszVolumeMountPoint AS _OFFSET, BYVAL lpszVolumeName AS _OFFSET)
  7.     FUNCTION GetVolumeInformation% ALIAS GetVolumeInformationA (BYVAL lpRootPathName AS _OFFSET, BYVAL lpVolumeNameBuffer AS _OFFSET, BYVAL nVolumeNameSize AS LONG, BYVAL lpVolumeSerialNumber AS _OFFSET, BYVAL lpMaximumComponentLength AS _OFFSET, BYVAL lpFileSystemFlags AS _OFFSET, BYVAL lpFileSystemNameBuffer AS _OFFSET, BYVAL nFileSystemNameSize AS LONG)
  8.     FUNCTION FindVolumeClose% (BYVAL hFindVolume AS LONG)
  9.     FUNCTION GetLastError& ()
  10.  
  11. DIM volumeID AS STRING * 256, volumePathName AS STRING * 256, volumeName AS STRING * 256, volumeFS AS STRING * 256
  12. DIM newMountPoint AS STRING
  13. DIM volumeSerialNumber AS _UNSIGNED LONG
  14. DIM handle AS LONG
  15.  
  16. handle = FindFirstVolume(_OFFSET(volumeID), 256)
  17.  
  18.     PRINT "Volume GUID = " + volumeID
  19.     bool = GetVolumePathNamesForVolumeName(_OFFSET(volumeID), _OFFSET(volumePathName), 256, _OFFSET(size))
  20.     IF LEN(MID$(volumePathName, 1, INSTR(volumePathName, CHR$(0)) - 1)) = 0 THEN
  21.         PRINT "Not mounted"
  22.         newMountPoint = firstFreeLetter + ":\"
  23.         IF SetVolumeMountPoint(_OFFSET(newMountPoint), _OFFSET(volumeID)) <> 0 THEN
  24.             bool = GetVolumePathNamesForVolumeName(_OFFSET(volumeID), _OFFSET(volumePathName), 256, _OFFSET(size))
  25.             PRINT "Now mounted on " + volumePathName
  26.         ELSE
  27.             PRINT "Couldn't mount to " + newMountPoint, GetLastError
  28.         END IF
  29.     ELSE
  30.         PRINT "Mounted on " + volumePathName
  31.     END IF
  32.     bool = GetVolumeInformation(_OFFSET(volumePathName), _OFFSET(volumeName), 256, _OFFSET(volumeSerialNumber), 0, 0, _OFFSET(volumeFS), 256)
  33.     PRINT "Volume name "; volumeName; " FS = "; volumeFS; " Serial = "; LTRIM$(STR$(volumeSerialNumber))
  34.     PRINT
  35.     volumePathName = "" + CHR$(0): volumeFS = "" + CHR$(0): volumeName = "" + CHR$(0): volumeSerialNumber = 0
  36. LOOP WHILE FindNextVolume(handle, _OFFSET(volumeID), 256)
  37.  
  38. bool = FindVolumeClose(handle)
  39.  
  40. FUNCTION firstFreeLetter$
  41.     DIM freeLetters AS _UNSIGNED LONG
  42.     freeLetters = GetLogicalDrives
  43.     IF freeLetters < 4 THEN EXIT FUNCTION
  44.     DIM letter AS INTEGER
  45.     letter = ASC("C")
  46.     i = 4
  47.     DO
  48.         letter = letter + 1
  49.         i = _SHL(i, 1)
  50.     LOOP WHILE (freeLetters AND i) <> 0
  51.     firstFreeLetter = CHR$(letter)
Shuwatch!

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Program code to simulate removing and inserting USB stick
« Reply #8 on: January 14, 2021, 08:49:51 pm »
@SpriggsySpriggs

Just to let you know on how your program is working so far...

I unmounted usb stick (J:\) as per first program - result seems to be OK

then tried second program to mount - did not work as expected




CMD>dir j:\  reports   "The device is not ready"



the usb stick (J:\) does have a lot of files on it.

« Last Edit: January 14, 2021, 09:02:33 pm by Richard »

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #9 on: January 14, 2021, 08:59:53 pm »
You must run the code as administrator and you cannot have File Explorer open while doing either code. I'm looking at more code right now that works a little better but still having problems with it.
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #10 on: January 14, 2021, 09:10:05 pm »
@Richard

Here is my newest code. You need to run it as administrator but you can leave File Explorer open with this one. The downside: Removing a USB using this code will not allow you to just unplug it and plug it back in to remount it. It just won't mount. You have to run the MountDrives sub to mount all the unmounted drives (or just run mountvol <driveletter>: <volumeGUID> in an elevated command prompt).

Code: QB64: [Select]
  1.  
  2. DIM driveLetter AS STRING
  3.     CLS
  4.     INPUT "Enter drive letter: ", driveLetter
  5. LOOP UNTIL driveLetter <> ""
  6. EjectDrive driveLetter
  7.  
  8.  
  9. MountDrives
  10.  
  11. SUB EjectDrive (driveLetter AS STRING)
  12.     SHELL _HIDE "mountvol " + driveLetter + ": /P"
  13.  
  14. SUB MountDrives
  15.     DECLARE DYNAMIC LIBRARY "Kernel32"
  16.         FUNCTION FindFirstVolume& ALIAS FindFirstVolumeA (BYVAL lpszVolumeName AS _OFFSET, BYVAL cchBufferLength AS LONG)
  17.         FUNCTION FindNextVolume% ALIAS FindNextVolumeA (BYVAL hFindVolume AS LONG, BYVAL lpszVolumeName AS _OFFSET, BYVAL cchBufferLength AS LONG)
  18.         FUNCTION GetVolumePathNamesForVolumeName% ALIAS GetVolumePathNamesForVolumeNameA (BYVAL lpszVolumeName AS _OFFSET, BYVAL lpszVolumePathNames AS _OFFSET, BYVAL cchBufferLength AS LONG, BYVAL lpcchReturnLength AS _OFFSET)
  19.         FUNCTION GetLogicalDrives& ()
  20.         FUNCTION SetVolumeMountPoint% ALIAS SetVolumeMountPointA (BYVAL lpszVolumeMountPoint AS _OFFSET, BYVAL lpszVolumeName AS _OFFSET)
  21.         FUNCTION FindVolumeClose% (BYVAL hFindVolume AS LONG)
  22.         FUNCTION GetLastError& ()
  23.     END DECLARE
  24.  
  25.     DIM volumeID AS STRING * 256, volumePathName AS STRING * 256, volumeName AS STRING * 256, volumeFS AS STRING * 256
  26.     DIM newMountPoint AS STRING * 4
  27.     DIM volumeSerialNumber AS _UNSIGNED LONG
  28.     DIM size AS _UNSIGNED LONG
  29.     DIM handle AS LONG
  30.     DIM bool AS INTEGER
  31.  
  32.     handle = FindFirstVolume(_OFFSET(volumeID), 256)
  33.  
  34.     DO
  35.         bool = GetVolumePathNamesForVolumeName(_OFFSET(volumeID), _OFFSET(volumePathName), 256, _OFFSET(size))
  36.         IF LEN(MID$(volumePathName, 1, INSTR(volumePathName, CHR$(0)) - 1)) = 0 THEN
  37.             newMountPoint = firstFreeLetter + ":"
  38.             SHELL _HIDE "mountvol " + newMountPoint + " " + MID$(volumeID, 1, INSTR(volumeID, CHR$(0)) - 1)
  39.         END IF
  40.     LOOP WHILE FindNextVolume(handle, _OFFSET(volumeID), 256)
  41.     bool = FindVolumeClose(handle)
  42.  
  43. FUNCTION firstFreeLetter$
  44.     DIM freeLetters AS _UNSIGNED LONG
  45.     freeLetters = GetLogicalDrives
  46.     IF freeLetters < 4 THEN EXIT FUNCTION
  47.     DIM letter AS INTEGER
  48.     letter = ASC("C")
  49.     i = 4
  50.     DO
  51.         letter = letter + 1
  52.         i = _SHL(i, 1)
  53.     LOOP WHILE (freeLetters AND i) <> 0
  54.     firstFreeLetter = CHR$(letter)
« Last Edit: January 15, 2021, 10:51:53 am by SpriggsySpriggs »
Shuwatch!

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Program code to simulate removing and inserting USB stick
« Reply #11 on: January 14, 2021, 10:04:35 pm »
@SpriggsySpriggs

I think I may have misunderstood what you just replied...

I copied your first program (to eject drive)  - called it USBeject.exe
I copied the latest second program   - called it USBmount.exe

both of these exe were copied to A:\

Ran CMD as ADMINISTRATOR

a:\USBeject
A:\USBmount
dir J:\

THEN USED File Explorer to verify results.  J:\    not there

******** IT APPEARS that the usb stick is "Dead" now - it cannot be seen when physically placed in another usb slot (meaning it would become K:\) **************

if I get another usb stick (but not running your programs at all with it) - the other usb stick shows up as J:\ when inserted into the usb slot as to where the original J:\ stick was placed.

The data on the now "dead" usb stick was not really important (just the reboot Windows 10 x64 for build 20H2).

  [ You are not allowed to view this attachment ]  

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #12 on: January 15, 2021, 12:03:01 am »
You've made your USB unmountable. Like I said, you need to run mountvol. You can open command prompt as administrator and run mountvol. It will tell you the drive that isn't mounted. Copy the full drive path and then follow the usage syntax for mounting it to a letter.

My latest code was for both functions as one source. The SLEEP command was to separate out the two things and to give you a pause for you to verify that the drive is unmounted. After you press enter it tries to mount whatever it finds that isn't mounted. First, run mountvol and follow my instructions for that since you've ran something out of order which is causing you to not get the drive back. You should be good after that.
« Last Edit: January 15, 2021, 10:51:13 am by SpriggsySpriggs »
Shuwatch!

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Program code to simulate removing and inserting USB stick
« Reply #13 on: January 15, 2021, 05:04:06 am »
@SpriggsySpriggs

As per your advice - my usb stick is now "alive" again

  [ You are not allowed to view this attachment ]  

I ran your combined program and the first part unmounted j:\ but still "listed" in file explorer but of course not "accessible" to view files (previously your first program did not "list" J:\ in file explorer)

  [ You are not allowed to view this attachment ]  

NOW (with usb stick not physically moved) allowed your combined program to continue

  [ You are not allowed to view this attachment ]  

BUT the device is STILL NOT READY.

NOW manually recovered J:\ to put J:\ back to normal

  [ You are not allowed to view this attachment ]  

Am I still missing something in the process?


Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Program code to simulate removing and inserting USB stick
« Reply #14 on: January 15, 2021, 10:43:20 am »
I don't know, @Richard .

Unless it is a versioning difference in Windows 10 (my version is Windows 10 64 bit 20H2, compiled in QB64 x64) I can't know for sure what is causing your run to be different than mine. It worked fine (for what it is) on my machine. What's your reason for wanting to eject and mount USB in QB64?

Are you running my code with simply the letter itself? No colon, no slash? You should only be having EjectDrive "J". Also, for remounting with mountvol, you only need it as mountvol J: \\?\Volume{whateveryourguidis}\
« Last Edit: January 15, 2021, 11:17:29 am by SpriggsySpriggs »
Shuwatch!