AI muốn lập trình DLL cho Excel và các loại bằng Delphi thì xem video này nhé!

Liên hệ QC

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,649
Được thích
10,138
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Lần chỉnh sửa cuối:
Để biết version của Excel. Hãy lấy thông tin từ chính đối tượng RANGE truyền vào cho hàm trong Delphi. Ví dụ như sau
Mã:
function RunSQLToRange(sFullName, sSQL: OleVariant;  Range: OleVariant): Longint; stdcall;
var
  cnn: TADOConnection;
  qry: TADOQuery;
  XlVersion: Integer;
begin
  XlVersion := Trunc(Range.Application.Version); //get version of excel application
  cnn := TADOConnection.Create(nil);
  try
    if XlVersion >= 12 then //from Excel 2007 or higher
       cnn.ConnectionString := 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=' + sFullName +
                                               ';Extended Properties="Excel 12.0 Xml;HDR=YES";'
    else
       cnn.ConnectionString := 'Provider= các bạn tự tìm trên Google' ;

    cnn.Connected := True;
    qry := TADOQuery.Create(nil);
    try
      qry.Connection := cnn;
      qry.SQL.Text := sSQL;
      qry.Open;
      Result := Range.CopyFromRecordset(qry.Recordset); //copy data from recordset to Excel range
    finally
      qry.Free;
    end;
  finally
    cnn.Free;
  end;

end;
 
Upvote 0
Để biết version của Excel. Hãy lấy thông tin từ chính đối tượng RANGE truyền vào cho hàm trong Delphi. Ví dụ như sau
Mã:
function RunSQLToRange(sFullName, sSQL: OleVariant;  Range: OleVariant): Longint; stdcall;
var
  cnn: TADOConnection;
  qry: TADOQuery;
  XlVersion: Integer;
begin
  XlVersion := Trunc(Range.Application.Version); //get version of excel application
  cnn := TADOConnection.Create(nil);
  try
    if XlVersion >= 12 then //from Excel 2007 or higher
       cnn.ConnectionString := 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=' + sFullName +
                                               ';Extended Properties="Excel 12.0 Xml;HDR=YES";'
    else
       cnn.ConnectionString := 'Provider= các bạn tự tìm trên Google' ;

    cnn.Connected := True;
    qry := TADOQuery.Create(nil);
    try
      qry.Connection := cnn;
      qry.SQL.Text := sSQL;
      qry.Open;
      Result := Range.CopyFromRecordset(qry.Recordset); //copy data from recordset to Excel range
    finally
      qry.Free;
    end;
  finally
    cnn.Free;
  end;

end;
Cách này mới thử cho tốc độ nhanh hơn cách sau và code ngắn hơn ... khoa học hơn
Rất mong Bạn chỉ dẫn từng bước học và cách tiếp cận như vậy ... ko lâu đâu các thành viên sẻ có bạn học tốt đó
Cảm ơn rất nhiều
Mã:
var
  Conn: TADOConnection;
  qry: TADOQuery;
  Excel: OLEVariant;
  ExcelVersion: string;
begin
      conn := TADOConnection.Create(nil);
   try
      Excel := CreateOLEObject('EXCEL.Application');
      ExcelVersion := Excel.version;
   finally

Có lẻ cách trên khởi tạo Object trong *.dll khi nó chạy load App Office mất thêm cung đoạn đó nên nó chậm (suy đoán vậy )?!
 
Upvote 0
Khi sử dụng lệnh khởi tạo Excel bằng Excel := CreateOLEObject('EXCEL.Application'); tức là bạn đang khởi tạo cả một phần mềm Excel chạy. Việc này chỉ cần thiết nếu phần mềm bạn viết ra chạy độc lập (không phải là nhúng DLL trong VBA,...) . Tức là bạn có phần mềm độc lập, cần có một điều khiển Excel mới để tương tác dữ liệu. Các bạn chú ý nếu theo tình huống này thì chỉ nên gọi một lần trong suốt quá trình chạy phần mềm, biến lưu giữ điều khiển Excel (Instance) phải được khởi tạo ở dạng Public hoặc trong Class chính của chương trình , khi thoát phần mềm phải có lệnh Excel.Quit; Tốc độ khởi tạo Excel cũng như khi bạn mở Excel mà thôi.

