Excel 2013 - funkcja z Delphi z min. 2 argumentami

0

Witam,

od wersji 2013 Excela przestały mi działać funkcje z DLL z Delphi, które mają więcej niż 1 parametr.

Delphi:

function Test(x : Variant) : Double; stdcall; // działa
begin
  Result := x;
end;
function Test2(x : Variant; a : Variant) : Double; stdcall; // nie działa, access violation z Excela
begin
  Result := 0;
end;

Makro z Excela:

Public Declare PtrSafe Function test Lib "fk2excel.dll" (ByVal Baza As Long) As Double
Public Declare PtrSafe Function test2 Lib "fk2excel.dll" (ByVal Baza As Long, ByVal abc As Long) As Double

Funkcja Test2 zadziała jeśli usunę jej drugi parametr. Czy macie jakiś pomysł co mogłoby być przyczyną?

1

long to na pewno nie jest variant. Dziwne, że kiedykolwiek działało. Podejrzewam, że wcześniej miałeś MSO 32bit i dużo szczęścia a teraz 64bit i szczęście się skończyło.
Zadeklaruj te funkcje z jakimś normalnym typem danych a nie variant

0

@abrakadaber - strzeliłeś w 10, wcześniej to były 32 bity, po zmianie na Int64 funkcja już się nie wywala. Jednak Int64 nie zawiera danych, które przekazuję. Daje 5, dostaję np. 193497152. Próbowałem ustalić jaki inny typ zmiennej ustawić zamiast Int64, jednak bez skutku.

Poniższa funkcja podpowiada, że mimo wszystko powinno być to Int64.

function Test(x : Int64) : Double; stdcall;
var
  typeString : String;
  basicType : Integer;
begin

 basicType := VarType(x ) and VarTypeMask;

  case basicType of
    varEmpty     : typeString := 'varEmpty';
    varNull      : typeString := 'varNull';
    varSmallInt  : typeString := 'varSmallInt';
    varInteger   : typeString := 'varInteger';
    varSingle    : typeString := 'varSingle';
    varDouble    : typeString := 'varDouble';
    varCurrency  : typeString := 'varCurrency';
    varDate      : typeString := 'varDate';
    varOleStr    : typeString := 'varOleStr';
    varDispatch  : typeString := 'varDispatch';
    varError     : typeString := 'varError';
    varBoolean   : typeString := 'varBoolean';
    varVariant   : typeString := 'varVariant';
    varUnknown   : typeString := 'varUnknown';
    varByte      : typeString := 'varByte';
    varWord      : typeString := 'varWord';
    varLongWord  : typeString := 'varLongWord';
    varInt64     : typeString := 'varInt64';
    varStrArg    : typeString := 'varStrArg';
    varString    : typeString := 'varString';
    varAny       : typeString := 'varAny';
    varTypeMask  : typeString := 'varTypeMask';
  end;

  ShowMessage(typeString); // varInt64

  Result := x;
end;
0

musiałbyś sprawdzić w helpie MS2013 jak wygląda sprawa rozmiaru typów danych

0

Problem jest zapewne przez to że zwracasz Double przy rzutowaniu mogło się coś popsuć.

0
szopenfx napisał(a):

Problem jest zapewne przez to że zwracasz Double przy rzutowaniu mogło się coś popsuć.

Chodziło mi, że zmienna x w tej funkcji ma złą wartość (duża liczba zamiast 5)

function Test(x : Int64) : Double; stdcall;

dodanie znacznika <code class="delphi"> - @furious programming

0

Ale podglądasz tą zmienną gdy jest w funkcji czy dopiero jak funkcja zwróci wartość? Najlepiej byłoby gdybyś gdzieś logował taki string:

IntToHex(x,16));

do pliku przed zwróceniem rezultatu. To nam może powie coś więcej

0

@szopenfx
Ten kod wywołany spod Excela wyświetlił mi 0000000000000002.

function Test(x : Variant) : Double; stdcall;
begin
  ShowMessage(IntToHex(x,16)); // 0000000000000002
  Result := x;
end;

Wiesz może co to za typ danych?

a po zamianie na:

function Test(x : Int64) : Double; stdcall;

zwraca: 000000000B0CADD0

P.S. W obu przykładach jako parametru w Excelu użyłem liczby 2.

0

A sprawdź czy to działa:

function Test(const x : int64) : Double; stdcall;
function Test2(const x, a : int64) : Double; stdcall;
0

Niestety też nie, podczas debugowania widać już, że zmienna X ma nieprawidłową wartość, z Excela przekazuje liczbę 2. Scren z Delphi w załączniku.

0

Niestety też nie, podczas debugowania widać już, że zmienna X ma nieprawidłową wartość, z Excela przekazuje liczbę 2.

Na załączniku widać, że zatrzymałeś program na linii z ustaleniem rezultatu funkcji, ale ta instrukcja nie została jeszcze wykonana, więc zmienna Result posiada wartość randomową; Żeby dowiedzieć się jaką wartość ma Result, musisz tę instrukcję wykonać.

1

Dla 64 bitowej wersji office long to nie jest Int64 tylko zwykły Integer. Jak chcesz przekazać Int64 to w makrze musisz użyć typu LongLong
http://msdn.microsoft.com/en-us/library/office/gg264421%28v=office.15%29.aspx

0

Hej,

spróbowałem obu opcji, jednak efekt taki sam jak poprzedno:
a) LongLong i Int64:

Private Declare PtrSafe Function Test Lib "fk2excel.dll" Alias "test" (ByVal a As LongLong) As Double
function Test(x : Int64) : Double; stdcall;
begin
  Result := x;
end;

b) Long i Int64

Private Declare PtrSafe Function Test Lib "fk2excel.dll" Alias "test" (ByVal a As Long) As Double
function Test(x : Integer) : Double; stdcall;
begin
  Result := x;
end;

Kombinowałem z wieloma typami danych w Excelu i Delphi, na razie najlepsze rezultaty przynosi w Delphi Variant/OleVariant (ale działa to tylko gdy funkcja ma jeden parametr). Próbowałem odczytać typ zmiennej Variant kodem: VarTypeAsText(VarType(x))) to zwraca mi Integer/Currency (zależnie od typu danych jaki zadeklaruję w makro w Excelu), jednak gdy zamieniam Variant na Integer/Currency to już przestaje działać (efekt jak poprzednio - losowa liczba w zmiennej x).

dodanie znaczników <code class="vbnet"> - @furious programming

0

@Tajiri - Delphi XE2 Professional, legalne
w załączniku przesyłam okrojony projekt z tymi 2 funkcjami i plik Excela, dziękuję za zaangażowanie

1

Nie mam Delphi, ale sprawdziłem pod Free Pascalem:

{$mode delphi}

library xcel;

function test(x:integer):double; stdcall;
begin
  result:=x+0.5;
end;

exports
	test;

end.

Excel 2010:

Private Declare PtrSafe Function Test Lib "c:\myprogs\pas\xcel.dll" Alias "test" (ByVal a As Integer) As Double

Public Sub Foo()
    MsgBox (Test(42))
End Sub

efekt:

XL.png

edit: w przypadku funkcji dwuparametrowej dostaję:

xl2.png

no to chyba nie pomogłem..

0

To spróbuj jeszcze zmienić konwencje wywołania z stdcall na cdecl albo safecall.

2

Zrobiłem parę testów i wynika z nich, że:

  1. przekazanie dwóch parametrów jako Variant nie działa powodując błędne wyniki, a w niektórych przypadkach zawieszenie excela
  2. nie udało mi się zmusić do działania tego pliku xlsa, który jest w paczce. Prawdopodobnie problem jest w trybie kompatybilności albo moją nieznajomością pisania makr ;)
  3. jak stworzyłem nowy plik i dodałem same deklaracje funkcji to też nie zadziałało. Funkcje zwracały stałe wyniki, ale za to bez żadnych błędów czy zawieszania.
  4. Dopiero jak opakowałem funkcje z dllki to zaczęło działać:

makro:

Private Declare PtrSafe Function Test Lib "fk2excel.dll" Alias "test" (ByVal a As LongLong) As Double
Private Declare PtrSafe Function Test2 Lib "fk2excel.dll" Alias "test2" (ByVal a As LongLong, ByVal b As LongLong) As LongLong
Private Declare PtrSafe Function Test3 Lib "fk2excel.dll" Alias "test3" (ByVal a As Integer) As Integer

Function test5(ByVal abc As LongLong, ByVal abc2 As LongLong) As LongLong
test5 = Test2(abc, abc2)
End Function

Function test6(ByVal abc As LongLong) As Double
test6 = Test(abc)
End Function

Function test7(ByVal abc As Integer) As Integer
test7 = Test3(abc)
End Function

dllka:

function Test3(x : Integer) : Integer; stdcall;
begin
  Result := x;
end;

function Test(x : Int64) : Double; stdcall;
begin
  Result := x;
end;

function Test2(a, b : Int64) : Int64; stdcall;
begin
  Result := a + b;
end;
0

@Tajiri - testuję Twoje rozwiązanie, ale coś mi się Excel rzuca o makra, mógłbyś mi przesłać Twój plik Excela?

2

Moja paczka.

0

@Tajiri - Twoje rozwiązanie działa doskonale, wysłałem Ci PW. Zamykam temat, mam nadzieję, że nikt więcej nie będzie musiał się z tym użerać ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1