Delphi Tips 
-----------------------------

キーワード:メモリ

>> Index

02/11 可変長レコードの扱い方
02/08 Delphi/CBuilder で作った DLL から VB に文字列を返す
02/08 複数プログラムから同一内容のメモリを参照/更新する
02/08 DLL のロード・アンロードでメモリリーク?
02/08 Delphi1.0でHugeポインタを使う
02/08 New/Dispose に Pointer 型のポインタを渡すと。。。
02/08 Delphi 1.0 (16bit)で、物理メモリアドレスに直接アクセスする

最終更新: 7125 日前

0149  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/11 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/11 osamu 編集
可変長レコードの扱い方

> 今、DirectPlayの送信部分を制作していますが、
>
> HRESULT Send(DPID idFrom, DPID idTo, DWORD dwFlags,
>              LPVOID lpData, DWORD dwDataSize);
>
> 上記のようになっていて、送信データ部分を構造体のポインタで渡す
> ようになっています。現在は、
>
>  TTextMessage = record
>    dwType: DWORD;  {  dwType は絶対に必要  }
>    Msg   :array[0..255]of char;
>  end;

>
> としてstring型からchar型に変換してから送受信をしていますが、
> 255文字以上送信できませんし、短い場合は無駄が生じるように思います。
> このmsgの部分を可変サイズにする方法はないでしょうか?

必要な量だけヒープから確保するのがよろしいかと思います。
具体的な案としては、以下のような手順が考えられます。
  1. 送信用に AllocMem等で 必要量だけ確保
  2. 確保した領域を編集しやすいように 予め用意しておいた型をかぶせ、編集
  3. 送信
  4. 確保領域の開放

例)
const
  Giga = (1024*1024*1024);
type
  // 編集用の型
  PTextMessage = ^TTextMessage;
  TTextMessage = record
    dwType: DWORD;
    Msg:    array[0..1*Giga -1] of Char;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pBuf: Pointer;
  pMsg: PTextMessage;
begin
  pBuf := AllocMem(Sizeof(pMsg^.dwType) + 4092);

  // 型をかぶせて編集
  pMsg := pBuf;
  with pMsg^ do
  begin
    dwType := $12345678;
    StrCopy(Msg, PChar(StringOfChar('A', 4091)));
  end;
  
  ... // 送信等

  FreeMem(pBuf);
end;
参照: [Delphi-ML:25091] <PASCAL>

0118  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
Delphi/CBuilder で作った DLL から VB に文字列を返す

> VB(VBA含む)は、ポインタが使えませんので、DLLで文字列を返す方法
> が知りたいです。 だれか、ご存知のかたおしえてください。

一番手軽なのは,SysAllocStringByteLen で文字列を確保して,そのまま返す方法です.

例)
  return SysAllocStringByteLen( "Hello", 5 );

内容は ShiftJIS で Ok.
ここで確保したメモリーは VB 側で開放されるそうです.
参照: [builder:6429] <Windows> <DLL>

0061  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
複数プログラムから同一内容のメモリを参照/更新する

Win32用に、中村@NECさんが、File Mapping を利用して実現したクラスを [Delphi-ML:19603] で紹介されています。
ただ、添付ファイルは Web からは取れません。
どこか他からダウンロードしないと。。。
参照: [Delphi-ML:19603] <Windows> <ファイル>

0106  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
DLL のロード・アンロードでメモリリーク?

プログラム中でDLLの呼び出し、開放を何度も行っていると、システムモニターの「アロケート済みメモリ」がどんどん増えていきます。
簡単なDLL呼出しプログラムを作って試しましたが、呼出・開放を2018回繰り返すとプログラムがとまります。DLLの内容には関係ありません。
また、NT4/BCB3を使っても同様の現象が発生します。

以下の方法で回避できます。

・インポートライブラリを使ってDLLをリンクする
または
・DLLを作成するときにVCLをリンクしない(DLLでVCLを使っていない場合のみ)
参照: [builder:5705] <バグ> <DLL>

0025  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
Delphi1.0でHugeポインタを使う

Delphi1.0用のHugeポインタルーチンです。
もう過去の遺物かな?

function HpSubOne( pointer:Pointer ):Pointer;
inline(
    $58/          {   POP AX      }
    $5A/          {   POP DX      }
    $2d/$01/$00/  {   SUB AX,1    }
    $73/$03/      {   JNB @@      }
    $83/$EA/$08   {   SUB DX,8    }
);                {@@:            }

