Visual Studio 2005의 Service pack 1 (sp1) 설치 방법과 장점

Visual Studio 2005 sp1을 설치하실때 설치가 안될 경우 아래의 사항을 체크해야 합니다.

1. 소프트웨어를 제한하는 부분이 있습니다.

관리도구 --> 로컬 보안 정책 --> 소프트웨어 제한 정책
--> 강요 속성 --> 로컬 관리자를 제외한 모든 사용자 선택 --> 적용

을 하시면 설치가 됩니다.

2. 관련 update를 설치를 하고도
Web Application Project를 인식하지 못하거나 생성하지 못할 경우, Windows update를 수행합니다.

이 문제는 설치된 sp1과 Visual studio 2005의 버전이 정확하게 일치 되지 않으면 인식을 못하는 문제로 보입니다.

참고로 제 Visual Studio 2005에는 
 

Microsoft Visual Studio 2005 Premier Partner Edition - 한국어 서비스 팩 1(KB926605)

Microsoft Visaul Studio 2005 Team Suite - ENU Service Pack 1 (KB926601)

두 가지 버전이 설치 되어 있습니다.
첫 번째를 설치하고, 잘 동작하지 않아서 Widnows update를 통하여 두 번째를 설치하였습니다. 

Windows update는 update 간의 순서와 우선순위가 있으며, Visual Studio 2005 sp1을 설치하기 전에 Windows 2003의 sp나 각종 보안 update 등의 update를 해야 추가로 생성될 수 있으며, 역시 시간은 꽤 걸립니다. 

3. 참고로 Visual Studio 2005의 sp1을 설치하는 가장 큰 이유는 Web application Project입니다.

Visual Studio 2003의 Web Application Project의 하위 버전 호환성을 위해서도 이 기능이 지원되어야 하지만, 일반 Web Site로 접근하는 것에 비하여 아래와 같은 장점이 있습니다.


Web Site로 접근하게 되면 bin folder와 csproj(프로젝트 정보 파일)이 없는 환경이기 때문에 모든 Web Folder의 참조가 web.config의 <assemblies>테그 아래에 저장됩니다.
따라서 특히 공동개발에서 web.config의 빈번한 update가 이루어지고, 버전의 충돌등의 문제점을 야기할 수 있습니다. 

또한 Web Site로 접근하게 되면 Web Folder가 개별로 빌드를 할 수가 없기 때문에 Web Application Project를 이용하여 개별로 빌드를 하는 것에 비교하여 빌드 시간이 늘어납니다.

Web Application Project를 이용하면 Visual Studio 2003의 Enterprise Template Project로 업무 별로 묶거나 Solution Folder를 이용해서 업무별로 Biz, Dac, Web 등을 한 단위로 처리할 수 있어서 공동개발 환경 구성하는데 보다 쉽게 구성할 수 있습니다.


Web Application Project를 생성할 수 있는 것 외에 Visual Studio 2005 sp1은 아래와 같은 개선점을 가지고 있습니다.

  1. Addition of Web Application Projects feature

     (note - does not apply to Visual Web Developer 2005 Express)

  2. Improvements to performance of ASP.NET features in the IDE

  3. Bug fixes to many customer reported issues

  4. Changes to enable support for ASP.NET AJAX scenarios

by minggoo | 2007/11/07 13:13 | Dev Tools | 트랙백 | 덧글(0)

web.config 파일 암호화 하기 (connectionStrings)

asp.net 2.0 에서는 configu 파일의 데이터를 암호화를 하기 위해서 Aspnet_regiis.exe 툴을 사용할 수 있습니다.

web.config 나 machine.config 파일 부분에서 사용이 가능합니다.

web form에서 다중의 서버상에 사용되는 configuration 파일들을 암호화하기 위해

부분적으로 RSA 암화화를 효과적으로 만들어 줍니다.

 

web.config 에서 connectionStrings 세션부분에 암호화를 해볼까요..

아래는 connectionStrings 세션부분에 db를 연결하는 부분을 기술했습니다.

 

<connectionStrings>

     <add name = "MyLocalSQLServer"

       connectionString = "Initial Catalog = aspnetdb ;

       data source = localhost ; Integrated Security = SSPI ;"

       providerName = "System.Data.SqlClient"/>

</connectionStrings>

 

이 세션부분에 대해서 암호화를 하기 위새서 Visual Studio 2005 의 명령 프롬프트를 실행시키시고,

 

aspnet_regiis -pe "connectionStrings" -app "/MachineRSA"

 

실행하면 암호화가 됩니다. 여기서 -app 다음에 MachineRSA 라고..가상디렉토리명을 기재하기고.

-pe는 구성섹션을 암호화 하는 것을 의미합니다.

 

aspnet_regiis.exe -pef "connectionStrings" C:\Projects\MachineRSA

 

-pef 인수를 사용해서 직접 물리적인 경로로 암호화를 할 수 있습니다.



복고화는 아래와 같이 -pd를 사용해서 해제하면 됩니다.

aspnet_regiis.exe -pd "connectionStrings" C:"\Projects\MachineRSA

 

 

디폴트는 RSA암호화 방식을 사용하는데 이 키가 machine.config 파일에 있습니다.

하지만 iis용 계정인 network service 계정이 이 키에 대한 접근 권한이 없다면 에러가 날 수 있습니다.

아래와 같은 명령어로 권한을 주면 해결이 가능하답니다.

 

aspnet_regiis -pa "NetFrameworkConfigurationKey" "NT Auhority\Network Service"


 

[출처 경로] http://cafe.naver.com/it2006.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=122

 

by minggoo | 2007/07/10 14:01 | ASP.NET | 트랙백(3) | 덧글(0)

Checking All CheckBoxes in a GridView Using Client-side script and a check all checkbox

Checking All CheckBoxes in a GridView Using Client-Side Script and a Check All CheckBox
By Scott Mitchell


Introduction
 Last week I authored an article titled Checking All CheckBoxes in a GridView that illustrated how to add functionality to check all CheckBoxes in a GridView at the click of a button. Specifically, the article examined how to accomplish this using both server-side code and client-side script. In both versions, the page contained two buttons - "Check All" and "Uncheck All" - that, when clicked, caused all of the checkboxes to become checked or unchecked. In the server-side code version, clicking either of these buttons caused a postback and programmatically checked or unchecked all of the CheckBoxes. In the client-side version, the checking and unchecking was handled with a bit of JavaScript code.

