윈도우즈 서비스 프로그램을 작성할 때
두가지 형태의 파라미터를 전달 받을 수 있습니다.

1. 서비스파일.exe  -install -u:aaa -p:pass 기타등등
2. 서비스 관리자 -> 시작 매개변수

이렇게 두가지로 전달 받을 수 있는데요..
1번과 같은 경우는 일반적인 콘솔 프로그램과 다를바가 없지요.
즉, main 의 argc, argv 파라미터를 바로 처리하면 됩니다.

2번도 사실 1번과 거의 동일합니다.
다만 파라미터를 검출하는 위치만 다를뿐 100% 같은 방식을 사용합니다.
차이점이라면 StartServiceCtrlDispatcher 에서 등록한 서비스 메인 함수에서
파라미터를 처리한다는 차이점이 있습니다. ^^;

상당히 많은 시간을 날렸네요.. ㅜㅜ

이전 내용하고 비슷한데요..
이전에 서비스계정에 이름은 있고, 암호는 없는 사용자를 처리할 때는

HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Lsa
의 limitblankpassworduse 값을 1로 변경하면서 마무리 지었습니다.
윈도우즈 XP프로와, 서버계열에서는 아주 잘되던데..

윈도우즈 XP 홈에서는 저기까지는 잘되는데 나머지 난관이 발생하더군요..
사용자 계정 자체에 서비스로 로그온할 수 있는 보안 권한을 상승 시켜주어야 했습니다.

계정과 암호 (비어있을 경우) 정상적인데도 불구하고 서비스가 시작되지 않더군요.
서비스 관리자를 열어서 다시 서비스의 로그온 정보에서 암호를 지워주고 시작을
누르면 계정을 "서비스로 로그온 권한을 상승하였습니다." 라는 메시지가 나오면서
시작이 잘되더군요.

문제는 이걸 프로그램적으로 자동화 시켜야한다는게 문제였는데.. ^^;


#include <Ntsecapi.h>
#define TARGET_SYSTEM_NAME L"."
LSA_HANDLE GetPolicyHandle()
{
 LSA_OBJECT_ATTRIBUTES ObjectAttributes;
 WCHAR SystemName[] = TARGET_SYSTEM_NAME;
 USHORT SystemNameLength;
 LSA_UNICODE_STRING lusSystemName;
 NTSTATUS ntsResult;
 LSA_HANDLE lsahPolicyHandle;
 
 // Object attributes are reserved, so initialize to zeros.
 ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
 
 //Initialize an LSA_UNICODE_STRING to the server name.
 SystemNameLength = wcslen(SystemName);
 lusSystemName.Buffer = SystemName;
 lusSystemName.Length = SystemNameLength * sizeof(WCHAR);
 lusSystemName.MaximumLength = (SystemNameLength+1) * sizeof(WCHAR);
 
 // Get a handle to the Policy object.
 ntsResult = LsaOpenPolicy(
        &lusSystemName,    //Name of the target system.
        &ObjectAttributes, //Object attributes.
        POLICY_ALL_ACCESS, //Desired access permissions.
        &lsahPolicyHandle  //Receives the policy handle.
  );
 
 if (ntsResult != ERROR_SUCCESS)
  return NULL;
 return lsahPolicyHandle;
}

BOOL InitLsaString(PLSA_UNICODE_STRING pLsaString, LPCWSTR pwszString)
{
  DWORD dwLen = 0;

  if (NULL == pLsaString)
      return FALSE;

  if (NULL != pwszString)
  {
      dwLen = wcslen(pwszString);
      if (dwLen > 0x7ffe)   // String is too large
          return FALSE;
  }

  // Store the string.
  pLsaString->Buffer = (WCHAR *)pwszString;
  pLsaString->Length =  (USHORT)dwLen * sizeof(WCHAR);
  pLsaString->MaximumLength= (USHORT)(dwLen+1) * sizeof(WCHAR);

  return TRUE;
}