function HpAddOne( pointer:Pointer ):Pointer;
inline(
    $58/          {   POP AX      }
    $5A/          {   POP DX      }
    $40/          {   INC AX      }
    $75/$03/      {   JNZ @@      }
    $83/$C2/$08   {   ADD DX,8    }
);                {@@:            }

function HpAddWord( pointer:Pointer ; offset:Word ):Pointer;
inline(
    $59/        {   POP CX      }
    $58/        {   POP AX      }
    $5A/        {   POP DX      }
    $01/$c8/    {   ADD AX,CX   }
    $73/$03/    {   JNC @@      }
    $83/$C2/$08 {   ADD DX,8    }
);              {@@:            }

function HpSubWord( pointer:Pointer ; offset:Word ):Pointer;
inline(
    $59/        {   POP CX      }
    $58/        {   POP AX      }
    $5A/        {   POP DX      }
    $29/$c8/    {   SUB AX,CX   }
    $73/$03/    {   JNB @@      }
    $83/$EA/$08 {   SUB DX,8    }
);              {@@:            }

function HpAddLong( pointer:Pointer ; offset:LongInt ):Pointer;
inline(
    $59/        {   POP CX      }
    $5B/        {   POP BX      }
    $58/        {   POP AX      }
    $5A/        {   POP DX      }
    $01/$c8/    {   ADD AX,CX   }
    $83/$D3/$00/{   ADC BX,0    }
    $B1/$03/    {   MOV CL,3    }
    $D3/$E3/    {   SHL BX,CL   }
    $01/$DA     {   ADD DX,BX   }
);

function HpSubLong( pointer:Pointer ; offset:LongInt ):Pointer;
inline(
    $59/        {   POP CX      }
    $5B/        {   POP BX      }
    $58/        {   POP AX      }
    $5A/        {   POP DX      }
    $29/$c8/    {   SUB AX,CX   }
    $83/$D3/$00/{   ADC BX,0    }
    $B1/$03/    {   MOV CL,3    }
    $D3/$E3/    {   SHL BX,CL   }
    $29/$DA     {   SUB DX,BX   }
);

参照: [Delphi-ML:13257]

0006  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
New/Dispose に Pointer 型のポインタを渡すと。。。

New や Dispose は与えられたポインタの型によって、正しく初期化/解放の処理をしますが、これは、Delphi がコンパイル時にポインタの型情報を隠れた引数としてこれらの関数に与えているからです。
従って、差している先のデータがなんであれ、Dispose はポインタの型を信じ込んでデータの解放を試みます。

すると、次のコードはあっという間にメモリを使い果たします。

var pString1:pString;
    pUnknown1:pointer;
    i:Integer;
begin
    for i:=0 to 2000 do begin
        New(pString1);
        SetLength(pString1^,10000000);
        pUnknown1:=pString1;
        Dispose(pUnknown1);
    end;
end;

ただし、MemGet/MemFree と同様、ポインタの直接差すメモリブロックのサイズに関しては、Dispose が実行時に取得するため、次のコードは、メモリリークを起こしません。

type
    TTest = array [0..1000000] of Double;
    pTest = ^TTest;
var
    pTest1: pTest;
    pUnknown1: pointer;
begin
    for i:=0 to 2000 do begin
        New(pTest1);
        pUnknown1:=pTest1;
        Dispose(pUnknown1);
    end;
end;
参照: [Delphi-ML:18664] <PASCAL>

0000  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
Delphi 1.0 (16bit)で、物理メモリアドレスに直接アクセスする


procedure TForm1.Button1Click(Sender: TObject);
var
  TestSelector:  word;                  {新しいセレクタ}
  P           : ^byte;                  {メモリマップアクセス用ポインタ}
begin
  TestSelector := AllocSelector(DSeg);  {新しいセレクタを作成}
  SetSelectorBase(TestSelector, $C8000);{ベースアドレスを$C8000に設定}
  SetSelectorLimit(TestSelector, $2000);{8KB($2000)確保}
  P  := Ptr(TestSelector, $0100);       {$0100 アドレスにポインタセット}
  P^ := $00;                            {$C8000+$0100=$C8100に$00を書込}
  FreeSelector(TestSelector);           {セレクタの解放}
end;
参照: [Delphi-ML:18551] <Windows>

[新規作成] [最新の情報に更新]

How To
Lounge
KeyWords

Tips
Delphi
Home
Osamu Takeuchi osamu@big.or.jp