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:
Post lên đây chia sẽ bà con cùng đọc, nhất là những ai đã lập trình trên C/C++ hay Delphi cũ mà đã quen với kiểu Ansi Char. Khi qua Unicode nó có vài điểm cần chú ý và đọc đi đọc lại cho kỹ. Tui cũng đang đọc lại đây :)
Với Delphi:
1. http://docwiki.embarcadero.com/RADStudio/Rio/en/Unicode_in_RAD_Studio
2. http://docwiki.embarcadero.com/RADStudio/Rio/en/Enabling_Applications_for_Unicode
Với C++Builder:
1. http://docwiki.embarcadero.com/RADStudio/Rio/en/Enabling_C++_Applications_for_Unicode
2. http://docwiki.embarcadero.com/RADStudio/Rio/en/TCHAR_Mapping
Mấy cái này hoàn toàn nằm trong Help của RAD Studio hết, không phải ở đâu xa xôi.
 
Upvote 0
Ai rảnh thì vô mà dòm coi nó Sao: Delphi Tips
Mạnh nói xong quên liền mai mốt lại nhớ .. xong lại quên ... khi nhớ lại là nhớ rất chi là lâu ... Lưu vào đây cho tiện :p ;)
 
Upvote 0
To @thuyyeu99 Hôm Mạnh thấy làm cái Group method
là làm xong chưa vậy ???!!!
Capture.JPG
 
Upvote 0
Upvote 0
Làm xong lâu rồi, mà nó ảnh hưởng tốc độ Group chưa tìm ra hướng giải quyết, (có thời gian em bận việc nhà bỏ bê từ đó tới giờ. Hiện tại bây giờ em chưa bắt kịp nhịp độ hhiiiii)
Lâu nay mạnh cũng bận lắm lu xu bu đủ việc ... nhớ Code két vào GPE à ơi tẹo đó mà
mấy ngày trước lang thang trên Google tìm ra cái Help đó cho Excel đang coi thấy có đủ thứ hết ... lưu sử dụng khi cần thiết quên lại mở ra coi như cẩm nang cơ bản vậy... nhiều lúc nghĩ có 1 dòng code mò cả tháng mới ra thấy vãi kinh

Coi thấy VBA và Delphi code nó gần gủi ghê
Mã:
Sub VBA()
    Range("A1:C10").Interior.PatternColor = RGB(255, 0, 0)
End Sub

Sub Delphi()
    Range['A1', 'C10'].Interior.PatternColor := RGB(255,0,0);
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Coi thấy VBA và Delphi code nó gần gủi ghê
Mã:
Sub VBA()
    Range("A1:C10").Interior.PatternColor = RGB(255, 0, 0)
End Sub

Sub Delphi()
    Range['A1', 'C10'].Interior.PatternColor := RGB(255,0,0);
End Sub

Gần thì phải viết thế này
Mã:
procedure Delphi()
begin
    Range['A1:C10', EmptyParam].Interior.PatternColor := RGB(255,0,0);
end;

Class RANGE trong Excel có hai tham số, phần lớn mọi người chỉ biết đến tham số thứ nhất, trong VBA tham số thứ hai được bỏ qua nếu không khai báo. Nhưng trong Delphi nó không bỏ qua vì thế phải đưa vào EmptyParam. Vẫn có cách bỏ qua nếu dùng thông qua biến OleVariant.
 
Upvote 0
Bác Tuân nói mình mới để ý tới vụ missing param này. Trước giờ cứ nghĩ thằng VBA compiler nó thông minh, với các hàm có tham số Optional hay ParamArrays thì khi gọi, tùy hàm truyền tham số thế nào nó sẽ push stack thế ấy chứ. Còn gọi xuống API của C/C++ dll thì sao ? Xem trong VBEx.dll, vd hàm InStr thôi thì lại thấy 1 mớ pVariant truyền vào.
Hì hì, xem kỹ, debug ra ngoài vào VBA thì phát hiện ra, ông ta truyền tất tần tật, optional, params array mà coder không gõ tường mình thì ông VBA cũng truyền tường minh pVariant với field vt = VT_MISSING luôn (= VBA IsMissing) hết. Nói chung là đầy đủ, chả có lợi hơn tẹo nào.
Với keyword ParamArray như của hàm Array (rtcArray in VBEx.dll) thì VBA truyền xuống pointer to 1 Variant chứa array của các Variant nhé (SAFEARRAY of VARIANT field).
 
Lần chỉnh sửa cuối:
Upvote 0
Mạnh coi cái Video bài 1 link sau thấy cũng mê lắm .... :p

Mấy ngày nay khi nào rảnh Mạnh tìm tài liệu hay code mẫu trên Delphi cũng bắt trước làm theo 1 cái mà thấy quá khó luôn

