QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: Richard on January 13, 2021, 08:21:44 am

Title: Program code to simulate removing and inserting USB stick
Post by: Richard 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?

Title: Re: Program code to simulate removing and inserting USB stick
Post by: euklides 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)
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Pete 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
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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 (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 (https://social.msdn.microsoft.com/Forums/vstudio/en-US/22790e90-d923-4069-a32c-caaf96f7d0f6/whats-eject-usb-drive-api-?forum=csharpgeneral)
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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."
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard 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.
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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 (https://stackoverflow.com/questions/16295774/winapi-calls-to-access-usb-storage-which-has-no-drive-letter)
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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)
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard 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.

Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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.
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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)
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard 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).

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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.
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard on January 15, 2021, 05:04:06 am
@SpriggsySpriggs

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

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

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)

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

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

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

BUT the device is STILL NOT READY.

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

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

Am I still missing something in the process?

Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs 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}\
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard on January 15, 2021, 02:34:08 pm
@SpriggsySpriggs

Thanks for your reply.

My (second-hand) laptop has, which various 3-rd party software reports as Windows "Enterprise", has Windows 10 x64 Pro build 20H2 and updated up to about a month ago. Sometimes there are some various small differences the way Windows seems to operate (maybe this thread is yet another one) and for instance once I tried to dual install Linux on to it and it really totally crashed my system - worst most complicated reinstall of Windows and files I ever experienced.

With your program I simply entered "J"  (no slashes or colon or ") as you might notice from the tiny screenshot above. I had to use the /D switch as part of the "recovery process"  as also shown in the screenshot above - essentially in the last screenshot, this is the minimum steps to restore my usb stick back to normal (i tried only as per your last sentence above - but it does not restore for me). I ran both QB64 AND CMD as ADMINISTRATOR for the tests.

A couple of reasons why I want to do it:-

A fair percentage of my usb sticks (I have many different models) run very hot (too hot to touch) but still appear to function properly - to date none have failed or died. Because of my program automated projects these usb sticks may be still connected for days continuously, and I am not always around (or awake), so if the program cycles round to say 3am in the morning to cross-check the usb stick data and only needs 5 minutes connection - I was hoping that shutting down ("ejecting") the usb sticks would allow the HOT ones to cool down (and stay cool) until when and if they are accessed again automatically.

I have "quarantined" a number of usb sticks that were "compromised" when my laptop was infected with a "virus" a year ago. Again automating the process - I am investigating "minimal connect time" of these suspect usb sticks of extracting all the data (and possibly purposely fragmenting the data) - so should a virus be present (it seemed to take windows automatic  defender anti-virus about 4 weeks (if my memory serves correct) to eliminate ALL the virus renamed-exe files (that is another story))  So, if automatically a suspect usb stick is only "connected" for say a minute rather than a day - it minimises the opportunity for the suspect usb stick virus (if present) to do damage - it can take a fair while for Windows anti virus to scan the many files on the usb sticks.

Hope this gives you some insight of why the "ejecting" etc (I like the idea of automated programs rather than me manually).

Thanks for all your efforts - at least the first program to eject only will be quite useful - I may have to try out SHELLing the mountvol for the remounting the usb sticks.

I forgot to mention that ALL my drives, except A:\, are FOLDER PROTECTED - so for the first time a new program runs (eg yours) I have to "Allow" the application access to J:\ - but thereafter I don't have to worry. All my results reporting to you  was when your program(s) were running from the second time onwards.
Title: Re: Program code to simulate removing and inserting USB stick
Post by: SpriggsySpriggs on January 15, 2021, 03:13:49 pm
@Richard Ok that makes more sense to me now. Hopefully some piece of that code will end up being useful to you. I'll keep this in mind for later down the road if I happen upon some code that better emulates the ejecting and mounting. It was actually a little difficult just to find the code that I gave you.
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard on January 15, 2021, 08:42:01 pm
@SpriggsySpriggs

Just going on a bit of a "tangent" (but it MAY still be related to all that has been said)...


I purchased a brand new usb stick 64 GByte some time ago - the only purpose of this "special" usb stick is solely for manually copying over files from a 2009 MAC computer to PC. The MAC I only use occasionally and I am too cheap to spend any more money on it (like buy software to enable MAC to write to NTFS   OR  buy software so that PC can read/write a MAC formatted external drive). I formatted the "special" usb stick as exFAT (the lowest common denominator for interchanging data).

The "special" usb stick worked perfectly a number of times with essentially completely overwriting ALL the data - then ALL OF A SUDDEN it became "write protected" (there is no physical switch etc on the "special" usb stick for write protect).

Apparently this problem is known to occasionally occur with some usb brands (including mine "Verbatim") and all the suggestions I tried from Google (from MAC sites AND from PC sites) do not cancel the write protect status. I have forgotten exactly but there was a fairly straightforward CMD solution (run as ADMINISTRATOR) which supposedly allowed one to change the status, applied same - cmd reports now OK (not write protected) - but the device is still write protected. I also tried the usual stuff like to reformat the "special" usb stick (I don't care about the files on it - I already made a backup). Except for the write protected - the "special" usb stick seems to be working perfectly.

Just for fun I tried mountvol with some switches to rectify the situation, but with no luck.

As I say it does not matter what happens to the data - I would just like to use the "special" usb stick without restrictions.

Maybe you have access to "more sophisticated methods" of cancelling the "write protected" (hoping)?
Title: Re: Program code to simulate removing and inserting USB stick
Post by: luke on January 15, 2021, 09:14:36 pm
If you plug it into a fresh computer and can't write to it, I'd suggest returning the product as faulty.
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Pete on January 15, 2021, 09:28:58 pm
For fun, look into the DOS command attrib. easydos.com is a good site for reference. You can use the command line, or write a SHELL in QB64. You might be able to re-write the write protect by doing so.

Pete
Title: Re: Program code to simulate removing and inserting USB stick
Post by: Richard on January 15, 2021, 11:11:10 pm
@Pete

Just for fun I tried ATTRIBUTE in CMD as ADMINISTRATOR. Attribute does not work with directory like the site says. I tried it for a particular file

originally the file's attribute was  "A" BUT when did ATTRIB -R ... (FOR Read+Write) got message

Unable to change attribute .... (for the path/filename)

@luke  will try with a "fresh" computer ASAP - because the usb stick was purchased I think over a year ago, I gather any warranties are now void. (I did previously use that same usb stick many times at almost full capacity - just that all of a sudden it became "write protected" - maybe the MAC did something very funny to the usb stick.

Just in case anyone reading this knows - is there any software (free I hope) that can read AND write individual bytes on a usb stick, especially in all the FAT boot etc records? I do own a file recovery software package but apparently it is only designed to recover to another drive (never intended to repair the faulty drive )(Note I do not need to recover the files as I can still read the usb stick normally)