BOOL GetUserSID(PSID sid, const char* username)
{
    DWORD SidBufSz;
    char DomainNameBuf[256] = {0};
    DWORD DomainNameBufSz;
    SID_NAME_USE SNU;
    DomainNameBufSz = 256;

    if(!LookupAccountName(NULL, username, sid, &SidBufSz, DomainNameBuf, &DomainNameBufSz, &SNU))
        return FALSE;
 return TRUE;
}

BOOL AddPrivileges(PSID AccountSID, LSA_HANDLE PolicyHandle)
{
 LSA_UNICODE_STRING lucPrivilege;
 NTSTATUS ntsResult;
 
 // Create an LSA_UNICODE_STRING for the privilege names.
 if (!InitLsaString(&lucPrivilege, L"SeServiceLogonRight"))
  return FALSE;
 
 ntsResult = LsaAddAccountRights(
  PolicyHandle,  // An open policy handle.
  AccountSID,    // The target SID.
  &lucPrivilege, // The privileges.
  1              // Number of privileges.
  );               

 if (ntsResult == ERROR_SUCCESS)
  return TRUE;
 else
  return FALSE;
}

사용법은 아래와 같습니다.
 LSA_HANDLE Policy = GetPolicyHandle();
 BYTE buffer[1024] = {0};
 PSID sid = (PSID)buffer;
 if(GetUserSID(sid, "까마귀"))
  AddPrivileges(sid, Policy);

위와 같은 방법으로 사용자 계정에다가 직접 SeServiceLogonRight 권한을 넣어
주는 방법으로 해결했습니다.

자료가 없어서 헤딩좀 했는데.. 도움이 되시기를...

이것 때문에 헤맨 시간을 생각하면.. ㅇㅎㅎ ㅠㅠ~~

윈도우즈 서비스로 제작한 프로그램을 서비스에 등록할 경우..
로컬시스템 계정이나 암호를 가진 유저는 잘 등록이 되고
동작도 잘됩니다.

하지만 빈문자열로 암호를 가지지 않은 계정은 서비스가 로그온에 실패하여
서비스가 정상적으로 수행되지 못합니다.

이걸 프로그램적으로 해결하기 위해 백방으로 찾아봤지만..
해결을 못했었는데 이렇게 허무할 수가 ㅠㅠ;

http://support.microsoft.com/kb/303846/ko


오류 메시지: 계정 제한 때문에 로그온할 수 없습니다

기술 자료 ID : 303846
마지막 검토 : 2001년 11월 21일 수요일
수정 : 1.0
이 문서는 이전에 다음 ID로 출판되었음: KR303846

현상

원격 데스크톱 도구를 사용하여 Windows XP 기반 컴퓨터에 연결하려고 하면 아래의 오류 메시지가 나타날 수도 있습니다.
계정 제한 때문에 로그온할 수 없습니다.

위로 가기

원인

연결하는 데 사용한 계정에 암호가 없으면 이러한 문제가 발생할 수 있습니다. 암호가 없는 계정을 사용할 때는 원격 데스크톱 연결을 설정할 수 없습니다.

위로 가기

해결 방법

원격 데스크톱 연결을 설정할 수 있도록 이 문제를 해결하려면 해당 컴퓨터의 콘솔에서 로그온한 다음 해당 사용자 계정에 대해 암호를 설정하십시오.

위로 가기

현재 상태

이것은 의도적으로 설계된 동작입니다.

위로 가기

추가 정보

정책을 사용하여 빈 암호 제한을 해제할 수 있습니다. 이 정책을 찾아서 변경하려면 다음과 같이 하십시오.
1. 시작을 누르고, 실행을 누르고, gpedit.msc를 입력한 다음 확인을 눌러 그룹 정책 편집기를 시작합니다.
2. 컴퓨터 구성\Windows 설정\보안 설정\로컬 정책\보안 옵션\계정: 콘솔 로그온 시 로컬 계정에서 빈 암호 사용 제한을 엽니다.
3. 콘솔 로그온 시 로컬 계정에서 빈 암호 사용 제한을 두 번 누릅니다.
4. 사용 안함을 누른 다음 확인을 누릅니다.
5. 그룹 정책 편집기를 종료합니다.
참고: 기본적으로 이 정책은 사용함으로 설정되어 있습니다.

