C++ -> dll

Hallo,

ist es eigentlich aufwändig, mit C++ eine DLL herzustellen?

Falls nein, hätte ich eine Bitte. :smile:

In VB6 kann ich mit …
CopyMemory Lib „kernel32“ Alias „RtlMoveMemory“ (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
… einige Bytes (Anzahl steht in Bytelen) verschieben. Um aber den Inhalt von zwei Bereichen auszutauschen muss ich das drei mal aufrufen und dann ist ein ‚Ringtausch‘ mit VB schneller. Ein ‚SWAP‘ gibt es nicht, b.z.w. ich konnte keine entsprechende DLL finden.

Hat Jemand so etwas oder kann mir das schreiben?

Gruß, Rainer

Hallo,

ist es eigentlich aufwändig, mit C++ eine DLL herzustellen?

Welche Umgebung? Hast Du Visual Studio98 mit
Visual C++ 6? Damit ist alles genannte sehr
einfach zumachen.

In VB6 kann ich mit …
CopyMemory Lib „kernel32“ Alias „RtlMoveMemory“ (pDst As Any,
pSrc As Any, ByVal ByteLen As Long)
… einige Bytes (Anzahl steht in Bytelen) verschieben. Um
aber den Inhalt von zwei Bereichen auszutauschen muss ich das
drei mal aufrufen und dann ist ein ‚Ringtausch‘ mit VB
schneller. Ein ‚SWAP‘ gibt es nicht, b.z.w. ich konnte keine
entsprechende DLL finden.

Ich verstehe das Problem nicht ganz. *Welchen* Speicherbereich?
Einen, den Du Dir selber geholt hast? Das wäre, gerade in
C/C++, trivial. Im Prinzip würde man so vorgehen:

  1. immer „maschinenwortweise“ tauschen (schnell),
  2. aufpassen, wenn Byte-Länge nicht durch Wortgröße (4) teilbar,
  3. aufpassen, dass Startadresse des Tauschens durch 4 teilbar

eventuell muss man die ersten und letzten Bytes gesondert
behandeln. Bei sehr grossen Speicherbereichen lohnt sich
das aber extrem ,wenn die Maschinenwortzugriffe DWORD-aligned
sind.

Beispiel: (WIN32):

 ...
 char spa[] = { 'h','a','l','l','o',' ','o','b','e','n','\0'};
 char spb[] = { 'u','n','t','e','n',' ','a','u','c','h','\0'};
 int Bytelen = sizeof(spa);

 unsigned long \*psrc = (unsigned long \*)spa;
 unsigned long \*pdst = (unsigned long \*)spb;
 unsigned long MWordLen = (Bytelen+3) / 4; /\* Achtung wenn: Bytelen % 4 != 0

 /\* fast swap loop, je 32bit (Maschinenwort) pro Schritt \*/
 for( ; MWordLen--; ++pdst,++psrc) \*pdst ^= \*psrc ^= \*pdst ^= \*psrc;
 ...

Aber das hängt natürlich davon ab, was Du tun willst. Bei
wenigen 10 KB kann man auch einfach byteweise tauschen.

Grüße

CMБ

Hallo,

Welche Umgebung? Hast Du Visual Studio98 mit
Visual C++ 6?

Ja. :smile: Aber mit C++ kann ich nicht umgehen.

Damit ist alles genannte sehr einfach zumachen.

In VB6 kann ich mit …
CopyMemory Lib „kernel32“ Alias „RtlMoveMemory“ (pDst As Any,
pSrc As Any, ByVal ByteLen As Long)
… einige Bytes (Anzahl steht in Bytelen) verschieben. Um
aber den Inhalt von zwei Bereichen auszutauschen muss ich das
drei mal aufrufen und dann ist ein ‚Ringtausch‘ mit VB
schneller. Ein ‚SWAP‘ gibt es nicht, b.z.w. ich konnte keine
entsprechende DLL finden.

Ich verstehe das Problem nicht ganz. *Welchen*
Speicherbereich?

In VB6 erfahre ich mit StrPtr() den Pointer eines Strings, mit VarPtr() den Pointer einer Variablen und mit Array(Index) wird der Pointer auf ein Feld in einem Array übergeben. Wenn ich dann Integer Variablen bearbeiten möchte, gebe ich zwei Byte an und kann die Inhalte mit CopyMemory bearbeiten.

Bei zwei Byte bringt das kaum Vorteil, wenn ich aber ein ByteArray damit bearbeite und einige bis einige Tausend Bytes verschiebe, ist der Geschwindigkeitsgewinn gegenüber einer Zählschleife mit VB enorm.

Einen, den Du Dir selber geholt hast? Das wäre, gerade in
C/C++, trivial. Im Prinzip würde man so vorgehen:

  1. immer „maschinenwortweise“ tauschen (schnell),

OK, auch wenn Wortweise schnell ist, wäre das ein Gewinn, ich kann mich ja daran anpassen.

  1. aufpassen, wenn Byte-Länge nicht durch Wortgröße (4)
    teilbar,
  2. aufpassen, dass Startadresse des Tauschens durch 4 teilbar

Klar. Ich werde das gleich mal prüfen, aber ich nehme an, daß VB die Pointer von Long-Variablen auf entsprechende Adressen setzt.

eventuell muss man die ersten und letzten Bytes gesondert
behandeln. Bei sehr grossen Speicherbereichen lohnt sich
das aber extrem ,wenn die Maschinenwortzugriffe DWORD-aligned
sind.

Ich nehme an, daß mir VB ‚passende‘ Pointer setzen wird, wenn ich die Arrays oder variablen als Long deklariere.

Beispiel: (WIN32):

 ...
 char spa[] = { 'h','a','l','l','o',' ','o','b','e','n','\0'};
 char spb[] = { 'u','n','t','e','n',' ','a','u','c','h','\0'};
 int Bytelen = sizeof(spa);

 unsigned long \*psrc = (unsigned long \*)spa;
 unsigned long \*pdst = (unsigned long \*)spb;
 unsigned long MWordLen = (Bytelen+3) / 4; /\* Achtung wenn:
Bytelen % 4 != 0

 /\* fast swap loop, je 32bit (Maschinenwort) pro Schritt \*/
 for( ; MWordLen--; ++pdst,++psrc) \*pdst ^= \*psrc ^= \*pdst ^=
\*psrc;
 ...

hmmm, das bespreche ich mal mit meinem Sohn, der kann das eventuell lesen. :smile:

Aber das hängt natürlich davon ab, was Du tun
willst. Bei
wenigen 10 KB kann man auch einfach byteweise tauschen.

Inzwischen geht es mir nicht mehr um eine konkrete Anwendung, auch wenn der Gedanke dadurch entstanden ist, daß ich einem Freund eine Prozedur beschleunigen möchte. Ich erzähle erst mal, was der treibt … Lotto. :smile:

Ein Byte-Array (1 to 49) wird mit 1 - 49 gefüllt.
Dann wird ein Zufallswert ermittelt und das ermittelte Byte mit dem letzten Byte getauscht.

MerkVariable = Array(Index)
Array(Index) = Array(49)
Array(49) = MerkVariable

Genau diese Stelle möchte ich beschleunigen.

Wenn das per DLL geht, wäre es aber angenehm, wenn man damit auch gleich noch andere Fälle erschlagen könnte.

VB-Beispiel: Das …

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias \_
"RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Private Sub Command1\_Click()
 Dim i As Integer 'Index für Zählschleifen
 Dim P1 As Integer 'Position1
 Dim P2 As Integer 'Position2
 Dim l As Integer 'Länge, Anzahl zu verschiebender Bytes
 Dim AM() As Byte 'Array zum zwischenspeichern
 Dim Arr(100) As Byte 'Das Array zum Arbeiten
 'erst mal das Array füllen, nur Beispiel
 For i = 0 To 100
 Arr(i) = i
 Next
 l = 7 'nur Beispiel, 7 Bytes verschieben
 P1 = 32 'Positionen, auch nur Beispiel
 P2 = 74
 ReDim AM(l) 'Das Array zum zwischenspeichern auf \_
 die richtige größe bringen
 CopyMemory AM(0), Arr(P1), l
 CopyMemory Arr(P1), Arr(P2), l
 CopyMemory Arr(P2), AM(0), l
End Sub

… müsste man mit der richtigen Dll doch so lösen können: …

Option Explicit

Private Declare Sub SwapMemory Lib "?????" \_
(pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Private Sub Command1\_Click()
 Dim i As Integer 'Index für Zählschleifen
 Dim l As Integer 'Länge, Anzahl zu verschiebender Bytes
 Dim Arr(100) As Byte 'Das Array zum Arbeiten
 'erst mal das Array füllen, nur Beispiel
 For i = 0 To 100
 Arr(i) = i
 Next
 l = 7 'nur Beispiel, 7 Bytes verschieben
 P1 = 32 'Positionen, auch nur Beispiel
 P2 = 74

 SwapMemory Arr(P1), Arr(P2), l 'gibt's scheinbar (noch) nicht.
End Sub

… was deutlich schneller wäre. :smile:

Nur kann ich das nicht selbst und mein Sohn auch (noch) nicht.

Gruß, Rainer

Hi Semjon,

Beispiel: (WIN32):

 ...
 char spa[] = { 'h','a','l','l','o',' ','o','b','e','n','\0'};
 char spb[] = { 'u','n','t','e','n',' ','a','u','c','h','\0'};
 int Bytelen = sizeof(spa);

 unsigned long \*psrc = (unsigned long \*)spa;
 unsigned long \*pdst = (unsigned long \*)spb;
 unsigned long MWordLen = (Bytelen+3) / 4; /\* Achtung wenn:
Bytelen % 4 != 0

 /\* fast swap loop, je 32bit (Maschinenwort) pro Schritt \*/
 for( ; MWordLen--; ++pdst,++psrc) \*pdst ^= \*psrc ^= \*pdst ^=
\*psrc;
 ...

OK. C++6 ist offen, neues Projekt DLL.
Das sieht im Moment so aus:

// Swap.cpp : Definiert den Einsprungpunkt für die DLL-Anwendung.
//

#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule, 
 DWORD ul\_reason\_for\_call, 
 LPVOID lpReserved
 )
{
 return TRUE;
}

Soll ich die erst Zeile so verstehen, daß der Einsprungspunkt jetzt schon ‚Swap‘ heißt? In der Klammer stehen die Variablen, die übergeben werden sollen? D.h. hier wird erst mal davon ausgegangen, daß ich ein Handle übergeben möchte? Oder wäre es so richtig?:

BOOL SWAP DllMain( pdst, psrc, Anzahl)

In CopyMoemory steht für die Positionen ‚As Any‘ und für Anzahl ‚As Long‘. Wie schreibt man das in C++? Doch sicher nicht ‚As Long‘ wie in VB. :smile:

Dann der Code selbst. Zurückgeben will ich nichts, Return kann ich mer dann also sparen. Richtig? Dann klaue ich mal aus Deinem Code …

{
 for( ; Anzahl--; ++pdst,++psrc) \*pdst ^= \*psrc ^= \*pdst ^=
\*psrc;
}

War’s das schon? Ist da ‚nur noch‘ die Deklaration falsch?

Gruß, Rainer

Hallo.

for( ; MWordLen–; ++pdst,++psrc) *pdst ^= *psrc ^= *pdst ^= *psrc;

Das ist so sehr unschöner Code, der nicht portabel ist, da das Verhalten dieser Zeile vom C-Standard nicht definiert ist. Auf *pdst wird zweimal schreibend zugegriffen, ohne das ein Sequenzpunkt dazwischen liegt.
Sollte man eher so schreiben:

for( ; MWordLen--; ++pdst,++psrc) {
 \*pdst ^= \*psrc;
 \*psrc ^= \*pdst;
 \*pdst ^= \*psrc;
}

Siehe auch hier: http://c-faq.com/expr/xorswapexpr.html

Sebastian.

Hallo Sebastian,

for( ; MWordLen--; ++pdst,++psrc) {
 \*pdst ^= \*psrc;
 \*psrc ^= \*pdst;
 \*pdst ^= \*psrc;
}

hmmm, ist nach

*pdst ^= *psrc;

nicht Quelle und Ziel gleich und die beiden weiteren Zeilen bewirken gar nichts mehr?

<sub>[völlig verwirrt]</sub>

Gruß, Rainer

Hallo.

for( ; MWordLen–; ++pdst,++psrc) {
*pdst ^= *psrc;
*psrc ^= *pdst;
*pdst ^= *psrc;
}

hmmm, ist nach

*pdst ^ = *psrc;

nicht Quelle und Ziel gleich und die beiden weiteren Zeilen
bewirken gar nichts mehr?

Nein, denn da steht ja ^= und nicht =.
Das ist die Kurzform zu

\*pdst = \*pdst ^ \*psrc;

Das stellt die XOR-Verknüpfung dar.

Sebastian.

1 Like

Hallo Sebastian,

Das stellt die XOR-Verknüpfung dar.

ahhh, OK. Danke! Dann verstehe ich den Code! :smile:

Gruß, Rainer

Hallo,

ich habe mal aus dem, was ich bisher dachte, ich hätte etwas verstanden das hier zusammengestrickt:

// swap.cpp : Definiert den Einsprungpunkt für die DLL-Anwendung.
//

#include "stdafx.h"

BOOL SWAP DllMain( unsigned long \*pdst, unsigned long \*psrc, unsigned long Anzahl)

for( ; Anzahl--; ++pdst,++psrc) {
 \*pdst ^= \*psrc;
 \*psrc ^= \*pdst;
 \*pdst ^= \*psrc;
 return TRUE;
}

Das läuft natürlich nicht.
Fehler:

 error C2146: Syntaxfehler : Fehlendes ';' vor Bezeichner 'DllMain'
 fatal error C1004: Unerwartetes Dateiende gefunden

Ist daran etwas richtig/zu retten?

Gruß, Rainer

Nachtrag …
Hallo,

noch mal, das hie wird schon mal kompiliert:

// swap.cpp : Definiert den Einsprungpunkt für die DLL-Anwendung.
//

#include "stdafx.h"

BOOL SWAP ( unsigned long \*pdst, unsigned long \*psrc, unsigned long Anzahl)

{
 for( ; Anzahl--; ++pdst,++psrc) {
 \*pdst ^= \*psrc;
 \*psrc ^= \*pdst;
 \*pdst ^= \*psrc;
 }
 return TRUE;
}

Beim Aufruf wird aber der Einsprungspunkt nicht gefunden, ob dann des Richtige passiert weiß ich deshalb auch nicht, läuft ja nicht. :frowning:

Gruß, Rainer

Hallo Rainer,

ich habe mal aus dem, was ich bisher dachte, ich hätte etwas
verstanden das hier zusammengestrickt:

OK, vergiss dasmit dem xor Zeug. Ich glaube,ich habe
erstmal jetzt verstanden, was Du machen willst.

Also, ich hab das mal probiert. Am besten, man fängt mit
einem Quellverzeichnis für die C-Sachen an:

[mkdir] C:\Projekte\DLL\Iswap

Dann öffnest Du das Visual-Studio-C/6 und erzeugst ein
Projekt:
File => New => Project => Win32 Dynamic Link Library
Bei Location:
C:\Projekte\DLL\
und bei Project name:
Iswap
Weiter zur nächsten Seite:
„Empty project“

Dann gehst Du in die „File“-View, das müsste leer
sein. Du erzeugst eine Datei (File => new => text file)
mit dem Namen iswap.c und schreibst folgendes rein

 \_\_declspec(dllexport) 
 void \_\_stdcall SwapMemoryBytewise(char \*p, char \*q, long nbytes)
{
 \_asm mov esi, p
 \_asm mov edi, q
 \_asm mov ecx, nbytes
 looper:
 \_asm mov al, byte ptr [edi]
 \_asm movsb
 \_asm mov byte ptr [esi-1], al
 \_asm loop looper
}

Das ist alles. Dann speicherst Du ab (richtiges
Verzeichnis!) und fügst die Datei dem Projekt zu
(Source Files/Add File to Folder).
Dann erzeugst Du eine def-Datei: iswap.def
im C-Verzeichnis mit folgendem Inhalt:

LIBRARY ISWAP
DESCRIPTION 'A C dll that can be called from VB'

EXPORTS
 SwapMemoryBytewise

Das ist wieder alles.
Das wird zugefügt bei „Resource Files“ im „File view“.

Nun gehst Du auf Win32-Release (Build options) und
machst „Build“. Die DLL wird nun erzeugt (hoffentlich).
Danach findest Du im Unterverzeichnis „Release“
eine iswap.dll.

Diese kommt ins VB-Verzeichnis, ich habe eben mal ein
kleines Testprojekt gemacht, um diese einzubinden

Private Declare Sub SwapMemoryBytewise Lib "ISWAP.dll" (ByVal pDst As Long, ByVal pSrc As Long, ByVal ByteLen As Long)

Private Sub Command1\_Click()
 Dim i As Integer 'Index für Zählschleifen
 Dim MyArr(49) As Byte 'Das Array zum Arbeiten
 For i = 0 To 48: MyArr(i) = i: Next

 Size = 1: P1 = 2: P2 = 47 'Positionen 0 .. 47

 MsgBox "bei P1=" & MyArr(P1) & "bei P2=" & MyArr(P2), vbOKOnly, "Vorher"
 Call SwapMemoryBytewise(VarPtr(MyArr(2)), VarPtr(MyArr(47)), Size)
 MsgBox "bei P1=" & MyArr(P1) & "bei P2=" & MyArr(P2), vbOKOnly, "Nachher"
End Sub

Das funktioniert auch. Es ist also gar nicht mal so schwer.
Versuch doch mal, ob es klappt.

Grüße

CMБ

1 Like

Hallo Semjon,

Danke! Das wars, genau das habe ich gesucht, läuft! :smile:

Gruß, Rainer

Hallo Sebastian,

for( ; MWordLen–; ++pdst,++psrc) *pdst ^= *psrc ^= *pdst ^= *psrc;

Das ist so sehr unschöner Code, der nicht portabel ist, da das
Verhalten dieser Zeile vom C-Standard nicht definiert ist. Auf
*pdst wird zweimal schreibend zugegriffen, ohne das ein
Sequenzpunkt dazwischen liegt.

Das ist schon richtig, aber im Grunde ist das
ein alter Programmierer-Joke aus Assembler-Zeiten.

So kan man in Assembler zwei Operanden mit xor austauschen:

 void tausche1()
{
 int a = 3333, b = 9999;
 \_\_asm mov eax, a
 \_\_asm mov edx, b
 \_\_asm **xor eax, edx**
 \_\_asm **xor edx, eax**
 \_\_asm **xor eax, edx**
 \_\_asm mov a, eax
 \_\_asm mov b, edx
}

Allerdings kann man das xor auch
komplett weglassen, da man ja üblicherweise
sowieso in Registern arbeitet, also:

 void tausche2()
{
 int a = 3333, b = 9999;
 \_\_asm mov eax, a
 \_\_asm mov edx, b
 \_\_asm mov b, eax
 \_\_asm mov a, edx
}

… tut im Wesentlichen dasselbe.

Wenn man das in C umsetzt, ist üblicherweise
die xor-Version (ob mit Sequenzpunkten oder ohne)
immer einen oder mehrere Maschinenbefehl(e) länger
als die Version mit simpler temp-Variable

Ausserdem: die Sequenz a ^= b ^= a ^= b; ist
zwar streng genommen „nicht definiert“, aber nach den
Prezedenzregeln ist sie synonym zu a ^= (b ^= (a ^= b));
und sollte von jedem vernünftigen Compiler obzwar ihrer
Standardungemäßheit korrekt übersetzt werden (was auch
so ist und leicht geprüft werden kann).

Aber wie gesagt. Wenn das Stichwort „Variablen tauschen“
kommt, muss auch die xor-Sequenz kommen :wink:
Grüße

CMБ

Hallo Rainer

Danke! Das wars, genau das habe ich gesucht, läuft! :smile:

Ich vermute mal, wenn ein „Lottogewinn“ daraus
resultiert, wird sich dies durch eine Flasche „guten
schottischen Whiskys“ auch für mich rentiert haben :wink:

Grüße

CMБ

Hi Semjon,

Danke! Das wars, genau das habe ich gesucht, läuft! :smile:

Ich vermute mal, wenn ein „Lottogewinn“ daraus
resultiert, wird sich dies durch eine Flasche „guten
schottischen Whiskys“ auch für mich rentiert haben :wink:

wenn wir uns mal treffen, besorge ich Dir die Flasche. :smile: Das Programm soll nur dazu dienen zu verdeutlichen, daß man beim Lotto nur verlieren kann. Ein Versuch, das ultimative Lottosystem zu finden ist nicht geplant! :smile:

… ahhh, übrigens … die DLL gab’s ja noch nicht, (wurde aber schon oft gesucht) ich rechne damit, daß da irgendwann Jemand Interesse anmeldet. Ist das Freeware? Darf ich die DLL weitergeben?

Gruß, Rainer

Hallo.

Das ist schon richtig, aber im Grunde ist das
ein alter Programmierer-Joke aus Assembler-Zeiten.

So kan man in Assembler zwei Operanden mit xor
austauschen:

void tausche1()
{
int a = 3333, b = 9999;
__asm mov eax, a
__asm mov edx, b
__asm xor eax, edx
__asm xor edx, eax
__asm xor eax, edx
__asm mov a, eax
__asm mov b, edx
}

Allerdings kann man das xor auch
komplett weglassen, da man ja üblicherweise
sowieso in Registern arbeitet, also:

void tausche2()
{
int a = 3333, b = 9999;
__asm mov eax, a
__asm mov edx, b
__asm mov b, eax
__asm mov a, edx
}

… tut im Wesentlichen dasselbe.

Wenn man das in C umsetzt, ist üblicherweise
die xor-Version (ob mit Sequenzpunkten oder ohne)
immer einen oder mehrere Maschinenbefehl(e) länger
als die Version mit simpler temp-Variable

Ein guter Compiler wird die Verwendung der temp-Variable dann ja wegoptimieren und eben so ziemlich genau die 4 Move-Befehle erzeugen, die du ja schon genannt hast. Das ist bei Verwendung des xor für den Compiler nicht mehr so einfach möglich.

Ausserdem: die Sequenz a ^= b ^= a ^= b; ist
zwar streng genommen „nicht definiert“, aber nach den
Prezedenzregeln ist sie synonym zu a ^= (b ^= (a ^=
b));
und sollte von jedem vernünftigen Compiler obzwar ihrer
Standardungemäßheit korrekt übersetzt werden (was auch
so ist und leicht geprüft werden kann).

Unter dem Link aus meinem letzten Post findet sich auch ein Beispiel, dass eben nicht jeder Compiler da das gewünschte Ergebnis liefert:

For example, it has been reported that when given the code

int a = 123, b = 7654;
a ^= b ^= a ^= b;

the SCO Optimizing C compiler (icc) sets b to 123 and a to 0.

Aber wie gesagt. Wenn das Stichwort „Variablen tauschen“
kommt, muss auch die xor-Sequenz kommen :wink:

Stimmt. Aber da sollte man dann auch drauf hinweisen, dass das kein standardkonformer Code ist.

Sebastian.

Swap DLL
Hi Semjon,

Darf ich die DLL weitergeben?

sorry, war 'ne dumme Frage. In ein paar Tagen ist der Quellcode ohnehin per Google zu finden …

Gruß, Rainer
PS. Ist das schnell! :smile:

Hallo,

Darf ich die DLL weitergeben?

sorry, war 'ne dumme Frage. In ein paar Tagen ist der
Quellcode ohnehin per Google zu finden …

Ich denke, man kann mit sehr geringem Aufwand hieraus etwas
Brauchbareres machen, z.B. so, dass man gleich die
entsprechenden Datentypen und -arrays kopieren (tauschen)
kann. IMHO haben in VB6 die Datentypen folgende Längen:

BYTE/CHAR - 1 Byte / 8 Bit
Integer - 2 Byte / 16 Bit
LONG - 4 Byte / 32 Bit
Single - 4 Byte / 32 Bit
Double - 8 Byte / 64 Bit

Man kann also mit 4 verschiedenen Funktionen
alles optimal erschlagen:

SwapmemBytes8(p,q,n)
SwapmemWords16(p,q,n)
SwapmemDwords32(p,q,n)
SwapmemDoubles64(p,q,n)

Ich hab das mal gemacht:
[iswap.c]

// iswap.c : Entry point here

 \_\_declspec(dllexport) 
 void \_\_stdcall SwapmemBytes8(char \*p, char \*q, long nbytes)
{ \_asm mov esi, p
 \_asm mov edi, q
 \_asm mov ecx, nbytes
 looper:
 \_asm mov al, byte ptr [edi]
 \_asm movsb
 \_asm mov byte ptr [esi-1], al
 \_asm loop looper
}

 \_\_declspec(dllexport) 
 void \_\_stdcall SwapmemWords16(char \*p, char \*q, long nwords)
{ \_asm mov esi, p
 \_asm mov edi, q
 \_asm mov ecx, nwords
 looper:
 \_asm mov ax, word ptr [edi]
 \_asm movsw
 \_asm mov word ptr [esi-2], ax
 \_asm loop looper
}

 \_\_declspec(dllexport) 
 void \_\_stdcall SwapmemDwords32(char \*p, char \*q, long ndwords)
{ \_asm mov esi, p
 \_asm mov edi, q
 \_asm mov ecx, ndwords
 looper:
 \_asm mov eax, dword ptr [edi]
 \_asm movsd
 \_asm mov dword ptr [esi-4], eax
 \_asm loop looper
}

 \_\_declspec(dllexport) 
 void \_\_stdcall SwapmemDoubles64(char \*p, char \*q, long ndoubles)
{ \_asm mov esi, p
 \_asm mov edi, q
 \_asm mov ecx, ndoubles
 looper:
 \_asm fld qword ptr [edi]
 \_asm fld qword ptr [esi]
 \_asm fstp qword ptr [edi]
 \_asm fstp qword ptr [esi]
 \_asm add edi, 8
 \_asm add esi, 8
 \_asm loop looper
}

sowie:
[swap.def]

LIBRARY ISWAP
DESCRIPTION 'A C dll that can be called from VB'

EXPORTS
 SwapmemBytes8
 SwapmemWords16
 SwapmemDwords32
 SwapmemDoubles64

Und als Testprogramm mit Einbindungsbeispiel:
[form1.frm]

Private Declare Sub SwapmemBytes8 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemWords16 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemDwords32 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemDoubles64 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)


