batchmode에서 안됨

    public static void SetCloudProjectId(string id, string name, string company)
    {
        var type = Type.GetType("UnityEditor.Connect.UnityConnect, UnityEditor, Version = 0.0.0.0, Culture = neutral, PublicKeyToken = null");
        PropertyInfo instanceInfo = type.GetProperty("instance");
        object settingsInstance = instanceInfo.GetValue(null, null);

        MethodInfo unbind = type.GetMethod("UnbindProject");
        MethodInfo bind = type.GetMethod("BindProject");

        Debug.Log("Unbinding current cloud project");
        unbind.Invoke(settingsInstance, new System.Object[] { });

        Debug.LogFormat("Binding cloud project to {0}: {1} ({2})", name, id, company);
        bind.Invoke(settingsInstance, new System.Object[] { id, name, company });

        Debug.LogFormat("Binded cloud project to {0}: {1} ({2})", name, id, company);
    }

실행후에 유니티 재시작필요.

Jenkins에서 호출하는경우 빌드전에 따로 실행

유니티 Bind를 사용하는 경우 batch mode에서 제대로 안되서 파일 직접변경이 필요.

    public static void SetCloudProjectIdFile(string name, string id, string company)
    {
        const string filepath = "ProjectSettings/ProjectSettings.asset";
        string[] lines = File.ReadAllLines(filepath);
        var new_lines = new List<string>();
        id = " " + id;
        name = " " + name;
        company = " " + company;

        foreach (string line in lines)
        {
            string[] command = line.Split(':');
            if (command.Length < 2)
            {
                new_lines.Add(line);
                continue;
            }

            switch (command[0])
            {
                case "  cloudProjectId":
                    if (command[1] != id)
                    {
                        Debug.Log($"Replace {command[0]}");
                        new_lines.Add(string.Join(":", command[0], id));
                    }
                    break;

                case "  projectName":
                    if (command[1] != name)
                    {
                        Debug.Log($"Replace {command[0]}");
                        new_lines.Add(string.Join(":", command[0], name));
                    }
                    break;

                case "  organizationId":
                    if (command[1] != company)
                    {
                        Debug.Log($"Replace {command[0]}");
                        new_lines.Add(string.Join(":", command[0], company));
                    }
                    break;

                default:
                    new_lines.Add(line);
                    break;
            }
        }

        File.WriteAllLines(filepath, new_lines.ToArray());
        AssetDatabase.Refresh();
    }

rm -f play.prop
touch play.prop

echo GIT_COMMIT_SHORT=$(git rev-parse --short ${GIT_COMMIT}) > play.prop
echo GIT_REVISION=$(git rev-list --count ${GIT_COMMIT}) >> play.prop

DECLARE @str varchar(8000)

SET @str = '10,2,3,4,5,6,7,8,9'


DECLARE @InputString varchar(8000)

SELECT @InputString = ',' + @str + ','


;with qry(n, names) as

       (select len(list.names) - len(replace(list.names, ',', '')) - 1 as n, substring(list.names, 2, len(list.names)) as names

        from (select @InputString names) as list

        union all

        select (n - 1) as n,

               substring(names, 1 + charindex(',', names), len(names)) as names

        from qry

        where n > 1)

 select substring(names, 1, charindex(',', names) - 1) dwarf

 from qry;





DECLARE @str varchar(8000)

SET @str = '10,2,3,4,5,6,7,8,9'


DECLARE @InputString varchar(8000)

SELECT @InputString = @str + ','


;WITH RecursiveCSV(x,y) 

AS 

(

    SELECT 

        x = SUBSTRING(@InputString,0,CHARINDEX(',',@InputString,0)),

        y = SUBSTRING(@InputString,CHARINDEX(',',@InputString,0)+1,LEN(@InputString))

    UNION ALL

    SELECT 

        x = SUBSTRING(y,0,CHARINDEX(',',y,0)),

        y = SUBSTRING(y,CHARINDEX(',',y,0)+1,LEN(y))

    FROM 

        RecursiveCSV 

    WHERE

        SUBSTRING(y,CHARINDEX(',',y,0)+1,LEN(y)) <> '' OR 

        SUBSTRING(y,0,CHARINDEX(',',y,0)) <> ''

)

(select x FROM RecursiveCSV)

OPTION (MAXRECURSION 32767);

Shift-JIS 코드표 : http://www.rikai.com/library/kanjitables/kanji_codes.sjis.shtml
상용한자표 : 
http://ja.wikipedia.org/wiki/%E5%B8%B8%E7%94%A8%E6%BC%A2%E5%AD%97%E4%B8%80%E8%A6%A7

자주 까먹어서 포스팅

