weiss jemand wie man SD-Cards per C#-Programm auswirft?
Im Explorer geht man hierzu ja auf das entsprechende
Laufwerk, drückt die rechte Maustaste und wählt dann
'Auswerfen'.
Doch wie macht man genau das per C#?
Unter Google finden sich hunderte Einträge, die sich
mit dem Thema 'CDs auswerfen' befassen, ich muss aber
SD-Cards 'auswerfen'. Es geht auch NICHT um "Stick
entfernen", wie das bei USB-Sticks zu machen ist!
Wer weiss Rat?
Schönen Dank für die Hilfe!!!
Tschüss!
Eddi
Das müsste doch der gleiche Sys-Befehl sein, der auch die CD auswirft. Hast
Du die Beispiele für das Auswerfen einer CD schon probiert?
mfg GP
> weiss jemand wie man SD-Cards per C#-Programm auswirft?
Falls man darauf Daten schreibt,
dann muss man wohl Win32 APIs wie (c++)
http://support.microsoft.com/?kbid=165721
nehmen, + PInvoke
Falls es doch wie USB -Stick entfernen wäre:
Und zwar geht es wohl über die Low-Level Setup- bzw PnP APIs wie
CM_Request_Device_Eject
CM_Query_And_Remove_SubTree
-> MSDN
C++:
http://www.google.com/groups?threadm=OEMfXND8CHA.2416%40TK2MSFTNGP10.phx.gbl
http://www.google.com/groups?selm=Extmp1vqDHA.1548%40cpmsftngxa06.phx.gbl
Win 9x/ME
http://www.google.com/groups?threadm=Xh0Q%236n1DHA.3908%40cpmsftngxa07.phx.gbl
--
Thomas Scheidegger - MVP .NET - 'NETMaster'
http://www.cetus-links.org/oo_dotnet.html - http://dnetmaster.net/
Vielen Dank für Deine Tipps!
> Falls man darauf Daten schreibt, dann muss man wohl
> Win32 APIs wie (c++) http://support.microsoft.com/
> ?kbid=165721 nehmen, + PInvoke
Unter Q165721 findet man ein recht gutes Beispiel, das die
Vorgehensweise demonstriert. Ich habe es nach C# umgesetzt,
bin jedoch in einer Sackgasse gelandet. Weiter unten
mehr ...
> Falls es doch wie USB -Stick entfernen wäre:
> Und zwar geht es wohl über die Low-Level Setup- bzw
> PnP APIs wie CM_Request_Device_Eject
> CM_Query_And_Remove_SubTree -> MSDN
Das dürfte wohl nicht der richtige Weg sein, zumindest,
wenn es sich um einen externen Kartenleser handelt, der
über USB angeschlossen ist. Wenn man dann wie bei einem
USB-Stick vorgeht, also "Hardware sicher entfernen" wählt,
wird der komplette Leser "entfernt". Man muß ihn dann aus-
und wieder einstecken. Es soll ja aber nur die
Speicherkarte (ohne Datenverlust) entnommen werden können.
Ich bin etwas darüber verwundert, warum das ganze so
kompliziert zu sein scheint. Es geht doch eigentlich nur
darum, daß die im Puffer befindlichen Daten auf die Karte
geschrieben werden, damit sie sicher entnommen werden
kann. Ich habe eigentlich erwartet, daß das mit einem
Aufruf erledigt ist.
Gibt es vielleicht eine Möglichkeit, die "Auswerfen"-
Funktion des Explorers "anzuzapfen", die ja so hervorragend
funktioniert?
Aber jetzt zu Q165721:
Unten poste ich eine Testversion mit den essentiellen
Code-Teilen. Die Konstanten kommen übrigens aus WINIOCTL.H.
Zuerst wird das betreffende Volume geöffnet (CreateFile),
dann wird es gelockt (DeviceIoControl mit
FSCTL_LOCK_VOLUME). Anschließend wird es abgemounted
(DeviceIoControl mit FSCTL_DISMOUNT_VOLUME).
CreateFile klappt hervorragend, jedoch der anchliessende
Lock schlägt (immer, auch nach vorangegangenem Boot) fehl.
Ignoriert man das und versucht Dismount, wird auch hier
'false' zurückgeliefert. Zum Lock habe ich den aussage-
kräftigen Satz "If any other application or the system
is using the volume, this IOCTL fails" gefunden.
Vielleicht greift ja Windows (in meinem Fall XP) "immer"
auf das Volume zu? Das Abmelden im Windows-Explorer
(rechte Maustaste über dem Laufwerk, dann "Auswerfen")
funktioniert natürlich problemlos.
Ich habe auch versucht, diese in Q165721 beschriebene
Vorgehensweise mit einem CD-Laufwerk durchzuführen.
Hier erhalte ich die gleichen Probleme!
Vielleicht hat jemand Erfahrung mit DeviceIoControl
und FSCTL_LOCK_VOLUME und kann mir weiterhelfen?!
Bin für jeden Tipp dankbar!
Grüsse
Eddi
=======================================================
Hier die Testversion:
public class Eject {
public const int FILE_SHARE_READ = 0x00000001;
public const int FILE_SHARE_WRITE = 0x00000002;
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint FSCTL_LOCK_VOLUME = 6;
public const uint FSCTL_DISMOUNT_VOLUME = 8;
public const uint IOCTL_STORAGE_MEDIA_REMOVAL = 0x0201;
public const int OPEN_EXISTING = 3;
public const int OPEN_ALWAYS = 4;
public const int INVALID_HANDLE_VALUE = -1;
[System.Runtime.InteropServices.DllImport("kernel32",
SetLastError=true)]
private static extern bool DeviceIoControl(
IntPtr FileHandle, uint dwIoControlCode,
IntPtr lpInBuf, uint nInBufSize, byte[] lpOutBuf,
uint nOutBufSize, out uint lpBytesReturned,
IntPtr lpOverlapped);
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern IntPtr CreateFile(
string lpFileName, uint dwDesiredAccess,
int dwShareMode, IntPtr lpSecurityAttributes,
int dwCreationDisposition, int dwFlagsAndAttributes,
IntPtr hTemplateFile);
public static bool Test() {
// HOWTO: Ejecting Removable Media in Windows NT/
// Windows 2000/Windows XP (Q165721)
// 1. Call CreateFile with GENERIC_READ|GENERIC_WRITE,
// FILE_SHARE_READ|FILE_SHARE_WRITE, and
// OPEN_EXISTING. The lpFileName parameter should
// be \\.\X: (where X is the real drive letter).
// All other parameters can be zero.
// 2. Lock the volume by issuing the FSCTL_LOCK_VOLUME
// IOCTL via DeviceIoControl. If any other
// application or the system is using the volume,
// this IOCTL fails. Once this function returns
// successfully, the application is guaranteed that
// the volume is not used by anything else in the
// system.
// 3. Dismount the volume by issuing the
// FSCTL_DISMOUNT_VOLUME IOCTL. This causes the
// file system to remove all knowledge of the
// volume and to discard any internal information
// that it keeps regarding the volume.
// ...
int i;
bool ok = false;
// 1. Create File
string VolumeStr = @"\\.\E:"; // Hier Lw E:\
uint AccessFlags = GENERIC_READ | GENERIC_WRITE;
int ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE;
int CreationDisp = OPEN_EXISTING;
IntPtr SecurityAtt = IntPtr.Zero;
IntPtr FileTemplate = IntPtr.Zero;
IntPtr hVolume = CreateFile(VolumeStr, AccessFlags,
ShareMode, SecurityAtt, CreationDisp, 0,
FileTemplate);
if ((int)hVolume==INVALID_HANDLE_VALUE) return false;
// 2. Lock Volume
IntPtr InBuf = IntPtr.Zero;
byte[] OutBuf = new byte[0];
uint BytesReturned = 0;
IntPtr Overlapped = IntPtr.Zero;
for (i=1; i<=20; i++) { // 20 Versuche
ok = DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME,
InBuf, 0, OutBuf, 0, out BytesReturned,
Overlapped);
if (ok) break;
System.Threading.Thread.Sleep(500);
}
if (!ok) return false;
// 3. Dismount Volume
ok = DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME,
InBuf, 0, OutBuf, 0, out BytesReturned,
Overlapped);
if (!ok) return false;
return true;
}
}
Direkt oder als API meines Wissens nicht,
was aber evtl. ginge ist den Eject-Dialog anzuzeigen,
per Prozess (Kommandozeile):
RUNDLL32.EXE shell32.dll,Control_RunDLL hotplug.dll
> Die Konstanten kommen übrigens aus WINIOCTL.H.
aber vermutlich ist 'FSCTL_LOCK_VOLUME' falsch,
ich bekomme 0x00090018, nicht 6, Formel ist
#define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
hier meine Probe-Variante [Hack, ungetestet]
// ===================================================================
bool ok;
IntPtr h = CreateFile( @"\\.\E:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero );
if( h.ToInt32() == -1 )
return;
for( int i = 0; i < 10; i++ )
{
int nout = 0;
ok = DeviceIoControl( h, FSCTL_LOCK_VOLUME, null, 0, null, 0, out nout, IntPtr.Zero );
if( ok )
break;
Thread.Sleep( 500 );
}
ok = CloseHandle( h );
.........................
// some Win32 constants
private const int INVALID_HANDLE_VALUE = -1;
private const int GENERIC_READ = unchecked( (int) 0x80000000 );
private const int GENERIC_WRITE = unchecked( (int) 0x40000000 );
private const int FILE_SHARE_READ = unchecked( (int) 0x00000001 );
private const int FILE_SHARE_WRITE = unchecked( (int) 0x00000002 );
private const int OPEN_EXISTING = unchecked( (int) 3 );
[DllImport("kernel32.dll", EntryPoint="CreateFileW", CharSet=CharSet.Unicode, SetLastError=true)]
private static extern IntPtr CreateFile(
string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr lpSecurityAttributes, int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile );
[DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true)]
private static extern bool CloseHandle( IntPtr handle );
private const int FSCTL_LOCK_VOLUME = unchecked( (int) 0x00090018 );
[DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true) ]
private static extern bool DeviceIoControl(
IntPtr hDevice, int dwIoControlCode, byte[] lpInBuffer, int nInBufferSize,
byte[] lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped );
// ===================================================================
ich bin echt BEEINDRUCKT, wie schnell man hier ECHTE
Hilfe bekommt! Vielen Dank für dein Engagement!
> ... was aber evtl. ginge ist den Eject-Dialog anzuzeigen,
> per Prozess (Kommandozeile):
> RUNDLL32.EXE shell32.dll,Control_RunDLL hotplug.dll
Prima! Das ist der Dialog "Hardware sicher entfernen",
der z. B. für die Abmeldung von USB-Sticks benötigt wird.
Wenn ich das jedoch mit meinem am USB-Port eingesteckten
externen SD-Kartenleser mache, ist der komplette
Leser "entfernt". Man muß ihn dann aus- und wieder
einstecken - wie einen USB-Stick eben. Trotzdem sicherlich
für andere Anwendungszwecke äusserst interessant!
> aber vermutlich ist 'FSCTL_LOCK_VOLUME' falsch,
> ich bekomme 0x00090018 ...
> hier meine Probe-Variante [Hack, ungetestet]
Dein als "Hack, ungetestet" bezeichneter Code hat sich als
Volltreffer entpuppt. Es sieht jetzt sehr gut aus. Auch
die weiteren Schritte funktionieren:
----------------------------------------------------------
// dismount volume (Schritt 3 zu Q165721)
int xout = 0;
const int FSCTL_DISMOUNT_VOLUME = unchecked( ( int )
0x00090020 );
ok = DeviceIoControl( h, FSCTL_DISMOUNT_VOLUME,
null, 0,
null, 0,
out xout, IntPtr.Zero );
if( !ok )
return false;
// prevent removal of volume (Schritt 4)
// ???
// eject media (Schritt 5)
const int IOCTL_STORAGE_EJECT_MEDIA = unchecked( ( int )
0x002D4808 );
ok = DeviceIoControl( h, IOCTL_STORAGE_EJECT_MEDIA,
null, 0,
null, 0,
out xout, IntPtr.Zero );
if( !ok )
return false;
// close handle (Schritt 6)
ok = CloseHandle( h );
return ok;
----------------------------------------------------------
Nur mit "Prevent removal of volume" (Schritt 4) habe ich
als C++ Legastheniker noch so meine Probleme. Wie muß ich
denn den Code umsetzen? Es geht speziell um die Übergabe
der struct. Hier der Original-Code
aus WINIOCTL.H:
// IOCTL_STORAGE_MEDIA_REMOVAL disables the mechanism
// on a storage device that ejects media. This function
// may or may not be supported on storage devices that
// support removable media.
//
// TRUE means prevent media from being removed.
// FALSE means allow media removal.
//
typedef struct _PREVENT_MEDIA_REMOVAL {
BOOLEAN PreventMediaRemoval;
} PREVENT_MEDIA_REMOVAL, *PPREVENT_MEDIA_REMOVAL;
aus Q165721:
BOOL PreventRemovalOfVolume(HANDLE hVolume, BOOL
fPreventRemoval)
{
DWORD dwBytesReturned;
PREVENT_MEDIA_REMOVAL PMRBuffer;
PMRBuffer.PreventMediaRemoval = fPreventRemoval;
return DeviceIoControl( hVolume,
IOCTL_STORAGE_MEDIA_REMOVAL,
&PMRBuffer, sizeof(PREVENT_MEDIA_REMOVAL),
NULL, 0,
&dwBytesReturned,
NULL);
}
}
Kannst Du mir da noch mal hilfreich unter die Arme greifen?
Besten Dank!
Eddi
vielen Dank für deinen Hinweis!
Wenn man nach "CD auswerfen" sucht, stösst man
immer auf
mciSendString("set cdaudio door open wait" ...)
oder
mciSendString("set cdaudio door closed wait" ...)
Ich denke mal, daß man damit keine SD-Karten
auswerfen kann.
Ich habe aber den unter Q165721
"HowTo: Ejecting Removable Media in Windows NT ..."
beschriebenen Weg mit meinem CD-Laufwerk getestet.
Die CD wurde hierbei - wie erwartet - ausgeworfen.
Grüsse
Eddi
>-----Originalnachricht-----
>.
>
solange es nur gerade jene struct ist,
dann würde ich eine Abkürzung wählen:
'BOOLEAN' ist dort ein einzelnes Byte,
und somit würde ich einfach den DllImport-Prototyp
für 'DeviceIoControl' aus meinem vorigen Post nehmen:
[DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true) ]
private static extern bool DeviceIoControl(
IntPtr hDevice, int dwIoControlCode, byte[] lpInBuffer, int nInBufferSize,
byte[] lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped );
und einfach ein Byte-Array mit Länge 1 übergeben, C# etwa:
byte[] flg = new byte[1];
flg[0] = 0; // 0 = FALSE
ok = DeviceIoControl( h, IOCTL_STORAGE_MEDIA_REMOVAL, flg, 1, ....
vielen Dank, es läuft jetzt hervorragend!
Unten noch mal der komplette (getestete) Democode:
Grüsse
Eddi
---------------------------------------------------------
using System;
using System.Runtime.InteropServices;
using System.Threading;
public class Eject {
private const int INVALID_HANDLE_VALUE = -1;
private const int GENERIC_READ = unchecked( (int)
0x80000000 );
private const int GENERIC_WRITE = unchecked( (int)
0x40000000 );
private const int FILE_SHARE_READ = unchecked( (int)
0x00000001 );
private const int FILE_SHARE_WRITE = unchecked( (int)
0x00000002 );
private const int OPEN_EXISTING = unchecked( (int) 3 );
private const int FSCTL_LOCK_VOLUME = unchecked( (int)
0x00090018 );
private const int FSCTL_DISMOUNT_VOLUME = unchecked(
(int) 0x00090020 );
private const int IOCTL_STORAGE_EJECT_MEDIA = unchecked(
(int) 0x002D4808 );
private const int IOCTL_STORAGE_MEDIA_REMOVAL = unchecked
( (int) 0x002D4804 );
[DllImport("kernel32.dll", EntryPoint="CreateFileW",
CharSet=CharSet.Unicode, SetLastError=true)]
private static extern IntPtr CreateFile(
string lpFileName, int dwDesiredAccess,
int dwShareMode, IntPtr lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile );
[DllImport("kernel32.dll", ExactSpelling=true,
SetLastError=true)]
private static extern bool CloseHandle( IntPtr handle );
[DllImport("kernel32.dll", ExactSpelling=true,
SetLastError=true) ]
private static extern bool DeviceIoControl(
IntPtr hDevice, int dwIoControlCode,
byte[] lpInBuffer, int nInBufferSize,
byte[] lpOutBuffer, int nOutBufferSize,
out int lpBytesReturned, IntPtr lpOverlapped );
public static bool Test() {
bool ok = false;
bool KarteKannEntnommenWerden = false;
// Schritt 1: Volume öffnen // Laufwerk anpassen! //
IntPtr h = CreateFile( @"\\.\E:", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero );
if( h.ToInt32() == -1 )
return false;
while( true ) {
// Schritt 2: Volume sperren
for( int i = 0; i < 10; i++ ) {
int nout = 0;
ok = DeviceIoControl( h, FSCTL_LOCK_VOLUME,
null, 0, null, 0, out nout, IntPtr.Zero );
if( ok )
break;
Thread.Sleep( 500 );
}
if( !ok )
break;
// Schritt 3: Volume dismounten
int xout = 0;
ok = DeviceIoControl( h, FSCTL_DISMOUNT_VOLUME,
null, 0, null, 0, out xout, IntPtr.Zero );
if( !ok )
break;
// ab hier kann die Karte ohne Datenverlust
// entnommen werden
KarteKannEntnommenWerden = true;
// Schritt 4: Prevent Removal Of Volume
byte[] flg = new byte[1];
flg[0] = 0; // 0 = false
int yout = 0;
ok = DeviceIoControl( h,
IOCTL_STORAGE_MEDIA_REMOVAL, flg, 1,
null, 0, out yout, IntPtr.Zero );
if( !ok )
break;
// Schritt 5: Eject Media
ok = DeviceIoControl( h,
IOCTL_STORAGE_EJECT_MEDIA,
null, 0, null, 0, out xout, IntPtr.Zero );
break;
}
// Schritt 6: Close Handle
ok = CloseHandle( h );
return KarteKannEntnommenWerden;
}
}