최근 현업에서 POS 개발을 을 진행하다 "디자이너"의 요청에 의해 만들게된 Toggle button.

 

TunaToggleButton

힘들게 만든거 치곤.. 먼가 허전한데..

 

 

'Delphi' 카테고리의 다른 글

윈도우즈에서의 cmd 실행 차이  (0) 2020.10.12
dll 내부에서의 TComport  (0) 2019.07.04
StringToOleStr memory leak  (0) 2019.01.14
IWebBrowser2 with Delphi  (0) 2018.09.28
7Zip with Delphi (펌)  (0) 2018.09.21
Posted by Revers Tuna
,

64bit환경에서의 윈도우는 cmd가 두개 존재 한다. system32와 sysWow64의 각 폴더 안에 존재 하며 각각은 64bit, 32bit로 동작을 하게 된다.

그럼 윈도우에서 cmd를 실행하면 어느 위치에 있는 cmd가 동작을 할까?

답은 어떠한 환경에서는 system32안에 있는 cmd.exe가 동작을 한다. 이는 일반적으로 윈도우에서 실행 했을때는 아무런 문제가 없다.

 

그럼 일반적으로 프로그램을 개발하는 시점에서 ShellExecute를 통해 cmd.exe를 호출하면 어떻게 될까?

프로그램을 개발하는 시점에서 윈도우의 시스템 디렉토리의 정보를 가져오는 GetSystemDirectory를 하게 되면 32bit, 64bit 환경에 상관없이 %windir%system32\를 가져오게 된다. 그럼 당연히 system32안에 있는 cmd.exe를 호출할 것 같지만 프로그램을 개발함에 있어서 딱히 Target을 정하지 않으면 32bit 프로그램을 만들게 될것이고, 그럼 system32가 아닌 sysWow64폴더 안에 있는 cmd.exe가 실행 될것이다.

이게 먼차이가 있는지... 어차피 sysWow64는 system32를 리다이렉트한것일 텐데...

 

실제 프로젝트를 진행하다 드라이버 재설치 관련하여 shell command를 실행해 보니, 두 cmd가 서로 차이가 있는걸 발견하였다.

각각의 cmd.exe를 실행후 bcdedit 명령을 실행 하였을때 system32의 cmd에서는 권한없음 또는 해당 정보가 출력되는 반면 sysWow64에서의 cmd에서는 해당 명령을 수행할 수 없다고 나왔다.

그렇다 위와 같은 상황에서는 반드시 system32안에 있는 cmd를 실행해야지만 원하는 결과를 얻을수 있다.

 

그렇다면 어떻게 64bit환경에서 32bit프로그램을 실행시 system32안에 있는 cmd를 실행할 수 있을까?

shellexecute 실행시 프로그램의 경로를 c:\windows\system32\cmd.exe로 고정을 해보자....

그리고 결과를 보면.. 원하는 cmd가 아니다.. 

경로를 고정한다 하더라도 호출의 수행은c:\windows\sysWow64\cmd.exe로 리다이렉트 될것이다.

32bit 응용프로그램은 system디렉토리에 접근을 할때 무조건 %windir%\sysWow64로 리다이렉트 되도록 되어 있다.

 

이를 해결하려면?

system32 or sysWow64로의 접근을 하지 않고 sysnative로 접근을 하면 이러한 문제는 해결이 된다.

즉, %windir%\sysNative\cmd.exe를 호출하면 원하는 결과를 얻을수 있다.

 

자 이제 windows explorer에서 sysNative를 검색해 보자

...

안나온다..보이지도 않고 접근도 안된다. 왜.....

 

sysNative

이 폴더는 64bit환경에서 보이지도 않고 접근을 할 수도 없다. 64bit환경에서 Windows Explorer는 64bit로 구동하기 때문에 아무것도 할 수 없다.

그래도 확인하고 싶다... 면 %windir%\sysWow64\cmd.exe를 통해 접근을 하면 된다.

sysNative에 대해서는 추후에 좀더 자세히 알아보도록 하겠다.

 

'Delphi' 카테고리의 다른 글

iOS를 따라한 Toggle Button  (0) 2023.05.02
dll 내부에서의 TComport  (0) 2019.07.04
StringToOleStr memory leak  (0) 2019.01.14
IWebBrowser2 with Delphi  (0) 2018.09.28
7Zip with Delphi (펌)  (0) 2018.09.21
Posted by Revers Tuna
,

