Seite 1 von 1

Stringrückgabe aus DLL...

Verfasst: 27.12.2009 13:54
von Frank Smart
Stringrückgabe aus DLL...

seit ein paar Tagen beschäftige ich mich mal wieder etwas intensiver mit PureBasic. Ich bin ja schon eine weile hier im Forum. Viele Themen habe ich noch nicht geschrieben. Aber ich lese viel mit. Insofern ist es schon mehrfach ein Thema gewesen, die Stringrückgabe aus einer DLL.

Alle Quellen dazu findet Ihr auch hier: http://em.q-soft.ch/files/get/kWRg449VWg/pbdlltest.zip.

(Visual Studio 2008 SP1 und PureBasic 4.40 x86)

Ich habe folgendes angefangen. Als Idee diente mir http://www.purebasic.fr/english/viewtop ... =5&t=28544.

Die Quellcodes habe ich leicht modifiziert.

PBdll.pb

Code: Alles auswählen

; http://www.purebasic.fr/english/viewtopic.php?f=5&t=28544
;

EnableExplicit

ProcedureDLL.l IsPowerOf2(x.l)
  Protected t.l=0 
  If (x&(x-1))=0 
     t=1 
  EndIf                      
  ProcedureReturn t 
EndProcedure 

ProcedureDLL.l IsPowerOf(power.l, base.l) 
  Protected basePow.l 
  Protected count.l 

  If base < 0 Or power < 0 
    ProcedureReturn -1 
  EndIf 
  If base = 0 
    If power = 1 
      ProcedureReturn 0 
    Else 
      ProcedureReturn 1 
    EndIf 
  EndIf 
  If base = 1 
    If power = 1 
      ProcedureReturn 1 
    Else 
      ProcedureReturn -1 
    EndIf 
  EndIf 
  If power = 1 
    ProcedureReturn 0 
  EndIf 

  basePow = base 
  While $7FFFFFFF / basePow > 1 
    count+1 
    If basePow = power 
      ProcedureReturn count 
    EndIf 
    basePow * base 
  Wend 
  ProcedureReturn -1 
EndProcedure 

Global ReturnString.s = ""
ProcedureDLL.s Round2n(n.f,places.l) 
    If places < 0 : places = 0 : EndIf 
    Protected r.f = 0.5 * Pow(10,-1*places) : Protected T.f = Pow(10,places) 
        
    ReturnString = StrF(Int((n+r)*T)/T,places)
    ProcedureReturn ReturnString
EndProcedure

Debug IsPowerOf2(33)
Debug IsPowerOf(49,7)
Debug Round2n(3.1415927,4)
testbed_pb.pb

Code: Alles auswählen

EnableExplicit
; Import-File created by Lib2PBImport
; Libname: PBdll.lib
; created: 2009/12/16  12:25

Import "PBdll.lib"
  IsPowerOf2.l(a.l)     As "_IsPowerOf2@4"
  IsPowerOf.l(a.l,b.l)  As "_IsPowerOf@8"
  Round2n(a.l,b.l)      As "_Round2n@8"
EndImport


Define a.s =""
Define ReturnString1.s =""
Define ReturnString2.s =""

Debug "IsPowerOf2(33) = "         + Str(IsPowerOf2(33))
Debug "IsPowerOf(49,7) = "        + Str(IsPowerOf(49,7))
Debug "Round2n(3.1415927, 4) = "  + Str(Round2n(3.1415927,4))

a.s = PeekS(Round2n(3.1415927, 4))
Debug a

; Versuch mit CallFunction
If OpenLibrary(0, "PBdll.dll")
ReturnString1.s = PeekS(CallFunction(0,"Round2n",3.1415927, 4))

EndIf

Debug "ReturnString1 = " + ReturnString1

Debug "Islibrary = " + Str(IsLibrary(0))

CloseLibrary(0)

; Versuch mit Prototype
Prototype.s protoRound2n(n.f, places.l)

If OpenLibrary(0, "PBdll.dll")

Define rstring.protoRound2n = GetFunction(0, "Round2n")

ReturnString2.s = rstring(3.1415927, 4)

EndIf

Debug "ReturnString1 = " + ReturnString1
Debug "ReturnString2 = " + ReturnString2


