jjzjj

c# - 更改主音量级别

coder 2024-05-22 原文

如何更改主音量?使用此代码

[DllImport ("winmm.dll")]
public static extern int waveOutSetVolume (IntPtr hwo, uint dwVolume);

waveOutSetVolume (IntPtr.Zero, (((uint)uint.MaxValue & 0x0000ffff) | ((uint)uint.MaxValue << 16)));

我可以设置波形音量,但如果主音量太低,这将没有任何效果。

感谢您的帮助。

最佳答案

好的,开始了:

const int MAXPNAMELEN            = 32;
const int MIXER_SHORT_NAME_CHARS = 16;
const int MIXER_LONG_NAME_CHARS  = 64;

[Flags] enum MIXERLINE_LINEF : uint{
    ACTIVE       = 0x00000001,
    DISCONNECTED = 0x00008000,
    SOURCE       = 0x80000000
}
[Flags] enum MIXER           : uint{
    GETLINEINFOF_DESTINATION     = 0x00000000,
    GETLINEINFOF_SOURCE          = 0x00000001,
    GETLINEINFOF_LINEID          = 0x00000002,
    GETLINEINFOF_COMPONENTTYPE   = 0x00000003,
    GETLINEINFOF_TARGETTYPE      = 0x00000004,
    GETLINEINFOF_QUERYMASK       = 0x0000000F,

    GETLINECONTROLSF_ALL         = 0x00000000,
    GETLINECONTROLSF_ONEBYID     = 0x00000001,
    GETLINECONTROLSF_ONEBYTYPE   = 0x00000002,
    GETLINECONTROLSF_QUERYMASK   = 0x0000000F,

    GETCONTROLDETAILSF_VALUE     = 0x00000000,
    GETCONTROLDETAILSF_LISTTEXT  = 0x00000001,
    GETCONTROLDETAILSF_QUERYMASK = 0x0000000F,

    OBJECTF_MIXER                = 0x00000000,
    OBJECTF_WAVEOUT              = 0x10000000,
    OBJECTF_WAVEIN               = 0x20000000,
    OBJECTF_MIDIOUT              = 0x30000000,
    OBJECTF_MIDIIN               = 0x40000000,
    OBJECTF_AUX                  = 0x50000000,
    OBJECTF_HANDLE               = 0x80000000,
    OBJECTF_HMIXER               = OBJECTF_HANDLE | OBJECTF_MIXER,
    OBJECTF_HWAVEOUT             = OBJECTF_HANDLE | OBJECTF_WAVEOUT,
    OBJECTF_HWAVEIN              = OBJECTF_HANDLE | OBJECTF_WAVEIN,
    OBJECTF_HMIDIOUT             = OBJECTF_HANDLE | OBJECTF_MIDIOUT,
    OBJECTF_HMIDIIN              = OBJECTF_HANDLE | OBJECTF_MIDIIN
}
[Flags] enum MIXERCONTROL_CT : uint{
    CLASS_MASK        = 0xF0000000,
    CLASS_CUSTOM      = 0x00000000,
    CLASS_METER       = 0x10000000,
    CLASS_SWITCH      = 0x20000000,
    CLASS_NUMBER      = 0x30000000,
    CLASS_SLIDER      = 0x40000000,
    CLASS_FADER       = 0x50000000,
    CLASS_TIME        = 0x60000000,
    CLASS_LIST        = 0x70000000,

    SUBCLASS_MASK     = 0x0F000000,

    SC_SWITCH_BOOLEAN = 0x00000000,
    SC_SWITCH_BUTTON  = 0x01000000,

    SC_METER_POLLED   = 0x00000000,

    SC_TIME_MICROSECS = 0x00000000,
    SC_TIME_MILLISECS = 0x01000000,

    SC_LIST_SINGLE    = 0x00000000,
    SC_LIST_MULTIPLE  = 0x01000000,

    UNITS_MASK        = 0x00FF0000,
    UNITS_CUSTOM      = 0x00000000,
    UNITS_BOOLEAN     = 0x00010000,
    UNITS_SIGNED      = 0x00020000,
    UNITS_UNSIGNED    = 0x00030000,
    UNITS_DECIBELS    = 0x00040000, /* in 10ths */
    UNITS_PERCENT     = 0x00050000, /* in 10ths */
}
[Flags] enum MIXERCONTROL_CONTROLTYPE : uint{
    CUSTOM         = MIXERCONTROL_CT.CLASS_CUSTOM | MIXERCONTROL_CT.UNITS_CUSTOM,
    BOOLEANMETER   = MIXERCONTROL_CT.CLASS_METER | MIXERCONTROL_CT.SC_METER_POLLED | MIXERCONTROL_CT.UNITS_BOOLEAN,
    SIGNEDMETER    = MIXERCONTROL_CT.CLASS_METER | MIXERCONTROL_CT.SC_METER_POLLED | MIXERCONTROL_CT.UNITS_SIGNED,
    PEAKMETER      = SIGNEDMETER + 1,
    UNSIGNEDMETER  = MIXERCONTROL_CT.CLASS_METER | MIXERCONTROL_CT.SC_METER_POLLED | MIXERCONTROL_CT.UNITS_UNSIGNED,
    BOOLEAN        = MIXERCONTROL_CT.CLASS_SWITCH | MIXERCONTROL_CT.SC_SWITCH_BOOLEAN | MIXERCONTROL_CT.UNITS_BOOLEAN,
    ONOFF          = BOOLEAN + 1,
    MUTE           = BOOLEAN + 2,
    MONO           = BOOLEAN + 3,
    LOUDNESS       = BOOLEAN + 4,
    STEREOENH      = BOOLEAN + 5,
    BASS_BOOST     = BOOLEAN + 0x00002277,
    BUTTON         = MIXERCONTROL_CT.CLASS_SWITCH | MIXERCONTROL_CT.SC_SWITCH_BUTTON | MIXERCONTROL_CT.UNITS_BOOLEAN,
    DECIBELS       = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_DECIBELS,
    SIGNED         = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_SIGNED,
    UNSIGNED       = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_UNSIGNED,
    PERCENT        = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_PERCENT,
    SLIDER         = MIXERCONTROL_CT.CLASS_SLIDER | MIXERCONTROL_CT.UNITS_SIGNED,
    PAN            = SLIDER + 1,
    QSOUNDPAN      = SLIDER + 2,
    FADER          = MIXERCONTROL_CT.CLASS_FADER | MIXERCONTROL_CT.UNITS_UNSIGNED,
    VOLUME         = FADER + 1,
    BASS           = FADER + 2,
    TREBLE         = FADER + 3,
    EQUALIZER      = FADER + 4,
    SINGLESELECT   = MIXERCONTROL_CT.CLASS_LIST | MIXERCONTROL_CT.SC_LIST_SINGLE | MIXERCONTROL_CT.UNITS_BOOLEAN,
    MUX            = SINGLESELECT + 1,
    MULTIPLESELECT = MIXERCONTROL_CT.CLASS_LIST | MIXERCONTROL_CT.SC_LIST_MULTIPLE | MIXERCONTROL_CT.UNITS_BOOLEAN,
    MIXER          = MULTIPLESELECT + 1,
    MICROTIME      = MIXERCONTROL_CT.CLASS_TIME | MIXERCONTROL_CT.SC_TIME_MICROSECS | MIXERCONTROL_CT.UNITS_UNSIGNED,
    MILLITIME      = MIXERCONTROL_CT.CLASS_TIME | MIXERCONTROL_CT.SC_TIME_MILLISECS | MIXERCONTROL_CT.UNITS_UNSIGNED
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct MIXERLINE{
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
    public struct TargetInfo{
        public uint   dwType;
        public uint   dwDeviceID;
        public ushort wMid;
        public ushort wPid;
        public uint   vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAXPNAMELEN)]
        public string szPname;
    }

    public uint            cbStruct;
    public uint            dwDestination;
    public uint            dwSource;
    public uint            dwLineID;
    public MIXERLINE_LINEF fdwLine;
    public uint            dwUser;
    public uint            dwComponentType;
    public uint            cChannels;
    public uint            cConnection;
    public uint            cControls;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_SHORT_NAME_CHARS)]
    public string          szShortName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_LONG_NAME_CHARS)]
    public string          szName;
    public TargetInfo      Target;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct MIXERCONTROL{
    [StructLayout(LayoutKind.Explicit)]
    public struct BoundsInfo{
        [FieldOffset(0)]
        public int    lMinimum;
        [FieldOffset(4)]
        public int    lMaximum;
        [FieldOffset(0)]
        public uint   dwMinimum;
        [FieldOffset(4)]
        public uint   dwMaximum;
        [FieldOffset(8), MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
        public uint[] dwReserved;
    }
    [StructLayout(LayoutKind.Explicit)]
    public struct MetricsInfo{
        [FieldOffset(0)]
        public uint   cSteps;
        [FieldOffset(0)]
        public uint   cbCustomData;
        [FieldOffset(4), MarshalAs(UnmanagedType.ByValArray, SizeConst=5)]
        public uint[] dwReserved;
    }

    public uint                     cbStruct;
    public uint                     dwControlID;
    public MIXERCONTROL_CONTROLTYPE dwControlType;
    public uint                     fdwControl;
    public uint                     cMultipleItems;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_SHORT_NAME_CHARS)]
    public string                   szShortName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_LONG_NAME_CHARS)]
    public string                   szName;
    public BoundsInfo               Bounds;
    public MetricsInfo              Metrics;
}
[StructLayout(LayoutKind.Explicit)]
struct MIXERLINECONTROLS{
    [FieldOffset(0)]
    public uint   cbStruct;
    [FieldOffset(4)]
    public uint   dwLineID;
    [FieldOffset(8)]
    public uint   dwControlID;
    [FieldOffset(8)] // not a typo!  overlaps previous field
    public uint   dwControlType;
    [FieldOffset(12)]
    public uint   cControls;
    [FieldOffset(16)]
    public uint   cbmxctrl;
    [FieldOffset(20)]
    public IntPtr pamxctrl;
}
[StructLayout(LayoutKind.Explicit)]
struct MIXERCONTROLDETAILS{
    [FieldOffset(0)]
    public uint   cbStruct;
    [FieldOffset(4)]
    public uint   dwControlID;
    [FieldOffset(8)]
    public uint   cChannels;
    [FieldOffset(12)]
    public IntPtr hwndOwner;
    [FieldOffset(12)] // not a typo!
    public uint   cMultipleItems;
    [FieldOffset(16)]
    public uint   cbDetails;
    [FieldOffset(20)]
    public IntPtr paDetails;
}
[StructLayout(LayoutKind.Sequential)]
struct VOLUME{
    public int left;
    public int right;
}
struct MixerInfo{
    public uint volumeCtl;
    public uint muteCtl;
    public int  minVolume;
    public int  maxVolume;
}

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerGetLineInfo      (IntPtr hmxobj, ref MIXERLINE pmxl, MIXER flags);

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerGetLineControls  (IntPtr hmxobj, ref MIXERLINECONTROLS pmxlc, MIXER flags);

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerGetControlDetails(IntPtr hmxobj, ref MIXERCONTROLDETAILS pmxcd, MIXER flags);

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerSetControlDetails(IntPtr hmxobj, ref MIXERCONTROLDETAILS pmxcd, MIXER flags);