void CopyClipboard( IN const tstring strTemp )
{
 if( ::OpenClipboard( NULL ) )
 {
  ::EmptyClipboard();

  HGLOBAL hBlock = ::GlobalAlloc( GMEM_MOVEABLE, sizeof(TCHAR) * ( strTemp.size() + 1 ) );
  if( hBlock )
  {
   TCHAR *pwszText = (TCHAR*)::GlobalLock( hBlock );
   if( pwszText )
   {
    _tcscpy( pwszText, strTemp.c_str() );
    ::GlobalUnlock( hBlock );
   }
   ::SetClipboardData( CF_UNICODETEXT, hBlock );
  }
  ::CloseClipboard();
  // We must not free the object until CloseClipboard is called.
  if( hBlock )
   ::GlobalFree( hBlock );
 }
}

OUT const tstring GetClipboard()
{
 if( ::OpenClipboard( NULL ) )
 {
  if( ::IsClipboardFormatAvailable( CF_UNICODETEXT ) == TRUE )
  {
   HANDLE hMem;
   hMem = ::GetClipboardData( CF_UNICODETEXT );
   const tstring reStr = (LPCTSTR)GlobalLock((HGLOBAL)hMem );
   GlobalUnlock( (HGLOBAL)hMem );

   return reStr;
  }
  ::CloseClipboard();
 }

 return tstring();
}

  1. 으핳 2008.03.23 17:08

    좋은 정보 감사합니다 ^^

거북이만 된다.
속성에 tsvn:logminsize 를 넣어준다.
속성이라는게 있다는걸 왜 인제 안 거지-ㅅ-ㅋ
  1. Favicon of http://writely.tistory.com BlogIcon hey 2007.01.05 10:05

    서브버전 후킹 스크립트 중에 해당 내용도 들어있어요 ^^

    • Favicon of https://www.larosel.com BlogIcon 쑥갓 2007.01.08 10:21 신고

      후킹 스크립트도 공부좀 해야 할것 같네요^^

그전까지 synchronized block구현을

#define __SAUTOLOCK_SCORPED(cs) cs.Lock(); for( int __synchronized_count = 0; __synchronized_count < 1; __synchronized_count++, cs.Unlock() )

이런식으로 사용하고 있었다.

안에서 break를 걸면 문제가 생기지만 그렇게 쓸일은 없고-ㅅ-;
익셉션 처리는 안하므로 별상관없고...라고 생각하고 있었는데
for문 안에서 사용하는 경우에 for문을 종료할려고 break를 한다던가 continue를 해보리면 먹어버린다는 엄청난 버그가-_-;
게다가 요새는 VERIFY_RETURN식으로 쓰는게 많아서 불안한 부분도 꽤 있다.

이에 대해 gpg에 여러 글들이 올라오는 와중에 획기적인 해결방법이 나왔다.

synchronized block in C++
http://ricanet.com/new/view.php?id=blog/050807

improved synchronized block in C++
http://ricanet.com/new/view.php?id=blog/050811a

코드도 상당히 깔끔하며 여러가지 문제점을 쉽게 해결을 해주는 솔루션이다...
역시 세상은 넓다

_BitScanReverse, _BitScanReverse64

#pragma intrinsic(_BitScanReverse)
를 꼭 같이 써주도록 하쟈

원본글 : http://www.devpia.com/forum/BoardView.aspx?no=7286&ref=7286&page=1&forumname=vc_lec&stype=

Windows XP에서 Fast User Switching을 지원하는 응용 프로그램 만들기

1. 개요


Windows XP부터는 Fast User Switching 기능이 도입되었습니다. 여기서는, 응용 프로그램 작성시, Fast User Switching이 일어날 때 개발자가 이 기능을 바르게 지원하기 위해서 필요한 것들에 대해서 알아보도록 하겠습니다.

2. Fast User Switching 지원을 위해서 응용 프로그램에서 해야 할 일 - 1

당연한 이야기이지만, Fast User Switching의 지원을 하기 위해서는, 우선 WindowsXP의 세션 상태의 변화를 응용 프로그램에서 감지 할 수 있어야 합니다.

WindowsXP는 세션의 상태가 변할 때 마다, WM_WTSSESSION_CHANGE 메세지를 특정한 윈도우들에게 보내줍니다. 이 메세지를 받기 위해서는 WTSRegisterSessionNoticiation 함수를 호출해야 합니다. 

컴파일시 대상 운영체제를 WindowsXP로 설정합니다.
#define _WIN32_WINNT 0x0501

#include <wtsapi32.h>
#pragma comment( lib , "Wtsapi32.lib")

윈도우를 생성한 다음 아래의 함수를 호출합니다.
WTSRegisterSessionNotification( hWnd , NOTIFY_FOR_THIS_SESSION );


이렇게 해서, 등록에 성공하면 WindowsXP는 WM_WTSSESSION_CHANGE 메세지를 보내 주면서, WPARAM의 값으로 세션의 변화를 알려 줍니다.

case WM_WTSSESSION_CHANGE:
switch( wParam )
{
case WTS_CONSOLE_CONNECT:
  ...
  break;
case WTS_CONSOLE_DISCONNECT:
  ...
  break;
case WTS_SESSION_LOCK:
    ...
  break;
case WTS_SESSION_UNLOCK:
    ...
  break;
default:
break;
}
break;