In the code download that accompanied Checking All CheckBoxes in a GridView I included an example that improved the user interface with the client-side script approach to include a "Check/Uncheck All" checkbox in the header above the column of CheckBoxes in the GridView (see the screen shot to the right). Checking the header checkbox would check all checkboxes in the GridView, while unchecking the header checkbox would have the opposite effect. Adding such functionality was a bit more tricky than I had at first anticipated due to a couple of subtleties. In this article we'll explore these issues and the workarounds I employed to overcome them. Read on to learn more!

If you've not yet read the original article, Checking All CheckBoxes in a GridView, please take a moment to read that first before continuing on with this article...

Adding a "Check/Uncheck All" CheckBox to the Header
The first step in implementing this user experience is to add the "Check/Uncheck All" checkbox to the header of the column of CheckBoxes. Since we implemented the column of CheckBoxes in the GridView as a TemplateField, adding a header CheckBox control is as simple as adding a HeaderTemplate to the TemplateField, like so (some formatting-related GridView properties have been removed for brevity):

<asp:GridView ID="FileList" runat="server"    AutoGenerateColumns="False" DataKeyNames="FullName">    <Columns>        <asp:TemplateField>            <HeaderTemplate>                <asp:CheckBox runat="server" ID="HeaderLevelCheckBox" />            </HeaderTemplate>            <ItemTemplate>                <asp:CheckBox runat="server" ID="RowLevelCheckBox" />            </ItemTemplate>        </asp:TemplateField>                <asp:BoundField DataField="Name" HeaderText="Name" />        ... Remaining BoundFields ...    </Columns></asp:GridView>

This addition puts a CheckBox in the header of the column of CheckBoxes; however, clicking the header CheckBox doesn't do anything. Upon having the header CheckBox checked we want to check all of the CheckBoxes in the grid; similarly, when unchecking the header CheckBox we want all of the CheckBoxes in the grid to be unchecked. Moreover, to provide a snappy user experience, all of this needs to be done on the client-side using JavaScript.

In Checking All CheckBoxes in a GridView we looked at a JavaScript function named ChangeAllCheckBoxStates(checkState) that checked (or unchecked) all of the CheckBoxes in the grid depending on the value of the checkState input parameter. In the client-side demo from that previous article, this function was called by the "Check All" and "Uncheck All" buttons passing in values of true and false for the checkState input parameter, respectively. My initial idea was to simply reuse this JavaScript function with the header CheckBox. Namely, the client-side onclick event for the header CheckBox would call ChangeAllCheckBoxStates passing in this.checked as the value of checkState.

To accomplish this I wired up the header CheckBox's client-side onclick event using the following server-side code in the GridView's DataBound event handler:

Protected Sub FileList_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles FileList.DataBound
   'Each time the data is bound to the grid we need to build up the CheckBoxIDs array

   'Get the header CheckBox
   Dim cbHeader As CheckBox = CType(FileList.HeaderRow.FindControl("HeaderLevelCheckBox"), CheckBox)

   'Run the ChangeCheckBoxState client-side function whenever the
   'header checkbox is checked/unchecked
   cbHeader.Attributes("onclick") = "ChangeAllCheckBoxStates(this.checked);"

   For Each gvr As GridViewRow In FileList.Rows
     'Get a programmatic reference to the CheckBox control
     Dim cb As CheckBox = CType(gvr.FindControl("RowLevelCheckBox"), CheckBox)

     'Add the CheckBox's ID to the client-side CheckBoxIDs array
     ClientScript.RegisterArrayDeclaration("CheckBoxIDs", String.Concat("'", cb.ClientID, "'"))
   Next
End Sub

The For Each loop builds up the CheckBoxIDs client-side array, which is iterated through in the ChangeAllCheckBoxStates function to check or uncheck all of the grid's CheckBoxes. Note that I grab a reference to the header CheckBox by searching the GridView's HeaderRow's control collection for the control with ID HeaderLevelCheckBox. (This was the value of the ID property of the header CheckBox in the declarative syntax examined earlier in this article.)

Once I get a reference to the header CheckBox I indicate that the client-side onclick event handler should invoke the ChangeAllCheckBoxStates passing in this.checked as the value of checkState by assigning the JavaScript to invoke to the CheckBox's Attributes collection's onclick setting. The Attributes collection provides a way to specify additional attribute values for the control's rendered HTML; see Working with Client-Side Script for more information on working with client-side HTML attributes and events using server-side ASP.NET code.

With this addition, checking the header CheckBox calls ChangeAllCheckBoxStates(true), checking all of the CheckBoxes in the grid; conversely, unchecking the header CheckBox calls ChangeAllCheckBoxStates(false), unchecking all of the CheckBoxes.

Problem #1: The "Check All" and "Uncheck All" Buttons Don't Check/Uncheck the Header CheckBox
After making this change, the first issue I found was that checking the "Check All" and "Uncheck All" buttons on the page checked (or unchecked) all of the CheckBoxes in the grid except for the header CheckBox. This is because these two buttons call ChangeAllCheckBoxStates and ChangeAllCheckBoxStates simply iterates through the CheckBoxIDs array, checking or unchecking all of the CheckBoxes registered in that array. Since the header CheckBox is not included in that array, it's not affected by the ChangeAllCheckBoxStates function. (Of course this isn't an issue when directly checking or unchecking the header CheckBox since the user-based action of checking or unchecking the header CheckBox handles this.)

To remedy this I simply added the header CheckBox to the list of IDs in the CheckBoxIDs array. This was accomplished by adding the following line of code to the GridView's DataBound event handler immediately after setting the header CheckBox's Attributes("onclick") property:

'Add the CheckBox's ID to the client-side CheckBoxIDs array
ClientScript.RegisterArrayDeclaration("CheckBoxIDs", String.Concat("'", cbHeader.ClientID, "'"))

By adding the header CheckBox's ID to the array, clicking the "Check All" or "Uncheck All" buttons correctly check or uncheck the header CheckBox along with the row-level CheckBoxes in the grid.