static MixerInfo GetMixerControls(){
    MIXERLINE         mxl = new MIXERLINE();
    MIXERLINECONTROLS mlc = new MIXERLINECONTROLS();
    mxl.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERLINE));
    mlc.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERLINECONTROLS));

    mixerGetLineInfo(IntPtr.Zero, ref mxl, MIXER.OBJECTF_MIXER | MIXER.GETLINEINFOF_DESTINATION);

    mlc.dwLineID  = mxl.dwLineID;
    mlc.cControls = mxl.cControls;
    mlc.cbmxctrl  = (uint)Marshal.SizeOf(typeof(MIXERCONTROL));
    mlc.pamxctrl  = Marshal.AllocHGlobal((int)(mlc.cbmxctrl * mlc.cControls));

    mixerGetLineControls(IntPtr.Zero, ref mlc, MIXER.OBJECTF_MIXER | MIXER.GETLINECONTROLSF_ALL);

    MixerInfo rtn = new MixerInfo();

    for(int i = 0; i < mlc.cControls; i++){
        MIXERCONTROL mxc = (MIXERCONTROL)Marshal.PtrToStructure((IntPtr)((int)mlc.pamxctrl + (int)mlc.cbmxctrl * i), typeof(MIXERCONTROL));
        switch(mxc.dwControlType){
        case MIXERCONTROL_CONTROLTYPE.VOLUME:
            rtn.volumeCtl = mxc.dwControlID;
            rtn.minVolume = mxc.Bounds.lMinimum;
            rtn.maxVolume = mxc.Bounds.lMaximum;
            break;
        case MIXERCONTROL_CONTROLTYPE.MUTE:
            rtn.muteCtl = mxc.dwControlID;
            break;
        }
    }

    Marshal.FreeHGlobal(mlc.pamxctrl);

    return rtn;
}
static VOLUME GetVolume(MixerInfo mi){
    MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
    mcd.cbStruct       = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
    mcd.dwControlID    = mi.volumeCtl;
    mcd.cMultipleItems = 0;
    mcd.cChannels      = 2;
    mcd.cbDetails      = (uint)Marshal.SizeOf(typeof(int));
    mcd.paDetails      = Marshal.AllocHGlobal((int)mcd.cbDetails);

    mixerGetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

    VOLUME rtn = (VOLUME)Marshal.PtrToStructure(mcd.paDetails, typeof(VOLUME));

    Marshal.FreeHGlobal(mcd.paDetails);

    return rtn;
}
static bool IsMuted(MixerInfo mi){
    MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
    mcd.cbStruct       = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
    mcd.dwControlID    = mi.muteCtl;
    mcd.cMultipleItems = 0;
    mcd.cChannels      = 1;
    mcd.cbDetails      = 4;
    mcd.paDetails      = Marshal.AllocHGlobal((int)mcd.cbDetails);

    mixerGetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

    int rtn = Marshal.ReadInt32(mcd.paDetails);

    Marshal.FreeHGlobal(mcd.paDetails);

    return rtn != 0;
}
static void AdjustVolume(MixerInfo mi, int delta){
    VOLUME volume = GetVolume(mi);

    if(delta > 0){
        volume.left  = Math.Min(mi.maxVolume, volume.left  + delta);
        volume.right = Math.Min(mi.maxVolume, volume.right + delta);
    }else{
        volume.left  = Math.Max(mi.minVolume, volume.left  + delta);
        volume.right = Math.Max(mi.minVolume, volume.right + delta);
    }

    SetVolume(mi, volume);
}
static void SetVolume(MixerInfo mi, VOLUME volume){
    MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
    mcd.cbStruct       = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
    mcd.dwControlID    = mi.volumeCtl;
    mcd.cMultipleItems = 0;
    mcd.cChannels      = 2;
    mcd.cbDetails      = (uint)Marshal.SizeOf(typeof(int));
    mcd.paDetails      = Marshal.AllocHGlobal((int)mcd.cbDetails);

    Marshal.StructureToPtr(volume, mcd.paDetails, false);

    mixerSetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

    Marshal.FreeHGlobal(mcd.paDetails);
}
static void SetMute(MixerInfo mi, bool mute){
    MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
    mcd.cbStruct       = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
    mcd.dwControlID    = mi.muteCtl;
    mcd.cMultipleItems = 0;
    mcd.cChannels      = 1;
    mcd.cbDetails      = 4;
    mcd.paDetails      = Marshal.AllocHGlobal((int)mcd.cbDetails);

    Marshal.WriteInt32(mcd.paDetails, mute ? 1 : 0);

    mixerSetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

    Marshal.FreeHGlobal(mcd.paDetails);
}

这段代码又大又丑。它是一些 C++ 代码的翻译,而且必须定义所有 P/Invoke 的东西,所以代码要多得多。但我已经测试过了,它确实有效。要使用它,您只需要像这样的东西:

MixerInfo mi = GetMixerControls();
AdjustVolume(mi, 100);    // add 100 to the current volume

MixerInfo mi = GetMixerControls();
AdjustVolume(mi, (mi.maxVolume - mi.minVolume) / 10);    // increase the volume by 10% of total range

MixerInfo mi = GetMixerControls();
SetVolume(mi, mi.maxVolume);    // let's get this party crunk'd!

MixerInfo mi = GetMixerControls();
SetMute(mi, true);    // shhhh!!!!!!

警告

由于使用了固定大小的整数和字段偏移量,这在 64 位 Windows 上可能会失败。我不知道,我没有测试过,也没有足够注意知道这些字段大小是否扩展到 64 位。 警告代码

编辑

为了简单起见(相对而言),我省略了任何错误处理。您真的应该检查所有 mixerXXX 函数的返回码,但我将把它留给读者作为练习(读作:我太懒了,没法这样做)。

关于c# - 更改主音量级别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/294292/

有关c# - 更改主音量级别的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby-on-rails - Ruby on Rails 迁移,将表更改为 MyISAM - 2

    如何正确创建Rails迁移,以便将表更改为MySQL中的MyISAM?目前是InnoDB。运行原始执行语句会更改表,但它不会更新db/schema.rb,因此当在测试环境中重新创建表时,它会返回到InnoDB并且我的全文搜索失败。我如何着手更改/添加迁移,以便将现有表修改为MyISAM并更新schema.rb,以便我的数据库和相应的测试数据库得到相应更新? 最佳答案 我没有找到执行此操作的好方法。您可以像有人建议的那样更改您的schema.rb,然后运行:rakedb:schema:load,但是,这将覆盖您的数据。我的做法是(假设

  3. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  4. ruby - Capistrano 3 在任务中更改 ssh_options - 2

    我尝试使用不同的ssh_options在同一阶段运行capistranov.3任务。我的production.rb说:set:stage,:productionset:user,'deploy'set:ssh_options,{user:'deploy'}通过此配置,capistrano与用户deploy连接,这对于其余的任务是正确的。但是我需要将它连接到服务器中配置良好的an_other_user以完成一项特定任务。然后我的食谱说:...taskswithoriginaluser...task:my_task_with_an_other_userdoset:user,'an_othe

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. ruby - 更改 ActiveRecord 中对象的类 - 2

    假设我有一个FireNinja我的数据库中的对象,使用单表继承存储。后来才知道他真的是WaterNinja.将他更改为不同的子类的最干净的方法是什么?更好的是,我很想创建一个新的WaterNinja对象并替换旧的FireNinja在数据库中,保留ID。编辑我知道如何创建新的WaterNinja来self现有FireNinja的对象,我也知道我可以删除旧的并保存新的。我想做的是改变现有项目的类别。我是通过创建一个新对象并执行一些ActiveRecord魔法来替换行,还是通过对对象本身做一些疯狂的事情,或者甚至通过删除它并使用相同的ID重新插入来做到这一点,这是问题的一部分。

  7. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  8. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

随机推荐