3F / DllExport

.NET DllExport with .NET Core support (aka 3F/DllExport aka DllExport.bat)
MIT License
940 stars 131 forks source link

External Exception E0434352 using netcore #130

Closed phillsonntag closed 4 years ago

phillsonntag commented 4 years ago

Hello,

I'm currently struggling with the implementation of an exported .NET Core function in my Delphi application. Just before I submitted this issue I also tried to implement the same dll with the same function in C++. Both implementations threw the same error 0xE0434352. When I inspect the dll using a dependency inspector, the right dependencies are set and the function is correctly exported.

Used configuration:


Source

ExportsDemo.dll -> Exports.cs:

using System.Windows.Forms;

namespace ExportsDemo
{
    public static class Exports
    {
        [DllExport]
        public static void DemoProc()
        {
            MessageBox.Show("Hello world!");
        }
    }
}

ExportsDemo -> ExportsDemo.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
  <PropertyGroup>
    <DllExportIdent>19A9E616-EECD-4703-897E-4BD8A257C0F0</DllExportIdent>
    <DllExportMetaLibName>DllExport.dll</DllExportMetaLibName>
    <DllExportNamespace>ExportsDemo</DllExportNamespace>
    <DllExportDDNSCecil>true</DllExportDDNSCecil>
    <PlatformTarget>x64</PlatformTarget>
    <DllExportOrdinalsBase>1</DllExportOrdinalsBase>
    <DllExportGenExpLib>false</DllExportGenExpLib>
    <DllExportOurILAsm>false</DllExportOurILAsm>
    <DllExportSysObjRebase>false</DllExportSysObjRebase>
    <DllExportLeaveIntermediateFiles>false</DllExportLeaveIntermediateFiles>
    <DllExportTimeout>30000</DllExportTimeout>
    <DllExportPeCheck>2</DllExportPeCheck>
    <DllExportPatches>0</DllExportPatches>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="DllExport, PublicKeyToken=8337224c9ad9e356">
      <HintPath>$(SolutionDir)packages\DllExport.1.7.0-beta3\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName)</HintPath>
      <Private>False</Private>
      <SpecificVersion>False</SpecificVersion>
    </Reference>
    <Reference Include="System.Windows.Forms">
      <HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Forms.dll</HintPath>
    </Reference>
  </ItemGroup>
  <ImportGroup Label=".NET DllExport">
    <Import Project="$(SolutionDir)packages\DllExport.1.7.0-beta3\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(SolutionDir)packages\DllExport.1.7.0-beta3\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
  </ImportGroup>
  <Target Name="DllExportRestorePkg" BeforeTargets="PrepareForBuild">
    <Error Condition="!Exists('$(SolutionDir)DllExport.bat')" Text="DllExport.bat is not found. Path: '$(SolutionDir)' - https://github.com/3F/DllExport" />
    <Exec Condition="('$(DllExportModImported)' != 'true' Or !Exists('$(SolutionDir)packages\DllExport.1.7.0-beta3\tools\net.r_eg.DllExport.targets')) And Exists('$(SolutionDir)DllExport.bat')" Command="DllExport.bat  -action Restore" WorkingDirectory="$(SolutionDir)" />
  </Target>
  <Target Name="DllExportRPkgDynamicImport" BeforeTargets="PostBuildEvent" DependsOnTargets="GetFrameworkPaths" Condition="'$(DllExportModImported)' != 'true' And '$(DllExportRPkgDyn)' != 'false'">
    <MSBuild BuildInParallel="true" UseResultsCache="true" Projects="$(MSBuildProjectFullPath)" Properties="DllExportRPkgDyn=true" Targets="Build" />
  </Target>
</Project>

C++ Project (PlusPlusDemo):

#include <iostream>
#include <windows.h>

typedef int(__stdcall *f_funci)();