작업하다보니 단축아이콘도 만들어야 겠네.. -_-;;
마우스로 드래그만 하면 되는데 플그램으로 짜려면 왤케 이것 저것 해야하는게 많은건지..
MSDN에서 권장하는 IShellLink 인터페이스를 이용하여 제작합니다.

클래스로 만들 이유는 없는데, 이것 저것 추가될깨 있어어 그냥 껍데기를 씌웠습니다.
참고로 일반적인 헤더 외에, 아래꺼 추가되야 할겁니다.
#include <atlbase.h>
#include <comdef.h>

VC++ 6.0 SP6, 및 Platfrom SDK 2004년 버전에서 테스트됨.

헤더파일
-----------------------------------------------------------------------------------
class CFileShell 
{
public:
 CFileShell();
 virtual ~CFileShell();

    // 지정한 파일로 주어진 특수 경로에 단축아이콘을 만든다.
    BOOL CreateShortCut(const char* filepath, int nFolder, const char* dispname = NULL, const char* description = NULL, const char* workdir = NULL);

    // 특수한 경로에 주어진 이름의 폴더를 만든다.
    CString CreateSpecialForder(int nFolder, const char* forder_name);
};

1. CreateShortCut
   filepath         전체경로를 가진 파일 이름
   nFolder         CSIDL Costants 를 지칭합니다. 참고 SHGetSpecialFolderPath
   dispname      단축아이콘의 이름, 설정하기 않으면 원본 파일이름으로 대체
   description   단축아이콘 설명
   workdir          아이콘이 실행될 경로, 설정하지 않으면 원본 파일의 경로로 대체

2. CreateSpecialForder
   nFolder          CSIDL Costants 를 지칭합니다. 참고 SHGetSpecialFolderPath  
   forder_name  생성할 폴더의 이름



소스 파일
-----------------------------------------------------------------------------------
BOOL CFileShell::CreateShortCut(const char* filepath, int nFolder, const char* dispname, const char* description, const char* workdir)
{
    char TargetPath[MAX_PATH];
    CString fromPath=filepath, fromName, fromExt, ShortCutName;

    if(!SHGetSpecialFolderPath(NULL, TargetPath, nFolder, FALSE))
        return FALSE;

    HRESULT hr;
     CComPtr<IShellLink>   pISL;

    hr = pISL.CoCreateInstance( CLSID_ShellLink );
     if(FAILED(hr))
        return FALSE;

    // 단축 아이콘의 대상이 되는 파일을 설정한다.
    hr = pISL->SetPath ( filepath );
     if(FAILED(hr))
        return FALSE;

    // 주어진 소스 파일의 경로를 이용하여, 전체 패스, 이름, 확장자를 나눈다.
    int find = fromPath.ReverseFind('\\');
    if(find == -1)
        return FALSE;

    fromName = fromPath.Right(fromPath.GetLength()-find-1);
    fromPath = fromPath.Left(find+1);

    find = fromName.ReverseFind('.');
    if(find != -1)
    {
        fromExt  = fromName.Right(fromName.GetLength()-find-1);
        fromName = fromName.Left(find);
    }

    // 별도로 디스플에이용 이름을 주면 그것을 사용한다.
    if(dispname)
        fromName = dispname;

    // 별도로 수행 경로를 주면 그것을 이용한다.
    if(workdir)
        fromPath = workdir;


    // 단축 아이콘에 대한 설명을 단다.
    if(description)
    {
        hr = pISL->SetDescription ( description );
        if(FAILED(hr))
            return FALSE;
    }
   
    // 워킹 폴더를 설정한다.
    hr = pISL->SetWorkingDirectory ( fromPath );
    if(FAILED(hr))
        return FALSE;

   
    WCHAR ShortCutNameW[256] = {0};
    ShortCutName = fromPath + fromName + ".lnk";

    MultiByteToWideChar(CP_ACP, MB_COMPOSITE, ShortCutName, ShortCutName.GetLength(), ShortCutNameW, 256*sizeof(WCHAR));

    CComQIPtr<IPersistFile> pIPF( pISL );
     hr = pIPF->Save ( ShortCutNameW, FALSE );
     if ( FAILED(hr) )
        return FALSE;

    return TRUE;
}

