헬마입니다.

char tmpBuf[128] = {0,};

// 현재 시간을 초단위로 얻음

__time64_t tmCurrentTime = _time64( NULL );

// 구하려는 시간을 초단위로

__time64_t tmBetweenTime = 100000;

__time64_t tmDestTime = tmCurrentTime - tmBetweenTime;

struct tm *ptmUpTime = _localtime64( &tmDestTime );

strftime( tmpBuf, 128, "%Y-%m-%d %H:%M:%S", ptmUpTime );

댓글을 달아 주세요

헬마입니다.

스프링노트에서 보내기 해봤는데 별로 깔끔하지 않네요.
깔끔하게 보시려면 이쪽으로~~
http://jgh0721.springnote.com/pages/911438


----------------

주로 서비스에서 UI 있는 프로그램을 서비스 세션이 아닌 대화형 데스크탑이 있는 세션에서 실행시킴으로써 정상적으로 작동시키기 위한 것.

부가적으로 이 함수로 실행시키면 비스타의 UAC 를 띄우지 않고 관리자 권한으로 실행시킬 수 있다.

서비스가 SYSTEM 계정으로 실행중이라면 실행시킨 프로그램도 SYSTEM 계정을 받아서 실행됨.


코드 프로젝트의 기사의 소스코드를 기반으로 2000 ~ Vista 에서 작동하도록 수정함.

