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

Liên hệ QC

Người dùng đang xem chủ đề này

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,842
Được thích
10,337
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Lần chỉnh sửa cuối:
Viết DLL = Delphi cũng có nhiều chiêu, tips nho nhỏ. Ví dụ không phải chỉ export được hàm/procedure, mà có thể export cả biến, kiểu record, pointer, object..., cách thưc hiện các code ở DllMain proc.
Tặng bạn Mạnh 1 code tui code cho IDA plugin, port từ C/C++ qua Delphi, export 1 biến kiểu record, và dùng API để search, patch memory của application mẹ.
Code này chỉ chạy đúng trên app 32bit thôi nhé, 64 bit phải sữa lại vài điểm nhỏ. Hì hì, trên chục năm rồi, từ thời IDA v6.1, giờ nó lên tới 7.3 rồi.
 

File đính kèm

Upvote 0
Viết DLL = Delphi cũng có nhiều chiêu, tips nho nhỏ. Ví dụ không phải chỉ export được hàm/procedure, mà có thể export cả biến, kiểu record, pointer, object..., cách thưc hiện các code ở DllMain proc.
Tặng bạn Mạnh 1 code tui code cho IDA plugin, port từ C/C++ qua Delphi, export 1 biến kiểu record, và dùng API để search, patch memory của application mẹ.
Code này chỉ chạy đúng trên app 32bit thôi nhé, 64 bit phải sữa lại vài điểm nhỏ. Hì hì, trên chục năm rồi, từ thời IDA v6.1, giờ nó lên tới 7.3 rồi.
đúng là bài học đầu tiên ;)

đang coi qua coi lại mà không biết xài nó như thế nào và ứng dụng vào thực tế của Mạnh ra làm sao ???!!!
Cho mạnh rồi thì chỉ cho Mạnh học cách xài nó và ứng dụng nó ... nếu được bạn có thể điều chỉnh viết lại code cho nó sử dụng được cả trên 32 & 64 được không ?

Xin cảm ơn
 
Upvote 0
Giờ thì chưa dùng gì được cho cậu đâu. Nó dùng ở lãnh vực khác.
Cậu chỉ coi code viết Dll, dùng pointer, API thôi
 
Upvote 0
Giờ thì chưa dùng gì được cho cậu đâu. Nó dùng ở lãnh vực khác.
Cậu chỉ coi code viết Dll, dùng pointer, API thôi
Mới đoán ra 1 cái
Mã:
uses
  Windows, dbfix;
vậy cái dbfix.pas là viết code vào đó khi uses thì nó sử dụng code trong đó ??

Vì có coi trong Delphi thấy mấy File *.pas đó khi ta Uses tên nó thì ta đang xài nó
Mò học là thế .... ===\. :p
 
Upvote 0
Hihi xong việc gia đình em lại mò tiếp delphi, sao chỉ co 1 thầy 1 trò vậy hihihi
 
Upvote 0
Bó 2 tay 3 chân luôn, giờ mà còn hỏi vậy.
 
Upvote 0
mò học ko có đầu mò ko có cuối thì phải thế chứ sao ... có cái rất khó mò ra ... có cái rất cơ bản mà ko biết !
Thế mới vui he -0-0-0-
 
Upvote 0
Mới đoán ra 1 cái
Mã:
uses
  Windows, dbfix;
vậy cái dbfix.pas là viết code vào đó khi uses thì nó sử dụng code trong đó ??

Vì có coi trong Delphi thấy mấy File *.pas đó khi ta Uses tên nó thì ta đang xài nó
Mò học là thế .... ===\. :p
Ở bài #259 rõ ràng ví dụ đang nói về việc một unit sử dụng một unit khác. Vd. unit1 sử dụng unit2.

Ở bài ấy còn nói thêm về cách tránh "circular unit reference"


Thế mà bây giờ còn hỏi cái này thì bó tay.

Mà bạn có đọc các vd. của Delphi, tìm kiếm trên mạng? Không có chỗ nào bạn thấy trường hợp một unit sử dụng một unit khác? Thế thì bạn may mắn đấy. Người khác "va chạm" với nó thường xuyên. Còn bạn "vọc" khá lâu mà không gặp thì cũng lạ.

Ngoài các unit của Delphi thì bạn có thể có những unit của mình chứ. Nếu không thì sao Delphi lại cho bạn khả năng thêm unit mới và viết code cho unit ấy??? Mà khi có rồi thì không phải để đấy làm cảnh. Mà muốn dùng thì cho vào uses thôi.
 
Upvote 0
Ở bài #259 rõ ràng ví dụ đang nói về việc một unit sử dụng một unit khác. Vd. unit1 sử dụng unit2.

Ở bài ấy còn nói thêm về cách tránh "circular unit reference"


Thế mà bây giờ còn hỏi cái này thì bó tay.

Mà bạn có đọc các vd. của Delphi, tìm kiếm trên mạng? Không có chỗ nào bạn thấy trường hợp một unit sử dụng một unit khác? Thế thì bạn may mắn đấy. Người khác "va chạm" với nó thường xuyên. Còn bạn "vọc" khá lâu mà không gặp thì cũng lạ.

Ngoài các unit của Delphi thì bạn có thể có những unit của mình chứ. Nếu không thì sao Delphi lại cho bạn khả năng thêm unit mới và viết code cho unit ấy??? Mà khi có rồi thì không phải để đấy làm cảnh. Mà muốn dùng thì cho vào uses thôi.
Vấn đề unit là gì nữa,
Cứ thế này bác càng đẩy vào rắc rối hơn. Liệu Delphi có rắc rối không nhỉ?
 
Upvote 0
Ở bài #259 rõ ràng ví dụ đang nói về việc một unit sử dụng một unit khác. Vd. unit1 sử dụng unit2.

Ở bài ấy còn nói thêm về cách tránh "circular unit reference"


Thế mà bây giờ còn hỏi cái này thì bó tay.

Mà bạn có đọc các vd. của Delphi, tìm kiếm trên mạng? Không có chỗ nào bạn thấy trường hợp một unit sử dụng một unit khác? Thế thì bạn may mắn đấy. Người khác "va chạm" với nó thường xuyên. Còn bạn "vọc" khá lâu mà không gặp thì cũng lạ.

Ngoài các unit của Delphi thì bạn có thể có những unit của mình chứ. Nếu không thì sao Delphi lại cho bạn khả năng thêm unit mới và viết code cho unit ấy??? Mà khi có rồi thì không phải để đấy làm cảnh. Mà muốn dùng thì cho vào uses thôi.
Từ ngày Em vào Nam đến giờ 20 Năm rồi vẫn ở cái nhà đó, ngõ đó, đường đó thế mà có người hỏi Em nhà Anh ở đường nào Em keo ko biết ... chỉ biết cổng 16 - dĩ an - bình dương

hàng ngày Em vẫn coi code Delphi vẫn Add thêm Unit để viết code mới và sử dung nó ... thế mà đùng 1 cái hỏi nó mới ghê

Tại em thấy khi em thêm 1 cái Unit thì trong Delphi tự động nó sinh ra như vầy trong Uses: ADO_Excel in 'ADO_Excel.pas',

còn bài 259 Anh chỉ Em như vậy mà em học xong quên mất tiêu ... giờ em lại nhớ tránh tham chiếu vòng vv ...

