2010-09-23 73 views
9

Tôi đang cố gắng viết một thư viện dll trong Delphi wih một hàm tạo ra một thể hiện của hậu duệ TFrame và trả về nó. Nhưng khi tôi nhập khẩu chức năng này trong một ứng dụng, mỗi khi tôi gọi nó, tôi sẽ nhận được một ngoại lệ như "điều khiển 'xxx' không có cửa sổ cha mẹ". Tôi không chắc chắn 100%, nhưng ngoại lệ xuất hiện trong hàm tạo của lớp đó khi bất kỳ điều khiển GUI nào được truy cập.Điều khiển 'xxx' không có cửa sổ cha mẹ

Bạn có thể cho tôi biết lý do của hành vi đó là gì không? Tôi có nên sử dụng con cháu TForm thay thế hay là có một giải pháp tốt hơn?

Cảm ơn bạn!

+0

Tại sao bạn sử dụng DLL và không phải gói? DLL là một cơn ác mộng để có được quyền. –

Trả lời

8

Về lỗi

Đó thông báo lỗi được huy động từ các đơn vị Controls.pas, từ phương pháp TWinControl.CreateWnd. Về cơ bản mã được sử dụng để tạo ra cửa sổ xử lý cho hậu duệ TWinControl của bạn (TFrame, TButton, TEdit ... nếu nó có thể có tiêu điểm bàn phím nó là một hậu duệ TWinControl), và nó thực sự là một thông báo lỗi rất hợp lý: Bạn không thể có một Cửa sổ mà không có WindowParent, và vì chúng ta đang nói về VCL ở đây, nó làm cho rất nhiều ý nghĩa để thử và có được cửa sổ cha mẹ xử lý từ TWinControl.Parent; Và đó không được chỉ định.

Đó không phải là tại sao thông báo lỗi xuất hiện. Bạn sẽ thấy thông báo lỗi đó vì một số mã bạn đang sử dụng để thiết lập khung yêu cầu một cửa sổ xử lý cho một số hoạt động. Nó có thể là bất cứ điều gì, như thiết lập các chú thích của một số thành phần (mà nội bộ yêu cầu một cửa sổ xử lý làm một số tính toán). Cá nhân tôi thực sự ghét nó khi điều đó xảy ra.Khi tôi tạo GUI từ mã, tôi cố gắng trì hoãn việc gán cho Cha mẹ càng nhiều càng tốt, trong một nỗ lực để trì hoãn việc tạo cửa sổ, vì vậy tôi bị cắn nhiều lần.

cụ thể để sử dụng DLL của bạn, có thể sửa chữa

Tôi sẽ đưa tâm lý đọc được ý nghĩ của tôi trên mũ. Vì bạn cần trả về FRAME từ DLL của bạn, và bạn không thể trả về Frame thực bởi vì đó là một đối tượng Delphi cụ thể và bạn không được phép trả về các đối tượng Delphi cụ thể qua các ranh giới DLL, tôi đoán bạn đang quay trở lại một Xử lý cửa sổ, như việc phải làm tất cả các API đẹp đó, sử dụng định nghĩa hàm như thế này:

function GiveMeTheNiceFrame:HWND; 

vấn đề là, thường xuyên đòi hỏi sự sáng tạo của Xử lý Window thực tế, thông qua việc gọi TWinControl.CreateWnd, và lần lượt mà cuộc gọi yêu cầu một cửa sổ cha mẹ xử lý để thiết lập các cuộc gọi đến Windows.CreateWindowEx, và thói quen không thể có được một cửa sổ cha mẹ xử lý, do đó, nó lỗi ra ngoài.

Hãy thử thay thế chức năng của bạn với một cái gì đó allong theo dòng:

function GiveMeTheNiceFrame(OwnerWindow:HWND):HWND; 
begin 
    Result := TMyNiceFrame.CreateParanted(OwnerWindow).Handle; 
end; 

... ví dụ: sử dụng CreateParented(AParentWindow:HWND) constructor, không phải là bình thường Create(AOwner:TComponent) và vượt qua một HWND chủ sở hữu để DLL của bạn.

+0