Debug "Islibrary = " + Str(IsLibrary(0))

CloseLibrary(0)
Ich habe das geniale Tool Lib2PBImport verwendet. An dieser Stelle hier, Vielen Dank dafür.

Als Ausgabe der Testbed erhalte ich:
IsPowerOf2(33) = 0
IsPowerOf(49,7) = 2
Round2n(3.1415927, 4) = 10093976
0.0000
ReturnString1 = 0.0000
Islibrary = 9505168
ReturnString1 = 0.0000
ReturnString2 =
Islibrary = 9505168
Round2n(3.1415927, 4) hat als Ergebnis 0.0000. Es sollte aber 3.1416 sein.

Wenn ich die Dll aus einem C/C++ Programm verwende ist das Ergebnis richtig.


Auch hier mein Testbed:

Code: Alles auswählen

// pbdlltest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#pragma comment(lib, "PBdll.lib")

extern "C" __declspec(dllimport) int __stdcall IsPowerOf2(int n);
extern "C" __declspec(dllimport) int __stdcall IsPowerOf(int power, int base);
extern "C" __declspec(dllimport) char* __stdcall Round2n(float n,int places);



int _tmain(int argc, _TCHAR* argv[])
{
	char ch;
	int n,m;
	n = IsPowerOf2(33);
	m = IsPowerOf(49, 7);
	printf("%d\n", n);
	printf("%d\n", m);
	printf("%s\n", Round2n((float) 3.1415927,4));
	printf("%s\n","press return to end");
	ch = getchar();

	return 0;
}

und die Ausgabe:
0
2
3.1416
press return to end
Ich habe hier schon im Forum einmal geschaut, insbesondere
http://forums.purebasic.com/german/vi ... light=dll

und andere.

Es wäre schön, wenn Ihr mir auf den richtigen weg helft.

Besten Dank!

Liebe Grüße,

Frank

Re: Stringrückgabe aus DLL...

Verfasst: 27.12.2009 14:14
von HeX0R
Dein Import ist falsch, richtig wäre:

Code: Alles auswählen

Round2n(a.f, b.l)      As "_Round2n@8"
Und die CallFunction[fast]-Methode solltest du eh gleich wieder vergessen, da veraltet.

Re: Stringrückgabe aus DLL...

Verfasst: 27.12.2009 15:04
von Frank Smart
HeX0R hat geschrieben:Dein Import ist falsch, richtig wäre:

Code: Alles auswählen

Round2n(a.f, b.l)      As "_Round2n@8"
Und die CallFunction[fast]-Methode solltest du eh gleich wieder vergessen, da veraltet.
Ich danke Dir, HeX0R. Genau das ist es gewesen. Diese CallFunction habe ich ja nur benutzt, weil ich mir nicht mehr anders zu helfen wusste. Aber das a float sein sollte, darauf hätte ich auch kommen können... :oops:

Manchmal sieht man den Wald vor Bäumen nicht.

Aber sonst ist alles okay mit dem Code?

Vielen Dank noch einmal für die Hilfe!

Gruß,

Frank

Re: Stringrückgabe aus DLL...

Verfasst: 27.12.2009 17:13
von mk-soft
Nicht den String von PB direkt zurück geben. Führt zu Speicherlecks.
Hier mal Methoden um Werte zurück zu geben. Zweimal als Object (Variant) und einmal als String direkt

vbDLL.DLL

Code: Alles auswählen

; This procedure is called once, when the program loads the library
; for the first time. All init stuffs can be done here (but not DirectX init)
;

Global mutex

ProcedureDLL AttachProcess(Instance)
  mutex = CreateMutex()
EndProcedure


; Called when the program release (free) the DLL
;
ProcedureDLL DetachProcess(Instance)
  FreeMutex(mutex)
EndProcedure


; Both are called when a thread in a program call or release (free) the DLL
;
ProcedureDLL AttachThread(Instance)
EndProcedure

ProcedureDLL DetachThread(Instance)
EndProcedure

; Compilermode ansi

;-T_BSTR
Procedure helpSysAllocString(*Value)
  ProcedureReturn SysAllocString_(*Value)
EndProcedure
Prototype.l ProtoSysAllocString(Value.p-unicode)