Nếu bạn tạo các hàm hay thủ tục API, nhúng nó vào bên trong Excel - VBA như ví dụ của tôi thì luôn làm theo cách truyền đối tượng Excel từ môi trường gọi nó vào tham số của hàm và thủ tục. Trong mã nguồn Delphi, dựa vào tham số truyền vào bạn coi nhưng đang sống chung cùng Excel mà không phải tạo mới - Đây chính là tương tác cùng Excel và người dùng. Đây là phương pháp làm đúng. Các bạn hãy xem lại ví dụ bài liền trước của tôi trong topic này - Hàm RunSQLtoExcel để hiểu rõ.

(*) Khuyến cáo chung. Nếu các bạn mới biết Delphi, chưa học các câu lệnh cơ bản, việc tương tác với object bên ngoài như ADO, Excel là một việc khó, trừu tượng không dễ mò, việc này dành cho những bạn có "năng khướu mò". Vậy các bạn hãy nên tạm gác lại những cái đi tắt đón đầu để quay về điểm xuất phát. Hãy học từ BEGIN...END;.
 
Upvote 0
Code bài 61 thiếu cái dấu ; cuối bạn nào tinh mắt nếu copy thêm vô ha:p:D
 
Upvote 0
var
Excel: OLEVariant;

try
Excel := GetActiveOleObject('EXCEL.Application');
except
Excel := CreateOLEObject('EXCEL.Application') ;
end

Delphi em thấy rất hay, tuy nhiên giờ cộng đồng ít quá nên nhiều lúc bí không biết hỏi ở đâu...
 
Upvote 0
var
Excel: OLEVariant;

try
Excel := GetActiveOleObject('EXCEL.Application');
except
Excel := CreateOLEObject('EXCEL.Application') ;
end

Delphi em thấy rất hay, tuy nhiên giờ cộng đồng ít quá nên nhiều lúc bí không biết hỏi ở đâu...

Hàm GetActiveOleObject chỉ chạy được nếu phần mềm đang chạy quyền Administrator hoặc UAC trong control panel tắt, ngầm định nó là on. Nên hàn này hay dùng trong WinXP trở về trước (bảo mật Win không cao). Từ Win Vista trở lại đây hàm này chỉ dùng gây lỗi khi môi trường không phải Admin.

Học Delphi cơ bản không khó vì ngôn ngữ Pacal, tài liệu tiếng Việt nhiều. Còn viết phần mềm với các component/control trên Form thì học của nước ngoài cũng không phải khó lắm.
 
Upvote 0
Hàm GetActiveOleObject chỉ chạy được nếu phần mềm đang chạy quyền Administrator hoặc UAC trong control panel tắt, ngầm định nó là on. Nên hàn này hay dùng trong WinXP trở về trước (bảo mật Win không cao). Từ Win Vista trở lại đây hàm này chỉ dùng gây lỗi khi môi trường không phải Admin.
Nhưng 2 hàm này không phải tương đương để mà kết luận: "để khỏi phiền phức thì tôi dùng CreateOLEObject thay cho GetActiveOleObject"

Có những lúc bắt buộc phải dùng GetActiveOleObject. Không thể thay GetActiveOleObject bằng CreateOLEObject được. Bởi mục đích việc làm bắt phải thế, và triết lý của chúng khác nhau.

1. CreateOLEObject luôn luôn tạo ra instance mới của server.

2. GetActiveOleObject luôn luôn trả về instance của server đang hoạt động. Nếu không có server đang hoạt động thì sẽ có lỗi.

Nếu việc tôi định làm bắt buộc phải làm với server đang hoạt động và chỉ khởi động mới khi không có server đang hoạt động thì bắt buộc tôi phải dùng code với try ... except như trên. Làm sao có thể thay bằng CreateOLEObject được. Bởi lúc đó tôi luôn làm việc với server mới, bất luận đã có server nào hoạt động chưa.

Nếu trong Windows > XP đòi hỏi phải có quyền thì người lập trình phải cho mình quyền thôi. Viết được những code thâm nhập sâu vào system mà lại bó tay với quyền sao?
 
Upvote 0
Nhưng 2 hàm này không phải tương đương để mà kết luận: "để khỏi phiền phức thì tôi dùng CreateOLEObject thay cho GetActiveOleObject"

Có những lúc bắt buộc phải dùng GetActiveOleObject. Không thể thay GetActiveOleObject bằng CreateOLEObject được. Bởi mục đích việc làm bắt phải thế, và triết lý của chúng khác nhau.

1. CreateOLEObject luôn luôn tạo ra instance mới của server.

2. GetActiveOleObject luôn luôn trả về instance của server đang hoạt động. Nếu không có server đang hoạt động thì sẽ có lỗi.