http://www.codeproject.com/KB/vista-security/VistaSessions.aspx


  1. #ifdef _UNICODE
     #define tstring wstring
    #else
     #define tstring string
    #endif

  2.  // Winlogon.exe 실행파일의 세션을 검색하여 같은 세션에서 실행시킴, 2000 ~ Vista
    BOOL LaunchAppIntoDifferentSessionAll(tstring strPath)
    {
     if( strPath.empty() )
      return FALSE;
     
     tstring strSearch;
     OSVERSIONINFO osi;
     ZeroMemory(&osi,sizeof(OSVERSIONINFO));
     osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
     GetVersionEx(&osi);
  3.  // 변수 선언
     BOOL bResult = FALSE;
     DWORD dwSessionId = 0; // 세션 ID
     DWORD dwWinlogonPID = 0; // Winlogon PID
  4.  typedef DWORD (*pfnWTSGetActiveConsoleSessionId)(void);
     HMODULE hModule = NULL;
     pfnWTSGetActiveConsoleSessionId fnWTSGetActiveConsoleSessionId = NULL;
  5.  if( osi.dwMajorVersion == 6 )
     {
      OutputDebugString(_T("Vista, 라이브러리 로딩 시도"));
      hModule = ::LoadLibrary(_T("Kernel32.dll"));
      if( hModule != NULL )
      {
       OutputDebugString(_T("Vista, 함수 로딩 시도"));
       fnWTSGetActiveConsoleSessionId = (pfnWTSGetActiveConsoleSessionId) GetProcAddress(hModule, "WTSGetActiveConsoleSessionId");
       if( fnWTSGetActiveConsoleSessionId == NULL )
        OutputDebugString(_T("WTSGetActiveConsoleSessionId 함수 로딩 실패"));
  6.    dwSessionId = fnWTSGetActiveConsoleSessionId();
      }
     }
     
     // 같은 세션으로 실행할 프로세스 찾기(Winlogon.exe)
     BOOL IsExistsWinlogon = FALSE;
     PROCESSENTRY32 pe32;
     pe32.dwSize = sizeof(PROCESSENTRY32);
     
     HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     if( hSnap == INVALID_HANDLE_VALUE )
     {
      bResult = FALSE;
      goto CleanUP;
     }
  7.  if( !Process32First(hSnap, &pe32) )
     {
      bResult = FALSE;
      goto CleanUP;
     }
  8.  if( osi.dwMajorVersion == 6 )
      strSearch = _T("winlogon.exe");
     else if( osi.dwMajorVersion == 5 )
      strSearch = _T("explorer.exe");
  9.  do
     {
      if( lstrcmpi(pe32.szExeFile, strSearch.c_str()) == 0 )
      {
       OutputDebugString(_T("WinLogon 프로세스 검색"));
       DWORD dwWLSessionID = 0;
       BOOL bRet = ProcessIdToSessionId(pe32.th32ProcessID, &dwWLSessionID);
  10.    if(  bRet && (osi.dwMajorVersion == 6) && (dwSessionId == dwWLSessionID) )
       {
        // 비스타
        OutputDebugString(_T("Vista"));
        dwWinlogonPID = pe32.th32ProcessID;
        IsExistsWinlogon = TRUE;
        break;
       }
       else if( bRet && (osi.dwMajorVersion == 5) )
       {
        // 2000 ~ XP
        OutputDebugString(_T("2000/XP"));
        dwSessionId = dwWLSessionID;
        dwWinlogonPID = pe32.th32ProcessID;
        IsExistsWinlogon = TRUE;
        break;
       }
      }
     } while ( Process32Next(hSnap, &pe32) );
  11.  if( IsExistsWinlogon == FALSE )
     {
      OutputDebugString(_T("Winlogon.exe 검색 실패"));
      bResult = FALSE;
      goto CleanUP;
     }
  12.  // WINLOGON 프로세스 열고 토큰 복사하여 특정 세션 지정
     HANDLE hProcess = ::OpenProcess(MAXIMUM_ALLOWED, FALSE, dwWinlogonPID);
     HANDLE hToken = NULL;
     HANDLE hTokenDup = NULL;
     if( hProcess == NULL )
      goto CleanUP;
     if( !::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID|TOKEN_READ|TOKEN_WRITE, &hToken))
     {
      OutputDebugString(_T("OpenProcessToken Failed"));
      bResult = FALSE;
      goto CleanUP;
     }
     LUID luid;
     if( !LookupPrivilegeValue(NULL,SE_DEBUG_NAME, &luid) )
     {
      OutputDebugString(_T("LookupPrivilegeValue Failed"));
      bResult = FALSE;
      goto CleanUP;
     }
     TOKEN_PRIVILEGES tp;
     tp.PrivilegeCount = 1;
     tp.Privileges[0].Luid = luid;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     if( !DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup) )
     {
      OutputDebugString(_T("DuplicateTokenEx Failed"));
      bResult = FALSE;
      goto CleanUP;
     }
     SetTokenInformation(hTokenDup, TokenSessionId, (LPVOID)&dwSessionId, sizeof(DWORD));
     if( !AdjustTokenPrivileges(hTokenDup, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL,NULL) )
     {
      OutputDebugString(_T("AdjustTokenPrivileges Failed"));
      bResult = FALSE;
      goto CleanUP;
     }
     if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
     {
      OutputDebugString(_T("AdjustTokenPrivileges Assign Failed"));
      bResult = FALSE;
      goto CleanUP;
     }
  13.  // CreateProcessAsUser 준비
     STARTUPINFO si;
     ZeroMemory(&si, sizeof(STARTUPINFO));
     si.cb = sizeof(STARTUPINFO);
     si.lpDesktop = _T("winsta0\\default");
     PROCESS_INFORMATION pi;
     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
     DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
     LPVOID pEnv = NULL;
     if( CreateEnvironmentBlock(&pEnv, hTokenDup, TRUE) )
      dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
     else
      pEnv = NULL;
  14.  LPTSTR lpszPath = _tcsdup(strPath.c_str());
     if( lpszPath == NULL )
     {
      OutputDebugString(_T("Path String Assign Failed"));
      bResult = FALSE;
      goto CleanUP;
     }
  15.  OutputDebugString(lpszPath);
  16.  bResult = ::CreateProcessAsUser(
        hTokenDup,
        NULL,
        lpszPath,
        NULL,
        NULL,
        FALSE,
        dwCreationFlags,
        pEnv,
        NULL,
        &si,
        &pi);
  17.  free(lpszPath);
  18.  if( !bResult )
      OutputDebugString(_T("CreateProcessAsUser Failed"));
     
     if( (bResult) && (pi.hProcess != INVALID_HANDLE_VALUE) )
      CloseHandle(pi.hProcess);
     if( (bResult) && (pi.hThread != INVALID_HANDLE_VALUE) )
      CloseHandle(pi.hThread);
  19. CleanUP:
     if( hModule )
      FreeLibrary(hModule);
     if( hSnap != INVALID_HANDLE_VALUE )
      CloseHandle(hSnap);
     if( hProcess != NULL )
      CloseHandle(hProcess);
     if( hToken )
      CloseHandle(hToken);
     if( hTokenDup )
      CloseHandle(hTokenDup);
  20.  return bResult;
    }
  21. BOOL EnablePrivilege(LPCTSTR szPrivilege)
    {
     BOOL bResult = FALSE;
     HANDLE hToken = NULL;
     TOKEN_PRIVILEGES tpOld, tpCurrent;
  22.  if( !OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken) )
      return bResult;
  23.  tpCurrent.PrivilegeCount = 1;
     tpCurrent.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  24.  if( ::LookupPrivilegeValue(NULL, szPrivilege, &tpCurrent.Privileges[0].Luid) )
     {
      DWORD dwOld = sizeof(TOKEN_PRIVILEGES);
      if( ::AdjustTokenPrivileges(hToken, FALSE, &tpCurrent, dwOld, &tpOld, &dwOld) )
       bResult = TRUE;
      else
       bResult = FALSE;
     }
     else
      bResult = FALSE;
  25.  CloseHandle(hToken);
     return bResult;
    }