Còn thấy cái đó khai báo ngắn gọn vậy nên lại nhầm nhọt sang trồng trọt ..........
quả là code két diết nhớ nhớ xong quên quên nó làm đơ người ra đó Anh-0-0-0-
 
Lần chỉnh sửa cuối:
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.
 
Upvote 0
Hỏi ông Gấu gồ: rtcTypeName, kết quả là 0, kkk :)
Hình như mấy hàm rtc này của VBA chưa có tài liệu nào nói.
 
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.
Tôi thì hay viết
Mã:
@pFunc := GetProcAddress(h, 'rtcTypeName');
...
sName := pFunc(v);
 
Upvote 0
Hỏi ông Gấu gồ: rtcTypeName, kết quả là 0, kkk :)
Hình như mấy hàm rtc này của VBA chưa có tài liệu nào nói.

Đúng đấy bác. Chúng ta hơi "hiểm" :D . Còn hàm INSTR và INSTRREV em có nhờ bác moi tiếp ở chủ để Undocument kia khi rảnh bác xem giúp nhé.
Bài đã được tự động gộp:

Tôi thì hay viết
Mã:
@pFunc := GetProcAddress(h, 'rtcTypeName');
...
sName := pFunc(v);

Vâng, viết thế chuẩn đó anh. Vì code trước em test để khai báo pFunc: TFarProc; nên lúc dùng thì lại bao lớp cấu trúc hàm, sau em chỉnh ở trên thì dưới lại chưa chình a.
 
Upvote 0
Thôi thôi bác Tuân ơi, InStr với InStrRev ẹ lắm, dài dòng, chậm, không bằng mấy hàm PChar của Delphi đâu. Bác tính dùng cho việc gì ?
Nó là 3 hàm rtcInStr, rtcInStrRev với rtcInStrChar đó.
Hầu như 100% các hàm của VBA đều được export hết đó bạn Tuân.
 
Lần chỉnh sửa cuối:
Upvote 0
Thôi thôi bác Tuân ơi, InStr với InStrRev ẹ lắm, dài dòng, chậm, không bằng mấy hàm PChar của Delphi đâu. Bác tính dùng cho việc gì ?
Nó là 3 hàm rtcInStr, rtcInStrRev với rtcInStrChar đó.
Hầu như 100% các hàm của VBA đều được export hết đó bạn Tuân.

Vâng. Hàm InStr có thể dùng bởi hàm Pos hoặc PosEx cảu Delphi rất nhanh, code nguồn Delphi hàm Pos() viết bằng Assemble rất ngọt. Tuy nhiên hàm InStrRev Delphi không viết giúp, mình phải tự viết và dùng vòng lặp và duyệt PChar nên cảm thấy chưa ưng. Nếu lấy được InStrRev em sẽ test để so sánh tốc độ với Delphi. Khi viết Delphi cho Office thì cứ thằng nào ngon nhất ta dùng. Ý em là vậy đó bác :).
 
Upvote 0
Hạn chế dùng mấy hàm và kiểu string của Delphi bác Tuân ơi, chậm lắm, dùng các hàm và trực tiếp trên kiểu PChar luôn. Vd StrPos, StrScan, StrRScan...
Dùng kiểu string, compiler của Delphi phải nhét rất nhiều code của các hàm internal vào, chuyển kiểu, thao tác ngầm trong đó.
Bác Tuân, bác Mạnh bật option build with debug dcu đi, debug vào từng lệnh 1 sẽ thấy. Nightmare đó.
 
Lần chỉnh sửa cuối:
Upvote 0
Sao mạnh thử tới lui nó cứ báo lỗi theo hình sau ... hay làm sai cái gì ??
Capture.JPG
 
Lần chỉnh sửa cuối:
Upvote 0
Lại gõ thêm cái gì phải kg ? {$R xxx} ? ??
Post nguyên xi nội dung file dpr/pas lên đi bạn
 
Upvote 0
Cái file Res nó tự sinh ra khi bạn tạo Application. Bạn có thể xó hoặc node comment lại vẫn biên dịch đc nhé.
 
Upvote 0
Mạnh Copy y trang Code bài 811 và làm như hình sau:
Capture.JPG
 
Upvote 0
Trong code project có đoạn {$ *.Res} tạm comment lại nhé
 
Upvote 0
Đọc kỹ hdsd trước khi dùng :)
 
Upvote 0
Vâng. Hàm InStr có thể dùng bởi hàm Pos hoặc PosEx cảu Delphi rất nhanh, code nguồn Delphi hàm Pos() viết bằng Assemble rất ngọt. Tuy nhiên hàm InStrRev Delphi không viết giúp, mình phải tự viết và dùng vòng lặp và duyệt PChar nên cảm thấy chưa ưng. Nếu lấy được InStrRev em sẽ test để so sánh tốc độ với Delphi. Khi viết Delphi cho Office thì cứ thằng nào ngon nhất ta dùng. Ý em là vậy đó bác :).
Hihi cái dụ này mấy bữa em mò client sever có đụng tới nó đó anh
 
Upvote 0
Chính xác chạy rất tốt xong thấy rất nhiều code
View attachment 222173
Lỗi {$ *.Res} không phải là lỗi nghiêm trọng.

Khi bạn tạo project mới thì Delphi tự thêm {$R *.RES}. Bạn chon Save All rồi đóng Delphi. Trên đĩa sẽ có project.res. Nếu bạn xóa project.res rồi mở lại project.dpr thì Delphi sẽ thông báo là không tìm thấy project.res. Nó tìm vì trong unit project.dpr có {$R *.RES}. Và nó hỏi có tạo lại project.res không. Chỉ cần gật đầu là xong. Đây không phải là lỗi nghiêm trọng.
-------
Bạn có nhìn thấy thông báo "CoInitialize has not been called" không?

Thực ra tôi chỉ cài Delphi 5 vì máy 18 tuổi, C có 10 GB, XP Home. Bạn hãy thử như sau.
Trước dòng
Mã:
App := GetActiveOleObject('Excel.Application');
thì thêm dòng
Mã:
if InitProc <> nil then TProcedure(InitProc);
------
Mà chỉ thêm như sau cũng được
Mã:
TProcedure(InitProc);

vì trong ComObj có
Mã:
initialization
begin
  ...
  if not IsLibrary then
  begin
    SaveInitProc := InitProc;
    InitProc := @InitComObj;
  end;
end;

Tức InitProc <> nil (InitProc := @InitComObj)

Tức sẽ thực hiện InitComObj
Mã:
procedure InitComObj;
begin
  if SaveInitProc <> nil then TProcedure(SaveInitProc);
  if (CoInitFlags <> -1) and Assigned(ComObj.CoInitializeEx) then
  begin
    NeedToUninitialize := Succeeded(ComObj.CoInitializeEx(nil, CoInitFlags));
    IsMultiThread := IsMultiThread or
      ((CoInitFlags and COINIT_APARTMENTTHREADED) <> 0) or
      (CoInitFlags = COINIT_MULTITHREADED);  // this flag has value zero
  end
  else
    NeedToUninitialize := Succeeded(CoInitialize(nil));
end;

Trường hợp này (nếu tôi không lầm thì trong trường hợp đang nói thì CoInitFlags =-1
Mã:
var
  CoInitFlags: Integer = -1;  // defaults to no threading model, call CoInitialize()
) sẽđược thực hiện
Mã:
NeedToUninitialize := Succeeded(CoInitialize(nil));
 