Nếu việc tôi định làm bắt buộc phải làm với server đang hoạt động và chỉ khởi động mới khi không có server đang hoạt động thì bắt buộc tôi phải dùng code với try ... except như trên. Làm sao có thể thay bằng CreateOLEObject được. Bởi lúc đó tôi luôn làm việc với server mới, bất luận đã có server nào hoạt động chưa.

Nếu trong Windows > XP đòi hỏi phải có quyền thì người lập trình phải cho mình quyền thôi. Viết được những code thâm nhập sâu vào system mà lại bó tay với quyền sao?

Bài viết khuyến cáo với hàm GetActiveOleObject là vấn đề Admin chứ có thay thể bàng hàm Create đâu anh :) . Nên mới lưu ý về vấn đề ứng dụng chạy độc lập hay nhúng thì cần có cách truyền tham số cho hàm hợp lý. Nếu mới lập teinhf không nắm đc việc này sẽ thấy lỗi cho mà xem. Phần mềm viết ra mang cho người khác dùng phải lưu ý cơ chế chạy nếu không thắc mắc lỗi tùm lum ngay.
 
Upvote 0
To @Nguyễn Duy Tuân
Mình sử dụng phương thức GetRows lấy dữ liệu vào mảng câu sau nó sai cái gì mà báo lỗi ... mong Bạn chỉ dùm
Mã:
Result:= cnn.Execute(qry.Recordset.GetRows());
 
Upvote 0
chi tiết sao chứ nói vậy mình ko hiểu :D
Lâu rồi không đụng vào, de mình nhờ người cài Delphi rồi mình úp ví dụ lên nhé, trong delphi van lay duoc Range và dua vào Arr na ná như trong Excel, đợt mình cũng có viết cái tiện ích nhập dữ liệu bằng Form bằng Delphi cũng gán Range vào arr rồi xử lý mà vướng cái không biết cách hook ký tự tiếng việt nên bỏ luôn
 
Upvote 0
To @giaiphap mấy ngày nay có học được cái chi mới ko đó mà thấy Im re vậy ?!
Vô link sau mà coi nhé rất căn bản .... đó he
http://www.delphibasics.co.uk/index.html
Cũng trang trên coi các Hàm của Delphi nhé
http://www.delphibasics.co.uk/ByType.asp?Type=Function
Mình cũng định nghiên cứu nhưng thấy hình như delphi ít ai quan tâm, nên mình định chuyển sang c# có lẽ sẽ phù hợp với mình. Mình đang xem VSTO ở đây bài 11.
https://www.giaiphapexcel.com/diendan/threads/Ứng-dụng-lập-trình-vsto.92536/
 
Upvote 0
chi tiết sao chứ nói vậy mình ko hiểu :D
Trong Tiện ích của tôi tôi gán value cua Rang vào Arr như sau
var
E,myArray: OleVariant;
R,C:integer;

MyArray := E.Range[Địa chỉ Rangenguon].Value;
for R := VarArrayLowBound(MyArray, 1) to VarArrayHighBound(MyArray, 1) do
begin
for C := VarArrayLowBound(MyArray, 2) to VarArrayHighBound(MyArray, 2) do
begin
ListItem := ListView1.Items.Add;
Listitem.Caption :=(MyArray[R, C] );
end;
end;r
 
Upvote 0
Trong Tiện ích của tôi tôi gán value cua Rang vào Arr như sau
var
E,myArray: OleVariant;
R,C:integer;

MyArray := E.Range[Địa chỉ Rangenguon].Value;
for R := VarArrayLowBound(MyArray, 1) to VarArrayHighBound(MyArray, 1) do
begin
for C := VarArrayLowBound(MyArray, 2) to VarArrayHighBound(MyArray, 2) do
begin
ListItem := ListView1.Items.Add;
Listitem.Caption :=(MyArray[R, C] );
end;
end;r
Hình như code này chuyển Mảng 2dArray thì phải ... cái này mình cũng rất cần có lẻ sẻ hỏi những bài sau cách chuyển mảng 2dArray trong file *.dll
Nó tương đương với hàm sau của VBA thì phải .... Hàm này Copy từ GPE
Mã:
Public Function TransArr(ByVal sArr As Variant) As Variant
    Dim tmpArr As Variant, x As Long, y As Long
    ReDim tmpArr(UBound(sArr, 2), UBound(sArr, 1))
    For x = 0 To UBound(sArr, 2)
        For y = 0 To UBound(sArr, 1)
            tmpArr(x, y) = sArr(y, x)
        Next y
    Next x
    TransArr = tmpArr