Private Sub Command1\_Click()
 Dim i As Integer 'Index für Zählschleifen
 Dim p(100) As Single
 Dim q(100) As Single
 For i = 0 To 99: p(i) = i + 0.01: q(i) = i + 100.01: Next

 ' tausche komplette "single" Arrays
 Call SwapmemDwords32(VarPtr(p(0)), VarPtr(q(0)), 100)

 MsgBox "bei p=" & p(0) & " und q=" & q(0), vbOKOnly, "Schau Schau"
 MsgBox "bei p=" & p(90) & " und q=" & q(90), vbOKOnly, "Schau Schau"

End Sub

Ich habe also die kompletten „single precision“ Arrays p und q
getauscht, da geht die 32’er Version. Vorsicht, die „Anzahl“ ist
nun nicht mehr die Byte-Anzahl sondern die Anzahl der Elemente.
Wenn man bei double (64 Bit) nicht mit der FPU arbeiten will, kann
man auch mit SwapmemDwords32() und der doppelten Anzahl
von Elementen arbeiten.

Ich wäre Dir dankbar, wenn Du eine Art „Dokumentation“
dazu machen kannst (mehr oder weniger Stichpunkte, aber
mit je Beispiel) und diese hier irgendwie einbauen
könntest :wink: Dann packe ich das in meinen Quelltextfundus
und hake das Problem ab …