Tìm trên Google thì thấy người ta toàn viết C/c++ Or DNA ... vvv còn tài liệu hay code mẫu trên Delphi rất ít
Hàm Mẫu
Mã:
function SampleFunc(x: LPXLOPER): LPXLOPER; stdcall;
type
  tmulti = array[0..0] of XLOPER;
  pmulti = ^tmulti;
var
  oper: XLOPER;
  i, j: Integer;
  lsum: Double;
  lchar, lbool, lerr, lempty: Integer;
begin
  Msg:='Blub';
  case (x.xltype and $0FFF) of
    xltypeNum     : Msg:= 'number';
    xltypeStr     : Msg:= 'string';
    xltypeBool    : Msg:= 'bool';
    xltypeErr     : Msg:= 'error';
    xltypeMissing : Msg:= 'nothing';
    xltypeNil     : Msg:= 'empty cell';
    xltypeMulti   : begin
      with x.val.xarray do begin
        lsum:=0;
        lchar:= 0;
        lbool:=0;
        lerr:=0;
        lempty:=0;
        for i:=0 to rows - 1 do begin
          for j:=0 to columns - 1 do begin
            oper:= pmulti(lparray)[i * columns + j];
            case (oper.xltype and $0FFF) of
              xltypeNum : lsum:= lsum + oper.val.num;
              xltypeStr : lchar:= lchar +
                Length(PShortString(oper.val.str)^);
              xltypeBool: inc(lbool);
              xltypeErr : inc(lerr);
              xltypeNil : inc(lempty);
            end;
          end;
        end;
        Msg:= 'range ' +
              IntToStr(rows)    + 'rows ' +
              IntToStr(columns) + 'cols ' +
              '; sum: '    + FloatToStr(lsum) +
              '; chars: '  + IntToStr(lchar) +
              '; bools: '  + IntToStr(lbool) +
              '; errors: ' + IntToStr(lerr) +
              '; empty: '  + IntToStr(lempty);
      end;
    end;
  end;
  if (x.xltype and xlbitXLFree) = xlbitXLFree then begin
    Msg:= Msg + ' XLFree';
    Excel4e(xlFree, nil, 1, [x]);
  end;
  res.xltype:= xltypeStr;
  res.val.str:= @Msg;
  result:=@res;
end;
XLLs – Add a Menu
Mã:
const
  CmdP : String[20] = 'MenuCommand';
  CmdT : String[20] = 'J';
  CmdF : String[20] = 'SampleCommand';

function xlAutoOpen: integer; stdcall;
begin
  {...}
    // register menu command
    Arg2.val.str:= @CmdP;
    Arg3.val.str:= @CmdT;         // always "J"
    Arg4.val.str:= @CmdF;
    Arg5.xltype:= xltypeMissing; // not required for commands
    Arg6.xltype:= xltypeInt;
    Arg6.val.w:= 2;              // 2 = command
    Excel4e(xlfRegister, nil, 6, // registers the command
      [@Arg1, @Arg2, @Arg3, @Arg4, @Arg5, @Arg6]);
  {...}
end;
mà thấy họ hướng dẫn kiểu nữa vời vậy coi tới lui vẫn tịt toàn tập luôn ...
vậy Mạnh muốn hỏi bạn nào biết chỉ dùm mạnh chỉ cơ bản như sau:

1/ khai báo trong Delphi làm sao khi mình Build *.XLL xong thì check trên Excel nó load vào luôn và ngược lại khi Uncheck nó
2/ Viết 1 cái hàm đơn giản như GetSum() trong *.XLL khi load trên Excel Gõ =Getsum() là nó chạy ok

Với mong muốn rất cơ bản và đơn giản vậy ai biết chỉ dùm ( nếu có code mẫu trên Delphi càng tốt hay tài liệu chỉ dùm )
Xin cảm ơn
 
Lần chỉnh sửa cuối:
Upvote 0
Ai can du đó, học C với Delphi cho chắc vô, nhất là Pointer, memory rồi mới đụng tới nó
 