End Function
Và linh sau từ xa xưa Bác Bill có nói ...
https://support.microsoft.com/vi-vn...rom-an-ado-recordset-to-excel-with-automation
Mã:
Function TransposeDim(v As Variant) As Variant
' Custom Function to Transpose a 0-based array (v)
    Dim X As Long, Y As Long, Xupper As Long, Yupper As Long
    Dim tempArray As Variant
    Xupper = UBound(v, 2)
    Yupper = UBound(v, 1)

    ReDim tempArray(Xupper, Yupper)
    For X = 0 To Xupper
        For Y = 0 To Yupper
            tempArray(X, Y) = v(Y, X)
        Next Y
    Next X
    TransposeDim = tempArray
End Function

Còn Link sau Delphi có nói tới 2 hàm Bạn nói:

1/ function VarArrayLowBound(const A: Variant; Dim: Integer): Integer;
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Variants.VarArrayLowBound

2/ function VarArrayHighBound(const A: Variant; Dim: Integer): Integer;
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Variants.VarArrayHighBound

3/ Mình mới thử sử dụng thư viên của bác Bill mà khai báo như sau trong *.dll khi gọi hàm từ Excel nó làm đơ file luôn
Mã:
var
  fso: OleVariant;
begin
  fso := CreateOleObject('Scripting.FileSystemObject');
Phải chăng nó sai phương pháp khi nhúng *.dll gọi hàm trong Excel .... mà cách trên chỉ áp dụng cho 1 ứng dụng chạy độc lập( suy đoán bạy vậy)
...
Vậy cách viết hàm chuyển mảng đó chi tiết trong *.dll như thế nào mà xài cho Excel khi lấy Mảng 2dArray lên với mình còn gặp khó khăn ?!

Mạnh đọc rất nhiều tài liệu cơ bản có hiểu phần nào chỉ mong các bạn chỉ thêm cho cách khai báo sử dụng thự viện của Windows trong *.dll và tương tác gọi hàm qua lại Excel To *.dll nữa là từng bước tiếp cận Delphi viết thư viện hàm cho Mình sơ xài dùng được ...
 
Lần chỉnh sửa cuối:
Upvote 0
Hình như code này chuyển Mảng 2dArray thì phải ... cái này mình cũng rất cần có lẻ sẻ hỏi những bài sau cách chuyển mảng 2dArray trong file *.dll
Nó tương đương với hàm sau của VBA thì phải .... Hàm này Copy từ GPE
Mã:
Public Function TransArr(ByVal sArr As Variant) As Variant
    Dim tmpArr As Variant, x As Long, y As Long
    ReDim tmpArr(UBound(sArr, 2), UBound(sArr, 1))
    For x = 0 To UBound(sArr, 2)
        For y = 0 To UBound(sArr, 1)
            tmpArr(x, y) = sArr(y, x)
        Next y
    Next x
    TransArr = tmpArr
End Function
Và linh sau từ xa xưa Bác Bill có nói ...
https://support.microsoft.com/vi-vn...rom-an-ado-recordset-to-excel-with-automation
Mã:
Function TransposeDim(v As Variant) As Variant
' Custom Function to Transpose a 0-based array (v)
    Dim X As Long, Y As Long, Xupper As Long, Yupper As Long
    Dim tempArray As Variant
    Xupper = UBound(v, 2)
    Yupper = UBound(v, 1)

    ReDim tempArray(Xupper, Yupper)
    For X = 0 To Xupper
        For Y = 0 To Yupper
            tempArray(X, Y) = v(Y, X)
        Next Y
    Next X
    TransposeDim = tempArray
End Function

Còn Link sau Delphi có nói tới 2 hàm Bạn nói:

1/ function VarArrayLowBound(const A: Variant; Dim: Integer): Integer;
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Variants.VarArrayLowBound

2/ function VarArrayHighBound(const A: Variant; Dim: Integer): Integer;
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Variants.VarArrayHighBound

3/ Mình mới thử sử dụng thư viên của bác Bill mà khai báo như sau trong *.dll khi gọi hàm từ Excel nó làm đơ file luôn
Mã:
var
  fso: OleVariant;
begin
  fso := CreateOleObject('Scripting.FileSystemObject');
Phải chăng nó sai phương pháp khi nhúng *.dll gọi hàm trong Excel .... mà cách trên chỉ áp dụng cho 1 ứng dụng chạy độc lập( suy đoán bạy vậy)
...
Vậy cách viết hàm chuyển mảng đó chi tiết trong *.dll như thế nào mà xài cho Excel khi lấy Mảng 2dArray lên với mình còn gặp khó khăn ?!