Lần chỉnh sửa cuối:
Upvote 0
Lỗi {$ *.Res} không phải là lỗi nghiêm trọng.

Khi bạn tạo project mới thì Delphi tự thêm {$R *.RES}. Bạn chon Save All rồi đóng Delphi. Trên đĩa sẽ có project.res. Nếu bạn xóa project.res rồi mở lại project.dpr thì Delphi sẽ thông báo là không tìm thấy project.res. Nó tìm vì trong unit project.dpr có {$R *.RES}. Và nó hỏi có tạo lại project.res không. Chỉ cần gật đầu là xong. Đây không phải là lỗi nghiêm trọng.
-------
Bạn có nhìn thấy thông báo "CoInitialize has not been called" không?

Thực ra tôi chỉ cài Delphi 5 vì máy 18 tuổi, C có 10 GB, XP Home. Bạn hãy thử như sau.
Trước dòng
Mã:
App := GetActiveOleObject('Excel.Application');
thì thêm dòng
Mã:
if InitProc <> nil then TProcedure(InitProc);
------
Mà chỉ thêm như sau cũng được
Mã:
TProcedure(InitProc);

vì trong ComObj có
Mã:
initialization
begin
  ...
  if not IsLibrary then
  begin
    SaveInitProc := InitProc;
    InitProc := @InitComObj;
  end;
end;

Tức InitProc <> nil (InitProc := @InitComObj)

Tức sẽ thực hiện InitComObj
Mã:
procedure InitComObj;
begin
  if SaveInitProc <> nil then TProcedure(SaveInitProc);
  if (CoInitFlags <> -1) and Assigned(ComObj.CoInitializeEx) then
  begin
    NeedToUninitialize := Succeeded(ComObj.CoInitializeEx(nil, CoInitFlags));
    IsMultiThread := IsMultiThread or
      ((CoInitFlags and COINIT_APARTMENTTHREADED) <> 0) or
      (CoInitFlags = COINIT_MULTITHREADED);  // this flag has value zero
  end
  else
    NeedToUninitialize := Succeeded(CoInitialize(nil));
end;

Trường hợp này (nếu tôi không lầm thì trong trường hợp đang nói thì CoInitFlags =-1
Mã:
var
  CoInitFlags: Integer = -1;  // defaults to no threading model, call CoInitialize()
) sẽđược thực hiện
Mã:
NeedToUninitialize := Succeeded(CoInitialize(nil));
Em chụp cái hình nó như sau
Em đang suy nghĩ tới lui mà chưa tìm ra cái giá trị đặc biệt khi ứng dụng nó viết trên Delphi

"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ý."

chắc tại trình độ của em chưa thể nhìn ra hết khi nào hội đủ kiến thức may nhìn ra !!???
CoInitialize.JPG
 
Lần chỉnh sửa cuối:
Upvote 0
Các cậu làm tớ chán rồi, ứ thèm post nữa :P
 
Upvote 0
Khổ bạn Mạnh ghê, mình nói học Debug trong IDE đi mà không chịu học, chỉ đông chỉ tây, làm nam làm bắc hết :P
 
Upvote 0
Hình này là sau khi thêm 2 dòng đó Anh
Tôi hỏi vì hình trước ở bài #827 của bạn có "EOleSysError: CoInitialize has not been called"

Bây giờ mới có "EOleSysErro: Operation unavailable"

Bạn đã đính hình rất cẩu thả.

2 lỗi này là khác nhau. Như vậy sau khi thêm if InitProc <> nil then TProcedure(InitProc); thì không còn lỗi 1 nữa.


Về lỗi thứ 2 thì bạn có khởi động Excel trước khi chạy code Delphi không? Nếu không thì có lỗi tại dòng

App := GetActiveOleObject('Excel.Application');

Làm gì có server Excel nào đang hoạt động mà đòi Get?

Tôi không tin là Tuân không gặp lỗi. Vì Tuân có thể cho code không hẳn như có trên đĩa (vụ vbTextCompare), không hẳn là đúng thực tế, nên khó đoán. Nếu là code đưa trên GPE thì nhất định có lỗi 1.

Debug thì thấy lỗi "EOleSysError: CoInitialize has not been called" nhưng trên cơ sở "Đọc kỹ hdsd trước khi dùng" thì có đến tết Công Gô kieu manh cũng không đoán được phải thêm cái gì. "Đọc kỹ hdsd trước khi dùng" là chung chung chả nói lên cái gì cụ thể.
 
Lần chỉnh sửa cuối:
Upvote 0
Bác Tuân đã post rõ ràng cách chạy code rồi, bạn Mạnh nhà ta xớn xa xớn xác thôi, nên tui mới ghi đọc kỹ hdsd trước khi dùng, kkk :P
 
Upvote 0
Tôi hỏi vì hình trước ở bài #827 của bạn có "EOleSysError: CoInitialize has not been called"

Bây giờ mới có "EOleSysErro: Operation unavailable"

Bạn đã đính hình rất cẩu thả.

2 lỗi này là khác nhau. Như vậy sau khi thêm if InitProc <> nil then TProcedure(InitProc); thì không còn lỗi 1 nữa.


Về lỗi thứ 2 thì bạn có khởi động Excel trước khi chạy code Delphi không? Nếu không thì có lỗi tại dòng

App := GetActiveOleObject('Excel.Application');

Làm gì có server Excel nào đang hoạt động mà đòi Get?

Tôi không tin là Tuân không gặp lỗi. Vì Tuân có thể cho code không hẳn như có trên đĩa (vụ vbTextCompare), không hẳn là đúng thực tế, nên khó đoán. Nếu là code đưa trên GPE thì nhất định có lỗi 1.

Debug thì thấy lỗi "EOleSysError: CoInitialize has not been called" nhưng trên cơ sở "Đọc kỹ hdsd trước khi dùng" thì có đến tết Công Gô kieu manh cũng không đoán được phải thêm cái gì. "Đọc kỹ hdsd trước khi dùng" là chung chung chả nói lên cái gì cụ thể.

Cái dòng màu đem như dân quãng ninh kia nhột lắm Anh ... Em mò học mà cứ quăng thế tết tây quá !!!
Mạnh Phán xanh dì 1 câu trên cung trăng có đầy vàng mi cứ lên đó mà lấy ........... vãi kinh hồn nuôn !!! /*-*/ -0-0-0-
 
Upvote 0
Tuân chỉ viết
Chạy Excel với quyền "Run as administrator"
kieu manh không làm đúng thì sẽ có lỗi "EOleSysError: Operation unavailable". Chỉ cần "Đọc kỹ hdsd trước khi dùng" một lần nữa thì sẽ hết lỗi này.

Nhưng còn lỗi khó hơn là "EOleSysError: CoInitialize has not been called" mà chỉ trên cơ sở "Đọc kỹ hdsd trước khi dùng" thì có đọc đến tết Công Gô kieu manh vẫn không biết phải sửa gì, thêm gì.
Bài đã được tự động gộp:

Cái dòng màu đem như dân quãng ninh kia nhột lắm Anh ... Em mò học mà cứ quăng thế tết tây quá !!!
Mạnh Phán xanh dì 1 câu trên cung trăng có đầy vàng mi cứ lên đó mà lấy ........... vãi kinh hồn nuôn !!! /*-*/ -0-0-0-
Cuối cùng cho tới bây giờ bạn đã làm được chưa?
 