Grüße

CMБ

PS: alles was ich hier mache ist „Public Domain“ oder so …

1 Like

Hi Semjon,

Ich denke, man kann mit sehr geringem Aufwand hieraus etwas
Brauchbareres machen,

*gg* das ist doch schon brauchbar!

z.B. so, dass man gleich die
entsprechenden Datentypen und -arrays kopieren (tauschen)
kann. IMHO haben in VB6 die Datentypen folgende Längen:

Das ist dann schon Luxus. :smile: Ein wenig rechnen darf schon sein.

BYTE/CHAR - 1 Byte / 8 Bit
Integer - 2 Byte / 16 Bit
LONG - 4 Byte / 32 Bit
Single - 4 Byte / 32 Bit
Double - 8 Byte / 64 Bit

Ja, genau so.

Man kann also mit 4 verschiedenen Funktionen
alles optimal erschlagen:

SwapmemBytes8(p,q,n)
SwapmemWords16(p,q,n)
SwapmemDwords32(p,q,n)
SwapmemDoubles64(p,q,n)

Ich hab das mal gemacht:
[iswap.c]

// iswap.c : Entry point here

__declspec(dllexport)
void __stdcall SwapmemBytes8(char *p, char *q, long nbytes)
{ _asm mov esi, p
_asm mov edi, q
_asm mov ecx, nbytes
looper:
_asm mov al, byte ptr [edi]
_asm movsb
_asm mov byte ptr [esi-1], al
_asm loop looper
}