Problem #2: Checking All CheckBoxes Manually or Unchecking a CheckBox Does Not Update the "Check/Uncheck All" Header CheckBox
Since the "Check/Uncheck All" header CheckBox checks all of the CheckBoxes in the grid I initially thought of the header CheckBox as a user interface item that solely performs some action - specifically, if you check the header CheckBox, all row-level CheckBoxes in the grid are checked. However, the header CheckBox really represents information about state of the CheckBoxes in the grid. That is, if the header CheckBox is checked, it indicates that all of the other CheckBoxes in the grid are also checked and if the header CheckBox is not checked then one or more of the other CheckBoxes in the grid must be unchecked.

Thinking of the header CheckBox in these terms, it became clear to me that the header CheckBox need to automatically be checked if all of the other CheckBoxes are manually checked; conversely, if all of the other CheckBoxes are checked and then the user manually unchecks one of them, the header CheckBox needs to be automatically unchecked. To accomplish this I decided to do the following:

  1. Add a JavaScript function named ChangeHeaderAsNeeded() that checks the header CheckBox if all of the other CheckBoxes in the grid are checked and unchecks the header CheckBox if there exists an unchecked CheckBox
  2. Wire up each row-level CheckBox's client-side onclick event to call the ChangeHeaderAsNeeded() function
Let's first look at the ChangeHeaderAsNeeded() function. This function assumes that the first CheckBoxIDs array element is the ID of the header CheckBox, and that the remaining elements are the IDs of the row-level CheckBoxes. The function loops through the row-level CheckBoxes and, for each CheckBox, determines if it's checked. If it is not, it unchecks the header CheckBox and exits the function. If the loop finishes without finding an unchecked row-level CheckBox, the header CheckBox is checked.