사용자 전환이 발생할 경우, WTS_SESSION_LOCK, WTS_CONSOLE_DISCONNECT, WTS_SESSION_UNLOCK, WTS_CONSOLE_CONNECT의 메세지가 차례로 발생합니다.

이렇게 해서, 세션의 변화 상태를 관찰할 수 있으면, 개발자는 그에 맞게 응용 프로그램의 행동을 결정해 주는 것이 필요합니다.

예를 들어서, 오디오 장치의 경우, 끓어진 세션에 있는 오디어 플레이어가 음악을 재생하고 있고, 새롭게 로그인 한 사용자가 이 오디오 장치를 이용하여, 넷미팅 같은 소프트웨어를 이용해서 원격 회의를 하려고 하는 경우, 원격 회의가 제대로 진행되기 어려울 수도 있습니다.

극단적인 예를 들었지만, 개발자가 세션이 변함에 따라서, 응용 프로그램의 행동을 미리 정의해 놓는 것은 반드시 필요합니다.

3. Fast User Switching 시 응용 프로그램에서 해야 할 일 -2

이러한 Fast User Switching을 잘 지원하기 위해서, 개발자는 아래의 것들을 응용 프로그램 개발시 고려해야 합니다.

인스턴스 관리

개발자가 응용 프로그램의 인스턴스가 단 하나만 동작하기를 원하는 경우, 아래의 경우를 고려해야 합니다.

FindWindow 혹은 FindWindowEx를 이용해서 응용 프로그램의 인스턴스를 찾는 경우, 다른 세션에 있는 이 함수들이 다른 세션에 있는 응용 프로그램의 인스턴스까지 찾을 수 없습니다.

뮤텍스 혹은 세마포어 같은 커널 오브젝트를 이용하여 인스턴스를 생성을 확인하는 경우, 이 객체들의 이름 앞에 '\Global\'이라는 접두사를 붙여서, 전역 객체로 선언해야 합니다. 각 세션은 세션 마다 커널 객체들의 네임스페이스가 존재하고, 위의 '\Global\'을 붙이지 않는 객체는 각 세션의 로컬 커널 객체로 선언되어, 다른 세션에 있는 동일한 이름의 뮤텍스 혹은 세마포어를 인식하지 못합니다. 예를 들어 "\Global\MutexTest" 이런 식으로 커널 객체의 이름을 명명해 주어야 합니다.

결국 단 하나만의 인스턴스가 동작하기를 원하는 경우, 전역 뮤텍스 혹은 세마포어을 생성해서, 기존에 이 커널 객체들이 생성이 되었는지의 여부를 이용하여 인스턴스를 생성을 제어해야 합니다.


사용자에 따른 데이타 분리

Windows XP의 이러한 기능 때문에 단일 프로그램을 여러 명의 사용자가 동시에 사용할 수 있고, 이 때문에 응용 프로그램은 사용자 별로 데이타를 저장하는 것이 필요합니다. 이 때 마이크로 소프트는 각 사용자 별로 생성되는 데이타는 각 사용자의 My Document 폴더 밑에 저장하는 것을 권장하고 있습니다. 이 My Document 폴더는 SHGetFolderPath에서 CSIDL_PERSONAL을 인자로 넘겨주어 알 수 있습니다.

마이크로 소프트에서 권장하는 디렉토리들을 좀 더 몇 개만 더 나열하면:

CSIDL_MYPICTURES: 이미지 파일을 저장시에 사용하기를 권장합니다. 각각의 개인마다 다르게 생성됩니다.

CSIDL_COMMON_APPDATA: 개인 데이타가 아닌 응용 프로그램의 데이타를 저장할 때 사용하기를 권장합니다. 기본 패스는 C:\Documents and Settings\All Users\Application Data 입니다.

CSIDL_LOCAL_APPDATA: 임시 파일등을 저장하기 위해서 사용됩니다.

레지스트리 저장소의 분리

위의 사용자별 데이타 저장과 연결되는 이야기로서, 사용자들의 데이타를 레지스트리에 저장할 필요가 있을 경우, HKCU (Current User)를 사용하시고, HKLM (Local Machine)을 사용하지 말라고 권장하고 있습니다.

4. 프로그램 종료시 할 일

WTSRegisterSessionNotification에서 등록한 윈도우를 Destroy하기 이전에, WTSUnRegisterSessionNotification을 호출해 주시기 바랍니다.

5. 결론

결국 Fast User Switching을 지원하기 위해서는 세션의 변화를 감지해야 하고, 데이타를 각각의 개인별로 분리하며, 전역으로 쓰는 자원에 대해서는 세션의 변화에 따라 어떤 식으로 활용해야 하는 지를 미리 정의해 놓아야 합니다.

6. 참고

http://support.microsoft.com/default.aspx?scid=kb%3Bko%3B310153
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwxp/html/winxpfus.asp

+ Recent posts