Dll 안에 TComport를 넣고 동작시 OnRxChar 이벤트가 동작 하지 않는 경우가 있다.

 

TComport 생성시 기본적으로 SyncMethod가 smThreadSync로 설정되어 있는데 이를 smWindowSync 또는 smNone으로 적용시 해결을 할 수 있다.

 

smThreadSync: 내부적으로 쓰레드를 실행

smWindowSync: 윈도우 메시지 발생

smNone: 이벤트 생성

'Delphi' 카테고리의 다른 글

iOS를 따라한 Toggle Button  (0) 2023.05.02
윈도우즈에서의 cmd 실행 차이  (0) 2020.10.12
StringToOleStr memory leak  (0) 2019.01.14
IWebBrowser2 with Delphi  (0) 2018.09.28
7Zip with Delphi (펌)  (0) 2018.09.21
Posted by Revers Tuna
,

StringToOleStr memory leak

Delphi 2019. 1. 14. 18:05
  
var
 ps: PWideChar;
begin
  ps := StringToOleStr(Edit1.Text);
  SysFreeString(ps); 
end;

'Delphi' 카테고리의 다른 글

윈도우즈에서의 cmd 실행 차이  (0) 2020.10.12
dll 내부에서의 TComport  (0) 2019.07.04
IWebBrowser2 with Delphi  (0) 2018.09.28
7Zip with Delphi (펌)  (0) 2018.09.21
Call function (procedure) as "Method Name"  (0) 2018.09.10
Posted by Revers Tuna
,

IWebBrowser2 with Delphi

Delphi 2018. 9. 28. 11:12
Show Internet Explorer with IWebBrowser2
  
uses ComObj, SHDocVw;

var
  ie: IWebBrowser2;
  sUrl: string;
  Flag, TargetFrameName, PostData, Headers: OleVariant;
  pvaIn, pvaOut: OleVariant;
begin
  // Create InternetExploer
  ie := CreateOleObject('InternetExplorer.Application') as IWebBrowser2;
  ie.Left := 0;
  ie.Top := 0;
  ie.Width := 100;  // Browser form width
  ie.Height := 300; // Browser form height

  ie.MenuBar := False;
  ie.AddressBar := False;
  ie.Resizable := False;
  ie.StatusBar := False;
  ie.ToolBar := 0;

  sUrl := 'www.google.com';
  ie.Navigate2(sUrl, Flag, TargetFrameName, PostData, Headers);

  // wait Document complate
  while ie.ReadyState < READYSTATE_INTERACTIVE do
  begin
    Application.ProcessMessages;
  end;

  try
    // Zoom 
    pvaIn := 100;
    pvaOut := Null;

    ie.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, pvaIn, pvaOut);

    // Location name (No title site)
    pvaIn := 'Web Title';
    ie.ExecWB(OLECMDID_SETTITLE, 0, pvaIn, pvaOut);

    ie.Visible := true;
  except
  end;
Posted by Revers Tuna
,

7Zip with Delphi (펌)

Delphi 2018. 9. 21. 13:02
7-zip Delphi API 
This API use the 7-zip dll (7z.dll) to read and write all 7-zip supported archive formats. - Autor: Henri Gourvest - Licence: MPL1.1 - Date: 15/04/2009 - Version: 1.1  
Reading archive: 
[Extract to path:]
  
 with CreateInArchive(CLSID_CFormatZip) do
 begin
   OpenFile('c:\test.zip');
   ExtractTo('c:\test');
 end;
[Get file list:]
  
 with CreateInArchive(CLSID_CFormat7z) do
 begin
   OpenFile('c:\test.7z');
   for i := 0 to NumberOfItems - 1 do
    if not ItemIsFolder[i] then
      Writeln(ItemPath[i]);
 end;
 
[Extract to stream]
  
 with CreateInArchive(CLSID_CFormat7z) do
 begin
   OpenFile('c:\test.7z'); 
   for i := 0 to NumberOfItems - 1 do
     if not ItemIsFolder[i] then
       ExtractItem(i, stream, false);
 end;