Mạnh đọc rất nhiều tài liệu cơ bản có hiểu phần nào chỉ mong các bạn chỉ thêm cho cách khai báo sử dụng thự viện của Windows trong *.dll và tương tác gọi hàm qua lại Excel To *.dll nữa là từng bước tiếp cận Delphi viết thư viện hàm cho Mình sơ xài dùng được ...

Mình cũng chưa hiểu ý bạn lắm nếu bạn lam viec lien quan den thu muc thi dùng OpenDialog1
openDialog1 := TOpenDialog.Create(self);
openDialog1.InitialDir := 'C:\';
openDialog1.Filter :=
'All files (*.*)|*.*';
if openDialog1.Execute
then
begin
temp := IncludeTrailingBackslash(ExtractFilePath(openDialog1.FileName)) + 'config.Dat';
ini := TIniFile.Create(temp);
sKey := ini.ReadString('main','Code','');
end
else exit;
openDialog1.Free;


Ví dụ UDF cua minh cong các ô Megr

Mã:
    function A_SumMG(const Cell, Cot1: OleVariant): OleVariant; safecall;

function TAddinCommanthuy.A_SumMG(const Cell, Cot1: OleVariant): OleVariant;
var
MerRng : OleVariant;
Cot:integer;
IRange: IxlRange;
begin
Self.COMAddInModule.ExcelApp.Volatile;
MerRng:=Cell.MergeArea;
  Result := ' ';
  case VarType(Cell) of
    varSmallint, varInteger, varSingle,
    varDouble, varCurrency, varShortInt, varByte,
   varWord, varLongWord, varInt64: Result := '';
  else
  Cot := 0;
  If Cell.Cells[1, 1].text = '' Then
  begin
    Result := cot;
  end
  Else
  begin
      try
  Cot := cot1.Column - MerRng.Column;
  Result := Self.COMAddInModule.ExcelApp.WorksheetFunction.Sum
  (MerRng.Offset[0, cot].Resize[MerRng.Rows.count,1],
    EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);//}
    except
      Result := CVErr(xlErrValue);
    end;
  end;
  end;
end;
 
Lần chỉnh sửa cuối:
Upvote 0
Mình cũng chưa hiểu ý bạn lắm nếu bạn lam viec lien quan den thu muc thi dùng OpenDialog1
openDialog1 := TOpenDialog.Create(self);
openDialog1.InitialDir := 'C:\';
openDialog1.Filter :=
'All files (*.*)|*.*';
if openDialog1.Execute
then
begin
temp := IncludeTrailingBackslash(ExtractFilePath(openDialog1.FileName)) + 'config.Dat';
ini := TIniFile.Create(temp);
sKey := ini.ReadString('main','Code','');
end
else exit;
openDialog1.Free;


Ví dụ UDF cua minh cong các ô Megr

Mã:
    function A_SumMG(const Cell, Cot1: OleVariant): OleVariant; safecall;

function TAddinCommanthuy.A_SumMG(const Cell, Cot1: OleVariant): OleVariant;
var
MerRng : OleVariant;
Cot:integer;
IRange: IxlRange;
begin
Self.COMAddInModule.ExcelApp.Volatile;
MerRng:=Cell.MergeArea;
  Result := ' ';
  case VarType(Cell) of
    varSmallint, varInteger, varSingle,
    varDouble, varCurrency, varShortInt, varByte,
   varWord, varLongWord, varInt64: Result := '';
  else
  Cot := 0;
  If Cell.Cells[1, 1].text = '' Then
  begin
    Result := cot;
  end
  Else
  begin
      try
  Cot := cot1.Column - MerRng.Column;
  Result := Self.COMAddInModule.ExcelApp.WorksheetFunction.Sum
  (MerRng.Offset[0, cot].Resize[MerRng.Rows.count,1],
    EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);//}
    except
      Result := CVErr(xlErrValue);
    end;
  end;
  end;
end;
Mình hỏi chút
1/ Phần khai báo câu sau thì trong uses khai báo sao
Mã:
Self.COMAddInModule.ExcelApp.Volatile
2/ Nếu Mình viết nó trong File *.dll thì trên Excel khai báo API sử dụng sao
3/ Mình có nghe lập trình kết nối qua COMAddInModule mà đang thấy mơ hồ quá ... mong bạn chỉ dùm
 
Upvote 0
Cái này mình sử dụng
Add-in Express for Microsoft Office and Delphi VCL
 
Upvote 0
Upvote 0
Web KT
Back
Top Bottom