function ChangeHeaderAsNeeded(){    // Whenever a checkbox in the GridView is toggled, we need to    // check the Header checkbox if ALL of the GridView checkboxes are    // checked, and uncheck it otherwise    if (CheckBoxIDs != null)    {        // check to see if all other checkboxes are checked        for (var i = 1; i < CheckBoxIDs.length; i++)        {            var cb = document.getElementById(CheckBoxIDs[i]);            if (!cb.checked)            {                // Whoops, there is an unchecked checkbox, make sure                // that the header checkbox is unchecked                ChangeCheckBoxState(CheckBoxIDs[0], false);                return;            }        }                // If we reach here, ALL GridView checkboxes are checked        ChangeCheckBoxState(CheckBoxIDs[0], true);    }}

Next, we need to configure each row-level CheckBox so that when its client-side onclick event is fired the ChangeHeaderAsNeeded() function is invoked. This can be accomplished by adding the following client-side code to the For Each loop in the GridView's DataBound event handler:

... Code omitted for brevity ...
For Each gvr As GridViewRow In FileList.Rows
    'Get a programmatic reference to the CheckBox control
    Dim cb As CheckBox = CType(gvr.FindControl("RowLevelCheckBox"), CheckBox)

   'If the checkbox is unchecked, ensure that the Header CheckBox is unchecked
   cb.Attributes("onclick") = "ChangeHeaderAsNeeded();"

    'Add the CheckBox's ID to the client-side CheckBoxIDs array
    ClientScript.RegisterArrayDeclaration("CheckBoxIDs", String.Concat("'", cb.ClientID, "'"))
Next

That's all there is to it! With that change, checking all of the CheckBoxes manually will cause the header CheckBox to become checked; similarly, when all row-level CheckBoxes are checked, unchecking a single one will uncheck the header CheckBox.

Problem #3: When Doing a Postback that Doesn't Rebind the Data to the GridView, the "Check/Uncheck All" Header CheckBox No Longer Works
The code in the DataBound event handler assigns the client-side attributes to the checkboxes in the GridView and also specifies the elements in the client-side CheckBoxIDs array. The Web controls' Attributes collection is stored in view state, but the script emitted using the ClientScriptManager class is not. This distinction is important because the DataBound event handler only executes when the data is explicitly bound to the GridView. If the GridView's view state is enabled (the default), on a non-grid related postback the GridView is reconstructed from view state and does not explicitly rebind to its data source. Consequently, the DataBound event handler does not run.

The net result of this behavior is that if the GridView's view state is enabled and a non-grid related postback occurs, the client-side CheckBoxIDs array is lost and the Check All / Uncheck All logic no longer works. To remedy this, we need to forgo using the ClientScriptManager and instead craft the necessary JavaScript for the array and store it in a Literal Web control. Since the Literal control's Text property is stored in view state, the client-side array will remain regardless of whether the DataBound event handler executes.

The following code achieves this by using a List of type String named ArrayValues to hold the array elements. Every place ClientScript.RegisterArrayDeclaration("CheckBoxIDs", value) used to be is replaced by ArrayValues.Add(value). Then, after all of the values have been added to the List, the Literal control's Text property is assigned the appropriate JavaScript syntax for creating this array. The bold sections highlight the changes from the previous code snippets.

Protected Sub FileList_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles FileList.DataBound
   ... Code omitted for brevity ...

   'Add the CheckBox's ID to the client-side CheckBoxIDs array
   Dim ArrayValues As New List(Of String)
   ArrayValues.Add(String.Concat("'", cbHeader.ClientID, "'"))


   For Each gvr As GridViewRow In FileList.Rows
     'Get a programmatic reference to the CheckBox control
     Dim cb As CheckBox = CType(gvr.FindControl("RowLevelCheckBox"), CheckBox)

     'If the checkbox is unchecked, ensure that the Header CheckBox is unchecked
     cb.Attributes("onclick") = "ChangeHeaderAsNeeded();"

     'Add the CheckBox's ID to the client-side CheckBoxIDs array
     ArrayValues.Add(String.Concat("'", cb.ClientID, "'"))
   Next

   'Output the array to the Literal control (CheckBoxIDsArray)
   CheckBoxIDsArray.Text = "<script type=""text/javascript"">" & vbCrLf & _
                   "<!--" & vbCrLf & _
                   String.Concat("var CheckBoxIDs = new Array(", String.Join(",", ArrayValues.ToArray()), ");") & vbCrLf & _
                   "// -->" & vbCrLf & _
                   "</script>"
End Sub

Conclusion
In this article we saw how to add a "Check/Uncheck All" header CheckBox to the GridView that, using client-side techniques, provides a means to quickly check or uncheck all row-level CheckBoxes. The header CheckBox, along with the "Check All" and "Uncheck All" buttons, provide the user with multiple ways for checking or unchecking all of the grid's CheckBoxes. Since client-side script is used to handle checking and unchecking the CheckBoxes, the interface provides a snappy user experience.

Happy Programming!

  • By Scott Mitchell


    Attachments

  • Download the code used in this article

  • by minggoo | 2007/05/17 13:54 | ASP.NET | 트랙백 | 덧글(0)

    Registry in C#

    Registry In's and Out's Using C#
    By Michael Bright
    This is an article on using the Registry in C#. Having been looking around the web and on the MSDN libraries, I have determined that there is not a complete guide on using all the registry tools accessible using C#. So this article is going to cover all methods exposed to the user when developing in C#. Firstly i would like to say that I am very aware that the current consensus is that we should not be using the Registry to hold information about our programs anymore and that the ".NET" way is to store information in XML files. Well if that is you then you don't really need this article, however if you are accessing information from the registry then this is the source for you, please note that i have also included information from my previous article on "Advance Registry Access in C#". Ok so lets get down to business.
    When using the .NET framework in C# the user needs to add access to the Microsoft.Win32 namespace in order to access registry manipulation tools, this is done as shown in the below code:
    using Microsoft.Win32;
    Once this reference has been added along with the other, it gives us access to the elements involved within registry control. There are two main classes associated with this namespace, RegistryKey and Registry. These classes are used together to allow us to do pretty much everything in the registry.
    Registry Class
    The Registry Class itself on its own doesn't do a lot for us this is because the Registry Class merely represents the seven top-level nodes within the registry for us to access and manipulate. The Registry class is powerful when used with the RegistryKey class. However if you open up Visual Studio.NET and add the namespace above and then just type, "Registry." you will see the list of the seven subnodes (Shown below).
    Registry Class name
    Registry Value
    What is does
    Registry.ClassesRootHKEY_CLASSES_ROOTResponsible for telling the shell how to handle different file formats
    Registry.CurrentConfigHKEY_CURRENT_CONFIGRepresents the current session configuration
    Registry.CurrentUserHKEY_CURRENT_USERRepresents the profile of the current user
    Registry.DynDataHKEY_DYN_DATAContains configuration data about dynamic data (i.e. in RAM)
    Registry.LocalMachineHKEY_LOCAL_MACHINEContains the System Information (i.e. Hardware)
    Registry.PerformanceDataHKEY_PERFORMANCE_DATAContain information about the performance of applications
    Registry.UsersHKEY_USERSA repository for information regarding users and their preferences.
    Now the we have identified what the Registry Class does and represents we can look at it's partner the RegistryKey Class.
    RegistryKey Class
    The RegistryKey class is the most important part in terms of registry manipulation when using C#. The RegistryKey class has many methods which the use can exploit to create and delete data. Before we can do any of this we have to create an Instance of a Registry Key, this is done using the code below:
    RegistryKey OurKey = Registry.Users;
    In the above code extract we have created a instance of RegistryKey called OurKey, we have initialized this key to be the HKEY_USERS subkey in the registry. Basically this mean that any method we use in this key will effect the direct subkeys of HKEY_USERS and their data items.
    OpenSubKey()
    OpenSubkey is a very important method when using the RegistryKey class, because it allows us to access/manipulate the subkey of the top key. This may sound a tad strange, basically if as above we have "OurKey" set to the HKEY_USERS key, using the OpenSubKey method we can set "OurKey" to a subkey of HKEY_USERS. The best we to demonstrate this is to show you some code (See below).
    RegistryKey OurKey = Registry.Users;
    OurKey = OurKey.OpenSubKey(".DEFAULT",true);

    In the above code sample we have created our instance of the RegistryKey class and given it the value of the HKEY_USERS key. The next step is that we have opened a subkey within HKEY_USERS called ".DEFAULT", we should also note that it doesn't have to be a top level subnode either, We could have opened ".DEFAULTsubkey". The second part to the method is defining if the key is opened and Read-only mode or Read/Write mode. If the value is true we can edit items within the key, and the key itself.
    DeleteSubKey() / CreateSubKey() & DeleteSubKeyTree()
    All these three methods are to do with the management of Subkeys under the current selected key. These methods are pretty self explanatory and the below code shows there implementation.
    RegistryKey OurKey = Registry.Users; // Create OurKey set to HKEY_USERS
    OurKey = OurKey.OpenSubKey(".DEFAULT",true); // Set it to HKEY_USERS.DEFUALT
    OurKey.CreateSubKey("OurSubKey"); // Create the key HKEY_USERS.DEAFULTOurSubKey
    OurKey.CreateSubKey(@"OurSubKeySubkey"); // Create a sub key HKEY_USERS.DEFAULTOurSubKeySubkey
    OurKey.DeleteSubKey(@"OurSubKeySubKey"); // Delete the subkey name "subkey"
    OurKey.DeleteSubKeyTree("OurSubKey"); // Delete the whole subkey and any subkeys below it
    GetSubKeyNames()
    GetSubKeyNames is an important method because it allows us to get all the names of the secondary subkeys, For instance we could get all the names of the subkeys below "HKEY_USERS". The only draw back being that it only gets the immediate subkey names. However with a little recursion we can get them all, the first code snippet is of the basic use of GetSubKeyNames().
    //The first example shows it using a foreach loop to display each subkeyname
    foreach
    (string Keyname in OurKey.GetSubKeyNames())
    {
     MessageBox.Show(Keyname);
    }
    //The second example shows how to tranfer the names into a string array
    string[] Keynames = OurKey.GetSubKeyNames();
    In the next snippet of code we see how we can use recursion to obtain all the names of the subkeys.
    private void GetSubKeys(RegistryKey SubKey)
    {
        foreach(string sub in SubKey.GetSubKeyNames())
        {
          MessageBox.Show(sub);
          RegistryKey local = Registry.Users;
          local = SubKey.OpenSubKey(sub,true);
          GetSubKeys(local); // By recalling itself it makes sure it get all the subkey names
        }
    }

    //This is how we call the recursive function GetSubKeys
    RegistryKey OurKey = Registry.Users;
    OurKey = OurKey.OpenSubKey(@".DEFAULTtest",true);
    GetSubKeys(OurKey);
    GetValue() / GetValuesNames()
    These methods are also very important when you want to extract information form the registry, when you used the other methods to get to the subkey that you want you would like to extract the information from them. The first methods, GetValue allows you obtain the value held in a registry value by specifying its name, For example in our "test" subkey used earlier, i have a string (REG_SZ) value named "Testvalue" with the value "This is a test". To extract the value i would use the below code.
    RegistryKey OurKey = Registry.Users;
    OurKey = OurKey.OpenSubKey(@".DEFAULTtest",true);
    MessageBox.Show(OurKey.GetValue("Testvalue").ToString());
    As you will have noticed when we get information out of the registry we are always converting it using the ToString() method, this is because values and subkeys within the registry are classed as object and must therefore be converted, either via the ToString() method or by Casting.
    As we have seen using the GetSubKeyNames() method we get the collection of names returned, this is also the case when we use the GetValueNames() methods. Using the code similar to the code used earlier we can obtain the names of the values, and also their values at the same time, as shown below.
    RegistryKey OurKey = Registry.Users;
    OurKey = OurKey.OpenSubKey(@".DEFAULTtest",true);

    foreach(string valuename in OurKey.GetValueNames())
    {
    MessageBox.Show(valuename + " " + OurKey.GetValue(valuename).ToString());
    }
    SetValue()
    The SetValue method does exactly what it says, it sets the value of a registry value, to use the function you supply the name of the value and what you want to set it to, if the value doesn't exist it creates it, simple as that, see the code below:
    RegistryKey OurKey = Registry.Users;
    OurKey = OurKey.OpenSubKey(@".DEFAULTtest",true);
    OurKey.SetValue("Testvalue",0);
    Using the SubKeyCount and Valuecount properties
    You can use these two properties to obtain firstly of how many subkeys there directly below the specified key, and also the number of values under the specified key. to help understand we can look at the below code:
    RegistryKey OurKey = Registry.Users;
    OurKey = OurKey.OpenSubKey(@".DEFAULTtest",true);
    MessageBox.Show(OurKey.SubKeyCount.ToString());
    MessageBox.Show(OurKey.ValueCount.ToString());
    Closing What we've worked on
    I should also mention that when you have finished working and using a section of registry it makes good error free practice to close the key you have been using, this is done via the Close() method. See code below.
    RegistryKey OurKey = Registry.Users;
    OurKey.Close()
    Unsupported Registry Functions
    The .NET Framework supports a lot and in C# the RegistryKey class is very powerful and can be used well to manipulate the registry. Most of the Win32 APIs which we would have had to use previously have been built in however there are a few that are not, and this is where i refer to my Article "Advance Registry Work in C#". These are RegLoadKey() and RegUnloadKey(). These two calls allow you to load registry Hives into the registry and then manipulate them. As you will have seen in that article you need to declare these calls and you also need to gain relevant privileges to use them. API's Declared
    [StructLayout(LayoutKind.Sequential)]public struct LUID{public int LowPart;public int HighPart;}[StructLayout(LayoutKind.Sequential)]public struct TOKEN_PRIVILEGES{public LUID Luid;public int Attributes;public int PrivilegeCount;}[DllImport("advapi32.dll", CharSet=CharSet.Auto)]public static extern int OpenProcessToken(int ProcessHandle, int DesiredAccess,ref int tokenhandle);[DllImport("kernel32.dll", CharSet=CharSet.Auto)]public static extern int GetCurrentProcess();[DllImport("advapi32.dll", CharSet=CharSet.Auto)]public static extern int LookupPrivilegeValue(string lpsystemname, string lpname,[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);[DllImport("advapi32.dll", CharSet=CharSet.Auto)]public static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs,[MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES Newstate, int bufferlength,int PreivousState, int Returnlength);[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]public static extern int RegLoadKey(uint hKey,string lpSubKey, string lpFile);[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]public static extern int RegUnLoadKey(uint hKey, string lpSubKey);public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;public const int TOKEN_QUERY = 0x00000008;public const int SE_PRIVILEGE_ENABLED = 0x00000002;public const string SE_RESTORE_NAME = "SeRestorePrivilege";public const string SE_BACKUP_NAME = "SeBackupPrivilege";public const uint HKEY_USERS = 0x80000003;public string shortname;bool unloaded = false;
    API's Used
    int token=0;int retval=0;TOKEN_PRIVILEGES TP = new TOKEN_PRIVILEGES();TOKEN_PRIVILEGES TP2 = new TOKEN_PRIVILEGES();LUID RestoreLuid = new LUID();LUID BackupLuid = new LUID();retval = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);retval = LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);retval = LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);TP.PrivilegeCount = 1;TP.Attributes = SE_PRIVILEGE_ENABLED;TP.Luid = RestoreLuid;TP2.PrivilegeCount = 1;TP2.Attributes = SE_PRIVILEGE_ENABLED;TP2.Luid = BackupLuid;retval = AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);retval = AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0);// Loading a Hive "C:NTUSER.DAT"RegLoadKey(HKEY_USERS,"NTUSER.DAT",@"C:NTUSER.DAT");)// Unloading a Hive "C:NTUSER.DAT"RegUnLoadKey(HKEY_USERS,"NTUSER.DAT");
    About the Author
    My name is Michael Bright, I'm a university student studying a Bsc in Computer Science at University College Chester. I have a love for all thing computers and have only worked with C# for about 3 months. I am currently training for my MCP in it. My other interests are C, VB, HTML, ASP and also Flash. I have interests in Developing Network app's and Network Security. My Web site is csharp.brightweb.co.uk where you can find examples of my Work, including my Defender Security applications.

    by minggoo | 2007/04/27 13:27 | .Net Framework | 트랙백 | 덧글(0)

    RecycleAppPools.vbs

    소스 수정후 재 배포한 후 IISRESET을 실행하여 변경사항을 반영하는데
    이렇게 하면 지금 꼭 배포를 해야 하는 Application Pool외에도 IIS에서 hosting하고 있는
    다른 Application Pool을 reset하여 시간도 많이 걸리며,
    다른 동작을 하는 Application Pool도 reset되어 dll이 재등록 하는 등 많은 내부 동작을 수행할 수 있다.


    아래와 같이 RecycleAppPools.vbs를 작성하여 실행하면
    iisreset을 실행할때 보다 훨씬 빠르게 다른 Application Pool에 영향을 주지 않고,
    변경된 dll을 반영하거나 gac에 있는 dll을 인식 시킬 수 있다.

    ' Created by minggoo, 2007-04-15
    Set locator = CreateObject("WbemScripting.SWbemLocator")
    Set Service = locator.connectserver(strServer, "root/MicrosoftIISv2")
    Set APCollection = Service.InstancesOf("IISApplicationPool")

    strAppName = "SharePoint - 80"
    For Each APInstance In APCollection
     'WScript.Echo  ApInstance.Name
     If UCase(ApInstance.Name) = UCase("W3SVC/AppPools/"&strAppName) Then
      APInstance.Recycle
            WScript.Echo "Recycling " & strServer & "/" & APInstance.Name & " is Recycled."
     End If
    Next

    by minggoo | 2007/04/23 16:19 | ASP.NET | 트랙백 | 덧글(0)

    트랜잭션 관리자가 원격/네트워크 트랜잭션에 대한 지원을 할 수 없습니다.

    트랜젝션에 관련된 에러는 종류가 다양하고 증상도 다양하다.
    그러나 구더기 무서워서 장 못 담구랴..
    COM+를 쓰지 않고 Transaction처리등 COM+의 기능을 구현하거나 이용하기 위해서
    얼마나 많은 코드들이 필요할 것인지를 생각하면 아래의 방법 대로 하나씩 해보자.

    1. 방화벽을 확인한다.

    2. MSDTC 관련 설정 변경

      1. 관리도구>구성요소서비스
      2. 내 컴퓨터 선택 후 단축메뉴에서 속성 선택
      3. MSDTC 탭 선택
      4. 트랜잭션 구성>보안구성 버튼 선택
      5. 보안설정의 네트워크 DTC 액세스, XA트랜잭션 사용 체크
      네트워크 DTC 액세스 중에서
      - 원격 클라이언트 허용
      - 트랜잭션 관리자 통신>아웃바운드 허용, 들어오는 호출자 인증필요??없음

    그리고  MSDTC 가 로컬 시스템 계정으로 실행되도록 아래와 같이 IIS나 machine.config을 설정한다.


    IIS에서는 응용프로그램품의 관련 응용프로그램의 속성의 ID 텝에서 응용 프로그램 풀 ID를
    미리 정의된 보안 계정인 "로컬 시스템"으로 설정한다.

    machine.config 에는 아래와 같은 processModel 테그를 아래와 같이 변경한다.
    <processModel autoConfig="true" userName = "SYSTEM" password = "AutoGenerate" />

    COM 보안 탭
    --------------------
    액세스 권한
    시작 및 활성화 권한

     

     

    by minggoo | 2007/03/20 11:39 | TroubleShooting | 트랙백 | 덧글(2)

    초기화 함수가 실패하여 COM+활성화에 실패했습니다.

    초기화 함수가 실패하여 COM+활성화에 실패했습니다.
    COM+ Activation failed because an initialization function failed


    COM+구성요소서비스 : 관리도구à구성요소서비스à컴퓨터à내 컴퓨터àCOM+응용프로그램의 아래에 뒤져보시면

    시스템관련 dll이 여기서 동작되고 있습니다.

    1.     COM+ 구성요소서비스에서 문제가 있는 관련 dll을 삭제 하시고 다시 빌드하면 동작할 수 있습니다.
    빌드하여 구성요소서비스에 등록이 되지 않으면, 아래와 같이 수동등록합니다.
    Regsvcs XXXX.Dac
    .dll

     

    또 다른 이유는 IIS에 문제가 있어서 관련 에러가 날 수 있습니다.

    2.     IIS를 보면 접근하려고 하는 폴더가 가상 디렉토리로 잡혀 있는 경우가 있을 것입니다.
    이 가상 디렉토리를 해제 해 주시면 됩니다.

     

    기타 event log와 activation관련 에러일 수 있습니다.

     

     

    1.

    Remove component from COM+ registration.

    Delete DLL.

    Re-compile DLL From source.

    Re-registered in COM+. Works fine.

     

    3.

    I found the problem. In my Activate event I was writing to the event log and the event log was full (and a fixed size). I've modified the event log to overwrite as necessary and it now works fine.

     

    by minggoo | 2007/03/20 10:32 | TroubleShooting | 트랙백 | 덧글(0)

    Summa Framework upgrade

    어제 밤10시에 전화를 받고, 작년에 지원했던 프로젝트에 지원하였습니다. 12시 도착05시 지원 완료 하였습니다. 우울하더군요.

    나름 느낀 바와Action Item이 있어서 이렇게 글을 남깁니다.

     

    작년11월에 5 MD를 계획하고1일 교육, 1일 샘플 개발 지원하였던 프로젝트 입니다.

     

    어제는 성능테스트를 하던 중, 락이 몇 개 걸리고, MS DTC에러가 다수 발생하고, 급기하DB 제한시간 만료까지 연달아 나면서

    Summa Framework의 성능 취약으로 판단하여 지원 요청한 것으로 보여지고

     

    가서 코드를 본 결과

    잘 개발한 코드는 아니었습니다.

    Manual TransactionException 처리도 없어서 Transaction의 완결성이 완전히 고려되지 않았으며

    Base Class에서 명시적으로 리소스를 소멸하는 코드도 추가하였습니다.

    마구 붙여진 TransactionOptionSerializationOption을 설정하고, 최근의 샘플처럼 Timeout도 추가하였습니다.

     

    에러는MS DTC 에러입니다.

    메모리가 늘어난다, 성능테스트를 하고4분이 지나면 락이 발생한다 등 전화로 들었던 내용인 것에 비하면 많이 보던 에러입니다.

     

    그러나 서버와 DB서버가 같은 서버로 동작하여 특별히 DTC 권한 설정이 필요한 것도 아니었고,

    몇 가지 특정 부분에만 지속적으로 발생하여 로직을 의심할 수 있는 수준이 아니었습니다.

     

    그냥 테스트를 하거나 기능 검증을 할 때는 별 이상이 없으나, 부하 테스트(5 User 부하, 시간 간격은 잘 모르겠음)를 하기 시작하면

    ExceptionApplication 전반적으로Random하게 천천히 쌓여갑니다. 동시에 DB락도 하나씩 늘어 갑니다.

    부하테스트를 하면서 페이지에 접근하면 페이지가2번에 한 번 꼴로 접근이 안됩니다.

     

    아무래도 찜찜한 부분이PCS프로젝트, 특허 정보, EMD 지금 한 서버에서 동작하는데

    이 부분에서 간혹 이런 에러가 발생한다는 얘기를 들어서 입니다.

    이랜드를 포함하여 지금 이5개 프로젝트가 +-한달 정도의 간격으로 거의 동시에 진행 되었거든요.

     

    그래서 Summa Framework를 업그레이드 하기로 결정했습니다. 최근엔 이런 문제가 없으니까요.

    이미 돌아가는ApplicationFramework의 업그레이드는 상당히 부담됩니다.

    꽤 많은 버그를 수정했으나, 적지 않게 업그레이드도 이루어 지고,

    프로젝트의 몇 요구사항도 포함되어Interface가 바뀔 수 있는 가능성이 있기 때문입니다.

    Assembly의 버전이 올라간 경우도 있고, Assembly가 제거된 경우도 있고요.

    혹은 자체 Version등의 차이에서 일어나는 문제가 있을 수 있다고 생각 했기 때문에Framework의 업그레이드는 함부로 막 하기 어려운 작업이었습니다.

    그치만 꽤 간단하더라구요.

     

    0. 전체 솔루션을 열어서

    1. 기존SummaFramework를 제거

    2. SummaFramework를 설치

    3. 그리고 나서 솔루션 다시 빌드

    4. iisreset

    5. .web.config 수정(Assembles에서 찾는assembly를 제거하는 등.)

    6. 기타 수정사항 있으면 수정(보통은 수정 사항 없음)

     

    대담한 방법

    A. iis stop

    B. Summa Framework 제거

    C. New Summa Framework 설치

    D. Web 폴더의Bin폴더 안에 있는assembly  새 버전으로 변경

    E. iis start

    이게 될런 지요? 테스트 필요합니다.

     

    Framework를 업그레이드 하고, 부하테스트를 거니 잘 동작합니다.

    프로젝트의 PM인 마부장님께 말씀드렸습니다.

    "코드의 문제가 전혀 없는 것은 아니지만 아무래도 작년 10월 버전이라서 에러가 있었던 것 같습니다.

    Framework를 새로 업그레이드 하고, 몇 가지 코드를 수정해서

    지금은 정상 동작하는 것으로 보여지고, 앞으로는 Framework의 새 버전이 나오면 바로 업그레이드를 하겠습니다."

     

    보람은 있었습니다.

    by minggoo | 2007/03/16 16:43 | Dev Tools | 트랙백 | 덧글(0)

    Web 2.0 이 주는 변화

    20070124_web20_seri.pdf

    Web 2.0이 주는 변화. by SERI

    근데 이거 여기 올려도 되남? ㅜㅡ;
     

    by minggoo | 2007/02/27 09:40 | Essay | 트랙백 | 덧글(0)

    Select Text Field

    이거만 가지고 3일째다.
    좀 더 손을 봐야겠지만, 이제 거의 다 되었당. ^^

    Requirements:
    - Get All value of a Field of this list
    - DataBinding with DropDownList
    - if value that we want isn't in dropdownlist, add with TextBox and Click
    - Satisfire New, Edit, Display mode.
    - etc. general Requirement.


    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI;
    using System.Collections;
    namespace SKCC.HPW.Common.SPS.Fields
    {
        /// <summary>
        /// Field
        /// </summary>
        public class SKCCHPW_SelectTextField : SPFieldText
        {
            /// <summary>
            /// 필드이름 상수
            /// </summary>
            public const string FieldName = "SKCCHPW_SelectText";
            public SKCCHPW_SelectTextField(SPFieldCollection fields, string fieldName)
                : base(fields, fieldName)
            {
            }
            public SKCCHPW_SelectTextField(SPFieldCollection fields, string fieldName, string displayName)
                : base(fields, fieldName, displayName)
            {
            }
            public override BaseFieldControl FieldRenderingControl
            {
                get
                {
                    BaseFieldControl fieldControl = new SKCCHPW_SelectTextFieldControl();
                    fieldControl.FieldName = InternalName;
                    return fieldControl;
                }
            }
        }
        /// <summary>
        /// 작성자 필드 컨트롤
        /// </summary>
        public class SKCCHPW_SelectTextFieldControl : BaseFieldControl
        {
            // controls
            private Label lblError;
            private TextBox txtSelectText;
            private DropDownList ddlSelectText;
            private Button btnSelectText;
            // for internal variables
            private string strValue; // for this.Value
            //private string strScript;
            // Values for DropDownList
            private SPListItemCollection columnValues;
            public override object Value
            {
                get
                {
                    TraceWrite("Value Get");
                    EnsureChildControls();
                    if (SPContext.Current.FormContext.FormMode != SPControlMode.Display)
                    {
                        strValue = txtSelectText.Text.Trim();
                        if (!string.IsNullOrEmpty(ddlSelectText.SelectedValue.Trim()))
                        {
                            strValue = ddlSelectText.SelectedValue.Trim();
                        }
                        base.Value = strValue;
                    }
                    else
                    {
                        strValue = lblError.Text;
                    } //  (string)this.ListItemFieldValue ; }
                    return strValue;
                }
                set
                {
                    TraceWrite("Value Set");
                    EnsureChildControls();
                    strValue = (string)value;
                    if (SPContext.Current.FormContext.FormMode != SPControlMode.Display)
                    {
                        txtSelectText.Text = strValue;
                        if (ddlSelectText.Items.FindByValue(strValue) != null)
                        {
                            ddlSelectText.SelectedValue = strValue;
                        }
                        // ListItemFieldValue
                        //ddlSelectText.SelectedValue = strValue;
                    }
                    else
                    {
                        lblError.Text = (string)value;
                    }
                }
            }
            protected override void OnLoad(EventArgs e)
            {
                TraceWrite("OnLoad");
                base.OnLoad(e);
                if (this.Page.IsPostBack) // 포스트 백이면 컨트롤의 값을 Field의 값에 채워주기
                {
                    TraceWrite("IsPostBack");
                }
                else
                {
                    TraceWrite("NotPostBack");
                    try
                    {
                        if (SPContext.Current.FormContext.FormMode != SPControlMode.Display)
                        {
                            // get ListItemCollection values from this field
                            columnValues = (SPListItemCollection)GetFieldValues();
                            FillDropDownList(columnValues, this.ddlSelectText);
                            // for Editing Mode
                            if (SPContext.Current.FormContext.FormMode == SPControlMode.Edit)
                            {
                                // 초기 선택
                                if (this.Value != null)
                                {
                                    ddlSelectText.SelectedValue = (string)this.Value;
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        lblError.Text = ex.Message;
                    }
                }
            }
            private void FillDropDownList(SPListItemCollection columnValues, DropDownList ddlField)
            {
                // hashtable for distinct list
                Hashtable hashValues = new Hashtable();
                // fill DropDownList
                foreach (SPListItem tmpItem in columnValues)
                {
                    // tmpItem[0].ToString()
                    // tmpItem[SKCCHPW_SelectTextField.FieldName].ToString()
                    string tmpString = tmpItem[0].ToString().Trim();
                    if (!hashValues.Contains(tmpString))
                    {
                        hashValues.Add(tmpString, tmpString);
                        ddlField.Items.Add(new ListItem(tmpString, tmpString));
                    }
                }
            }
            private object GetFieldValues()
            {
                // 여기서 뿌려주자.
                SPQuery getSelectTextValues = new SPQuery();
                // 조건은 NotNull NotEq "" 빈
                // make query
                StringBuilder sbQuery = new StringBuilder();
                sbQuery.AppendLine("<Where>");
                sbQuery.AppendLine("<IsNotNull>");
                sbQuery.AppendLine(string.Format("<FieldRef Name=\"{0}\" />", SKCCHPW_SelectTextField.FieldName));
                sbQuery.AppendLine("</IsNotNull>");
                sbQuery.AppendLine("</Where>");
                sbQuery.AppendLine("<OrderBy>");
                sbQuery.AppendLine(string.Format("<FieldRef Name=\"{0}\" Ascending=\"TRUE\" />", SKCCHPW_SelectTextField.FieldName));
                sbQuery.AppendLine("</OrderBy>");
                // make viewField
                StringBuilder sbViewField = new StringBuilder();
                sbViewField.AppendLine(string.Format("<FieldRef Name=\"{0}\" />", SKCCHPW_SelectTextField.FieldName));
                // setup query
                getSelectTextValues.Query = sbQuery.ToString();
                getSelectTextValues.ViewFields = sbViewField.ToString();
                // get ListItemCollection values from this field
                SPListItemCollection slc = SPContext.Current.List.GetItems(getSelectTextValues);
                return slc;
            }
            protected override void Render(HtmlTextWriter writer)
            {
                // for Script
                //writer.Write(strScript); // or RegisterClientScriptInclude
                TraceWrite("Render");
                // base.Render(writer); 을 또 실행하면 두번 그려진다.
                if (SPContext.Current.FormContext.FormMode != SPControlMode.Display)
                {
                    // Controls
                    ddlSelectText.RenderControl(writer);
                    btnSelectText.RenderControl(writer);
                    txtSelectText.RenderControl(writer);
                    // Error Label
                    lblError.RenderControl(writer);
                }
                else {
                    base.Render(writer);
                }
            }
            protected override void CreateChildControls()
            {
                string strErrorLable = string.Empty;
                TraceWrite("CreateChildControls");
                base.CreateChildControls();
                if (SPContext.Current.FormContext.FormMode != SPControlMode.Display)
                {
                    ddlSelectText = new DropDownList();
                    ddlSelectText.Attributes["id"] = "ddlSelectText";
                    ddlSelectText.TabIndex = this.TabIndex;
                    ddlSelectText.CssClass = this.CssClass;
                    this.Controls.Add(ddlSelectText);
                    btnSelectText = new Button();
                    btnSelectText.Attributes["id"] = "btnSelectText";
                    //btnSelectText.Attributes["onclick"] = "javascript:HPW_Bind()";
                    btnSelectText.Attributes["href"] = "#";
                    //btnSelectText.InnerText = "<<값추가"; //for HtmlButton
                    btnSelectText.Text = "<<값추가"; //for Button
                    //btnSelectText.Style = "height:10px;width:75px;";
                    btnSelectText.Click += new EventHandler(btnSelectText_Click);
                    btnSelectText.TabIndex = this.TabIndex;
                    btnSelectText.CssClass = this.CssClass;
                    this.Controls.Add(btnSelectText);
                    txtSelectText = new TextBox();
                    txtSelectText.Attributes["id"] = "txtSelectText";
                    txtSelectText.Width = 75;
                    txtSelectText.TabIndex = this.TabIndex;
                    txtSelectText.CssClass = this.CssClass;
                    this.Controls.Add(txtSelectText);
                    // 모든 ControlMode 에서 그려지는 control
                    lblError = new Label();
                    strErrorLable = "<font color=\"red\" size=\"2\">ErrorMessage</font>";
                    lblError.Text = strErrorLable;
                    lblError.TabIndex = this.TabIndex;
                    lblError.CssClass = this.CssClass;
                    this.Controls.Add(lblError);
                }
            }
            public void btnSelectText_Click(object sender, EventArgs e)
            {
                // Javascript를 이용하면 최종적으로 Javascript 에서 추가된 값을
                // server-side에서 접근할 수 없다. 그래서 Server event를 이용.
                // 향후에 AJAX를 이용하여 구현하거나 UpdatePanel 적용
                ddlSelectText.Items.Add(new ListItem(this.txtSelectText.Text, this.txtSelectText.Text));
                ddlSelectText.SelectedValue = this.txtSelectText.Text;
            }
            private void TraceWrite(string message)
            {
                System.Diagnostics.Trace.WriteLine(message);
            }
        }
    }

    by minggoo | 2007/02/26 15:40 | SharePoint | 트랙백 | 덧글(0)

    ◀ 이전 페이지          다음 페이지 ▶