1

I'm getting an access violation when I try to link to a function within a DLL file. DLL was made in Delphi 6 and we are upgrading to Delphi 11 (wild jump I know). So the code once upon a time worked and somewhere in the many changes to Delphi has come crumbling down. The Delphi environment is running on a Windows 10 machine. The DLL in use has been compiled and built within Delphi 11 (though components have changed). Delphi claims that it can compile and build, if that is where this issue possibly lies I can get more code as I have the source (and project files) for the DLL.

procedure TForm1.Button1Click(Sender: TObject);
type
  TShowUserList = function : TForm; stdcall;
var
 LHandle: THandle;
 LUserList : TShowUserList;
 LForm : TForm;
begin
  LHandle := LoadLibrary('E:\D11 Projects\Test Dll\Win32\Debug\BOUsers.Dll');
  if LHandle <> 0 then
  begin
    @LUserList := GetProcAddress(LHandle, 'ShowUserList');
    if @LUserList <> nil then
    begin
     LForm := LUserList; //Here is where error shows, doesn't even get to the ShowUserList function
    end;
  end;
end;

When debugging, when I get to LForm := LUserList, LUserList is an 'inaccessible value'. Which would make sense as to why the error is occurring.

And within the BOUsers.dll is a UserList form,

FUNCTION ShowUserList : TForm; 
    BEGIN
      IF NOT G.FormExists('UsersListForm') THEN
        BEGIN
          UsersListForm := TUsersListForm.Create(Application);
          Result        := UsersListForm;
        END
      ELSE
        Result := G.ReturnForm('UsersListForm');
    END;

EXPORTS
   ShowUserList;

Debugging doesn't even enter here (I have made sure debugging is enabled for the dll, though it seemingly was by default). I'd be slightly happy if I could get into this code and debug!

I'm aware there isn't the full amount of error handling (not that there was much on the D6 version). However this is just a test project to help figure out the issue in the main application, I can add it if needs be though.

My expectation is that LForm would be the UserListForm within the dll and that I'd be able to display it within a window pane, debug it for issues and use it as expected.

I've tried to look through various similar questions on StackOverflow (though if I've missed one feel free to link it and I will look through it). Copied the example embarcadero supplied.

Honestly, haven't worked with DLL's before past applications were all units and forms within the exe basically.

TedEdGo
  • 13
  • 4
  • 1
    The EXE's `TForm` is very different from the DLL's `TForm`. – Andreas Rejbrand Jun 12 '23 at 11:37
  • Is that the case even if the DLL's been compiled and built in Delphi 11? – TedEdGo Jun 12 '23 at 11:42
  • 1
    @TedEdGo Yes. It is typically unsafe to use non-trivial data types, like VCL objects, across a raw DLL boundary. If you want to directly use a `TForm` object across the boundary, you need to enable Runtime Packages in both DLL and EXE, and also compile both in the same compiler version. Otherwise, you would need to re-design the DLL to abstract the `TForm` access behind something that is compatible with C, either by using plain flat functions, or a COM interface. The code shown would not work safely as-is even if the EXE were written in D6 without using Runtime Packages. – Remy Lebeau Jun 12 '23 at 20:10
  • 1
    Wondering how the heck it worked in the first place but thanks for the explanation. Think you've helped me get through a few humps with this migration malarkey. Only about 30 DLL's to get through re-designing / where applicable re-integrating with the main application as no-one here could answer why some of the DLL's were made. But thank you very much for your help here and with the other question, it is much appreciated. – TedEdGo Jun 14 '23 at 14:01

1 Answers1

0

You didn't mention it, but what OS are you running this on? Win XP by any chance? Are you trying to migrate the app to Win 10 or 11?

I'm asking because I'm not sure D11 will even run in Win XP, so I'd be very surprised if you could access a DLL created in D6 from code written in the latest version of Delphi (D11).

I think your best bet is to get the source code for the DLL and rebuild it in D11 first. Short of that, if it's from a 3rd-party vendor, look into getting a newer version.

If you don't have the source code, then I'm not sure what to suggest other than rewriting it or replacing it with an equivalent library. (Surely someone has written something similar in the past 20 years!)

At my last job, I was given the task of migrating a Delphi app from XP to Win Server 2016. They thought it would be a simple recompile, but that didn't work as the DLL wouldn't run in Win10. We were not compliant with the vendor's licensing terms anyway, and the vendor wanted to charge us an arm and three legs for a newer solution. I eventually proposed we replace the DLL with a component library written natively in Delphi that ultimately did the job much cheaper and WAY faster.

