2010-04-12 20 views
6

Tôi có một DLL mà cung cấp một chức năng giải mã, như sau:Pre-cấp phát bộ nhớ giữa HostApp và DLL

function MyDecode (Source: PChar; SourceLen: Integer; var Dest: PChar; DestLen: Integer): Boolean; stdcall; 

Các HostApp gọi là "MyDecode", và chuyển vào các thông số nguồn, SourceLen và đích, các DLL trả về giải mã Dest và DestLen. Vấn đề là: HostApp không thể biết được độ dài giải mã của Dest, và do đó sẽ không biết cách phân bổ trước bộ nhớ của Dest.

Tôi biết rằng có thể chia nhỏ "MyDecode" thành hai chức năng:

function GetDecodeLen (Source: PChar; SourceLen: Integer): Integer; stdcall; // Return the Dest's length 
function MyDecodeLen (Source: PChar; SourceLen: Integer; var Dest: PChar): Boolean; stdcall; 

Nhưng, quá trình giải mã của tôi là rất phức tạp, vì vậy nếu chia thành hai chức năng sẽ ảnh hưởng đến hiệu quả.

Có giải pháp nào tốt hơn không?


Có Alexander, đây có thể là giải pháp tốt. đang HostApp:

//... 
MyDecode(....) 
try 
    // Use or copy Dest data 
finally 
    FreeDecodeResult(...) 
end; 

mã DLL:

function MyDecode(...): Boolean; 
begin 
    // time-consuming calculate 

    // Allocate memory 
    GetMem(Dest, Size); 
    // or New()? 
    // or HeapAlloc()? 
end; 

procedure FreeDecodeResult(Dest: PChar); 
begin 
    FreeMem(Dest); 
    // or Dispose(Dest); ? 
    // or HeapFree(Dest); ? 
end; 

Có lẽ tôi nên thay đổi loại đích để con trỏ.

Phương pháp nhớ cấp phát nào tốt hơn? GetMem/Mới hoặc HeapAlloc?

+0

Bạn đang cố giải quyết vấn đề nào: 1) cách tìm ra số tiền cần phân bổ trước; 2) làm thế nào để phối hợp quản lý bộ nhớ động giữa người gọi và callee? –

+0

Để Marcelo: 1) Người gọi không thể tìm ra số tiền để phân bổ trước. 2) Có. – Leo

+0

> Phương pháp bộ nhớ phân bổ tốt hơn là gì? Trong trường hợp của bạn, điều này không quan trọng. Sử dụng phương pháp mà bạn quen thuộc (tôi thích GetMem/FreeMem). – Alex

Trả lời

8

Bạn có thể chia "MyDecode" thành hai thói quen bằng cách khác:

function MyDecode(Source: PChar; SourceLen: Integer; out Dest: PChar; out DestSize: Integer): Boolean; stdcall; 
procedure FreeDecodeResult(Dest: PChar); stdcall; 

Tức là - bạn cấp phát bộ nhớ trong MyDecode thay vì yêu cầu người gọi thực hiện điều đó.

+0

Lưu ý rằng bạn không thể thực hiện FreeDecodeResult, nếu bạn sẽ sử dụng một số cấp phát chung cho cả người gọi và callee. Ví dụ, nếu bạn cấp phát bộ nhớ thông qua LocalAlloc thay vì GetMem. Sau đó, người gọi nên gọi LocalFree thay vì FreeDecodeResult. – Alex

5

Bạn có thể sử dụng cùng một kỹ thuật mà hầu hết Windows API sử dụng, nghĩa là nếu bộ đệm của bạn không đủ lớn, hàm sẽ trả về kích thước bộ đệm cần thiết. Bằng cách đó, bạn có thể cấp phát bộ đệm có kích thước phù hợp với chức năng gọi.

function MyDecode (Source: PChar; SourceLen: Integer; Dest: PChar; var Len: Integer): Boolean; stdcall; 

procedure SomeProc; 
var iSourceLen, iLenNeeded : Integer; 
    pSource, pDest : Pointer; 
begin 
    MyDecode(pSource, iSourceLen, nil, iLenNeeded); 
    GetMem(pDest,iLenNeeded); 
    try 
    MyDecode(pSource, iSourceLen,pDest, iLenNeeded); 
    finally 
    FreeMem(pDest); 
    end; 
end; 

EDIT: Theo đề nghị của mghie. Vì tham số là PCHAR, nó sẽ được giả định là iLenNeeded được trả về bởi MyDecode sẽ là số lượng TCHAR yêu cầu như là (chủ yếu?) Tiêu chuẩn bởi API cửa sổ.