__declspec(dllexport)
void __stdcall SwapmemWords16(char *p, char *q, long nwords)
{ _asm mov esi, p
_asm mov edi, q
_asm mov ecx, nwords
looper:
_asm mov ax, word ptr [edi]
_asm movsw
_asm mov word ptr [esi-2], ax
_asm loop looper
}

__declspec(dllexport)
void __stdcall SwapmemDwords32(char *p, char *q, long
ndwords)
{ _asm mov esi, p
_asm mov edi, q
_asm mov ecx, ndwords
looper:
_asm mov eax, dword ptr [edi]
_asm movsd
_asm mov dword ptr [esi-4], eax
_asm loop looper
}

__declspec(dllexport)
void __stdcall SwapmemDoubles64(char *p, char *q, long
ndoubles)
{ _asm mov esi, p
_asm mov edi, q
_asm mov ecx, ndoubles
looper:
_asm fld qword ptr [edi]
_asm fld qword ptr [esi]
_asm fstp qword ptr [edi]
_asm fstp qword ptr [esi]
_asm add edi, 8
_asm add esi, 8
_asm loop looper
}

sowie:
[swap.def]

LIBRARY ISWAP
DESCRIPTION ‚A C dll that can be called from VB‘

EXPORTS
SwapmemBytes8
SwapmemWords16
SwapmemDwords32
SwapmemDoubles64