ADDED (per your request below): you didn't ask for this exactly, but since you have the source code, you might consider refactoring by turning these DLLs into micro-services using a locally hosted REST service (ie, inside of your intranet). That would work great for things where timing isn't an issue and would simplify the deployment to other machines (if that's even a need) where the DLLs would require more attention. There are lots of libs that can be used to build services with Delphi; I'm partial to TMS XData. There are a couple of online courses they sell that take you step-by-step through the process. (It's part of their Biz Components lib.) Unless you're returning PODOs from the DLLs (not likely) then it should be really straightforward to migrate them to a micro-service architecture.

David Schwartz
  • 1,756
  • 13
  • 18
  • I didn't mention that! Whoops, I'll edit the answer. But I am running Windows 10. So Delphi 11 is fine on that front. I've also compiled and built the DLL in Delphi 11 and it say's it's fine. Whether that is actually the case is another matter. Ideally this would be built for Windows 10 (and 11 down the line). All dll's are inhouse built so I have the source still. The only thing's that have changed in them is a few visual controls but I'm not even getting that far yet :( I'll amend the question to include this extra information. But migrating is a pain in the backside right! – TedEdGo Jun 12 '23 at 11:39
  • Can you update the answer with the new information? But *** *** *** *** *** *** *** *** *** *** ***[without](https://meta.stackexchange.com/a/131011)*** *** *** *** *** *** *** *** *** *** *** *** "Edit:", "Update:", or similar - the answer should appear as if it was written right now. – Peter Mortensen Jun 12 '23 at 11:49
  • Ok, so if you recompiled the DLLs, then you also need to register them, and then create an interface unit for them (although it's optional). I've never liked working with DLLs because of all the errors that can crop up even tho you don't get any compiler errors. In this case, it may well be "fine", but the run-time error seems to be telling you that it can't find that function name or entry point. IOW, it's saying it can't figure out how many fingers you're holding up when you make the call. (You can use a name or an index#, but it's a lot easier to work with an interface unit.) – David Schwartz Jun 12 '23 at 11:50
  • @PeterMortensen Done that now! Thanks for pointing to the meta list of things not to do. Old habit of mine sorry about that. David, well registering the DLL is a new issue popping up (yay!) Though I'm not entirely sure they were ever registered. Most of the app is built up of inhouse developed DLL's with a main application driving them. A few 3rd party integrations but mostly inbuilt ones to 'segment' code it seems. And I have the same (maybe less) level of liking DLL's as you. Sometimes they are a neccessary evil, today they are just evil! I'll need to look into interface units (homework!) – TedEdGo Jun 12 '23 at 14:40
  • 2
    @DavidSchwartz "*I'm not sure D11 will even run in Win XP*" - correct, the IDE itself won't, but that is not a factor here. "*I'd be very surprised if you could access a DLL created in D6 from code written in the latest version of Delphi (D11).*" - yes, of course you can. Provided the DLL is designed so its interface is compatible across different compilers. Which, unfortunately, this DLL is not, it seems. – Remy Lebeau Jun 12 '23 at 20:09
  • @TedEdGo depending on the long-term objectives of this project, you might want to consider converting the DLLs into REST-based micro-services. They are analogous approaches to code re-use; DLLs are intended for 100% use within the same machine environment, while micro-services can be hosted either internally or as middle-tier services. You could combine the logic from several DLLs into one or more micro-services without a lot of fuss, and maybe even find other things to off-load from the main code along the way. Just a thought. – David Schwartz Jun 13 '23 at 00:38
  • @TedEdGo the "registration" I'm talking about is required to allow DLLs to work in Windows. You need to call regsvr32 once from a command shell (proably running in Admin mode) to tell Windows where to find the DLL file when some process requests it. DLLs are simply run-time libraries that are loaded dynamically at run-time rather than statically at link-time. – David Schwartz Jun 13 '23 at 00:42
  • @TedEdGo following on from the earlier comment on micro-services: what Remy said above regarding passing classes through the DLL interface is right. A micro-service might be simpler to work with, although I've found that most of the time I wanted to pass entire objects to or from a REST service, most of the data was extraneous. Saving or fetching records is different. But mostly it's like calling a function with a few params and getting back one or a few results, sometimes an array of data. Forms are UI creatures; a middle-tier service doesn't typically need UI data. – David Schwartz Jun 13 '23 at 00:51
  • 1
    I got the registration side (had to do that way too much at the last workplace). But seems like the solution will be a mix of Micro-services, redesigning DLL's and re-integrating some of the DLL's back into the main application as they don't really need to be a DLL. Didn't think of using Micro-services, though I was also trying to minimise the changes made to the system (as much as possible during the migration at least). Seems that won't be the case so a good ol' hammer and get stuck in approach seems best fit. But thank you for your help. – TedEdGo Jun 14 '23 at 14:04
  • 1
    @DavidSchwartz Might be worthwhile adding information of micro-services to the original answer as that with the combination of redesign / removal of some is the end result (allows SO to have an answer). Also, I can't answer why forms and such as attached to a DLL process, and nor can anyone who works here. An undocumented decision of a bygone era! :) – TedEdGo Jun 14 '23 at 14:07
  • I edited this reply to include something about micro-services. Also, keep in mind that "migrations" like this typically involve a certain amount of refactoring, although in my experience Management can be very resistant. But replacing old DLLs with a micro-seervice architecture seems like a justifiable trade-off to me. Push some of that code back into the main code units and move common functions into the REST service. – David Schwartz Jun 14 '23 at 19:50