Global T_BSTR.ProtoSysAllocString = @helpSysAllocString()

ProcedureDLL GetString(*var.variant)

  Protected temp.s, hr
  
  LockMutex(mutex)
  hr = VariantClear_(*var)
  If hr = #S_OK
    temp = "Hello World"
    *var\vt = #VT_BSTR
    *var\bstrVal = T_BSTR(temp)
  EndIf
  UnlockMutex(mutex)
  ProcedureReturn hr
  
EndProcedure

ProcedureDLL GetFloat(*var.variant)

  Protected hr
  
  LockMutex(mutex)
  hr = VariantClear_(*var)
  If hr = #S_OK
    *var\vt = #VT_R4
    *var\fltVal = 99.8
  EndIf
  UnlockMutex(mutex)
  ProcedureReturn hr
  
EndProcedure

ProcedureDLL GetDirectString()

  Protected result, temp.s
  
  LockMutex(mutex)
  temp = "Hello World"
  result = SysAllocStringLen_(temp, Len(temp))
  UnlockMutex(mutex)
  ProcedureReturn result

EndProcedure
vb Modult test

Code: Alles auswählen

Module test

    Declare Function GetString Lib "vbDLL.dll" (ByRef var As Object) As Int32
    Declare Function GetFloat Lib "vbDLL.dll" (ByRef var As Object) As Int32

    Declare Ansi Function GetDirectString Lib "vbDLL.dll" () As String

End Module
Teilcode zum Aufruf der DLL

Code: Alles auswählen

    Private Sub ToolStripMenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem2.Click

        Dim result As Object

        'result = "Testing"
        test.GetString(result)
        Me.ListBox1.Items.Add(result)
        test.GetFloat(result)
        Me.ListBox1.Items.Add(result)

        Me.ListBox1.Items.Add(test.GetDirectString())

    End Sub
Mit den type Object kann man auch ganze Arrays zurück geben. Ein Beispiel habe ich dafür.

FF :wink:

Re: Stringrückgabe aus DLL...

Verfasst: 27.12.2009 18:20
von Frank Smart
Hallo,

@mk-soft,

Super! Vielen Dank für Dein Feedback! Entsteht das Speicherleck nur bei VB? Oder auch, wenn man eine PB-DLL auch in PB nutzt? Wie entsteht es, denn da stehe ich im Moment auf dem Schlauch.
mk-soft hat geschrieben:Mit den type Object kann man auch ganze Arrays zurück geben. Ein Beispiel habe ich dafür.
Das wäre ganz lieb, wenn Du mir dieses Beispiel zukommen lassen könntest. Da bin ich sehr interessiert dran.

Was ist eigentlich die Aufrufkonvention bei einer PureBasic DLL? stdcall?

Vielen Dank für die Hilfe!

Gruß,

Frank

Re: Stringrückgabe aus DLL...

Verfasst: 27.12.2009 18:48
von ts-soft
Frank Smart hat geschrieben: Was ist eigentlich die Aufrufkonvention bei einer PureBasic DLL? stdcall?
ProcedureDLL = stdcall
ProcedureCDLL = cdecl

Re: Stringrückgabe aus DLL...

Verfasst: 28.12.2009 16:38
von mk-soft
Beitrag umgeleitet nach http://www.purebasic.fr/german/viewtopi ... =8&t=21519

Hatte noch ein dicken Bug im Beispiel...

FF :wink:

Re: Stringrückgabe aus DLL...

Verfasst: 07.01.2010 15:05
von Frank Smart
Hallo an Alle!

Erst einmal ein frohes neues Jahr an Alle! Ich habe mich ein paar Tage zurückgezogen, aus diesem Grund schreibe ich auch jetzt erst.

Das Thema hat sich für mich und ich hoffe auch für andere ein wenig erhellt.

Ich danke allen, die mit ihren Kommentaren und Beispielen geholfen haben. Mein ganz besondere Dank geht an mk-soft für sein tolles Beispiel. Ich lese ja schon viel mit in diesem Board, jetzt habe ich gelernt auch einmal die Ankündigungen im englischen Forum zu lesen :oops: . Dann hätte ich dieses Variant Helper Include schon eher gefunden.

Liebe Grüße,

Frank