이 글은 스프링노트에서 작성되었습니다.

댓글을 달아 주세요

헬마입니다.

얼마전에 The Old New Thing 블로그를 보다가 흥미가 가는 글이 하나 올라와서 옮겨봅니다.

단축아이콘을 만들떄 관리자 권한으로 실행 표시를 프로그래밍으로 해주는 방법에 대한 코드입니다.

원문 : http://blogs.msdn.com/oldnewthing/archive/2007/12/19/6801084.aspx

#include <windows.h>
#include <shlobj.h>
#include <atlbase.h>

void MarkShortcutRunAs(LPCWSTR pszShortcut)
{
 CComPtr<IPersistFile> sppf;
 if (FAILED(sppf.CoCreateInstance(CLSID_ShellLink))) return;
 if (FAILED(sppf->Load(pszShortcut, STGM_READWRITE))) return;
 CComQIPtr<IShellLinkDataList> spdl(sppf);
 if (!spdl) return;
 DWORD dwFlags;
 if (FAILED(spdl->GetFlags(&dwFlags))) return;
 dwFlags |= SLDF_RUNAS_USER;
 if (FAILED(spdl->SetFlags(dwFlags))) return;
 if (FAILED(sppf->Save(NULL, TRUE))) return;
 wprintf(L"Succeeded\n");
}

int __cdecl wmain(int argc, wchar_t *argv[])
{
 if (argc == 2 && SUCCEEDED(CoInitialize(NULL))) {
  MarkShortcutRunAs(argv[1]);
  CoUninitialize();
 }
 return 0;
}

댓글을 달아 주세요

  1. BlogIcon deneb 2007.12.28 22:41 Address Modify/Delete Reply

    오! 최근에 인스톨쉴드 안쓰고 설치할 프로그램을 만들일이 있고
    WTL을 사용하느라 여기저기 둘러보다 이 글도 참조 했었는데
    보셨다니...
    그리고 갠적으로 스크랩은 스프링노트를 쓰느라...

헬마입니다.

회사에서 ActiveX 를 몇번 작업했는데

분명 버전업이 된걸로 새로 올려서 자동 업데이트가 되어야하는데 자동 업뎃이 안되더군요.