Upvote 0
thôi tạm dừng đó Anh ... lúc nào Em coi lại các bước sau
Em vẫn làm theo hướng dẫn bài 811 mà ... Chạy Excel Run As
mmm.png
 
Upvote 0
Bạn @kieu manh đang làm theo hàm rctTypeName chứ không phải hàm InStrRev anh @batman1 a. Code cả project em để full không che ấy. EM chạy không lỗi gì.

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.
 
Upvote 0
thôi tạm dừng đó Anh ... lúc nào Em coi lại các bước sau
Em vẫn làm theo hướng dẫn bài 811 mà ... Chạy Excel Run As
View attachment 222214
Tóm lại đã hết "EOleSysError: CoInitialize has not been called". Chỉ còn "EOleSysError: Operation unavailable"? Nếu bạn đã khởi động Excel mà còn lỗi này thì có nghĩa là lỗi không còn ở code nữa. Tôi không rõ lắm về các quyền vì tôi chỉ quan tâm tới XP Home của tôi thôi. Nhưng nếu nói về quyền thì theo lôgíc của tôi phải phân quyền cho Project1.exe. Tức run project1.exe với quyền "Run as administrator". Dù sao thì theo tôi lỗi không nằm trong code nữa.
 
Upvote 0
Tóm lại đã hết "EOleSysError: CoInitialize has not been called". Chỉ còn "EOleSysError: Operation unavailable"? Nếu bạn đã khởi động Excel mà còn lỗi này thì có nghĩa là lỗi không còn ở code nữa. Tôi không rõ lắm về các quyền vì tôi chỉ quan tâm tới XP Home của tôi thôi. Nhưng nếu nói về quyền thì theo lôgíc của tôi phải phân quyền cho Project1.exe. Tức run project1.exe với quyền "Run as administrator". Dù sao thì theo tôi lỗi không nằm trong code nữa.

AH. ĐÚng rồi. Bạn Mạnh thử chạy Delphi với quyền "Run as administrator" xem.
 
Upvote 0
Tóm lại đã hết "EOleSysError: CoInitialize has not been called". Chỉ còn "EOleSysError: Operation unavailable"? Nếu bạn đã khởi động Excel mà còn lỗi này thì có nghĩa là lỗi không còn ở code nữa. Tôi không rõ lắm về các quyền vì tôi chỉ quan tâm tới XP Home của tôi thôi. Nhưng nếu nói về quyền thì theo lôgíc của tôi phải phân quyền cho Project1.exe. Tức run project1.exe với quyền "Run as administrator". Dù sao thì theo tôi lỗi không nằm trong code nữa.
Chạy tới lui các kiểu nó cũng báo cái dòng màu đỏ đó Anh còn lại ko thấy chi hết ... còn tại sao Em Tịt ....!
Em thử chạy cái File Delphi *.exe Run As nó cũng thế
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn @kieu manh đang làm theo hàm rctTypeName chứ không phải hàm InStrRev anh @batman1 a. Code cả project em để full không che ấy. EM chạy không lỗi gì.
Tôi biết chứ. Vì kieu manh viết rõ là thử nghiệm code bài #811 mà.
Trích bài #821
Mạnh Copy y trang Code bài 811 và làm như hình sau:

Theo tôi với code bài #811 thì chắc chắn có lỗi "EOleSysError: CoInitialize has not been called"

Tôi chạy trên XP Home + Delphi 5 cũng có lỗi "EOleSysError: CoInitialize has not been called" - xem AVI
Bài đã được tự động gộp:

Chạy tới lui các kiểu nó cũng báo cái dòng màu đỏ đó Anh còn lại ko thấy chi hết ... còn tại sao Em Tịt ....!
Em thử chạy cái File Delphi *.exe Run As nó cũng thế
Tôi đã viết rồi. Nếu Excel có khởi động mà vẫn còn lỗi thì lỗi không nằm ở code nữa. Tôi chạy code thấy bình thường - xem AVI của tôi ở bài #841. Tất nhiên tôi phải thêm if InitProc <> nil then TProcedure(InitProc); để loại bỏ lỗi "EOleSysError: CoInitialize has not been called"

Nếu không thêm if InitProc <> nil then TProcedure(InitProc); thì như hình sau

sau.JPG

Code sau khi thêm

test.JPG
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Office bạn mạnh là 2013 x32, thế exe delphi bạn Mạnh build ở 32 hay 64 đấy?
 
Upvote 0
@Nguyễn Duy Tuân: đây cũng bị giống Tuân nè, kiểu Double thôi đó
Lỗi quá tinh vi, quá hiểm, kg phải dân "rờ chim em" thì thua :p
Tạm thời xin can bác @kiều mạnh build và dùng cái VBLibrary.dll trên mode 64bit nhé.
 
Lần chỉnh sửa cuối:
Upvote 0
@Nguyễn Duy Tuân: đây cũng bị giống Tuân nè, kiểu Double thôi đó
Lỗi quá tinh vi, quá hiểm, kg phải dân "rờ chim em" thì thua :p
Tạm thời xin can bác @kiều mạnh build và dùng cái VBLibrary.dll trên mode 64bit nhé.

Quả dùng function mà declare trực tiếp trên 64-bit căng đây. Em đang tính làm thử cái DLL trên VC rồi thử có bị vấn đề giống nhau không?
 
Upvote 0
Cứ bình tĩnh, qua VC là bước cuối cùng rồi. Để debug, rờ em kỹ xem có phải bug của Delphi compiler kg đã. Nếu đúng thì nâng cấp Rad studio lên Tokyo luôn. Nếu vẫn bị thì phải qua Vc hay C# thôi
 
Upvote 0
Cứ bình tĩnh, qua VC là bước cuối cùng rồi. Để debug, rờ em kỹ xem có phải bug của Delphi compiler kg đã. Nếu đúng thì nâng cấp Rad studio lên Tokyo luôn. Nếu vẫn bị thì phải qua Vc hay C# thôi

Em đang tò mò và nghi VC hay C# cũng vậy vì đây là cách dùng function từ declare mới bị vậy, em chưa thấy tài liệu nào từ MS nói về cách làm này?! Compiler của Delphi vẫn chuẩn theo cách call thông thường.
 
Upvote 0
mình chợt nghĩ tại sao từ VBA call hàm trong Dll x64 thì đúng, vba 64 sinh code asm truyền param đúng, mà từ Excel 64 call lại bị sai???
Vậy nên phải debug cả từ Excel và VBA, cả 2,để xem khác nhau chỗ nào.
Trên 32 thì stdcall là đơn giản, standard rồi, thằng vc, delphi, gcc 32 đều sinh mã đúng, như nhau.
Trên 64 thì chỉ còn fastcall nhưng Vc64 và gcc64 sinh code khác nhau đó. Hồi sáng mình có nói à Delphi nay dùng GCC 64 compiler à đó. Đây cũng là điểm cần kiểm tra. Vì Excel 64 build = VC64, còn dll của mình là GCC64
 
Lần chỉnh sửa cuối:
Upvote 0
Chả lẽ do align stack sai chăng, vì thấy Delphi dùng các thanh ghi RCX, RDX, R8,R9 đúng mà, còn stack của hàm thì thấy hơi dị dị ???
 