Upvote 0
Mạnh đã viết được Add-ins trên Delphi *.XLL chạy rất tốt ........... Trân trọng thông báo với cả nhà vậy @!>><_+)(9
Ai thích nghiên cứu vào link bài #869 nghiên cứu kỹ nhiều bài trong đó là viết ok ....
Nó quá đơn giản luôn .... trong đó nó chỉ cà Viết Menu Ribbon đó he ....
Google như tàng kinh các vậy .... chia sẻ link đó ai có đam mê là viết quá ngon luôn ko Phải C/c++ Or DNA làm cái chi cho mệt
 
Upvote 0
Trong VBA có hàm TypeName() để lấy tên của class hay tên của lớp khai báo control hay các object. Khi các bạn lập trình Delphi cho Excel muốn dùng hàm TypeName không thể được vì nó không cùng môi trường VBA. Giải pháp chúng ta phải biết hàm API nào trong thư viên VBAxxx xuất nó ra đồng thời phải biết tham số để sử dụng. Chúng ta thực sự khó nếu không có tài liệu cung cấp từ nhà lập trình ra vbaxxx.

Nhân chủ đề Undocument API VBA ở bài số #10 tại đây

Code mà bác CU Anh moi ra nó là C và rất phức tạp đây:

Mã:
VARIANT *__stdcall rtcTypeName(VARIANT *pVar)
{
    int vt; // edi@1
    int (__stdcall ***v2)(_DWORD, _DWORD, _DWORD); // esi@9
    const unsigned __int16 *v3; // ebx@11
    VARIANT *result; // eax@14
    unsigned int v5; // esi@18
    int v6; // eax@31
    int v7; // esi@34
    int v8; // eax@34
    int v9; // esi@38
    VARIANT *v10; // esi@40
    LONG v11; // eax@53
    LONG v12; // eax@55
    int v13; // eax@56
    int v14; // eax@57
    int v15; // [sp+Ch] [bp-14h]@35
    int v16; // [sp+10h] [bp-10h]@33
    unsigned int bCurrency; // [sp+14h] [bp-Ch]@1
    int v18; // [sp+18h] [bp-8h]@34
    BSTR bstrString; // [sp+1Ch] [bp-4h]@1

    vt = pVar->vt & 0x9FFF;
    bCurrency = (pVar->vt >> 13) & 1;
    bstrString = 0;
    if ( vt > VT_UNKNOWN )
    {
        if ( vt == VT_DECIMAL || vt == VT_UI1 )
        {
            goto DecimalOrUI1;
        }
        if ( vt != VT_RECORD )
        {
            goto LABEL_44;
        }
        if ( dword_65246024 )
        {
            EbRaiseExceptionCode(VT_UNKNOWN);
        }
        if ( !bCurrency )
        {
            if ( pVar->cyVal.Hi && (*(*pVar->cyVal.Hi + 28))(pVar->cyVal.Hi, &pVar) >= 0 )
            {
                return pVar;
            }
            goto LABEL_50;
        }
        if ( pVar->vt & VT_BYREF && (v11 = pVar->lVal, *v11) && *(*v11 + 2) & 0x20 )
        {
            v12 = *pVar->plVal;
        }
        else if ( pVar->vt & VT_BYREF || (v12 = pVar->lVal, !(*(v12 + 2) & 0x20)) || !*(v12 - 4) )
        {
            v3 = off_6524FDD0;
            goto LABEL_17;
        }
        v13 = (*(**(v12 - 4) + 28))(*(v12 - 4), &bstrString);
        if ( v13 < 0 )
        {
            v14 = EberrOfHresult(v13);
            EbRaiseExceptionCode(v14);
        }
        v3 = bstrString;
LABEL_17:
        if ( bCurrency )
        {
            v5 = wcslen(v3);
            pVar = SysAllocStringLen(0, v5 + 2);
            if ( !pVar )
            {
                EbRaiseExceptionCode(14);
            }
            sub_65012860(&pVar->vt, v5 + 3, v3);
            v6 = 2 * v5;
            *(&pVar->vt + v6) = 40;
            *(&pVar->wReserved1 + v6) = 41;
            *(&pVar->vt + v5 + 2) = 0;
            goto LABEL_13;
        }
LABEL_12:
        pVar = SysAllocString(v3);
        if ( !pVar )
        {
            EbRaiseExceptionCode(14);
        }
LABEL_13:
        SysFreeString(bstrString);
        return pVar;
    }
    if ( vt == VT_UNKNOWN )
    {
        goto LABEL_6;
    }
    if ( vt < 0 )
    {
LABEL_44:
        EbRaiseExceptionCode(458);
    }
    if ( vt <= 8 )
    {
DecimalOrUI1:
        v3 = (&off_6524FD88)[2 * vt];
        goto LABEL_17;
    }
    if ( vt != 9 )
    {
        if ( vt <= 9 )
        {
            goto LABEL_44;
        }
        if ( vt > 11 )
        {
            if ( vt == 12 )
            {
LABEL_19:
                if ( dword_65246024 )
                {
                    EbRaiseExceptionCode(VT_UNKNOWN);
                }
                goto DecimalOrUI1;
            }
            goto LABEL_44;
        }
        goto DecimalOrUI1;
    }
LABEL_6:
    if ( bCurrency )
    {
        goto LABEL_19;
    }
    if ( dword_65246024 )
    {
        EbRaiseExceptionCode(VT_UNKNOWN);
    }
    if ( pVar->vt & 0x4000 )
    {
        v2 = *pVar->plVal;
    }
    else
    {
        v2 = pVar->lVal;
    }
    if ( !v2 )
    {
        v3 = L"Nothing";
        goto LABEL_12;
    }
    if ( (**v2)(v2, &IID_IProvideClassInfo, &v16) < 0 )
    {
        if ( (**v2)(v2, &IID_IDispatch, &v15) < 0 )
        {
LABEL_41:
            v3 = (&off_6524FD88)[2 * vt];
            goto LABEL_12;
        }
        v7 = (*(*v15 + 16))(v15, 0, 1033, &v18);
        v8 = v15;
    }
    else
    {
        v7 = (*(*v16 + 12))(v16, &v18);
        v8 = v16;
    }
    (*(*v8 + 8))(v8);
    if ( v7 < 0 )
    {
        goto LABEL_41;
    }
    v9 = (*(*v18 + 48))(v18, -1, &pVar, 0, 0, 0);
    (*(*v18 + 8))(v18);
    if ( v9 < 0 )
    {
LABEL_50:
        v3 = pVar;
        goto LABEL_12;
    }
    result = pVar;
    if ( 95 != pVar->vt )
    {
        return result;
    }
    v10 = SysAllocStringLen(&pVar->wReserved1, (pVar[-1].cyVal.Hi >> 1) - 1);
    SysFreeString(&pVar->vt);
    result = v10;
    return result;
}

Điều quan trọng là bác @ThangCuAnh đã giúp lấy được tên hàm API của hàm TypeName cùng prototype, tôi viết lại code để chúng ta dùng trong Delphi như sau.

1. Các bạn thao khảo cách dùng bên VB/VBA (mang tính học tập vì trong VBA hàm TypeName đã có.

Option Explicit
#If VBA7 Then
Declare PtrSafe Function rtcTypeName Lib "VBE7.DLL" (v As Variant) As String
#Else
Declare Function rtcTypeName Lib "VBE6.DLL" (v As Variant) As String
#End If

Sub TestTypeNameAPI()
Dim sName As String
sName = rtcTypeName(ActiveCell)
MsgBox StrConv(sName, vbFromUnicode)
End Sub

2. Cách khai báo API hàm rtcTypeName() để dùng trong Delphi. Nếu chúng ta tạo Add-in thì đây là tư liệu quý.

Code dưới đây tôi viết trong chương trình chính dạng Console (màn hình đen trắng). Các bạn làm trình tự như sau.

1. Từ Delphi, tạo application dạng Console (nếu ai có add-in rồi thì chỉ cần code chính của nó).
2. Paste đoạn code dưới đây.
3. Chạy Excel với quyền "Run as administrator"
4. Chạy progam trong Delphi (F9)

Mã:
program Test_VBA_API.dproj;
//This code call function API in VBAxxx "rtcTypeName()". In VBA its name is TypeName()
{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Winapi.Windows,
  ComObj,
  System.Variants;

type
  TFuncVBAPI_rtcTypeName = function(var v: OleVariant): PWideChar; stdcall;

var sName: WideString;
  h: HMODULE;
  pFunc: TFuncVBAPI_rtcTypeName;
  App: OleVariant;
  v: OleVariant;
begin
  try
    App := GetActiveOleObject('Excel.Application');
    Writeln('Ung dung da ma la', ': ', App.Name);
    h := LoadLibrary('C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL');
    try
      pFunc := GetProcAddress(h, 'rtcTypeName');
      v := App.ActiveCell;
      sName := PWideChar(TFuncVBAPI_rtcTypeName(pFunc)(v));
      Writeln('Gia tri bien la', ': ', sName);
    finally
      FreeLibrary(h);
      App := Unassigned;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;

end.
Có thể viết hàm mà không cần LoadLibrary không anh ?
 
Upvote 0
Upvote 0
Phải kiểm tra xem thằng lạ ở ngoài load nó chịu lên kg, DllMain nó return False là xong. Đồ MS nó khôn, quái lắm. Vì nó được viết riêng để dùng cho Office.
Mà load lên được chắc gì nó chạy đúng kg, vì nó có share, set chung gì với apps Office thì sao ?
 
Lần chỉnh sửa cuối:
Upvote 0
....
 
Lần chỉnh sửa cuối:
Upvote 0
khổ ghê chưa biết chi đang mò mà thằng nào cười thằng đó như con bò ... mới sinh ra có thằng nào ăn bắp ngay được ... bú sữa đã xong mới ăn cơm chứ -0-0-0- quy luật lẻ thường vậy mà ko hiểu thì ko = con Bò
 
Upvote 0
Lập trình XLL tài liệu C/C++ thì Microsoft có và update bản mới nhất. Các bạn muốn tự tay làm trên Delphi thì cần học C/C++ rồi viết lại trên Delphi. Và món này không dễ ăn như lập trình trên VCL đâu. Trong Delphi phải hiểu lập trình hàm API, kiểu dữ liệu, đặc biệt pointer,
 
Upvote 0
Web KT
Back
Top Bottom