function SomeProc(sEncode : String) : string; 
var iLenNeeded : Integer; 
begin 
    MyDecode(PChar(sEncode), Length(sEncode), nil, iLenNeeded); 
    SetLength(Result, iLenNeeded); //if iLenNeeded include a null-terminating character, you can use (iLenNeeded - 1) instead 
    if not MyDecode(PChar(sEncode), Length(sEncode), PChar(Result), iLenNeeded) then 
    SetLength(Result, 0); 
end; 
+0

+1, nhưng điều này có một số tiềm năng cho sự nhầm lẫn, như 'Len' có thể có nghĩa là chiều dài bộ đệm hoặc chiều dài chuỗi. Để được ở bên an toàn, tôi sẽ không sử dụng 'GetMem()' nhưng 'SetLength()' trên một chuỗi, điều này sẽ chăm sóc ký tự null sau. Về hiệu quả: Nếu DLL lưu trữ dữ liệu được giải mã trong một 'threadvar', nó không cần phải kém hiệu quả. – mghie

+0

Tùy thuộc vào việc thực hiện chính xác MyDecode, sử dụng pchar có thể không phải là ý tưởng hay nhất ... Nhưng tôi sẽ chỉnh sửa nó và hiển thị sử dụng thay thế. –

+0

Có, tôi biết đây là giải pháp API Windows. Nó có thể là một giải pháp tốt nhất nếu dll funtion là đơn giản. Nhưng "MyDecode" của tôi là một chức năng tốn thời gian, vì vậy tôi không muốn gọi nó hai lần. – Leo

2

Một tùy chọn khác là chuyển vào dll một con trỏ hàm để cấp phát bộ nhớ. Các dll gọi chức năng này khi nó cần bộ nhớ và kể từ khi bộ nhớ được phân bổ bằng cách sử dụng quản lý bộ nhớ của ứng dụng các ứng dụng chỉ có thể giải phóng nó.

Thật không may điều này không thực sự giải quyết vấn đề của bạn nhưng chỉ di chuyển nó vào dll mà sau đó phải tìm ra bao nhiêu bộ nhớ nó cần. Có lẽ bạn có thể sử dụng nhiều bộ đệm được lưu trữ trong một danh sách liên kết để mỗi khi hàm giải mã hết bộ nhớ, nó chỉ cấp phát bộ đệm khác.

1

Tôi không chắc chắn nếu điều này sẽ phù hợp với bạn, nhưng (trong đặc biệt ví dụ này), bạn có thể sử dụng WideString:

function MyDecode(Source: PChar; SourceLen: Integer; out Dest: WideString): Boolean; stdcall; 

Hoặc:

function MyDecode(Source: PChar; SourceLen: Integer): WideString; stdcall; 

Bằng cách sử dụng WideString bạn có thể tránh vấn đề phân bổ bộ nhớ.

Tại sao điều này sẽ hoạt động? Bởi vì WideString là bí danh cho loại BSTR của hệ thống. Và BSTR có quy tắc đặc biệt: bộ nhớ của nó phải được cấp phát qua trình quản lý bộ nhớ hệ thống cụ thể. I E. khi bạn làm việc với WideString, Delphi gọi trình quản lý bộ nhớ hệ thống này chứ không phải của chính nó. Vì trình quản lý bộ nhớ hệ thống có thể truy cập từ mỗi mô-đun (và nó giống nhau cho mỗi mô-đun) - điều này có nghĩa là cả người gọi (exe) và callee (DLL) sẽ sử dụng cùng một trình quản lý bộ nhớ, cho phép họ truyền dữ liệu mà không gặp vấn đề gì.

Vì vậy, bạn có thể sử dụng WideString và chỉ tạo ra kết quả mà không phải lo lắng về bộ nhớ. Chỉ cần lưu ý rằng các charaters trong WideString là unicode - tức là 2 byte. Bạn sẽ có một chi phí nhỏ với việc chuyển đổi ANSI < -> unicode, nếu bạn đang sử dụng D2007 trở xuống. Điều này (thường) không phải là một vấn đề, kể từ khi ứng dụng điển hình làm cho một tấn các cuộc gọi WinAPI - và mỗi cuộc gọi WinAPI có nghĩa là ANSI < -> unicode hội tụ (kể từ khi bạn đang gọi A-chức năng).

+0

sẽ làm việc ngay cả khi hostapp không phải là một chương trình delphi? –

+0

Có, kể từ khi WideString là BSTR, ứng dụng không phải Delphi sẽ cần phải sử dụng BSTR, đó là loại hệ thống (thực sự, một loại COM). Xem tại đây: http://msdn.microsoft.com/en-us/library/ms221069(VS.100).aspx Ví dụ về việc sử dụng BSTR từ C++: http://msdn.microsoft.com/en- us/library/xda6xzx7 (VS.100) .aspx – Alex

Các vấn đề liên quan