Upvote 0
Ẩu quá đáng lẻ nó ko phát sinh một mớ bài ... cứ Copy nguyên bài 811 cho nên nó sai

Path DLL bài 811 là
Mã:
h := LoadLibrary('C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL');
Path DLL thực tế trên máy Mạnh là
Mã:
h := LoadLibrary('C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7.1\VBE7.DLL');
Như vậy cho nên nó cứ sai tè le ra mặc dù Anh @batman1 có hướng dẫn rất chi tiết làm y trang mà nó cứ linh tinh là thế :p
=========== khúc trên coi như xong bạn nào test thì sửa lại Path DLL thực tế trên máy mình =============

1/ vần đề đặt ra là nếu nay nó VBA7.1 mai nó 7.2 + n thì sao ??!!!
2/ Office x32 và x64 ????

Anh @batman1 cho Em hỏi chút

1/ Có cách nào lấy cái Path đặc biệt của DLL đó theo bộ Office mà mình cài đặt hay ko ???
2/ nếu có Viết cho nó vào đó 1 cái Hàm là xong khỏi mất công khai báo thủ công rồi lại thay đổi theo Ver của nó thì mêt lắm
3/ hoặc có cách nào khác ko cần path DLL của nó mà vẫn load được DLL đó ( Em nghỉ khó )
.....
Em hình dung ra cách ứng dụng nó một chút rồi :p

cảm ơn tất cả mọi người
OK.JPG
Bài đã được tự động gộp:

Office bạn mạnh là 2013 x32, thế exe delphi bạn Mạnh build ở 32 hay 64 đấy?
Mạnh xài Windows10x64 - Office2016x32
 
Lần chỉnh sửa cuối:
Upvote 0
Chả lẽ do align stack sai chăng, vì thấy Delphi dùng các thanh ghi RCX, RDX, R8,R9 đúng mà, còn stack của hàm thì thấy hơi dị dị ???

EM vừa có một thí nghiệm và tạm khá yên tâm. EM tạo hàm trả về số Long thì mọi nguyên đẹp đẽ :). Như vậy khoanh vùng vấn đề ở OleVariant.
 
Upvote 0
Anh @batman1 cho Em hỏi chút

1/ Có cách nào lấy cái Path đặc biệt của DLL đó theo bộ Office mà mình cài đặt hay ko ???
2/ nếu có Viết cho nó vào đó 1 cái Hàm là xong khỏi mất công khai báo thủ công rồi lại thay đổi theo Ver của nó thì mêt lắm
3/ hoặc có cách nào khác ko cần path DLL của nó mà vẫn load được DLL đó ( Em nghỉ khó )
Tôi nghĩ là cứ tìm trong csdl của system là Registry thôi. Chả nhẽ csdl mà lại không có?

Trên XP Home của tôi thì là khóa HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VBA

Nếu tôi đoán được những trò "voc" của bạn thì bạn muốn tạo DLL để nhúng vào Excel. Nếu thế thì bạn chỉ cần tên DLL: VBE6.dll hoặc VBE7.dll. Để có được HMODULE thì bạn nhập tên DLL thôi. Muốn thế thì bạn dùng GetModuleHandle

Help của Delphi 5


help.JPG

Từ đỏ đỏ bạn thấy là bạn có thể nhập tên DLL thôi. Hàm sẽ tìm DLL bạn cần trong các module được ánh xạ vào không gian địa chỉ của process Excel.

Tóm lại
Mã:
h := GetModuleHandle('VBE7.DLL');

Tất nhiên với Excel thấp thì chỉ có VBE6, với phiên bản mới thì có cả VBE6 và VBE7. Vậy thì trước tiên nhập VBE7.dll. Nếu h = 0 thì gọi lại và nhập VBE6.dll. Nếu cũng h = 0 thì "dọn đồ chơi". Nhưng chắc chắn là 1 trong 2 module này được load.

Từ xanh xanh thì bạn bỏ không gọi (xóa đi)
Mã:
FreeLibrary(h);

Tôi ra cho bạn bài tập nhé.

1. Code của bài dùng 'rtcTypeName' bạn cho vào MyDll.dll
2. DLL exports 1 hàm GetTypeName trả về PWideChar.
3. Hàm GetTypeName gọi hàm rtcTypeName trong VBE7.dll để lấy kết quả và trả về kết quả ấy cho caller trong VBA.

Ở trên tôi nói là bạn không phải gọi FreeLibrary. Nếu bạn cố tình gọi FreeLibrary thì trước khi trả về kết quả cho caller, tức trước khi result := ... thì bạn phải từ kết quả do rtcTypeName trả về "soạn" kết quả cho caller. Nếu không "soạn" mà trả về cho caller cái mà rtcTypeName trả về thì Excel sẽ bị chết đột ngột - Excel bị đóng mà không có thông báo gì. Nguyên nhân thì đọc kỹ chỗ xanh xanh.

Khai báo bên Excel
Mã:
Declare Function GetTypeName Lib "MyDll.dll" () As String
 
Upvote 0
Bạn Mạnh mà nắm vững debug trên Delphi thì mấy cái lỗi này là muỗi. Ai biểu chỉ biết F9 Run làm chi thì ráng chịu.
Code đó của bạn Tuân bị memory leak đó, chưa dùng được đâu. Và return string về cho VBA bị ông VBA lanh chanh thọt võ làm sai.
Bài đã được tự động gộp:

Vậy 90% là do stack align rồi Tuân ơi. Lỗi do mình chứ kg phải do Delphi compiler.
Tuân tiếp tục thử với hàm có kiểu trả về có SizeOf lớn hơn 8 nhé, như record, double, real, single...
Của bạn kia là kiểu Double đó.
Tuân nhớ x64 dll, MyCaller, nếu truyền V: OleVariant , truyền nguyên 3 Param mỗi cái 24 byte vào stack, bị văng ngay _VarAddRef không?
 
Lần chỉnh sửa cuối:
Upvote 0
return PWideChar thôi. Tất nhiên rtcTypeName cũng return PWideChar chứ không như Tuân mong muốn là WideString. Thế thôi.

Và dĩ nhiên phải convert String mà GetTypeName trả về - from Unicode.
 
Upvote 0
Bạn Mạnh mà nắm vững debug trên Delphi thì mấy cái lỗi này là muỗi. Ai biểu chỉ biết F9 Run làm chi thì ráng chịu.
Code đó của bạn Tuân bị memory leak đó, chưa dùng được đâu. Và return string về cho VBA bị ông VBA lanh chanh thọt võ làm sai.
Bài đã được tự động gộp:

Vậy 90% là do stack align rồi Tuân ơi. Lỗi do mình chứ kg phải do Delphi compiler.
Tuân tiếp tục thử với hàm có kiểu trả về có SizeOf lớn hơn 8 nhé, như record, double, real, single...
Của bạn kia là kiểu Double đó.
Tuân nhớ x64 dll, MyCaller, nếu truyền V: OleVariant , truyền nguyên 3 Param mỗi cái 24 byte vào stack, bị văng ngay _VarAddRef không?
memory leak là sao. Anh có thể giải thích rõ chúc đựợc không?
 
