0
votes

I've got a 64bit Delphi (XE4) dll. I call it from Excel VBA.

I use the following trick : http://www.devx.com/tips/Tip/37587

It works for 32bit and 64bit excel-2010, but not with excel-2013

StrDataSizePtr^:=Length(tmpStr);//Access Violation here

What can be the problem? Does excel-2013 vba has new String format?

Thank You!

EDIT:

Delphi

{$IFDEF WIN64}
TPtrLong = UInt64;
{$ELSE}
TPtrLong = Longword;
{$ENDIF}

procedure StrToVBAStr(Str : String;VAR VBAStr : PAnsiChar);
VAR
  VBAStrPtr : TPtrLong absolute VBAStr;
  ResStrSizePtr : PLongword;
begin
  if Length(Str)>Integer(StrLen(VBAStr))
  then raise Exception.Create('StrToVBAStr :     '+IntToStr(Length(Str))+'>'+IntToStr(StrLen(VBAStr)));

  ResStrSizePtr:=Ptr(VBAStrPtr-4);//this points to VBA String size
  VBAStr:=StrPLCopy(VBAStr,Str,Length(Str));//copy to VBAStr-be
  ResStrSizePtr^:=Length(Str);//set VBAStr length
end;

function GetLastError(VAR Error : PAnsiChar) : Longint; stdcall;
VAR
  sError : String;
begin
  TRY
    Result := _GetLastError(sError);
    StrToVBAStr(sError, Error);
  EXCEPT
    Result := -1;
  END;
end;

VBA

Private Declare PtrSafe Function XLDLL_GetLastErrorA Lib "XL.dll" Alias "GetLastError" ( _
ByRef Result As String) As Long

Public Sub XLDLL_Error(Optional ByVal Source As String = "")
  Dim XLErr As String

  XLErr = Space(1001)
  If XLDLL_GetLastErrorA(XLErr) <> -1 Then
    XL_LastError = XLErr
    If XL_LastError <> "" Then
      Err.Raise vbObjectError + 1000, Source, XL_LastError
    End If
  Else
    Err.Raise vbObjectError + 1000, "XLDLL_Hiba", "XLDLL_GetLastErrorA hiba"
  End If
End Sub
2
Are you sure "PAnsiChar" rather than "PWideChar" ? VBA is unicode-aware languageArioch 'The
1) since you modified the original code you link and due to StackOverflow aims and rules, i ask you to explicitly show your actual code in your question above. 2) "Access Violation here" - show full and exact text of error. Removing error information is not a good idea for us to understand your errors. 3) "StrDataSizePtr^:" show the real code again! 4) "Does excel-2013 vba has new String format?" It does not matter, tmpStr should anyway anyway be isolated from VBA by prepending Delphi bridging code, if you did not removed itArioch 'The
5) "function GetLastError(VAR Error" where did that VAR came from ? it is not i nthe original example. 6) what is the value of "StrDataSizePtr" right before the AV ? 7) what is the value of "Pointer(Error)" ? What is your VBA code that CALLS the function ?Arioch 'The
I'm totally sure that PAnsichar is good. VBA converts Unicode String to Ansi String before the dll call. blog.nkadesign.com/2013/vba-unicode-strings-and-the-windows-apiE_Pluribus_Unum81
BTW, even if VBA 2013 would not change the implementation of stirngs-to-DLL bridging, the BA 2015 can or VBA 2020. Using hacks liek that you just placed a timebomb in your application. Do you think it is fare regarding your customers ?Arioch 'The

2 Answers

1
votes

That code has never been correct. It might have worked by chance in the past. It's possible that the internal private implementation of the VBA string has been changed. Or it is possible that it has stayed the same and your luck has just run out.

In any case, the correct solution is to stop relying on the private internal implementation detail of the VBA string. Passing a string from native code to VBA is simply enough. Do it like this:

Delphi

procedure GetString(Str: PAnsiChar; var Len: Integer); stdcall;
var
  Value: AnsiString;
begin
  Value := ...;
  StrLCopy(Str, PAnsiChar(Value), Len);
  Len := Min(Len, Length(Value));
end;

VBA

Private Declare PtrSafe Sub GetString Lib "XL.dll" ( _
    ByVal str As String, ByRef len As Long)
....
len = 1024
buff = Space(len)
GetString(buff, len)
buff = Left(buff, len)
0
votes

It looks like the problem was caused by an other Excel plugin. On a pure new Excel-2013 install it works fine. After removing plugins from the Excel-2013, the error gone.

(The VBA "string hack" still works in Excel-2013)