2010-07-17 5 views
1

동적으로로드 된 DLL에서 호스트 프로그램의 TDictionary 변수에 액세스하려고 할 때 매우 심각한 문제가 발생합니다. 여기에 완전한 코드가 있습니다. 누구든지 도움을 줄 수 있습니까? 감사!큰 문제가 있습니다 : TDictionary 및 Delphi 2010에서!

=========== 메인 프로그램 프로젝트 소스 코드 ===================

program main; 

uses 
    ShareMem, 
    Forms, 
    uMain in 'uMain.pas' {Form1}, 
    uCommon in 'uCommon.pas'; 

{$R *.res} 

begin 
    Application.Initialize; 
    Application.MainFormOnTaskbar := True; 
    Application.CreateForm(TForm1, Form1); 
    Application.Run; 
end. 

==== ========== 부 uMain ================

unit uMain; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls, uCommon; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 


var 
    Form1: TForm1; 


implementation 

{$R *.dfm} 


type 
    Tfoo = function(ADic: TMyDic): string; stdcall; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    Dic: TMyDic; 
    HLib: THandle; 
    foo: Tfoo; 
begin 
    Dic := TMyDic.Create; 
    try 
    Dic.Add(1, 'World!'); 
    Dic.Add(2, 'Hello, '); 

    HLib := LoadLibrary('Mydll.dll'); 
    try 
     @foo := GetProcAddress(HLib, 'foo'); 
     ShowMessage(foo(Dic)); 
    finally 
     FreeLibrary(HLib); 
    end; 
    finally 
    Dic.Free; 
    end; 
end; 

end. 

=============== == dll 프로젝트 소스 코드 ===================

library MyDll; 

uses 
    ShareMem, 
    SysUtils, 
    Classes, 
    uCommon in 'uCommon.pas'; 

function foo(ADic: TMyDic):string; stdcall; 
var 
    I: Integer; 
    S: string; 
begin 
    for I in ADic.Keys do 
    begin 
    S := S + ADic[I]; 
    end; 
    Result := s; 
end; 


exports 
    foo; 

end. 

============== unit uCommon ==============

unit uCommon; 

interface 
uses 
    SysUtils, Generics.Collections; 

type 
    TMyDic = TDictionary<Integer, string>; 



implementation 

end. 
+3

StackOverflow에 오신 것을 환영합니다. Neo.당신은 코드에 대해 아주 잘 설명해 주었고이 상황에서 잘못 될 가능성이있는 것에 대한 최선의 추측을 바탕으로 질문에 대답하려고 시도했지만 실제로 문제를 설명하면 더 나은 결과를 얻을 수 있습니다 *뿐만 아니라. "이 코드를 사용하면 프로그램에서 의 세 번째 줄에 예외가 발생합니다." –

+0

AFAIK, Delphi 2010! 아직 공개되지 않았습니다. –

답변

5

예외가 발생합니까? 액세스 위반 또는 잘못된 포인터 작업 일 수 있습니까?

DLL에 자체 메모리 관리자가있는 경우 Delphi와 DLL간에 문자열과 개체를 공유 할 수 없습니다. Delphi 2010을 사용하고 있기 때문에 기본적으로 FastMM이 설치되어 있어야합니다. 의 첫 번째 항목으로 "SimpleShareMem"을 추가하면 DLL과 EXE 모두에 대해 목록이 사용되며 문제가 해결되지 않는지 확인하십시오.

편집 : 당신은 DLL을 언로드 후에는 dic.free를 호출하고

: 포스터에서 추가 정보에 대응. 메모리 관리자를 공유하더라도 액세스 위반이 발생할 수 있습니다. 이유가 여기 있습니다.

무료 호출 TObject.Destroy는 가상 메서드입니다. 컴파일러는 객체의 Virtual Method Table에서 코드를 찾아 봅니다. 그러나 VMT는 메모리 관리자가 할당 한 공유 메모리가 아니라 모듈에 특정한 정적 메모리에 저장됩니다. DLL을 언로드하고 개체의 VMT 포인터 아래에서 깔개를 당겨서 가상 메서드를 호출하려고하면 액세스 위반이 발생합니다.

DLL을 언로드하기 전에 을 호출하기 전에을 호출하여이 문제를 해결할 수 있습니다. 또는 DLL 대신 런타임 패키지를 사용할 수 있습니다. DLL을 사용하기 전에 언로드되지 않는 외부 패키지에 개체의 VMT를 저장하여이 문제를 해결할 수 있습니다.

+0

안녕하세요, Mason : 예, 'Dic.Free'가 실행될 때이 코드는 예외가 발생합니다. 그리고, 당신의 제안을 시도하고, "ShareMem"또는 "SimpleShareMem"을 사용 목록에 추가하십시오. 그러나 문제는 해결 될 수 없습니다. 고마워! – user394386

+0

@ neo2010 : 좋습니다. 내 대답을 편집 할게. –

+0

생각 해봐, 메이슨. 알았어, 너의 충고는 나에게 매우 도움이된다! 다시 한번, 많은, 많은 감사합니다! :) – user394386

5

실행 파일과 일반 DLL간에 개체 인스턴스를 전달하지 않는 것이 좋습니다. 주로 당신이 겪고있는 정확한 이유 때문입니다. DLL이 다시 빌드되고 일부 호환되지 않는 미묘한 방법으로 개체를 변경 한 경우 어떻게됩니까?

Mason이 지적했듯이 패키지는 응용 프로그램을 모듈로 분할하는 기본 방법입니다.

+3

그리고 개체 자체를 변경할 필요가 없습니다. 내가 틀렸다면 나를 정정해라.하지만 컴파일러 지시문만으로는 객체의 바이너리 표현을 바꿀 수있다. (데이터 정렬이 객체에 아무런 영향을 미치지 않는다면, "최소 열거 형 크기"스위치가 될 것이라고 확신합니다.) –

+0

예, 그 중에서도. 선언에서 가상 메소드의 순서를 추가 또는 재배치합니다. 메서드 매개 변수 (유형, 숫자 등) 또는 호출 규칙 변경 다시 말하지만, 여전히 패키지에 적용되지만, 적어도 Windows 로더는 메소드의 내보내기 서명이 변경되기 때문에 많은 것을 포착해야합니다. 또한 컴파일러는 종속 모듈을 다시 컴파일해야하는지 감지하는 데 도움이됩니다. C++, OTOH, 정말 바람에 더 많이 비틀어집니다. ;-) –

+0

정확히. 패키지를 사용하거나 객체를 공유하지 마십시오. –

0

나는 마침내 진짜 문제가 무엇인지 발견했습니다! 다음과 같이 보인다 : "for..in을 키는"루프 TDictionary는 데이터 필드 FKeyCollection에 대한 인스턴스를 생성하게됩니다 : DLL이 언로드 그래서 때, FKeyCollection도 해제됩니다 가리키는 메모리는, 따라서

function TDictionary<TKey,TValue>.GetKeys: TKeyCollection; 
begin 
    if FKeyCollection = nil then 
    FKeyCollection := TKeyCollection.Create(Self); 
    Result := FKeyCollection; 
end; 

왼쪽 "매달린 포인터".

destructor TDictionary<TKey,TValue>.Destroy; 
begin 
    Clear; 
    FKeyCollection.Free; //here will throw an exception 
    FValueCollection.Free; 
    inherited; 
end; 
+0

DLL과 기본 EXE 사이의 TObject에 대한 포인터를 공유하고 있다면 델파이 "죄"를 저지르고 있으며 앞으로는 더 많은 결함을 발견하게 될 것입니다. 하지마. –

관련 문제