이리저리 방법을 찾다가 자동 업데이트를 하는 방법을 찾아서 이렇게 올려봅니다.

ActiveX 자동 업데이트 하는 방법

1. 프로젝트 리소스의 VERSIONINFO 리소스의 버전 정보를 수정한다.
 
  이때 FILEVERSION 과 PRODUCTVERSION 은 일치하는 것이 좋으며 버전정보는 , 로 구분되는 숫자 4개로 이루어져야함.
  예). 1,2,3,4
 
2. CAB 파일을 만들기 위한 INF를 작성한다.
 
  이때도 INF 에 버전정보를 적는데 VERSIONINFO 에 적은 버전정보와 같은 형식으로 , 로 구분하여 적는다.

  예).
 
  [TEST.ocx]
  file-win32-x86=thiscab
  ; *** add your controls CLSID here ***
  clsid={4132944C-862D-463F-BB47-ABF99043AB1C}
  ; Add your ocx's file version here.
  FileVersion=1,0,1,3
  RegisterServer=yes
 
3. CAB 파일을 생성 후 서버에 업로드 후 ActiveX 를 참조 하는 OBJECT 태그의 버전정보를 수정한다.

  이때 역시 버전정보는 , 로 구분되는 숫자 4개여야 함.

  예)
  <OBJECT id=Service classid="CLSID:3201944C-862D-463F-BB47-ABF99043AB1C" codebase="/Include/AutoUPOCX.CAB#version=1,0,1,3" width="0" height="0" type="application/x-oleobject">
  </OBJECT>
 
  따라서, 편한 버전 업데이트를 위해서는 ActiveX 를 참조하는 OBJECT 태그를 별도의 스크립트 파일로 분리하여 Include 하는 것이 관리하는데 도움을 줄 수 있음
 
 
4. IE는 서버에 새번이 있더라도 페이지에서 구버전을 요청하면 업데이트를 시행하지 않으므로 OBJECT 태그의 버전정보를 반드시 새버전으로 변경해야한다.

  또한, 구버전이 이미 로딩된 상태로 다음 페이지에서 새버전을 요구하면 업데이트 할 때 재부팅을 요구할 수 있으므로 반드시 ActiveX 가 처음 사용되는 페이지의 버전정보를 변경한다.

댓글을 달아 주세요

  1. BlogIcon 너남아라 2007.12.13 17:08 신고 Address Modify/Delete Reply

    헉~ 이건 넘 어려워서 ~ ㅎㅎ pass합니다.. !!

  2. 감사 2007.12.19 21:53 Address Modify/Delete Reply

    헬마님 emeditor7 정식버젼이 나왔네요.
    리팩좀 부탁드립니다.

PIDL 이란?

프로그래밍/C++ 2006. 11. 1. 07:42 |
헬마입니다.

  PIDL (Pointer to Item Identifier List) 윈도 쉘을 알기위해선 결코 지나칠 수 없는 이름이다. 그러면, pidl을 공부하

기 전에 윈도 쉘을 살펴보고 pidl 이란 것이 어떻게 생겨났는지 알아보자.

  윈도는 3.1에서 95로 버전업을 하면서 쉘을 파일중심에서 문서, 객체지향적인 관점에서 바라보는 것으로 이동하

였다. <이에 대한 가장 간단한 예는 exe 같은 실행파일이 아닌 txt 등의 파일을 탐색기에서 더블 클릭해도 뭔가 프

로그램이 실행된다는 것이다. 도스에서는 절대 있을 수 없는 일이다> 하지만, 파일을 벗어나 객체를 중심으로 쉘을
조직화하고 바탕화면 같은 새로운 객체들 <그러면서 시스템에 실제 파일이나 디렉토리로 존재하지 않는> 이 생겨

나면서 문제가 생겼다. 각 객체를 이전의 파일처럼 유일성을 보장하여 서로 구분할 새로운 방법이 필요해진 것이