[적용 테스트 후 수정]
  
 with CreateInArchive(CLSID_CFormat7z) do
 begin
    OpenFile('c:\test.7z'); 
    for i := 0 to NumberOfItems - 1 do
    begin
      if not ItemIsFolder[i] then
      begin     
        try
          stream := TFileStream.Create(ItemPath[i], fmCreate);
          ExtractItem(i, stream, false);
        finally
          stream.free;
        end;
      end;
    end;
 end;
[Extract "n" Items]
  
function GetStreamCallBack(sender: Pointer; index: Cardinal;
  var outStream: ISequentialOutStream): HRESULT; stdcall;
begin
  case index of ...
    outStream := T7zStream.Create(aStream, soReference);
  Result := S_OK;
end;

procedure TMainForm.ExtractClick(Sender: TObject);
var
  i: integer;
  items: array[0..2] of Cardinal;
begin
  with CreateInArchive(CLSID_CFormat7z) do
  begin
    OpenFile('c:\test.7z');
    // items must be sorted by index!
    items[0] := 0;
    items[1] := 1;
    items[2] := 2;
    ExtractItems(@items, Length(items), false, nil, GetStreamCallBack);
  end;
end;
 
[Open stream]
  
 with CreateInArchive(CLSID_CFormatZip) do
 begin
   OpenStream(T7zStream.Create(TFileStream.Create('c:\test.zip', fmOpenRead), soOwned));
   OpenStream(aStream, soReference);
   ...
 end;
[Progress bar]
  
 function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
 begin
   if total then
     Mainform.ProgressBar.Max := value else
     Mainform.ProgressBar.Position := value;
   Result := S_OK;
 end;

 procedure TMainForm.ExtractClick(Sender: TObject);
 begin
   with CreateInArchive(CLSID_CFormatZip) do
   begin
     OpenFile('c:\test.zip');
     SetProgressCallback(nil, ProgressCallback);
     ...
   end;
 end;
[Password]
  
 function PasswordCallback(sender: Pointer; var password: WideString): HRESULT; stdcall;
 begin
   // call a dialog box ...
   password := 'password';
   Result := S_OK;
 end;

 procedure TMainForm.ExtractClick(Sender: TObject);
 begin
   with CreateInArchive(CLSID_CFormatZip) do
   begin
     // using callback
     SetPasswordCallback(nil, PasswordCallback);
     // or setting password directly
     SetPassword('password');
     OpenFile('c:\test.zip');
     ...
   end;
 end;
 
[Writing archive]
  
 procedure TMainForm.ExtractAllClick(Sender: TObject);
 var
   Arch: I7zOutArchive;
 begin
   Arch := CreateOutArchive(CLSID_CFormat7z);
   // add a file
   Arch.AddFile('c:\test.bin', 'folder\test.bin');
   // add files using willcards and recursive search
   Arch.AddFiles('c:\test', 'folder', '*.pas;*.dfm', true);
   // add a stream
   Arch.AddStream(aStream, soReference, faArchive, CurrentFileTime, CurrentFileTime, 'folder\test.bin', false, false);
   // compression level
   SetCompressionLevel(Arch, 5);
   // compression method if <> LZMA
   SevenZipSetCompressionMethod(Arch, m7BZip2);
   // add a progress bar ...
   Arch.SetProgressCallback(...);
   // set a password if necessary
   Arch.SetPassword('password');
   // Save to file
   Arch.SaveToFile('c:\test.zip');
   // or a stream
   Arch.SaveToStream(aStream);
 end;


Posted by Revers Tuna
,
▶Rtti를 이용한 함수명으로 함수를 찾아서 호출
  
uses System.Rtti;

var
  FPersistentClass: TPersistentClass;
  Rtti: TRttiContext;
  RTyp: TRttiType;
  RMtd: TRttiMethod;
  Arg: array of TValue;
  RetValue: TValue;
begin
  FPersistentClass := GetClass('CLASS NAME');
  if FPersistentClass <> nil then
  begin
    Rtyp := Rtti.GetType(FPersistentClass);
    for RMtd in RTyp.GetMethods do
      if (RMtd.Parent = RTyp) and (RMtd.Name = 'METHOD NAME') then
      begin
        SetLength(Arg, 2);   // Set Parameter count
        Arg[0] := AObject;   // Parameters into Arg
        Arg[1] := jObj;

        // PROCEDURE type
        RMtd.Invoke(FPersistentClass, Arg); <-- Run Procedure
        // FUNCTION type
        RetValue := RMtd.Invoke(FPersistentClass, Arg); <-- Run function
        Break;
      end;
  end;
