When an EXE raised an exception message like "access violation at address XXXXXXXX...", the address XXXXXXXX is a hex value, and we can get the source code line number that caused the exception, by looking at the map file. Details below (by madshi at EE):
you need to substract the image base,
which is most probably $400000.
Furthermore you need to substract the
"base of code" address, which is
stored in the image nt headers of each
module (exe/dll). It's usually $1000.
You can check which value it has by
using the freeware tool "PEProwse
Pro". It's the field "Base Of Code" in
the "Details" of the "Optional
Header". You'll also find the image
base address there.
My question is: How to get the source line number for a DLL? Does the same calculation apply? Thanks!
Note 1: the map file is generated by Delphi and I'm not sure if this matters.
Note 2: I've been using JCL DEBUG but it couldn't catch the exception which seems occurred at the startup of the DLL (an Office add-in, actually).
Same calculations apply, with the following note: instead of image base address of EXE you'll need to take actual address where DLL had been loaded. Base of code for a DLL should taken in the same manner as for EXE (stored in PE's IMAGE_OPTIONAL_HEADER).
Btw, EXE and DLL are actually the same thing from the point of view of PE format.
Two binaries can not be loaded at the same address. So the image base address stored in the DLL/EXE is only a suggestion for which the binary is optimized. Where the binary actually gets loaded into memory depends on many factors, like other binaries loaded in the process first, Windows version, injected 3rd party dlls, etc.
As suggested you can use a debugger or a tool like Process Explorer to find out at what address the DLL is loaded at that time. Or if you want to know from code you can by getting the HInstance or HModule from the DLL since both are the same and are the address in memory the DLL is loaded at. Delphi gets you the HModule for other DLL's through the GetModuleHandle method. Newer Delphi versions also have other methods to find HInstance.
Related
We have a COM server written in Delphi 10.2 Tokyo which contains 4 classes, all of which inherit from TAutoObject, and all have an initialization section containing the call to TAutoObjectFactory.Create.
In a typical installation, we have approximately 60 other programs that make use of the classes in the COM server. This all works fine. Each of the classes has their own GUID and the Delphi COM server does its usual thing of ensuring they are all registered correctly as part of the InitComServer process that runs from Application.Initialize.
We now have a need to run multiple installations side-by-side, where they may be different versions. Since we don't control all of the programs, we can't produce a different version of the COM server with different GUID's for every installation.
I had found this Microsoft document:
DLL/COM Redirection on Windows
But that approach does not work. When the main program starts and creates an instance of the first COM server class, I can see that it is the registered EXE that is running, not the COM server EXE in the same folder as the main program. This might be because it's an EXE and not a DLL, and Delphi's initialization is circumventing the DLL/COM redirection.
Here's an example of using one of the COM classes:
function ProduceReport(const ATenantID, AReportID: Integer; const AFilter: String): String;
var
AReportServer: IReportServer;
begin
Result := '';
AReportServer := CoReportServer.Create;
try
if AReportServer.Connect(ATenantID) then
begin
if not AReportServer.Print(AReportID, AFilter) then
Result := AReportServer.GetLastError;
end
else
Result := AReportServer.GetLastError;
except on E: Exception do
Result := E.Message;
end;
AReportServer := nil;
end;
The main thing of note is that the CoReportServer.Create does this internally:
Result := CreateComObject(CLASS_ReportServer) as IReportServer;
which in turn is doing this:
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, Result));
I can't see what CoCreateInstance is doing, but I am wondering if there is a way to call CoGetInstanceFromFile instead? Perhaps by changing the CoReportServer.Create in the TLB to use my own code instead of the CreateComObject?
Would changing the call to TAutoObjectFactory.Create to use a different TClassInstancing, such as ciSingleInstance, make any difference?
I just want it so that when a program calls CoReportServer.Create, it creates an instance of the server from the EXE it has in the program's folder, not by looking up the InprocServer from the Registry. I don't want the Registry to be used at all.
[Edit]
Since we also have an OCX that will need to be registry-free, I followed the steps discussed in this post:
Generate manifest files for registration-free COM
I have a manifest file for the OCX test app and a manifest file the OCX itself, both of which are in the same folder as the test app and the OCX. The manifest file specifies the same version number of the OCX as the one in the folder.
I then copied an old version of the OCX to a different folder, ran an elevated CMD prompt, and used Regsvr32 to register that OCX in that folder.
Running the OCX test app, which includes the ActiveX control on the main form, I can see from Process Explorer that it is using the registered OCX, and not the OCX in the same folder.
This may indicate that Delphi EXE programs can't handle the manifest approach to this, even if I include the custom manifest in the Project Options.
We have a mix of COM server EXE, ActiveX visual control OCX, and ActiveX type library DLL, all written in Delphi 10.2 Tokyo, that we need to make registry-free.
[Edit 23/03/2022]
I've made some progress on this by referencing the information contained in the answer for Registration-free COM/DLL?
I now have a manifest file for the DLL containing an <assemblyIdentity> tag, a <file> tag containing a list of <comClass> tags for each CLSID in my DLL, and a list of <comInterfaceExternalProxyStub> tags to link the CLSID's and IID's with the interface name.
And there is a manifest file for the host EXE, that includes the same <assemblyIdentity> tag information, but contained within a <dependentAssembly> tag.
Using SxSTrace, I can see "INFO: Activation Context generation succeeded.", so at least the side-by-side configuration is correct.
However, when running the test program, at the point it is calling CoReportServer.Create, it is falling over with a "Error loading type library/DLL" message. Based on the stack trace, this is from the LoadTypeLibrary call inside the TAutoObjectFactory.Create process from the initialization section of the first class implementation of the first interface.
Since the SO answer I looked at mentions making sure it will work when the DLL is registered, I tried using regsvr32 from an elevated command prompt, but get a "Runtime error 217" popup, followed by "The module "reportserver.dll" failed to load. A dynamic link library (DLL) initialization routine failed."
The Result from LoadTypeLibEx is -2147312566, which doesn't seem to match the TYPE_E_CANTLOADLIBRARY result.
So it looks like I may have got the registry-free COM part to work, but the conversion of the out-of-process COM server to an in-process COM library isn't right.
So I went back to the COM server EXE, and re-registered that to confirm the test program still works. It did. Then I altered the test program manifest to point to the COM server EXE instead of my trial conversion DLL, made a new manifest file for the COM server EXE, containing all the IID's and CLSID's, and un-registered the EXE.
The test program reports "Class not registered", so that pretty much confirms the activation context approach only works on DLL's.
That leaves me with figuring out why the DLL will not load, although another issue I just thought of is that the DLL is in-process, meaning every program that uses the library has to create it's own instance. That could be very resource intensive.
[Edit 25/03/2022]
Skipping the out-of-process COM server for the moment, I've now taken a look at our ActiveX Control, which is a descendent of a TProgressBar and is used either on a VCL form or created at runtime.
Following the same approach for defining the manifest file, I've created the <assemblyIdentify>, <file> and <comInterfaceExternalProxyStub> tags, but I noticed that in the _TLB.pas, there is one extra TGUID for DIID_IReportControlEvents.
Reviewing the Microsoft documentation for manifest files, I cannot see any reference to an events type of interface, so I'm not sure how that one will work.
In any case, a manifest file doesn't work for an OCX. I just get the "Class not registered" error.
The SxStrace file shows "INFO: Activation Context generation succeeded." but it doesn't include any information about the manifest for the OCX, so it is not loading it at all.
This is probably because the _TLB.pas for the OCX shows it is using OleControl rather than COM. Removing the {$E ocx} from the DPR means Delphi creates a DLL instead, but that also fails.
I have copied a newer version of GdiPlus.dll to my projects directory.
However windows always loads it form
"C:\WINDOWS\WinSxS\x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.6002.23084_x-ww_f3f35550\GdiPlus.dll"
I'm on XP.
To exert complete control over DLL loading, pass the full path of the DLL to LoadLibrary.
This is viable if you use runtime linking. For load time linking you are reliant on the DLL search order. The first thing searched is the directory from where the exe loaded. That said, it is plausible that for an OS component like GDI+ it is plausible that the system will use the SxS DLL no matter what.
Your question title says "current directory" but you never want to rely on the value of the current directory when linking DLLs. The current directory's value is unpredictable.
Finally, GDI+ is a system component and I think it doubtful that providing your own GDI+ DLL is a wise move. It is quite possibly illegal too since redistribution of GDI+ is not permitted. Perhaps you are choosing the wrong solution to your problem.
you can use this declaration to set the directory of dll to a defined path.
function SetDllDirectory(lpPathName:PWideChar): Bool; stdcall; external 'kernel32.dll' name 'SetDllDirectoryW';
remember to change the path after working with your dll.
problem description :
..... ( this code is executed inside a thread.execute
...
if fileexists(myFile) then
begin
// call functions inside a DLL
...
dll_process_data ( .....) ;
....
end;
..... (clean up )
write_clean_data_fct(...)
..... //
... // AV happens here inside kernel.dll , no further information
If the file is not found the complete routine / multithreaded algos is executed without any problem; If i add the additional files some data is processed inside a dll, this is also done without trouble.
Somewhere after the write_clean_data_fct the AV happens now. INside the dll I open a file read data and do data processing with these data.
Q: what might be the reasin and how to debugg this issue ??
Can#t post more lines of code because the complete code sequence is ~ 15.000 LOC.
You can debug the DLL like this:
Open the DLL project in your IDE.
Make sure the host is going to load the DLL from the same path as your DLL project's output directory.
Set a break point on the function you wish to debug.
From the menu select Run | Host application and specify the host executable.
Run.
You should now be able to debug code in the DLL. In modern versions of Delphi, you should also be able to step into the DLL code when debugging the host executable. This requires you to have built the DLL with debug information, and for the host executable to load the DLL from the DLL project's output path.
Your actual problem sounds a little tricky. The access violation perhaps occurs long after the erroneous code executes. To debug that, you might first identify the code that raises the exception. Which part of that code has a pointer that could be invalid? Then track back to anything in the DLL that could have modified that pointer.
If static analysis does not help then you can add debugging tools. You'll want:
Range checking enabled to detect obvious buffer overruns.
Full FastMM debug to detect heap corruptions, access after free etc.
madExcept to give detailed diagnostics and stack traces for the actual exception.
You'll want these tools in all modules.
i am using Delphi XE4 to create a Voip program. i am using an outdated VOIP SDK From a company called BigSpeed which is no longer around the current code points to the following path 'C:\Program Files (x86)\BigSpeed Voice SDK\' where the active x controls are stored.
LIBID_bsVoiChatCln: TGUID = '{D2A88515-99E0-4EEE-A030-E5D2AB306A03}';
IID_IbsVoiChatClnX: TGUID = '{5055A626-56A1-4E58-A461-000A69CA3E03}';
DIID_IbsVoiChatClnXEvents: TGUID = '{665DB561-22D3-4624-B55B-4416309A2E03}';
CLASS_bsVoiChatClnX: TGUID = '{BE761C1E-1F6C-46F8-A99B-0AB29C9B2D03}';
How can i create a new GUID and have the program access the active x controls from a new directory.
You don't want to create new GUIDs. The GUIDs are the identifiers of that component. All you want to do, as far as I can tell from the question and your comments, is to register the DLL at a different location.
The ActiveX DLL almost certainly uses self-registration. This means that you can put the DLL somewhere else and register it there. For instance, suppose the DLL is located in:
C:\MyFolder\MyDll.dll
Then you could register it by executing this command:
regsvr32 C:\MyFolder\MyDll.dll
Looks like you do not understand (or do not explain) relations between your program, the library and the GUIDs.
How can i create a new GUID and
1) GUID is just a 128-bit random number. So you can "create new GUID" simply by editing its hexadecimal string. Or you can press Ctrl+Shift+G in Delphi source editor in designtime. In runtime you can use CreateGUID function of SysUtils unit.
http://docwiki.embarcadero.com/CodeExamples/XE5/en/UsingGUIDs_(Delphi)
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.CreateGUID
http://en.wikipedia.org/wiki/GUID
But i don't think creating new GUID will do you any good. If anything, it should mean explicitly declared incompatibility with old GUIDs (hence incompatibility with VOIP library)
from a new directory.
2) Why do you think your VoIP library is arranged as a set of ActiveX control ? Just because there are GUIDs there? Not any text file with GUIDs inside would be ActiveX.
ActiveX are specifically arranged Windows servers, that are registered in the registry so that any program could call them. Sometimes you can register them after the fact, if the installer failed it.
http://en.wikipedia.org/wiki/Activex
http://en.wikipedia.org/wiki/Regsvr32
http://support.microsoft.com/?id=207132
So you should read manuals for your library whether they constitute ActiveX or not, and if they do, how to register them in Windows (should be done by the library installer)
If installer does not provide for it, then you can not be sure that the library can work from a different place. Not only your program needs a connection to it, but also the library itself may need connection to its other parts.
have the program access the active x controls
3) If your library really conforms to ActiveX specifications and if it was correctly installed (registered) then you can just import them into Delphi IDE and drop them onto the form like you drop tables and dialogs.
http://docwiki.embarcadero.com/RADStudio/XE3/en/Import_Component_Wizard
http://delphi.about.com/library/howto/htaddactivex.htm
http://www.delphisources.ru/pages/faq/master-delphi-7/content/LiB0125.html
4) if you do not want to drop your VoIP component onto the form, then you can try to create it in runtime with CoCreateInstance. But first you have to read some tutorial about Microsoft COM for beginners. You may miss some advanced concepts, but you should understand the most basic things like how interfaces are similar to and different from classes, how their lifetime is managed, how COM runtime is initialized and finalized for your program and so on.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686615.aspx
http://delphi.about.com/library/weekly/aa121404a.htm
http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1135
How to use CoCreateInstance() to get a com object?
https://stackoverflow.com/search?q=%5Bdelphi%5D+cocreateinstance
all the MS Office related examples in c:\RAD Studio\9.0\OCX\Servers\
5) you may also try to bypass the proper Windows ways of locating and loading the server and try to do it yourself, using DllGetClassObject routines in proper DLLs. If the library is permissive, it will work. But if it does rely on Windows-provided services that has a potential for all kinds of crashes and unmet expectations.
https://www.google.com/search?client=opera&q=CoCreateInstance+and+DllGetClassObject&sourceid=opera&ie=utf-8&oe=utf-8&channel=suggest
http://msdn.microsoft.com/en-us/library/windows/desktop/ms680760.aspx
How do I use a COM DLL with LoadLibrary in C++
DllGetClassObject return "No such interface supported" while CoCreateInstance can find it successful
If my memory serves me, You can find examples of that approach in early HTML Help units for delphi. Microsoft HTML Help provides for both late binding using CoCreateInstance and runtime ActiveX servers registry, or early binding towards htmlhlp.ocx treated as DLL. Early versions of HTML Help API for Delphi provided for both options. But i may be wrong here.
I have a application that spits out the same error each time i do something specific:
Exception EListError in module foo.exe at 000277CF.
List index out of bounds(0)
(Physical address: XXXXXXX)
As a programmer I would love it if there were a way I could open it up and pinpoint the orgin of the error to the developers.
Would that be possible? I know the language and it's a mix of .NET and with some old Delphi code.
Any want to point me in the right direction? Tutorials? Programs to use?
The EListError exception seems indicate which the executable is a delphi app, Now Is not possible retrieve the original source code from a Delphi Win32 application, because the variable names, methods, parameters and addtional elements are converted to memory address and are lost when the executable is created. So the original code cannot be recreated.
You best option is use a decompiler like IDA to disassemble and debug the exe and determine the location of the issue and then you can create a patch to fix that, but this task is not easy and requiere a lot of knowledge about the pe structures, memory address location and assembly.
Also in your question you mention the posibility which the application is a mix of .net code and delphi code, if that is the case you can use reflector or Telerik JustDecompile to retrieve the original source code.