다. < 파일은 예로 C:\test.txt 라는 경로명 + 파일이름으로 유일성을 보장할 수 있다. 동일한 경로에 동일한 파일

은 하나밖에 없다 > 파일시스템처럼 경로명을 사용하면 된다고 생각하겠지만 이 역시 난관이 많다. 탐색기를 열어

좌측의 폴더트리를 한번 보자. 실제로 디렉토리가 아님에도 나타나는 폴더들이 많다 < 당장 제일 상위 노드인 바탕

화면 부터 실제로 존재하지 않는다 > 이런 것들은 실제로 시스템에 존재하는 것이 아니라 가상으로 구현되어 쉘에

의해 관리되고 파일이나 디렉토리처럼 또 하나의 문서로 취급되는 것이다. 결국 윈도는 이러한 객체들과 이전의 파

    typedef struct _ITEMIDLIST
   {
       SHITEMID mkid;
   } ITEMIDLIST *LPITEMIDLIST;

음, 별로 도움이 되지 않는다. SHITEMID 라는 구조체를 다시 살펴보자.

    typedef struct _SHITEMID
    {
         UINT cbSize; // 구조체 크기, 자신 포함
         BYTE abID[1]; // 실제적인 데이터
    } SHITEMID

  이제 뭔가 조금 알 것 같다. 선언에는 데이터가 1바이트짜리 배열이지만 실제로 이렇게 1바이트만 덜렁 쓰는일은

없을것이다. 뭔가 알듯하지만 여전히 PIDL이 뭔지 감조차 오지 않는다. 그러면 더욱 쉽게 PIDL 과 디렉토리 경로명

의 구조를 비교하면서 감을 잡아보자.

  C : \ example \ test \ temp.ext

라는 파일이 있다고 하자 이는 세개의 경로명과 하나의 파일명으로 이루어져 있다. 이와 같은 것을 PIDL 로 구성하면 구조가 아래와 같이 나온다.



일, 디렉토리들을 통합하여 하나의 방법으로 완전히 구별할 수 있는 방법이 필요해졌고 이에 대한 해결책이 PIDL

이다. 윈도 쉘은 기본적으로 PIDL 로 파일, 폴더, 가상 폴더등의 객체를 인식하고 관리한다.


PIDL은 ITEMIDLIST 라는 구조체에 대한 포인터이다. 그러면 이 구조체를 살펴보자

  PIDL
   |
  cbSize   abID                cbSize    abID                  cbSize    abID               cbSize   abID
     4          C:                    9         example                 6         test                   10      temp.ext

같은 모습이 된다. PIDL은 이러한 연속적인 메모리 구성의 제일 처음을 가리키는 포인터이다. 따라서, 다음 구성요소를 구하려면 PIDL 에서 크기만큼 바이트 이동하면 된다. 현재 PIDL 은 C: 구성요소를 가리키고 있고 다음 구성요소인 example 을 얻고 싶다면 아래와 같이 바이트이동해서 얻으면 된다.
 
  LPITEMIDLIST((LPBYTE)pidl + 4)

  위와 같이 주소 연산을 하면 PIDL은 두번쨰 example을 가리키게 된다. 또한 이러한 연결은 cbSize 가 0 인 곳에

서 끝이 난다. 즉 연결의 마지막은 cbSize 가 0인가를 검사하면 된다. 이러한 PIDL 이란 구조를 이용하여 쉘은 모

든 파일, 폴더, 객체를 구분하고 있다. 앞으로는 이러한 PIDL 을 이용하는 예를 알아보자.

<Visual C++ Shell Programming > 참조.

댓글을 달아 주세요

  1. BlogIcon 금메달.아빠 2010.09.17 01:20 신고 Address Modify/Delete Reply

    좋은 정보 감사합니다. 잘 읽고 엮인글(트랙백)을 추가하였습니다.

    행복한 하루 되세요.