RetValue is function return value


Posted by Revers Tuna
,
일부 완성된 프로그램을 보완중.... DataSet의 null에 의해 문제가 발생한 경우가 있다. 상황1) Field.AsString := '1'; 일때
  
var
  i: Integer;
begin
  i := Field.AsInteger;  <-- 정상적으로 1이 rerutn된다.
end;

상황2) Field.Value := null 일때
  
var
  i: Integer;
begin
  i := Field.AsInteger; <-- 에러를 발생하게 된다.
end;
['' is not integer value ....] 애초에 이러한 문제를 피해 개발을 해야 겠지만.. 이미 개발되어 있는 경우 많은 부분을 수정, 보완 해야 한다.
  
if Field.IsNull then
  i := 0
else
  i := Field.AsInteger;
or
i := StrToIntDef(Field.AsString, 0);
등등.. 많은 방법이 있을수 있겠다..
현재 검토중인 소스를 기반으로 보았을때 null일 경우는 모두 0으로 바꿔야 한다는 전재 조건이 있어 위와 같이 수정을 하려 보니 ... ㅜㅜ
좀더 쉽게 접근하는 방법을 찾아 보자...
  
TStringField = class(Data.DB.TStringField)
protected
  function GetAsInteger: LongInt; override;
end;

function TStringField.GetAsInteger: LongInt;
begin
  Result := StrToIntDef(AsString, 0);
end;

or

function TStringField.GetAsInteger: LongInt;
begin
  if AsString = '' then
    Result := 0
else
    Result := inherited(AsInteger);
end;
위 방법을 사용해 보니 Design time에서 생성된 ClientDataSet의 경우 잘 동작 한다. 하지만... Run time에서 동적생성한 ClientDataSet은 동작하지 않았다... ㅡㅡ 그래서 찾은 방법... 프로그램의 Default로 지정된 TField를 바꿔보자..
  
TMyStringField = class(Data.DB.TStringField)
protected
  function GetAsInteger: LongInt; override;
end;

function TMyStringField.GetAsInteger: LongInt;
begin
  if AsString = '' then
    Result := 0
else
    Result := inherited(AsInteger);
end;

initialization
  RegisterClass(TMyStringField);
  DefaultFieldClasses[ftString] := TMyStringField;
finalization
  UnRegisterClass(TMyStringField);
ㅎ.... 잘된다.. 원하는 대로..

'Delphi' 카테고리의 다른 글

7Zip with Delphi (펌)  (0) 2018.09.21
Call function (procedure) as "Method Name"  (0) 2018.09.10
ApplicationEvent.OnException 한곳에서 처리  (0) 2018.09.10
FireDAC DataSet의 변경된 Record 확인  (0) 2018.09.10
GetFileInfo  (0) 2018.09.06
Posted by Revers Tuna
,
Using TApplicationEvent component event OnException
Apply try .. except
  
try
except
  on e: exception do
    Application.OnException(self, e);  // into TApplicatoinEvent.OnException event
Posted by Revers Tuna
,
TFDDataSet의 변경 이력은 ChangeLog를 통해 기록에 남게 된다.
변경된 Record만 검색 하는 방법으로는 


1. FilterChange를 이용.

  
FDDataSet.FilterChange := [rtModified, rtInserted, rtDeleted];
를 하게 되면 변경된 데이터만 남게 된다.

 단점으로는 변경된 Record는 알수 있지만 변경 순서까지는 확인이 되지 않는다. 

  (Sorting이 안됨....)


2. 두번째로는 TFDDatSUpdatesJournal를 이용하는 방법이다. 
  
var
  oJournal: TFDDatSUpdatesJournal;
  oRow: TFDDatSRow;
begin
  oJournal := FDDataSet.Delta.DataView.Manager.Updates;
  if oJournal = nil then
    Exit;

  oRow := oJournal.FirstChange;
  while oRow <> nil then
  begin
    // To Do
    oRow := oJournal.NextChange(oRow);
  end;
end; 

- Data는 GetValue, GetData를 사용

  oRow.GetData("Column Name or Column Index", TFDDatSRowVersion)

  [TFDDatSRowVersion]

  rvOriginal: 원본 (OldValue)

  rvDefault: 현재값(NewValue)

Posted by Revers Tuna
,