Cảm ơn câu trả lời. Tôi nghĩ rằng nó sẽ giúp; Tôi sẽ thử lại sau. BTW, hãy để tôi hỏi bạn một câu hỏi. Tại sao nó không được phép trả về một đối tượng cụ thể Delphi trên các DLL? Tôi sẽ sử dụng thư viện này chỉ với các ứng dụng Delphi. Hàm trả về kiểu TFrame nhưng đối tượng thực được tạo là một thể hiện của lớp con cháu. Không ổn sao? Cảm ơn! –

+1

Không được phép vì lý do phiên bản: Sử dụng đối tượng trả về yêu cầu kiến ​​thức về bố cục bộ nhớ của nó và có thể thay đổi (có thể thay đổi với phiên bản của Delphi).Nó sẽ làm việc khi bạn kiểm tra nó, và nó sẽ hoạt động nếu Exe và Dll được xây dựng với cùng một trình biên dịch và các thiết lập trình biên dịch tương tự, nhưng nếu bạn định áp đặt các hạn chế như vậy bạn cũng có thể sử dụng các gói. –

+0

Xin cảm ơn, tôi rất biết điều đó. Tôi đã làm những gì bạn đề nghị và tôi vẫn nhận được ngoại lệ :-(. Các chức năng xuất khẩu thực hiện điều này: Kết quả: = TFrame1.CreateParented (ParentWindowHandle); Và ParentWindowHandle là một xử lý đến cửa sổ chính của một ứng dụng gọi phương thức (Self.Handle, khi được gọi từ lớp cửa sổ chính) –

0

Có vẻ như bạn chỉ cần gán thành phần (một biểu mẫu hoặc một phần của biểu mẫu, như bảng điều khiển) giữ khung cho khung ảnh.

Bạn không thể thực hiện công việc GUI trước khi được gán. Khung là một phần của biểu mẫu để sử dụng lại và thường cần chỉ định một số phụ huynh cho chúng.

Di chuyển mã GUI sang onshow hoặc thủ tục bạn gọi một cách rõ ràng, để mã gọi có thể gán cho cha mẹ.

Hoặc đặt cha mẹ làm thông số trong hàm.

+0

Khi đọc câu trả lời thứ hai của bạn (tôi bị mù một chút), có vẻ như câu trả lời của bạn bị thiếu một chút sau khi "bình thường cần" ... –

+0

được sửa. Viết vội vàng luôn là vấn đề đối với tôi. Làm việc trên tất cả các câu cùng một lúc :) –

1

Có một vài điều quan trọng cần nhớ:

  1. Khi sử dụng DLL, cả DLL và EXE của bạn mỗi người đều có một ví dụ ứng dụng đó đang đấu tranh để kiểm soát. Các điều khiển trong DLL của bạn sẽ thấy thể hiện ứng dụng thuộc về DLL; các điều khiển trong EXE của bạn sẽ thấy thể hiện ứng dụng thuộc về EXE. Cuộc đấu tranh đó không có khi sử dụng các gói, vì thế sẽ chỉ có một cá thể Ứng dụng.
  2. Khung là Điều khiển nhưng không phải là Biểu mẫu.
  3. Khi sử dụng Điều khiển trong ứng dụng, chúng không thể tồn tại một cách trực quan nếu không có Kiểm soát gốc (thường là Biểu mẫu hoặc vùng chứa có phân cấp cha đối với Biểu mẫu).
  4. Một số điều khiển không thể hiển thị đầy đủ chức năng của chúng trừ khi chúng tồn tại trực quan và có cha mẹ hợp lệ.

Cố gắng tái tạo sự cố của bạn bên trong EXE; nếu bạn không thể tái sản xuất, nó có lẽ là điều đầu tiên trong danh sách trên.

--jeroen

0

Tôi thấy điều này (CreateParams được gọi như là một phần của CreateWnd):

procedure TCustomFrame.CreateParams(var Params: TCreateParams); 
begin 
    inherited; 
    if Parent = nil then 
    Params.WndParent := Application.Handle; 
end; 

Và Application.Handle = 0 nên nó luôn luôn ném lỗi sau trong CreateWnd.
Sau khi đọc này Delphi: How to call inherited inherited ancestor on a virtual method?

tôi đã giải quyết nó bằng cách ghi đè CreateParams trong khung của tôi bỏ lỡ phiên bản tCustomFrame:

type 
    tCreateParamsMethod = procedure(var Params: TCreateParams) of object; 

type 
    tMyScrollingWinControl = class(TScrollingWinControl); 

procedure TDelphiFrame.CreateParams(var Params: TCreateParams); 
var 
    Proc: tCreateParamsMethod; 
begin 
    TMethod(Proc).Code := @TMyScrollingWinControl.CreateParams; 
    TMethod(Proc).Data := Self; 

    Proc(Params); 
end; 

Bây giờ nó chỉ ném lỗi khi cố gắng để thiết lập focus trên subcontrols, mà Tôi nghĩ rằng tôi sẽ sửa chữa bằng cách ngăn chặn WM_FOCUS nhưng chúng tôi sẽ làm thế nào nó đi từ đây.

function CreateFrame(hwndParent: HWnd): HWnd; stdcall; 
var 
    frame: tFrame; 
begin 
    Result := 0; 
    try 
    frame := TDelphiFrame.CreateParented(hwndParent); 
    Result := frame.Handle; 
    except on e: Exception do 
    ShowMessage(e.Message); 
    end; 
end; 
0

Bạn có thể tránh thông điệp này bằng cách gán bằng không để sự kiện OnClose cha mẹ, đôi khi nó hoạt động:

SomeControl.Parent := nil;//Before free your TControl 
SomeControl.Free; 
+3

Đây là chương trình ngẫu nhiên. – Ampere

+0

Kachwahed: Bạn không nên lập trình bằng cách phỏng đoán. Nó sẽ chỉ xả rác mã của bạn với rác không cần thiết thường hoạt động như một chất xúc tác cho các lỗi tinh tế và bất ngờ. Vâng, nó có thể phức tạp; nhưng bạn cần phải đặt nỗ lực vào phân tích nguyên nhân để bạn có thể hiểu đúng vấn đề. Trong trường hợp cụ thể này, câu trả lời của bạn hoàn toàn không liên quan đến vấn đề của OP. –

+0

Và là điểm thứ hai, bạn không nên cố gắng "tránh thông báo lỗi". Thông báo lỗi không phải là _problem_ ... Chúng chỉ đơn giản là thông báo lỗi *** về sự cố. Vì vậy, không chỉ bắn sứ giả. Tìm hiểu vấn đề _actual_ là gì, và ** sửa vấn đề * real * **! –

0

Tôi nghĩ rằng đây là giải pháp rất mát mẻ. Tôi nghĩ rằng nó không phải là cố gắng trước :) Tôi đang sử dụng một phụ huynh giả (đó là một hình thức).

function MyFrame_Create(hApplication, hwndParent:THandle; X, Y, W, H:Integer):Pointer; stdcall; 
var Fr: TMyFrame; 
    F: TForm; 
    CurAppHandle: THandle; 
begin 
    CurAppHandle:=Application.Handle; 
    Application.Handle:=hApplication; 
    //--- 
    F:=TForm. Create(Application);//Create a dummy form 
    F.Position:=poDesigned; 
    F.Width:=0; F.Top:=0; F.Left:=-400; F.Top:=-400;//Hide Form 
    F.Visible:=True; 
    //--- 
    Fr:=TMyFrame.Create(Application); 
    Fr.Parent:=F;//Set Frame's parent 
    //Fr.ParentWindow:=hwndParent; 
    Windows.SetParent(Fr.Handle, hwndParent);//Set Frame's parent window 
    if CurAppHandle>0 then Application.Handle:=CurAppHandle; 
    //--- 
    Fr.Left:=X; 
    Fr.Top:=Y; 
    Fr.Width:=W; 
    Fr.Height:=H; 
    Result:=Fr; 
end;//MyFrame_Create 

procedure MyFrame_Destroy(_Fr:Pointer); stdcall; 
var Fr: TMyFrame; 
    F: TObject; 
begin 
Fr:=_Fr; 
F:=Fr.Parent; 
Fr.Parent:=Nil; 
if (F is TForm) then F.Free; 
//SetParent(Fr.Handle, 0); 
//Fr.ParentWindow:=0; 
Fr.Free; 
end;//MyFrame_Destroy 
Các vấn đề liên quan