int main()
{
    HINSTANCE hGetProcIDDLL = LoadLibraryA("C:\\Users\\psn\\Desktop\\ExportsDemo.dll");

    if (!hGetProcIDDLL) {
        std::cout << "could not load the dynamic library" << std::endl;
        return EXIT_FAILURE;
    }

    // resolve function address here
    f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "DemoProc");
    if (!funci) {
        std::cout << "could not locate the function" << std::endl;
        return EXIT_FAILURE;
    }

    funci();
    return EXIT_SUCCESS;
}

(I don't really know the syntax of C++, just wanted to make sure the exception doesn't belong to Delphi.)


Logs

C++ Exception: Unhandled exception at 0x00007FFB9336A839 (KernelBase.dll) in PlusPlusDemo.exe: 0xE0434352 (parameters: 0xFFFFFFFF80070002, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00007FFB85F00000).

C++ CallStack:

KernelBase.dll!00007ffb9336a839()
clr.dll!00007ffb85f1a3f1()
clr.dll!00007ffb860ee7d9()
clr.dll!00007ffb86338585()
mscoreei.dll!00007ffb869fb26a()
mscoreei.dll!00007ffb86a099c2()
mscoreei.dll!00007ffb869e644e()
PlusPlusDemo.exe!main() Line 27
PlusPlusDemo.exe!invoke_main() Line 79
PlusPlusDemo.exe!__scrt_common_main_seh() Line 288
PlusPlusDemo.exe!__scrt_common_main() Line 331
PlusPlusDemo.exe!mainCRTStartup() Line 17
kernel32.dll!00007ffb940c7bd4()
ntdll.dll!00007ffb9562ced1()

Output of Delphi using the Visual Studio Debugger: VS2019_Delphi.log

Thank you in advance for the time and effort you put in this project.

3F commented 4 years ago

@phillsonntag, Did you try with rebasing system object? Rebase System Object option.

See also related #125.

3F commented 4 years ago

Thank you in advance for the time and effort you put in this project.

Yes, recent 2 years have been very difficult for me :( And it was very difficult to combine all that with opensource activities. Really hard for my time and generally for me personally.

But I really like that all this are still helpful, and I still can continue develop something like this. Who follows me also knows that I had plan (and still have, see related #90) for native implementation. But today we're still using this through ilasm and through my custom assembler features for .NET Core support, as you can see. Well, time will tell. I'm still alive and this is good point to continue develop something in OSS and at all :)

I want to thanks again to all who supported me! Thank you, guys!

This project personally aggregated a bit of sum (especially when comparing with stars on github with my other projects). For all this years it was just about $80 for DllExport :) Soon I will try count this in details and personally donate to Robert and Cecil project something from that amount.

3F commented 4 years ago

@phillsonntag,

I've just checked your examples. Thanks for the detailed report information! I really appreciate it because it really helps to save a bit of time in attempts to reproduce problem.

So, I don't see the problems when rebasing system object. Thus, please consider use the related option. Details in #125 as I mentioned above.

Let me know if you have other problems. Thanks!

3F commented 4 years ago

ah yes:

typedef int(__stdcall *f_funci)();

FYI: We're using __cdecl by default for any exports! For __stdcall you need to configure DllExport attribute.

phillsonntag commented 4 years ago

Finally! It works, thank you very much for your help. The rebase did it. I missed something, but it finally works now. THANKS! :D

EDIT: I also tried to define the stdcall before, but it didn't work either, so I tried everything I could find :D

phillsonntag commented 4 years ago

FYI: When I explicitly define Cdecl in the DllExport attribute, and import it in C++ via StdCall it also works. I don't know if this is wanted behaviour.

3F commented 4 years ago

Finally! It works, thank you very much for your help. The rebase did it.

Good!

Cdecl in the DllExport attribute, and import it in C++ via StdCall it also works.

For your case above it does not matter:

C++ funci() -> ... -> CLR MessageBox -> (__stdcall) -> user32

Calling convention just declares the rules for how to use stack, registers, arguments between calling; Mainly, who cleans the stack and how arguments are passed. Please read MSDN for details.