Und als Testprogramm mit Einbindungsbeispiel:
[form1.frm]

Private Declare Sub SwapmemBytes8 Lib
„ISWAP.dll“ (ByVal p As Long, ByVal q As Long, ByVal n As
Long)
Private Declare Sub SwapmemWords16 Lib „ISWAP.dll“ (ByVal p As
Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemDwords32 Lib „ISWAP.dll“ (ByVal p
As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemDoubles64 Lib „ISWAP.dll“ (ByVal p
As Long, ByVal q As Long, ByVal n As Long)

Private Sub Command1_Click()
Dim i As Integer 'Index für Zählschleifen
Dim p(100) As Single
Dim q(100) As Single
For i = 0 To 99: p(i) = i + 0.01: q(i) = i + 100.01: Next

’ tausche komplette „single“ Arrays
Call SwapmemDwords32(VarPtr(p(0)), VarPtr(q(0)), 100)

MsgBox „bei p=“ & p(0) & " und q=" & q(0), vbOKOnly, „Schau
Schau“
MsgBox „bei p=“ & p(90) & " und q=" & q(90), vbOKOnly,
„Schau Schau“

End Sub

Das ‚klaue‘ ich mir morgen gleich. Hier (privat) habe ich nur die Autorenversion von VC++, das muß ich dann morgen ohnehin noch einmal machen.

Ich habe also die kompletten „single precision“ Arrays p und q
getauscht, da geht die 32’er Version. Vorsicht, die „Anzahl“
ist
nun nicht mehr die Byte-Anzahl sondern die Anzahl der
Elemente.

Ja, das ist mir oben bei der Beschreibung schon aufgefallen. :smile:

Wenn man bei double (64 Bit) nicht mit der FPU arbeiten will,
kann
man auch mit SwapmemDwords32() und der doppelten Anzahl
von Elementen arbeiten.

Ich wäre Dir dankbar, wenn Du eine Art „Dokumentation“
dazu machen kannst (mehr oder weniger Stichpunkte, aber
mit je Beispiel) und diese hier irgendwie einbauen
könntest :wink: Dann packe ich das in meinen Quelltextfundus
und hake das Problem ab …

Ja, das mach ich doch gern! Ich bau ein paar hübsche Beispiele … heute Mittag habe ich mal mit der vorhandenen Version Beispiel für einen Zeitvergleich geschrieben … Faktor 155! :smile:

PS: alles was ich hier mache ist „Public Domain“ oder so …

Dann packe ich die Dokumentation im VB-Brett in die FAQ, dann kann ich schneller/leichter antworten, wenn danach gefragt wird.

Erst noch mal herzlichen Dank! Beispiel e.t.c. kommt morgen.

Gruß, Rainer

Hi Joe,

ein schön übersichtliches Beispiel:

'Den Code in ein neues Projekt kopieren, es werden keine Steuerelemente benötigt.

Option Explicit

'Sleep für den Programmablauf mit verwenden, hat mit Swap sonst nichts zu tun.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Declare Sub SwapmemBytes8 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemWords16 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemDwords32 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)
Private Declare Sub SwapmemDoubles64 Lib "ISWAP.dll" (ByVal p As Long, ByVal q As Long, ByVal n As Long)

Private Sub Form\_Load()
 Dim i As Integer 'Index für Zählschleifen
 Dim Txt As String 'Textvariable'
 Dim Bytes(1 To 50) As Byte 'Byte-Array
 Dim Words(1 To 50) As Integer 'Array aus Inetger-Variablen
 Dim DWords(1 To 50) As Long 'Array aus Long-Variablen
 Dim Doubles(1 To 50) As Double 'Array aus Double-Variablen
 Dim tm As Double

 Me.Caption = "Demo - iswap"
 Me.Enabled = False
 Me.WindowState = 2 ' Form maximieren
 Me.Show

 'Füllen der Text-Variablen und der Felder mit Daten

 Txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

 For i = 1 To 50
 Bytes(i) = i
 Words(i) = i
 DWords(i) = i
 Doubles(i) = i
 Next

 Me.Print
 Me.Print " Inhalt der Textvariablen:" & vbCrLf
 Me.Print " " & Txt & vbCrLf
 Me.Print " Die ersten 26 Zeichen werden gegen die letzten 26Zeichen getauscht:" & vbCrLf

 'Swap ausführen
 SwapmemWords16 StrPtr(Txt), (StrPtr(Txt) + 52), 26
 ' ^ Ziel ^ Quelle ^ Anzahl Elemente
 ' Bei der Berechnung der Adresse muss in Bytes
 ' gerechnet werden. Ein Zeichen ist 2 Bytes lang.

 'Ergebnis anzeigen
 Me.Print " " & Txt & vbCrLf
 Me.Print " Dabei fällt auf, daß ein Zeichen im Text aus zwei Bytes besteht!" & vbCrLf
 Me.Print " 10 Sekunden Pause ..."
 DoEvents
 Sleep 10000
 Me.Cls

 Me.Print
 Me.Print " Inhalt des Byte-Arrays:" & vbCrLf & vbCrLf & " ";
 For i = LBound(Bytes) To UBound(Bytes) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Bytes(i)), 3);
 Next
 Me.Print vbCrLf
 Me.Print " Es werden die ersten 5 Elemente mit 5 Elementen ab der Position 20 getauscht"

 'Swap ausführen
 SwapmemBytes8 VarPtr(Bytes(1)), VarPtr(Bytes(20)), 5
 ' ^ Ziel ^ Quelle ^ Anzahl Elemente
 ' 1. Element des Arrays
 ' 20. Element des Arrays
 ' 5 Elemete, hier gleich Bytes
 Me.Print
 Me.Print " ";

 'geändertes Array ausgeben

 For i = LBound(Bytes) To UBound(Bytes) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Bytes(i)), 3);
 Next
 Me.Print vbCrLf
 Me.Print " 10 Sekunden Pause ..."
 DoEvents
 Sleep 10000
 Me.Cls

 Me.Print
 Me.Print " Inhalt des Word-Arrays:" & vbCrLf & vbCrLf & " ";
 For i = LBound(Words) To UBound(Words) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Words(i)), 3);
 Next
 Me.Print vbCrLf
 Me.Print " Es werden ab dem 2. Element 3 Elementen ab der Position 15 getauscht"

 'Swap ausführen
 SwapmemBytes8 VarPtr(Words(2)), VarPtr(Words(15)), 5
 ' ^ Ziel ^ Quelle ^ Anzahl Elemente
 ' 2. Element des Arrays
 ' 15. Element des Arrays
 ' 5 Elemete, hier gleich 10 Bytes
 Me.Print
 Me.Print " ";

 'geändertes Array ausgeben

 For i = LBound(Words) To UBound(Words) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Words(i)), 3);
 Next
 Me.Print vbCrLf
 Me.Print " 10 Sekunden Pause ..."
 DoEvents
 Sleep 10000
 Me.Cls

 Me.Print
 Me.Print " Auch ein Tausch zwischen verschiedenen Typen ist möglich."
 Me.Print " Zur Demonstration tauschen wir das Word-Array zurück:" + vbCrLf

 SwapmemBytes8 VarPtr(Words(2)), VarPtr(Words(15)), 5
 ' ^ Ziel ^ Quelle ^ Anzahl Elemente
 ' 2. Element des Arrays
 ' 15. Element des Arrays
 '

 Me.Print " Inhalt des Word-Arrays:" & vbCrLf & vbCrLf & " ";
 For i = LBound(Words) To UBound(Words) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Words(i)), 3);
 Next

 Me.Print vbCrLf
 Me.Print " Auch das den Txt bringen wir wieder in den Urzustand" & vbCrLf

 SwapmemWords16 StrPtr(Txt), (StrPtr(Txt) + 52), 26
 ' ^ Ziel ^ Quelle ^ Anzahl Elemente
 ' Bei der Berechnung der Adresse muss in Bytes
 ' gerechnet werden. Ein Zeichen ist 2 Bytes lang.
 'Ergebnis anzeigen
 Me.Print " " & Txt & vbCrLf

 Me.Print " Und tauschen die ersten drei Zeichen des Textes gegen die letzen drei 'Integer' aus.." & vbCrLf

 'Swap ausführen
 SwapmemWords16 VarPtr(Words(48)), StrPtr(Txt), 3
 ' ^ Ziel ^ Quelle ^ Anzahl Elemente
 ' 47. Element des Arrays
 ' 47. Zeichen des Textes
 ' 5 Elemete, hier gleich 10 Bytes
 Me.Print
 Me.Print " ";

 Me.Print " " & Txt & vbCrLf & vbCrLf & " ";

 'geändertes Array ausgeben

 For i = LBound(Words) To UBound(Words) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Words(i)), 3);
 Next
 Me.Print vbCrLf
 Me.Print " 20 Sekunden Pause ..."
 DoEvents
 Sleep 20000

 Me.Print
 Me.Print " Um die Geschwindigkeit zu verdeutlichen wiederholen wir das nach den Countdown 30 001 mal ..." & vbCrLf
 For i = 5 To 0 Step -1
 Sleep 1000
 Me.Print " " & i
 Next

 Me.Print

 tm = Timer 'Zeit merken
 For i = 0 To 30000
 SwapmemWords16 VarPtr(Words(48)), StrPtr(Txt), 3
 Next
 tm = Timer - tm 'verbrauchte Zeit berechnen


 Me.Print " " & Txt & vbCrLf & vbCrLf & " ";

 For i = LBound(Words) To UBound(Words) 'Schleife vom kleinesten Index bis zum Größten
 Me.Print Right(" " & CStr(Words(i)), 3);
 Next

 Me.Print vbCrLf
 Me.Print " Das hat " & CStr(Round(tm, 2)) & "Sekunden gedauert." & vbCrLf
 Me.Print " 20 Sekunden Pause ..."
 DoEvents
 Sleep 20000

 'Ohne Ausgabe, nur Codee ...
 SwapmemDwords32 VarPtr(DWords(20)), VarPtr(DWords(10)), 3
 SwapmemDoubles64 VarPtr(Doubles(20)), VarPtr(Doubles(10)), 3
 End
End Sub

Gruß, Rainer

1 Like