Upvote 0
Cái hình Winhex tui post bên kia các bác chưa nhìn kỹ rồi. Khổ.
Memory leak nghĩa là nếu hàm đó được gọi cỡ triệu lần thì Excel văng luôn đó em gái. Chịu khó đọc source C của rtcTyName mà tui đã post, khúc cuối hàm đó.
Bài đã được tự động gộp:

Nhỏ hơn hoặc = 8 thì trả về qua RAX, hôm qua mình nói rồi, lớn hơn thì qua stack hay RDX:RAX.
Nên mình có nói stack nó dị dị, còn dị ra sao thì debug kỹ.
Chắc vì vậy nên mấy cái pointer POleViant trỏ sai hết
 
Lần chỉnh sửa cuối:
Upvote 0
Rất rất nhiều bạn code Delphi mà hầu như không hề quan tâm tới internal của biến, kiểu trong Delphi nó "nà như thế lào". Ví dụ rõ nhất là kiểu string. Cứ var xxx:string rồi xài tẹt ga. Hay các kiểu record, array, dynamic array...
Thật ra cũng không sao, cứ có cho thì tui xài, quan tâm xuống dưới "nàm" chi cho mệt óc. Nhưng rồi từ từ sẽ tới lúc sẽ bắt buộc phải đào xuống, ví dụ khi dùng API, dùng Pointer, hay gặp bug tinh vi, memory leak, crash, dập, tràn trên tràn dưới bộ nhớ... hay "rờ em" Delphi app tìm bug, exploit, "mèo què", hì hì và cả cờ rắc nữa :P
Vì vậy cu anh tui thành thực khuyên các bạn, bỏ chút ít thời gian đọc và tìm hiểu về internal data type của Delphi, xem, biết cách nó tổ chức, nằm trong bộ nhớ ra sao, phải thao tác như thế nào với nó cho đúng.
Trong Delphi document, help, đã có nói rất rõ về vấn đề này, nhưng hầu như ít ai đọc kỹ, hầu như lướt qua cho xong.
Minh "ngu phép" đăng lại ở đây để các bạn đọc, nhất là các bạn đang bắt đầu học Delphi, như bạn ở Dĩ An chẵng hạn, hay em gái xinh đẹp làm GUI ngon lành đấy: :)
Gác chân lên bàn, hay mang lap ra ban công đều được, kkk.
1. http://docwiki.embarcadero.com/RADStudio/Rio/en/Internal_Data_Formats_(Delphi)
2. http://docwiki.embarcadero.com/RADStudio/Rio/en/Unicode_in_RAD_Studio
Đọc đi đọc lại, đọc tới đọc lui, đọc xuôi đọc ngược, khi nào thuộc thì thôi,
Có gì "thét mét" cứ đừng ngại ngùng mà đăng lên nhé, mọi người cùng trao đổi.
Cứ từ từ, căn bản từ gốc đã rồi sẽ có ngày hái quả ngon.Đừng nôn nóng.
 
Upvote 0
Nói đi thì cũng suy lại 1 tẹo !!!!

1/ Khi Mạnh với vọc VBA thấy người ta code vậy mà mình copy để dùng hoài mà sao ko có chạy được ( vì chưa biết bật mắt rô )
sau này vọc hoài mới biết .... nói chung copy code cho nó chạy được là mừng hết lớn nuôn chứ chưa nói viết code

Ngày trước Anh quanghai1969 khi rảnh ghé chơi chỉ cho mạnh viết mấy cái hàm rất đơn giản như abc ấy thế mà ko biết nó ra làm sao cả ... giờ nhìn lại thấy phì cười tại sao nó đơn giản thế mà khi đó mình ngu thế ko biết là nàm sao ???!!!

Code két hay cái chi đó nó phải có quá trình hấp thụ nó ... Nếu quá trình thu nạp 1 số lượng nhất định khi vượt giới hạn của nó thì chất nó sẻ biến đổi đó là cái quy luật muôn đời nó vẫn cứ thế ???!!!

2/ Mạnh vọc Delphi cũng thế biến khai báo sao hiện tại kệ bà nó miễn sao nó chạy là mừng hết lớn luôn ... sau này kiến thức khá thêm chút nhìn lại khắc tự biết tại sao mình ko khai báo như thế lày mà lại khai báo như thế lọ .... sao ngơ ngơ thế nhỉ
....
Cảm ơn cái tấm chân tình tốt của @ThangCuAnh đã chỉ cho Mạnh học nhưng phải có thời gian mà thu nạp nó chứ nói khơi khơi kiểu cao cấp thế thì còn lâu lắm lắm Mạnh mới theo kịp

Nói chi xa xôi chỉ cho Mạnh Kỹ thuật Debug trong Delphi khi viết DLL cho Excel như cái VBlibray ấy .... chi tiết chút thì tốt .... chứ cứ keo khơi khơi Debug ....Debug ....Debug đi thì cũng thua ===\. -0-0-0-
 
Upvote 0
Khổ ghê, cao xa cái gì trời, nó nằm ngay trong Help của RAD Studio của cậu đó, chứ ở đâu xa. Hình như phần Object Pascal Language Guide, nếu tôi nhớ không lầm.
Còn cái debug thì cái hình hôm qua tôi đã nói rõ rồi, nó chình ình ra đó, chỉ click vào và đọc thôi chứ ở đâu xa.
Sao cậu không tự thân vận động nhỉ, cái gì cũng phải có người khác làm giùm, chỉ giùm 100% mới chịu.
Đến phát bực với cậu "nuôn, nắm nắm" rồi đó.
 

File đính kèm

  • Capture.JPG
    Capture.JPG
    509.8 KB · Đọc: 21
Upvote 0
Lưu nó vô đây để nhớ sau này quên thì vô coi lại đầy code mẫu ....!!???
Nếu cứ coi sách mà ai cũng học được thì mấy tay giáo sư, tiến sĩ đứng bục thất nghiệp hết ... đói ăn ra làm bốc xếp dễ ko chừng mấy tay mặt ác chân tay to nó keo : người đâu ấy chứ xếp mấy cục gạch mà làm ko xong thì làm nên cái trò chi ko biết !!!???
Mạnh coi các mẫu code là mới hình dung ra được còn nói sáo rỗng như con két thì thua toàn tập -0-0-0-

Ngày xưa rất xưa có Ông hàng xóm hay nói câu "Nhân Vô Thập toàn mà cháu" ... mấy chục năm sau nhớ lại thấy Ông ấy Nói đúng
 
Lần chỉnh sửa cuối:
Upvote 0
Người học VBA cũng vậy. Nếu chỉ làm việc với RANGE, Worksheet, Array thì trên GPE này cả rừng người, trên mạng thì vô số. Nhưng số người biết ứng dụng khá tốt lập trình Windows API trên GPE thì đếm trên đầu ngón tay, không hết một bàn tay đâu nhé. Mình nói lại, nó là một lĩnh vực rộng.

Kiến thức Delphi rộng lắm nhé. Nếu lan man bạn sẽ lấn vào một lĩnh mà không phải ai cũng biết đâu -> đó là lập trình hàm API cho môi trường Windows. API là giao tiếp với môi trường chung của Windows mà các phần mềm hỗ trợ lập trình có thể giao tiếp được với nhau ->Đó là chuẩn khai báo Interface, data type... API là lĩnh vực can thiệp rộng tới mọi ngõ ngách của hệ điều hành. Nên việc này khó hiểu là đương nhiên. Ngay cả với người chuyên lập trình phần mềm trên nền tảng VCL của Winform trên Delphi động đến nó còn phải mất ăn mất ngủ để tra cứu tư liệu gốc của Microsoft và Delphi. Bạn nào tự học theo thói quen trải nghiệm bản thân rồi tự đúc rút qua một tá ví dụ thì nó khó là đương nhiên. Liệu cần có bao nhiêu ví dụ để đủ trải nghiệm?