CString CFileShell::CreateSpecialForder(int nFolder, const char* forder_name)
{
    char TargetPath[MAX_PATH];
    if(!SHGetSpecialFolderPath(NULL, TargetPath, nFolder, FALSE))
        return "";

    CString FullName = TargetPath;
    FullName += "\\";
    FullName += forder_name;

    if(!CreateDirectory(FullName, NULL))
    {
        DWORD le = GetLastError();
        if(le != ERROR_ALREADY_EXISTS)
            return "";
    }

    return FullName;
}

업무에 필요해서 파일의 버전을 읽어 오려다 보니.. MSDN에서 다음과 같은 함수를 찾을 수 있었다.
GetFileVersionInfoSize
GetFileVersionInfo
VerQueryValue
이 세가지 함수를 조합하여, 실행파일(dll. exe, etc..)에 포함된 표준 버전 정보를 읽을 수 있다.



그런데 하나씩 코드를 짜다보니, 분명 누군가 만들어 놓은게 있을 꼬야.. 하고 찾아보니
1998년 4월 MSJ에 이미 잘 만들어진 코드가 올라와 있더라 ㅎㅎ;
http://www.microsoft.com/msj/0498/c0498.aspx

설명도 잘 나왔고, 샘플까지.. ㅎㅎ;

원문 소스 중에.. 다음 함수의 일부 구문이 변경되었다.
[당근 원문은 보존하고..]
BOOL CModuleVersion::GetFileVersionInfo(LPCTSTR modulename)
원래는 시스템 이미지에 매핑된 실행파일만 읽도록 되어있는 것을..
일반 파일도 읽을 수 있도록 하였다.


 FTP에서의 파일 업로드   |  1999-12-27
권진호 (crowback )  권진호님께 메시지 보내기권진호님을 내 주소록에 추가합니다.권진호님의 개인게시판 가기 번호: 621   / 평점:  (9.0)  / 읽음:4,512

까막입니다. ^^

어떤 분이 FTP에서 파일 업로드 기능을 부탁하셨길래 올립니다.

아주 간단하게 FTP 접속 부분과
파일 전달 부분만 구현 한 것입니다.

-------------------------------------------------------------------
CFtpConnection* ftpCon = NULL;
CInternetSession* session;
BeginWaitCursor();
session = new CInternetSession(_T("My Session"));
try
{
 ftpCon = session->GetFtpConnection(IP, ID, Pass);
 // 여기서 IP, ID, Pass는 다들 아시겠죠 ^^;
}
catch(CInternetException* e)
{
 EndWaitCursor();
 MessageBeep(0xFFFFFFFF);
 e->ReportError();
 ftpCon = NULL;
 session->Close();
 delete session ;
 e->Delete();
 return FALSE;
}

EndWaitCursor();
// 현재 연결된 계정의 루트 패스를 구하는 부분입니다.
CString OriginFTPDir;
ftpCon->GetCurrentDirectory(OriginFTPDir);
CString Server = OriginFTPDi+name;  // 서버에서 받을 패스와 파일 이름
CString Client = cszFolderPath+name;// 보낼 파일의 패스와 파일 이름
try
{
 ftpCon->PutFile(Client, Server);
}
catch(CInternetException* e)
{
 EndWaitCursor();
 MessageBeep(0xFFFFFFFF);
 e->ReportError();
 e->Delete();
}
EndWaitCursor();

 FlashWindow()를 이용한 아이콘 껌뻑이기   |  2000-04-21
권진호 (crowback )  권진호님께 메시지 보내기권진호님을 내 주소록에 추가합니다.권진호님의 개인게시판 가기 번호: 938   / 평점:  (-)  / 읽음:851
까막임니다...

어떤 프로그램이 셑업이나 파일 다운로드가 끝나면...
밑에 작업 윈도우(트레이 윈도운가?)에 프로그램 이름들어간 아이콘들이 쫙 떠있자나여?

