tags:

views:

1664

answers:

2

I am successfully calling a function in a DLL from Inno Setup, however upon returning I get a Runtime Error...Exception: Access violation at address XXXXXXX. Write of address XXXXXX.

The function is declared as:

function CompleteInstall(szIntallPath: String) :  Integer;
external 'CompleteInstall@files:InstallHelper.dll stdcall setuponly';

And called:

procedure CurStepChanged(CurStep: TSetupStep);
begin
   if CurStep = ssPostInstall then begin
      CompleteInstall('Parm1'); // ExpandConstant('{app}')
   end;
end;

There is no problem if I change the function to not take a parameter. It still occurs if I change it to take a single integer parameter or declare it as a function and change the function to be a void function with an integer parameter.

The called function does nothing but return:

__declspec(dllexport) int CompleteInstall(char* szInstallPath)
{
    //AfxMessageBox ("Got here" /*szInstallPath*/, MB_OK);
    return 1;
}
+4  A: 

You have a mismatch of the calling conventions. Either make the DLL function use stdcall as well:

__declspec(dllexport) __stdcall int CompleteInstall(char* szInstallPath)
{
    //AfxMessageBox ("Got here" /*szInstallPath*/, MB_OK);
    return 1;
}

or change the function declaration to use cdecl instead of stdcall:

function CompleteInstall(szIntallPath: String) : Integer;
    external 'CompleteInstall@files:InstallHelper.dll cdecl setuponly';
mghie
That is a good thought. My project settings in Visual Studio 2008 when set to _stdcall (C++...Advanced...Calling Convention to __stdcall /Gz) results in a Cannot Import Runtime error (with stdcall being used in the iss script). I tried again and it seems using __cdecl in the .iss script and c++ code is the only thing that works. Thanks for your help.
AlanKley
@AlanKley, you really shouldn't change the calling convention for *everything*, just this one function. That is what adding __stdcall to the function's declaration on the C side is doing. Regardless, the function's implementation and the caller's assumptions about calling convention *must* agree.
RBerteig
A: 

Although according to mghie (see comments) it shouldn't make a difference in this case, you might want to use PChar instead of String as that would be the more accurate equivalent of the C-declaration char*.

String is a Pascal-native type which is usually managed quite differently than a PChar (though apparently not so much in Inno's PascalScript).

Oliver Giesen
-1, wrong answer in the context of Inno Setup. See the CodeDll.iss example, the declaration of the MessageBox() API function. In Inno Setup scripting DLL function declarations *do* use String instead of PChar.
mghie
Just because that example uses String instead of PChar doesn't necessarily render my statement wrong. I always use PChar in InnoSetup scripts when calling API functions that expect string parameters and never had any issues with that. I hadn't looked at the CodeDll.iss example though... strange... wouldn't have expected that to work... Have you actually tried changing "String" to "PChar"?
Oliver Giesen
Sorry, maybe my comment wasn't clear enough. I expect that PChar works just as well as String, they should result in the same internal representation in the scripting engine. However, this answer is not helpful for the OP, as the problem he has would manifest in just the same way if he used PChar instead of String. In the context of Inno Setup scripting these *are* equivalent. It's a strange thing, but it's the way it is. OTOH, using the real String type for functions exported from a DLL would be a bad idea, anyway.
mghie
Hmm, do you have any reference on that? I've spent a full hour googling and search the IS list archive but couldn't find any mention that PChar and String are identical in InnoSetup script. Unfortunately I'm currently not at a computer where I could simply try this myself. I just vaguely remember that I explicitly changed some function signatures from String to PChar in the past because it wasn't it working otherwise.
Oliver Giesen
After having a closer look at uPSRuntime.pas in RemObjects Pascal Script I have to agree with you, they are not completely equivalent. The CodeDll.iss example just uses them in a way that the difference doesn't matter (both are treated as a const pointer to the first character), and I never used them in any other way either. I think that the difference would only matter if allocation and re/deallocation of strings would happen in different modules. For variables that don't have their length changed in the DLL it doesn't matter. Have you used such DLL functions?
mghie
I will revoke the downvote if you edit your post (otherwise the vote's too old to be changed), as the answer seems more correct than not. However I still think it isn't helpful with regards to the problem the OP has.
mghie
Done. Could you maybe elaborate a little more on the difference between String and PChar in PascalScript? And does this only apply to DLL-parameters or the types as used in PS in general?
Oliver Giesen
As long as the length of the string is not changed both String and PChar are treated as pointer to first character in a null-terminated string. As long as this is writeable memory the contents can be changed. Using String for a DLL parameter is a bad idea, as then both DLL and host app would need to use the same memory manager. If parameters are PChars it is possible to declare and read them as String. Writing to them (as String) would lead to crashes. Inside of PS one would probably always change the length, so they should indeed be String, not PChar. For details see source in uPSRuntime.pas.
mghie