Topic này mình kỳ vọng các bạn biết thêm được hướng tự tạo hàm API cơ bản chuyên để phân tích dữ liệu phục vụ cho xử lý dữ liệu (DATA) trên Excel là chính, nên nếu học ngôn ngữ Delphi/Pascal/Object Pascal cùng với phương pháp lập trình API dễ như bài đầu thì sẽ học được thôi. Nhưng nếu lan rộng ra lập trình các ngõ ngách của Windows là to chuyện đấy. Mình nói vậy để các bạn mới vào topic này đừng xem các bài viết trên mà nản vì các câu hỏi và trả lời đó đang đi vào khu vực khai thác rộng chứ không phải chỉ DATA trên Excel. Nên tôi khuyên những ai mới biết đến Delphi và muốn lập trình DLL tạo hàm API hãy tập trung và xử lý DATA trước cùng Delphi và đừng quên học ngôn ngữ, đọc ngấm từng chữ trong tài liệu Object Pascal nhé. Các vấn đề khác dần dần tính tiếp khi đã nắm được ngôn ngữ khá tốt.
 
Lần chỉnh sửa cuối:
Upvote 0
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
Thế mà tui thấy cậu Kiều Mạnh đòi nhảy thẳng đi nhậu luôn đó.
Đô được 2 3 lon kg Mạnh, hôm nào đi nhậu với tui ?
Từ xưởng tui xuống nhà cậu khoảng vài cây, 5 10 phút à
 
Lần chỉnh sửa cuối:
Upvote 0
Vì mình dùng hàm TypeName trong DLL nên bắt buộc phải loadlibrary mới lấy được con trỏ hàm đó. Nếu tự viết hàm này đùng như TypeName mình chưa tự viết được :)
Cái vụ này em cũng đi theo hướng IDispatch và vartypeinfo không biết mò ra không ( đôi với yêu cầu của em thì em ăn gian ok rồi )
 
Upvote 0
Cái vụ này em cũng đi theo hướng IDispatch và vartypeinfo không biết mò ra không ( đôi với yêu cầu của em thì em ăn gian ok rồi )

A cũng có hướng dùng cái đó nhưng chưa thử làm. Hàm em viết cí lấy được kiểu biến như String, Double (không chỉ tên class) không?
 
Upvote 0
Thế mà tui thấy cậu Kiều Mạnh đòi nhảy thẳng đi nhậu luôn đó.
Đô được 2 3 lon kg Mạnh, hôm nào đi nhậu với tui ?
Từ xưởng tui xuống nhà cậu khoảng vài cây, 5 10 phút à
OK đón tiếp nhiệt Tình
Mạnh ko sống vì code ... nhưng đam mê code
Mạnh ko có trí thông Minh nhưng có lòng kiên nhẫn
bất cứ ai trạm tới cái tôi của mạnh ... bất cứ giá nào phải xử nó !
Thế thôi

Mong vui vẻ và hiểu nhau hơn
Thân
 
Upvote 0
A cũng có hướng dùng cái đó nhưng chưa thử làm. Hàm em viết cí lấy được kiểu biến như String, Double (không chỉ tên class) không?

The solution below gets the actual type name of such an object in three steps:
  1. Cast the object to the IDispatch type.
  2. Get the ITypeInfo interface via IDispatch.GetTypeInfo().
  3. Get the type name using ITypeInfo.GetDocumentation().
Mã:
using System;
using System.Runtime.InteropServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;

namespace ComUtils
{
    public class ComHelper
    {
        /// <summary>
        /// Returns a string value representing the type name of the specified COM object.
        /// </summary>
        /// <param name="comObj">A COM object the type name of which to return.</param>
        /// <returns>A string containing the type name.</returns>
        public static string GetTypeName(object comObj)
        {

            if (comObj == null)
                return String.Empty;

            if (!Marshal.IsComObject(comObj))
                //The specified object is not a COM object
                return String.Empty;

            IDispatch dispatch = comObj as IDispatch;
            if (dispatch == null)
                //The specified COM object doesn't support getting type information
                return String.Empty;

            ComTypes.ITypeInfo typeInfo = null;
            try
            {
                try
                {
                    // obtain the ITypeInfo interface from the object
                    dispatch.GetTypeInfo(0, 0, out typeInfo);
                }
                catch (Exception ex)
                {
                    //Cannot get the ITypeInfo interface for the specified COM object
                    return String.Empty;
                }

                string typeName = "";
                string documentation, helpFile;
                int helpContext = -1;

                try
                {
                    //retrieves the documentation string for the specified type description
                    typeInfo.GetDocumentation(-1, out typeName, out documentation,
                        out helpContext, out helpFile);
                }
                catch (Exception ex)
                {
                    // Cannot extract ITypeInfo information
                    return String.Empty;
                }
                return typeName;
            }
            catch (Exception ex)
            {
                // Unexpected error
                return String.Empty;
            }
            finally
            {
                if (typeInfo != null) Marshal.ReleaseComObject(typeInfo);
            }
        }
    }