근데.. 어떤걸 보면.. 이게 무신 메시지를 보내느라 그러는지.. 파란색으로 껌뻑 껌뻑 하자나여?

전 이놈이 왜 껌뻑 거릴까? 생각해 봤더니.. 무신 작업이 끝났다는걸 사용자에게 알리기 위해서 그러는 거래여...

그래서 그걸 구현해 봤어여... 정말 시버여..

여러분들도 이제 넣어봐여.. 금방 넣을 수 있을겁니다..

미니마이즈 버튼을 눌러서 아이콘화 시키면 껌뻑 거려여... ^^

 [심플] 간단한 음성채팅 풀소스  | WinSock 2000-08-21 오전 7:31:28
권진호 (crowback )  권진호님께 메시지 보내기권진호님을 내 주소록에 추가합니다.권진호님의 개인게시판 가기 번호: 2294   / 평점:  (-)  / 읽음:5,408
까막입니다..

열심히 설명를 달았었는데.. 업로드 하니까..헤그덕 --;;; 날라가버리는 군여..

환경만 간단히 다시 설명을 달겠습니다.

프로토타입(일단 동작을 중시)으로 만들었던것이라 코덱은 PCM을 그대로 씁니다.
다이렉트 엑스를 이용한 관계로 NT에선 동작이 않됩니다.
(그때 왜 다이렉트X를 썼는지 지금은이해가 않됨 --;;;)

코덱을 변경하실라믄.. 녹음하는 부분에서 코덱 정보를 읽어 오는 부분을 쩜
수정하셔야 됩니다.

참고로, GSM 13Kbps는 윈98이상에서 지원되구여..
G.723.1은 5.6kbps & 6.3kbps는 윈2000이상에서 그냥 지원해줍니다.

맘에 드시는걸로 골라쓰세여..

[요청자료] Frame(SDI' MDI) 내부에 머무르는 모달리스 다이알로그  | VC++ 일반 2000-11-10 오후 5:13:31
권진호 (crowback )  권진호님께 메시지 보내기권진호님을 내 주소록에 추가합니다.권진호님의 개인게시판 가기 번호: 3171   / 평점:  (-)  / 읽음:2,861
까막임돠...

SDI나 MDI로 프로그램을 제작할 경우..
모달리스 다이알로그가 프레임 밖으로 벗어 나는 경우에 대하여
이를 막을 수 있는 방법에 대한 질문이 여러번 중복되어 들어 와서 자료로
올립니다.

실제 모달리스 다이알로그의 행동반경이 MDI에서의 차일드 프레임처럼
Frame내부의 뷰 안에 머물도록 하였습니다.

그럼 도움이 되었으면 합니다.

 [답변][까막] 썸네일 1차 완성판이 나왔습니다.. *^^* 2001-02-12 오후 12:41:58
권진호 (crowback )  권진호님께 메시지 보내기권진호님을 내 주소록에 추가합니다.권진호님의 개인게시판 가기 번호: 4418   / 평점:  (-)  
안냐세염.. 까막임돠..

1차 완성판이 나왔습니다. 지적해주신 문제도 조금 수정하구여..

탐색트리, 썸네일, 이미지뷰어 모두 일단 올렸구여..
특별한 동작상의 문제는 없습니다.

속도 문제를 위하여. 쓰레드나 메시지 펌핑은 하지 않았습니다.

이미지 한 100개정도를 읽어 들이는데 걸리는 시간은 시스템에 따라 다르겠지만
ACDSee보다는 쫌 빠르군여.. --;
(당근 지원하는 포멧이 간단한거만 하니까 -_-;)

추가된 기능은 이미지뷰어를 넣었다는 것이구여..
(역시 무지 심플안 이미지뷰어...)

썸네일에 대한 파일정보 툴팁을 넣어봤습니다.

그럼 이만..


아래 요청 사항이 있으셔서, 6.0과 2008로 컴파일 되도록 급수정 하였습니다.
전체 소스는 달라진건 없고, 6.0에만 있는 기능이 있어서, 공용으로 쓰이도록 수정하였습니다.

+ Recent posts