    /// <summary>
    /// Exposes objects, methods and properties to programming tools and other
    /// applications that support Automation.
    /// </summary>
    [ComImport()]
    [Guid("00020400-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IDispatch
    {
        [PreserveSig]
        int GetTypeInfoCount(out int Count);

        [PreserveSig]
        int GetTypeInfo(
            [MarshalAs(UnmanagedType.U4)] int iTInfo,
            [MarshalAs(UnmanagedType.U4)] int lcid,
            out ComTypes.ITypeInfo typeInfo);

        [PreserveSig]
        int GetIDsOfNames(
            ref Guid riid,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
            string[] rgsNames,
            int cNames,
            int lcid,
            [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);

        [PreserveSig]
        int Invoke(
            int dispIdMember,
            ref Guid riid,
            uint lcid,
            ushort wFlags,
            ref ComTypes.DISPPARAMS pDispParams,
            out object pVarResult,
            ref ComTypes.EXCEPINFO pExcepInfo,
            IntPtr[] pArgErr);
    }
}
Variant có cái type đó anh. String, double, inter,... Range cung co cách, tên class thi chưa anh. Giống như đợt trước em lấy field type đó anh
 
Lần chỉnh sửa cuối:
Upvote 0
Ghét cái ông Sì sáp này quá, dùng API gì cũng phải copy paster "đì cờ le"...
 
Upvote 0
Upvote 0
Hì hì, bé nhầm giữa TypeInfo RTTI của Delphi và TypeInfo của COM/OLE rồi :P
 
Upvote 0
Cho chú xem source Delphi của bé thử ?
 
Upvote 0
Mã:
  OleType := VarType(V) and VarTypeMask;
          //if VarType(V) and VarArray<>0
          //then VArray:='Array of'
          //else VArray:='';
  case OleType of
    varEmpty     : typeString := 'Empty';
    varNull      : typeString := 'Null';
    varSmallInt  : typeString := 'SmallInt';
    varInteger   : typeString := 'Integer';
    varSingle    : typeString := 'Single';
    varDouble    : typeString := 'Double';
    varCurrency  : typeString := 'Currency';
    varDate      : typeString := 'Date';
    varOleStr    : typeString := 'OleStr';
    varDispatch  : typeString := 'Range';//'varDispatch'; 'OLE Object';
    varError     : typeString := 'Error';
    varBoolean   : typeString := 'Boolean';
    varVariant   : typeString := 'Variant';
    varUnknown   : typeString := 'Unknown';
    varByte      : typeString := 'Byte';
    varWord      : typeString := 'Word';
    varLongWord  : typeString := 'LongWord';
    varInt64     : typeString := 'Int64';
    varStrArg    : typeString := 'StrArg';
    varString    : typeString := 'String';
    varAny       : typeString := 'Any';
    varTypeMask  : typeString := 'TypeMask';
    varArray    : typeString :='[Array]';//   (VArray: PVarArray);
  end;
  Result:=typeString;
 
Upvote 0
Hì hì, bé phát minh lại bánh xe rồi, Delphi nó có sẵn hàm VarTypeAsText trong unit Variants.pas rồi, sao không dùng mà đi viết lại.
Trong file bác @kieu manh gởi đây :)
Mã:
function VarTypeAsText(const AType: TVarType): string;
const
  CText: array [varEmpty..varUInt64] of string = ('Empty', 'Null', 'Smallint', //Do not localize
    'Integer', 'Single', 'Double', 'Currency', 'Date', 'OleStr', 'Dispatch', //Do not localize
    'Error', 'Boolean', 'Variant', 'Unknown', 'Decimal', '$0F', 'ShortInt', //Do not localize
    'Byte', 'Word', 'Cardinal', 'Int64', 'UInt64'); //Do not localize
var
  LHandler: TCustomVariantType;
begin
  if AType and varTypeMask <= varUInt64 then
    Result := CText[AType and varTypeMask]
  else if AType = varString then
    Result := 'String' //Do not localize
  else if AType = varUString then
    Result := 'UnicodeString' //Do not localize
  else if AType = varAny then
    Result := 'Any' //Do not localize
  else if FindCustomVariantType(AType, LHandler) then
    Result := Copy(LHandler.ClassName, 2, High(Integer))
  else
    Result := HexDisplayPrefix + IntToHex(AType and varTypeMask, 4);

  if AType and varArray <> 0 then
    Result := 'Array ' + Result; //Do not localize
  if AType and varByRef <> 0 then
    Result := 'ByRef ' + Result; //Do not localize
end;
 
Upvote 0
Hì hì, bé phát minh lại bánh xe rồi, Delphi nó có sẵn hàm VarTypeAsText trong unit Variants.pas rồi, sao không dùng mà đi viết lại.
Trong file bác @kieu manh gởi đây :)
Mã:
function VarTypeAsText(const AType: TVarType): string;
const
  CText: array [varEmpty..varUInt64] of string = ('Empty', 'Null', 'Smallint', //Do not localize
    'Integer', 'Single', 'Double', 'Currency', 'Date', 'OleStr', 'Dispatch', //Do not localize
    'Error', 'Boolean', 'Variant', 'Unknown', 'Decimal', '$0F', 'ShortInt', //Do not localize
    'Byte', 'Word', 'Cardinal', 'Int64', 'UInt64'); //Do not localize
var
  LHandler: TCustomVariantType;
begin
  if AType and varTypeMask <= varUInt64 then
    Result := CText[AType and varTypeMask]
  else if AType = varString then
    Result := 'String' //Do not localize
  else if AType = varUString then
    Result := 'UnicodeString' //Do not localize
  else if AType = varAny then
    Result := 'Any' //Do not localize
  else if FindCustomVariantType(AType, LHandler) then
    Result := Copy(LHandler.ClassName, 2, High(Integer))
  else
    Result := HexDisplayPrefix + IntToHex(AType and varTypeMask, 4);

  if AType and varArray <> 0 then
    Result := 'Array ' + Result; //Do not localize
  if AType and varByRef <> 0 then
    Result := 'ByRef ' + Result; //Do not localize
end;
em biết chứ hihi, tự cái hàm này em còn làm cái khác nữa, cũng có thể em nhìn không ra...
 
Upvote 0
Mã:
  OleType := VarType(V) and VarTypeMask;
          //if VarType(V) and VarArray<>0
          //then VArray:='Array of'
          //else VArray:='';
  case OleType of
    varEmpty     : typeString := 'Empty';
    varNull      : typeString := 'Null';
    varSmallInt  : typeString := 'SmallInt';
    varInteger   : typeString := 'Integer';
    varSingle    : typeString := 'Single';
    varDouble    : typeString := 'Double';
    varCurrency  : typeString := 'Currency';
    varDate      : typeString := 'Date';
    varOleStr    : typeString := 'OleStr';
    varDispatch  : typeString := 'Range';//'varDispatch'; 'OLE Object';
    varError     : typeString := 'Error';
    varBoolean   : typeString := 'Boolean';
    varVariant   : typeString := 'Variant';
    varUnknown   : typeString := 'Unknown';
    varByte      : typeString := 'Byte';
    varWord      : typeString := 'Word';
    varLongWord  : typeString := 'LongWord';
    varInt64     : typeString := 'Int64';
    varStrArg    : typeString := 'StrArg';
    varString    : typeString := 'String';
    varAny       : typeString := 'Any';
    varTypeMask  : typeString := 'TypeMask';
    varArray    : typeString :='[Array]';//   (VArray: PVarArray);
  end;
  Result:=typeString;

Cái này a biết. Em đã thử hàm của em
myTypeName(range(“a1”)
kết quả đúng trả về “Range” chưa?
 
Upvote 0
Bé viết theo code C# hả ? Hì hì, chưa chính xác đâu :)
Code của MS coder cho hàm rtcTypeName, cho VT_DISPATCH đây, bonus mọi người viết theo đúng vậy trên ngôn ngữ của mình.
Chú mới "rờ em" nó ra full, đầy đủ rồi đó.
Theo bé thì nên viết theo code C# hay nên viết theo code C/C++ này ? :p
Tự like cho mình 1 cái, hổng ai like hết ! Cho cái vỗ tay đê !

Untitled.png
 
Lần chỉnh sửa cuối:
Upvote 0
Bé viết theo code C# hả ? Hì hì, chưa chính xác đâu :)
Code của MS coder cho hàm rtcTypeName, cho VT_DISPATCH đây, bonus mọi người viết theo đúng vậy trên ngôn ngữ của mình.
Chú mới "rờ em" nó ra full, đầy đủ rồi đó.
Theo bé thì nên viết theo code C# hay nên viết theo code C/C++ này ? :p
Tự like cho mình 1 cái, hổng ai like hết ! Cho cái vỗ tay đê !

View attachment 223222
:clap::clap::clap::clap::clap::clap::clap::clap::clap::clap:
Code C đâu phải của em của Addmin trên diễn đàn nước ngoài, anh chuyển giùm qua Delphi cho em đi /-*+//-*+//-*+//-*+//-*+/
 
Upvote 0
Chú không biết code Delphi bé, và cũng không có cài Delphi.
 
Upvote 0
Cũng thua luôn bé. Hỏi bác Tuân đi. C++ chú cũng không biết.
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom